diff options
-rw-r--r-- | C-IF | 204 | ||||
-rw-r--r-- | ChangeLog | 713 | ||||
-rw-r--r-- | FAQ | 65 | ||||
-rw-r--r-- | MANIFEST | 84 | ||||
-rw-r--r-- | Makefile.in | 100 | ||||
-rw-r--r-- | README | 182 | ||||
-rw-r--r-- | README.EXT | 308 | ||||
-rw-r--r-- | ToDo | 5 | ||||
-rw-r--r-- | array.c | 374 | ||||
-rw-r--r-- | assoc.c | 150 | ||||
-rw-r--r-- | bignum.c | 147 | ||||
-rw-r--r-- | class.c | 76 | ||||
-rw-r--r-- | compar.c | 29 | ||||
-rw-r--r-- | configure.in | 176 | ||||
-rw-r--r-- | defines.h | 16 | ||||
-rw-r--r-- | dir.c | 85 | ||||
-rw-r--r-- | dln.c | 539 | ||||
-rw-r--r-- | dln.h | 12 | ||||
-rw-r--r-- | dmyext.c | 4 | ||||
-rw-r--r-- | enum.c | 118 | ||||
-rw-r--r-- | env.h | 22 | ||||
-rw-r--r-- | error.c | 41 | ||||
-rw-r--r-- | eval.c | 1537 | ||||
-rw-r--r-- | ext/MANIFEST | 3 | ||||
-rw-r--r-- | ext/Setup | 7 | ||||
-rw-r--r-- | ext/dbm/MANIFEST | 5 | ||||
-rw-r--r-- | ext/dbm/dbm.c | 435 | ||||
-rw-r--r-- | ext/dbm/dbm.doc | 107 | ||||
-rw-r--r-- | ext/dbm/depend | 1 | ||||
-rw-r--r-- | ext/dbm/extconf.rb | 4 | ||||
-rw-r--r-- | ext/etc/MANIFEST | 5 | ||||
-rw-r--r-- | ext/etc/depend | 1 | ||||
-rw-r--r-- | ext/etc/etc.c (renamed from etc.c) | 143 | ||||
-rw-r--r-- | ext/etc/etc.doc | 73 | ||||
-rw-r--r-- | ext/etc/extconf.rb | 7 | ||||
-rw-r--r-- | ext/extmk.rb.in | 398 | ||||
-rw-r--r-- | ext/marshal/MANIFEST | 4 | ||||
-rw-r--r-- | ext/marshal/depend | 2 | ||||
-rw-r--r-- | ext/marshal/marshal.c | 565 | ||||
-rw-r--r-- | ext/marshal/marshal.doc | 45 | ||||
-rw-r--r-- | ext/socket/MANIFEST | 5 | ||||
-rw-r--r-- | ext/socket/depend | 1 | ||||
-rw-r--r-- | ext/socket/extconf.rb | 6 | ||||
-rw-r--r-- | ext/socket/socket.c | 785 | ||||
-rw-r--r-- | ext/socket/socket.doc | 227 | ||||
-rw-r--r-- | ext/tkutil/MANIFEST | 3 | ||||
-rw-r--r-- | ext/tkutil/extconf.rb | 11 | ||||
-rw-r--r-- | ext/tkutil/tkutil.c | 54 | ||||
-rw-r--r-- | file.c | 782 | ||||
-rw-r--r-- | gc.c | 161 | ||||
-rw-r--r-- | glob.c | 9 | ||||
-rw-r--r-- | hash.c | 283 | ||||
-rw-r--r-- | ident.h | 26 | ||||
-rw-r--r-- | inits.c | 9 | ||||
-rw-r--r-- | io.c | 544 | ||||
-rw-r--r-- | io.h | 11 | ||||
-rw-r--r-- | lib/base64.rb | 55 | ||||
-rw-r--r-- | lib/find.rb | 38 | ||||
-rw-r--r-- | lib/getopts.rb (renamed from sample/getopts.rb) | 0 | ||||
-rw-r--r-- | lib/mailread.rb | 45 | ||||
-rw-r--r-- | lib/parsearg.rb (renamed from sample/parsearg.rb) | 0 | ||||
-rw-r--r-- | lib/parsedate.rb | 42 | ||||
-rw-r--r-- | lib/tk.rb | 1215 | ||||
-rw-r--r-- | lib/tkcanvas.rb | 318 | ||||
-rw-r--r-- | lib/tkclass.rb | 36 | ||||
-rw-r--r-- | lib/tkentry.rb | 74 | ||||
-rw-r--r-- | lib/tktext.rb | 160 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | math.c | 46 | ||||
-rw-r--r-- | missing/MANIFEST | 10 | ||||
-rw-r--r-- | missing/crypt.c | 276 | ||||
-rw-r--r-- | missing/dup2.c | 36 | ||||
-rw-r--r-- | missing/nt.c | 1732 | ||||
-rw-r--r-- | missing/nt.h | 246 | ||||
-rw-r--r-- | missing/setenv.c | 73 | ||||
-rw-r--r-- | node.h | 28 | ||||
-rw-r--r-- | numeric.c | 367 | ||||
-rw-r--r-- | object.c | 314 | ||||
-rw-r--r-- | pack.c | 20 | ||||
-rw-r--r-- | parse.y | 963 | ||||
-rw-r--r-- | process.c | 184 | ||||
-rw-r--r-- | random.c | 11 | ||||
-rw-r--r-- | range.c | 80 | ||||
-rw-r--r-- | re.c | 387 | ||||
-rw-r--r-- | re.h | 10 | ||||
-rw-r--r-- | regex.c | 18 | ||||
-rw-r--r-- | regex.h | 1 | ||||
-rw-r--r-- | ruby.1 | 248 | ||||
-rw-r--r-- | ruby.c | 107 | ||||
-rw-r--r-- | ruby.h | 140 | ||||
-rw-r--r-- | ruby.texi | 5044 | ||||
-rw-r--r-- | sample/MANIFEST | 63 | ||||
-rw-r--r-- | sample/aset.rb | 7 | ||||
-rw-r--r-- | sample/attr.rb | 14 | ||||
-rw-r--r-- | sample/blk.rb | 9 | ||||
-rw-r--r-- | sample/case.rb | 14 | ||||
-rw-r--r-- | sample/cat.rb | 5 | ||||
-rw-r--r-- | sample/cat2.rb | 5 | ||||
-rw-r--r-- | sample/clnt.rb | 3 | ||||
-rw-r--r-- | sample/clone.rb | 18 | ||||
-rw-r--r-- | sample/const.rb | 24 | ||||
-rw-r--r-- | sample/dbm.rb | 2 | ||||
-rw-r--r-- | sample/exyacc.rb | 20 | ||||
-rwxr-xr-x | sample/from.rb | 102 | ||||
-rw-r--r-- | sample/gctest.rb | 67 | ||||
-rw-r--r-- | sample/gctest2.rb | 71 | ||||
-rw-r--r-- | sample/hash.rb | 11 | ||||
-rw-r--r-- | sample/list3.rb | 6 | ||||
-rw-r--r-- | sample/marshal.rb | 13 | ||||
-rw-r--r-- | sample/math.rb | 4 | ||||
-rw-r--r-- | sample/mkproto.rb | 27 | ||||
-rw-r--r-- | sample/occur2.rb | 2 | ||||
-rw-r--r-- | sample/opt_s.rb | 12 | ||||
-rw-r--r-- | sample/opt_x.test | 10 | ||||
-rw-r--r-- | sample/rcs.rb | 2 | ||||
-rw-r--r-- | sample/ruby-mode.el | 305 | ||||
-rw-r--r-- | sample/samp.rb | 15 | ||||
-rw-r--r-- | sample/sieve.rb | 2 | ||||
-rw-r--r-- | sample/split.rb | 13 | ||||
-rw-r--r-- | sample/struct.rb | 8 | ||||
-rw-r--r-- | sample/svr.rb | 2 | ||||
-rw-r--r-- | sample/system.rb | 2 | ||||
-rw-r--r-- | sample/t1.rb | 20 | ||||
-rw-r--r-- | sample/t2.rb | 23 | ||||
-rw-r--r-- | sample/test.rb | 1043 | ||||
-rw-r--r-- | sample/tkbiff.rb | 121 | ||||
-rw-r--r-- | sample/tkbrowse.rb | 69 | ||||
-rw-r--r-- | sample/tkdialog.rb | 62 | ||||
-rw-r--r-- | sample/tkfrom.rb | 115 | ||||
-rw-r--r-- | sample/tkhello.rb | 13 | ||||
-rw-r--r-- | sample/tkline.rb | 29 | ||||
-rw-r--r-- | sample/tktimer.rb | 49 | ||||
-rw-r--r-- | sample/trap.pl | 6 | ||||
-rw-r--r-- | sample/trap.rb | 3 | ||||
-rw-r--r-- | sample/tt.rb | 100 | ||||
-rw-r--r-- | sig.h | 28 | ||||
-rw-r--r-- | signal.c | 200 | ||||
-rw-r--r-- | spec | 3538 | ||||
-rw-r--r-- | sprintf.c | 49 | ||||
-rw-r--r-- | st.c | 91 | ||||
-rw-r--r-- | st.h | 2 | ||||
-rw-r--r-- | string.c | 708 | ||||
-rw-r--r-- | struct.c | 399 | ||||
-rw-r--r-- | time.c | 239 | ||||
-rw-r--r-- | util.c | 1 | ||||
-rw-r--r-- | variable.c | 553 | ||||
-rw-r--r-- | version.c | 7 | ||||
-rw-r--r-- | version.h | 4 |
148 files changed, 21293 insertions, 9071 deletions
@@ -1,204 +0,0 @@ -.\" C-IF - -*- Text -*- created at: Tue Oct 12 14:15:00 JST 1993 - -Ruby-C インターフェース - -型 - - VALUE - - Rubyオブジェクトを表現する型. 必要に応じてキャストして用いる. 組み - 込み型を表現するCの型はruby.hに記述してあるRで始まる構造体である. - VALUE型をこれらにキャストするためにRで始まる構造体名を全て大文字に - した名前のマクロが用意されている. - -変数・定数 - - Qnil - - 定数: nilオブジェクト - - Qself - - 変数: 現在のselfオブジェクトの値. 一般にメソッドにはselfを指す引数 - が与えられるので, この変数にアクセスする必要はない. この変数の値を - 変更する時は以後のselfの値そのものが変わってしまうので, 特別な事情 - がない限り代入してはならない. - - TRUE - - 定数: tオブジェクト(真のデフォルト値) - - FALSE - - 定数: nilオブジェクト - -クラス・モジュール定義 - - VALUE rb_define_class(char *name, VALUE super) - - superのサブクラスとして新しいRubyクラスを定義する. - - VALUE rb_define_module(char *name) - - 新しいRubyモジュールを定義する. - - void rb_include_module(VALUE class, VALUE module) - - モジュールをインクルードする. classがすでにmoduleをインクルードして - いる時には何もしない(多重インクルードの禁止). - -大域変数 - - void rb_define_variable(char *name, VALUE *var, - VALUE (*get_hook), VALUE (*set_hook)(), void *data) - - RubyとCとで共有するグローバル変数を定義する. get_hookがQnilでない時, - 変数参照の際にget_hookにセットされた関数が呼ばれる. set_hookがQnil - でない時には代入の時にset_hookが呼ばれる. hook関数には変数名を示す - ID,(set hookの場合新しい値も)とともにdataで与えたデータが引数とし - て渡される. - - 変数名が`$'で始まらない時には自動的に追加される. 変数名としてrubyの - 識別子として許されない文字(例えば` ')を含む場合にはrubyプログラムか - らアクセスできなくなる. - - void rb_global_variable(VALUE *var) - - GCのため,Rubyプログラムからはアクセスされないが, Rubyオブジェクト - を含む大域変数をマークする. - - void rb_read_only_hook() - - 読み出し専用の変数のためのset_hook関数. 値を設定しようとすると例外 - を発生させる. - -クラス定数 - - void rb_define_const(VALUE class, char *name, VALUE val) - - クラス定数を定義する. - -メソッド定義 - - rb_define_method(VALUE class, char *name, VALUE (*func)(), int argc) - - メソッドを定義する. argcはselfを除く引数の数. argcが-1の時, 関数に - は引数の数(selfを含まない)を第1引数, 引数の配列を第2引数とする形式 - で与えられる(第3引数はself). argcが-2の時, 引数はself, args(argsは - 引数を含むrubyの配列)という形式で与えられる. - - rb_define_single_method(VALUE class, char *name, VALUE (*func)(), int argc) - - 特異メソッドを定義する. 引数はrb_define_method()と同じ. - - rb_scan_args(int atgc, VALUE *argv, char *fmt, ...) - - argc,argv形式で与えられた引数を分解する. fmtは必須引数の数, 付加引 - 数の数, 残りの引数があるかを指定する文字列で, "数字数字*"という形式 - である. 2 番目の数字と"*"はそれぞれ省略可能である. 必須引数が一つ - もない場合は0を指定する.第3引数以降は変数へのポインタで, 該当する - 要素がその変数に格納される. 付加引数に対応する引数が与えられていな - い場合は変数にQnilが代入される. - -Rubyメソッド呼び出し - - VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) - - メソッド呼び出し. 文字列からmidを得るためにはrb_intern()を使う. - - VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv) - - メソッド呼び出し. 引数をargc,argv形式で渡す. - - VALUE rb_eval_string(char *str) - - 文字列をrubyとスクリプトしてコンパイル・実行する. - - ID rb_intern(char *name) - - 文字列に対応するIDを返す. - - char *rb_id2name(ID id) - - IDに対応する文字列を返す(デバッグ用). - - char *rb_class2name(VALUE class) - - classの名前を返す(デバッグ用). classが名前を持たない時には, 一番近 - い名前を持つクラスの名前を返す. - -インスタンス変数 - - VALUE rb_iv_get(VALUE obj, char *name) - - objのインスタンス変数の値を得る. `@'で始まらないインスタンス変数は - Rubyプログラムからアクセスできない. - - VALUE rb_iv_set(VALUE obj, char *name, VALUE val) - - objのインスタンス変数をvalにセットする. - -制御構造 - - VALUE rb_iterate(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2) - - func2をブロックとして設定し, func1をイテレータとして呼ぶ. func1に - は arg1が引数として渡され, func2には第1引数にイテレータから与えられ - た値, 第2引数にarg2が渡される. - - VALUE rb_yield(VALUE val) - - valを値としてイテレータブロックを呼び出す. - - VALUE rb_resque(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2) - - 関数func1をarg1を引数に呼び出す. func1の実行中に例外が発生した時に - は func2をarg2を引数として呼ぶ. 戻り値は例外が発生しなかった時は - func1の戻り値, 例外が発生した時にはfunc2の戻り値である. - - VALUE rb_ensure(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2) - - 関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発生して - も) func2をarg2を引数として実行する. 戻り値はfunc1の戻り値である(例 - 外が発生した時は戻らない). - -例外・エラー - - void Warning(char *fmt, ...) - - 標準エラー出力に警告情報を表示する. 引数はprintf()と同じ. - - void Fail(char *fmt, ...) - - 例外を発生させる. 引数はprintf()と同じ. - - void Fatal(char *fmt, ...) - - 致命的例外を発生させる. 通常の例外処理は行なわれず, インタープリタ - が終了する(ただしensureで指定されたコードは終了前に実行される). - - void Bug(char *fmt, ...) - - インタープリタなどプログラムのバグでしか発生するはずのない状況の時 - 呼ぶ. インタープリタはコアダンプし直ちに終了する. 例外処理は一切行 - なわれない. - -rubyの初期化・実行 - - void ruby_init(int argc, char **argv, char **envp) - - rubyインタプリタの初期化を行なう. - - void ruby_run() - - rubyインタプリタを実行する. - - void ruby_script(char *name) - - rubyのスクリプト名($0)を設定する. - -/* - * Local variables: - * fill-column: 70 - * end: - */ @@ -1,3 +1,709 @@ +Thu Dec 21 00:56:57 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.95 - fj.sourcesに + + * eval.c (rb_eval): rescueのロジックをrb_rescue()に一元化. + +Wed Dec 20 19:30:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Makefile.in: 不要なコンパイルの回避(より完全に). + + * class.c (singleton_class_new): `single'->`singleton' + +Tue Dec 19 07:14:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (closed?): IOがcloseされているかどうかを知る述語. + + * parse.y (primary): 特異メソッドの引数のlex_stateが不適切. + + * lib/tk.rb: tcl->rubyの変換関数の用意. + + * ext/extmk.rb.in (install): installの2重コンパイルの回避. + + * array.c (range_beg_len): range指定の不適切なエラーを訂正. + + * string.c (str_aref): range指定のバグを削除. + + * lib/tk.rb (tk_split_list): Tclのリストに対応. + +Mon Dec 18 09:58:12 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.94 + + * dln.c (dln_load): HP対応(未確認) + + * eval.c (Init_Proc): BlockをProcに改名. + +Sat Dec 16 13:46:14 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): retryでイテレータの再実行ができるように. + +Fri Dec 15 17:14:30 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c: proc:lambdaの親しみやすい別名 + +Thu Dec 14 17:21:55 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (dyna_var_asgn): イテレータブロック内で最初に初期化された + ローカル変数の有効範囲をそのブロック内に限定.これでlambdaと呼べ + ないことはない. + +Wed Dec 13 02:30:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c: autoloadのマークミス. + + * lib/tk.rb: wishからの複数行の戻り値に対応 + + * lib/tkcomposite.rb: 複合widget + + * variable.c (rb_class2path): ICLASSに対応してなかった. + + * eval.c (ruby_run): exit(0)のバグ + +Sat Dec 9 01:21:24 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c (dumps|load): 文字列に対する入出力を可能に + した(ただし実はファイル経由なのだ). + +Fri Dec 8 18:29:11 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c: シンボルを一度だけ初期化する. + +Thu Dec 7 07:58:50 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): 第1引数の正規表現の認識にエラーがあった.同時に + 状態数を減らした. + + * string.c (str_sub): 置換でスキップ幅が大きすぎた. + +Wed Dec 6 15:14:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub_method): sub/gsub(!なし)は置換が行なわれなかっ + た時,置換前の文字列を返す. + +Tue Dec 5 00:55:15 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): 括弧を省略した時の引数展開の`*'に対応. + + * eval.c (ruby_run): EXITハンドラ内での例外に対応. + + * bignum.c (big_cmp): BignumとFixnumの比較で落ちる. + +Mon Dec 4 14:21:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (call_op): コンパイル時の定数式の展開をやめた.労多くし + て益少ないと判断したので. + +Thu Nov 30 01:35:15 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tk.rb: {Radio,Check}Buttonのvariableの実装. + + * eval.c (rb_yield_0): Block.callがネストした時のバグ. + + * io.c (f_select): 常に配列3つをふくむ配列を返すように + + * lib/tk.rb: fileeventをruby側で実装. + +Wed Nov 29 17:53:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_ivar_get): selfを常に指定するように. + +Tue Nov 14 00:07:29 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tk.rb: Tk4.0対応 + +Mon Nov 13 16:23:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.93 + +Thu Nov 9 23:26:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (gc_mark): モジュールのMixinのマーク忘れ. + + * parse.y (f_arglist): メソッド定義の引数を括弧で括らなくても良い + ようにした. + +Wed Nov 8 00:17:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_yield_0): 未初期化のローカル変数があった. + + * eval.c (rb_eval): pendig signalのチェックをeval実行後に行うよう + にした.でないとシグナルの発生と検出が遠く離れてしまう事がある. + + * parse.y: class文のsuperclass部を定数から式に拡張した. + + * lib/tk.rb: Tkのほぼ全ウィンドウクラスに対応.キャンバスとテキス + ト上のオブジェクトが残っている. + +Tue Nov 7 08:18:37 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (trap): ブロックを指定できるように. + +Mon Nov 6 16:44:00 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (f_caller): 呼出元の情報を得る. + + * ext/tkutil/tkutil.c: wishのstderr出力を監視することで,エラー処 + 理を行う. + + * ext/tkutil/tkutil.c: wishとの通信部をCで記述. + +Sat Nov 4 01:12:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-calculate-indent): インデントの計算を + もう少しスマートにした(正規表現のチェック,継続行のチェック). + + * eval.c (rb_call): 無限再帰を避けるため,関数のネストレベルの制限 + を行なう. + + * lib/tk.rb: Tkインターフェース.まだ不完全だが. + + * eval.c (rb_yield_0): 空のBlockのバグ. + + * sample/ruby-mode.el (ruby-calculate-indent): 行末の演算子による + 行継続に対応. + +Fri Nov 3 12:56:21 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_call): 本体が空の関数の実行にバグ. + + * parse.y (var_extend): 文字列の末尾の変数展開のバグ. + + * variable.c (rb_gvar_set): traceの評価時にに変数値を与えるように. + + * eval.c (f_require): ruby scriptのrequireにbug. + + * variable.c (rb_const_get): モジュールのinclude対策. + +Thu Oct 19 13:56:06 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * dln.c (dln_load): HP対応でのtypo. + +Wed Oct 18 17:39:39 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.92 + + * object.c (krn_type): オブジェクトの動的な型を返すメソッド. + +Tue Oct 17 00:48:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (proc_options): -X オプション.chdirだけを行う. + + * re.c (reg_search): 漢字コードを途中で変更できるように.コンパイ + ル時のコードが変更された時にはマッチの直前に正規表現の再コンパイ + ルを行う.定数KCODEから変数$KCODEへ. + + * parse.y: ()のなかにcompexprを許す. + + * re.c (reg_search): メモリリークを直した. + +Fri Oct 13 13:19:19 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub): 文字列置換にバグ. + + * string.c (str_strip_bang): 文字列の後ろの長さの調整が行われてい + なかった. + + * re.c (reg_search): $&, $1...のはローカルに束縛するようになった. + 呼び出したメソッドでのマッチは現スコープの$&などの値に影響しない. + マッチの情報をスコープ外で得たいときには$~を使って束縛情報を持ち + 出す必要がある. + +Thu Oct 12 00:33:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (reg_search): String:split, String:indexでは$&, $1...が変化 + しないようにした. + + * io.c (rb_str_setter): setterの仕様が変更になっていた. + + * variable.c (f_trace_var): 第2引数を省略してイテレータとして呼べ + るように. + +Wed Oct 11 11:50:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.91 + + * variable.c (var_setter): 引数が間違っていた.致命的バグ. + + * io.c (pipe_open): $stderrの値が変更されている時にはそちらを + 子プロセスのstderrに設定する. + +Mon Oct 9 13:06:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (mod_to_s): モジュール内のモジュールは`::'を使った表現 + で表示されるように. + + * variable.c (rb_gvar_set): 代入によるループが発生しないように, + trace内での代入ではtraceを評価しない. + + * struct.c (struct_equal): structのequal判定にクラスの一致を含めた. + +Sat Oct 7 00:18:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): defined?の機能を拡張(yieldのチェック,superの + 存在など). + +Fri Oct 6 12:06:47 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.90 + + * st.c (st_foreach): 要素を削除した時に要素数が変化していなかった. + + * hash.c (hash_values): バグ修正.keysを返していた…. + + * parse.y (call_op): defined? の引数では定数の畳み込みを行わない + (チェックする前にコンパイルエラーになっては困る). + + * スコープ生成の一部見直し. + +Thu Oct 5 00:29:43 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * 関数とクラスの命名規則を変更した.関数名,変数名の全面書き換え. + + * gc.c (looks_pointerp): ヒープチェックの高速化. + + * struct.c (Fstruct_aset): 構造体に対する`[]='. + (struct_set): 構造体メンバに対する代入. + +Wed Oct 4 09:54:07 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.89 + + * eval.c (Frequire): ダイナミックロードのエラーチェックを厳しく. + + * struct.c: structの構造を完全に書き換えた.以前は順序付きの + id->valueの連想配列であったが,今度は構造体毎に新しいクラスを生 + 成するようにした. + + * parse.y: `::'の意味をAssocの生成からクラス(モジュール)内の定数ア + クセスへ変更. + + * assoc.c: なくす. + +Tue Oct 3 13:31:08 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (Ftrace_var): trace_var, 大域変数への書き込みhookを設 + 定する. + + * variable.c: global_entryの構成を書き換えた.これでtrace_varを実 + 装できる. + + * file.c (Ffile_stat): "&"で直前のfstatの結果も参照できるように. + +Fri Sep 29 14:15:13 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.88 + + * dln.c (dln_load): AIXとHPに対応したコードを入れた(動作は未確認). + + * ext/extmk.rb.in: 必要に応じて,定数EXTLIBを定義するように. + + * dln.c (dln_load): dln独立に書き換える.将来の拡張用. + (load_1): dln_a_outにおいてソースコードでライブラリを明示的にロー + ドする必要がないように変更した. + +Thu Sep 28 13:31:37 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el: もっとましなhilit19対応(正規表現). + +Wed Sep 27 04:12:44 1995 Takahasi Mamoru <taka@soum.co.jp> + + * sample/test.rb: echoで-nを使わないように(SysV対策). + + * ext/extmk.rb.in: sub -> sub! + +Tue Sep 26 19:12:42 1995 Yasuo OHBA <jammy@csg.mes.co.jp> + + * dln.c (dln_find_1): `.', `..'から始まるパスに対応した. + +Mon Sep 25 12:33:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.87 + +Sat Sep 23 10:00:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (Fmod_modfunc): メソッドをprivateにし,同時に特異メソッド + も定義するメソッド.パッケージ的使い方のモジュール用. + +Fri Sep 22 11:02:44 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/find.rb: findを提供するライブラリ + + * variable.c (rb_define_variable): hookの設定を分離. + (add_hook): 1変数に対して複数のhookを設定できるように. + +Thu Sep 21 00:22:11 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_frozen): 文字列が更新不可かどうかをチェックする述 + 語メソッド. + + * hash.c (Fhash_aset): keyが文字列の時,キーの内容が変化しないよう + に,dupしてfreezeする. + +Wed Sep 20 16:12:44 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.86 + + * ext/extmk.rb.in (have_header): キャッシュにバグ. + + * ext/extmk.rb.in (have_library): 引数の順序が変わった. + +Thu Sep 14 18:00:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (obj_is_instance_of): is_member_ofから名称変更. + +Wed Sep 13 15:44:35 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_tr_bang): 範囲外の文字に対する変換バグ. + +Tue Sep 12 14:27:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (Sfile_expand_path): expand_file_name -> expand_pathに名 + 称変更. + + * enum.c (Fenum_member): includes? -> member? に名称変更. + + * string.c (Fstr_each_byte): StringはByteArrayであるという基本に戻っ + て,eachの定義をeach_byteに変更した.今までのeachはeach_lineでア + クセスできる. + +Mon Sep 11 18:31:17 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (cache_stat): ファイル名として"&"を指定すると直前の + stat(2)の結果を再利用するように. + +Fri Sep 8 14:18:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.texi: `!', `?'に対応してアップデート. + + * parse.y: defined -> defined? + + * file.c: FileOpの一文字メソッドをなくす.一文字テストはtestメソッ + ドにまかせる. + + * parse.y (yylex): 変数名の後ろに`?'も許す.述語メソッドの後ろに + `?'を追加する. + +Thu Sep 7 20:01:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c: 文字列の中身を更新するメソッドの名前の終りに`!'を付加. + `!'の無いバージョンも用意した. + + * parse.y: 変数名の後ろに`!'を許す. + +Wed Sep 6 14:12:19 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.85 + + * string.c (Fstr_dup): 文字列の複製を作る + (Fstr_freeze): 文字列の更新不可属性を設定できるように. + (Fsub/Fgsub): $_の内容をdupしてから置換を行うように. + + * ruby.h (CLONESETUP): flagsの状態もコピー + +Tue Sep 5 01:27:50 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/test.rb: 失敗の検出を厳しく. + +Fri Aug 25 14:31:02 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (Ffork): イテレータとしても動作するように. + + * version 0.84 + + * signal.c (sig_beg): ハンドラが設定されている時には再設定しない. + + * ext/extmk.rb.in (create_makefile): shared objectのリンクの際に + `-l'オプションを指定するように. + + * signal.c (trap): `EXIT'で終了処理を行う設定が出来る. + +Wed Aug 16 00:13:22 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (sig_beg): デフォルトではbegin節の中でだけSIGINTを捕捉 + するように変更. + + * io.c (io_ctl): fcntlを持たないシステムにも対応. + + * 各ディレクトリに分散していたMANIFESTをまとめた.拡張モジュール毎 + には必要. + + * string.c (Sstr_new,str_sub,Fstr_crypt): 引数を自動的に文字列に変 + 換するように. + +Sat Aug 12 00:44:02 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_crypt): PD cryptを用意した. + +Fri Aug 11 14:37:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * assoc.c (Fassoc_clone): assocもcloneできるように. + + * io.c: マクロREAD_DATA_PENDINGの定義を変更(Linux対応) + + * io.c (io_fptr_finalize): ftprの開放時の処理を指定できるように. + +Wed Aug 9 16:52:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_provided): 複数のfeatureをロードすると無限ループに落 + ちるという単純な(しかし凶悪な)ミス. + + * ext/extmk.rb.in (install): dlopen対応を行った.今までdlnにしか十 + 分に対応していなかった. + +Tue Aug 8 14:17:06 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.83 + +Mon Aug 7 12:47:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y: resque -> rescue.恥ずかしいがtypoを残しておくわけには + いかないよなあ.なんで今まで気がつかなかったのか…. + +Thu Aug 3 18:18:05 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * missing/nt.c: NT移植用の関数群をまとめた. + + * variable.c (rb_const_get): また例外を発生するようにした.defined + がある以上例外を発生させない理由がないので(例外が発生した方がタ + イプミスの検出などの点で有利). + + * variable.c (Fautoload): autoloadを実装.今度は使えるか. + +Mon Jul 31 15:44:21 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (arg_ambiguous): 第1引数のあいまいさを警告(-vオプション + で有効). + + * eval.c (rb_eval): `-v'オプションをつけて`def'が呼ばれると不必要 + なエラーメッセージが出た. + + * parse.y (yylex): メソッドの第1引数の判定をもうちょっと賢くした. + +Fri Jul 28 19:04:43 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): `+/-/['の直前に空白が来るかどうかで動作を変更し + た(混乱のもとか?) + +Wed Jul 26 09:21:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.82a + + * sprintf.c (Fsprintf): `%s'で'\0'を含む文字列に対応. + + * pack.c (Fpck_pack): packの要素確保のバグ. + + * eval.c (Floop): 無限ループのイテレータ. + + * io.c (next_argv): 存在しないファイル名が指定された時のエラー処理 + が行われていなかった. + +Mon Jul 24 17:37:34 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.82 + + * ext/extmk.rb.in (install): 拡張モジュールをstatic linkする場合は + そのモジュールが既にrequireされたのと同じようにfeatureを設定する. + これで拡張モジュールの機能が必要な時には(static linkされているか + どうかにかかわらず)requireすればよくなる. + + * eval.c (Frequire): `$"'に格納する文字列をフルパスでなくフィーチャ + 名とする.rubyスクリプトをロードした時には`.rb',オブジェクトを + ロードした時には`.o'をフィーチャ名に付加する.lispのrequireと + provideの働きに(少し)近い. + +Thu Jul 20 12:50:05 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Makefile.in (test): make testができるように. + + * struct.c (struct_new): typo. + + * eval.c (rb_eval): `defined'を追加.メソッド/変数/定数の定義状態 + を知る事が出来る. + +Wed Jul 19 18:04:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.81 + +Mon Jul 17 14:53:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_const_get): 未初期化のCONSTANTの値をnilにした.し + かし,今後また例外に戻す可能性はある.要はoptionalなクラス/モジュー + ルが存在するかチェックしたいだけなんだな. + + * st.c (int): grow_factorを固定にした(大嶋さんのマシンに対応). + +Fri Jul 14 00:48:40 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/extmk.rb.in: キャッシュのバグを修正. + + * parse.y (var_extend): #{$数字}に対応した. + + * dln.c (dln_load_1): `Init_FILENAME'だけを有効に.`init_*'は今後 + 実行しない. + + * ext/etc/etc.c : Etcモジュールを拡張モジュールとして分離.実はNT + 対応への布石だったりするかもしれない. + +Tue Jul 11 17:12:48 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gcc -Wallで出たwarningを元にソースを変更. + + * signal.c (trap): typo. + +Fri Jul 7 10:08:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.80 + + * ruby.texi: texinfo documentを提供.specとruby.1は無くなった. + + * signal.c (Ftrap): 割込み禁止中の例外発生に対応. + + * eval.c (Flambda): Blockオブジェクトを返す.Block.newと同義. + +Thu Jul 6 00:35:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (Ftrap): SIG_DFLの処理を変更.SIGINTへのデフォルトハン + ドラを用意(例外を発生する). + + * file.c (Sfile_expand_fname): パス名を絶対パスに展開するメソッド. + (Sfile_basename): basenameを得るメソッド.拡張子も外せる. + (Sfile_dirname): basenameの反対. + + * eval.c (rb_call): argument評価中の例外発生に対応. + + * file.c (Ftest): `M', `A', `C'を追加. + +Tue Jul 4 12:36:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (Ftest): ファイルテスト用メソッド. + + * ruby.c (proc_options): `-r'オプションを追加. + + * parse.y (f_args): デフォルト引数を追加. + + * eval.c (rb_call): 該当する引数が無い時,rest引数の値をnilに. + + * numeric.c (num_equal): 数値以外との比較で例外が発生していた. + FALSEを返すように. + + * eval.c (masign): 多重代入のrest部の動作がおかしかった. + +Sat Jun 17 01:03:16 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (gettable): 未初期化のローカル変数の参照(独立した識別子) + は正式にメソッド呼び出しとした. + + * parse.y (read_escape): tokenbufを使わないように修正.それにとも + ない,`\C-x',`\M-x'などのエスケープ表現を復活.これでドキュメン + トと実際の処理系が一致した. + +Thu Jun 15 15:42:00 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (re_regcomp): cacheのチェックを改善. + +Mon Jun 12 18:50:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.79 + +Sat Jun 10 00:25:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (re_regcomp): cache判定に`$='の値も反映させた. + + * sample/test.rb: test suite作成. + +Fri Jun 9 15:58:34 1995 Yukihiro Matsumoto <matz@ix-02> + + * re.c (re_regcomp): cacheの判定が間違っていた. + +Fri Jun 9 00:01:35 1995 Yukihiro Matsumoto (matz@dyna) + + * eval.c (rb_yield): block構造体に初期化していないメンバ(iter)があっ + たのでイテレータのネストが正しく動作しなかった. + +Thu Jun 8 00:59:03 1995 Yukihiro Matsumoto (matz@dyna) + + * re.c (=~): String以外との比較がFALSEを返すように(例外を発生して + いた). + + * extmk.rb.in: 判定した値をファイルにキャッシュするようにした. + + * assoc.c (to_a): to_aメソッドが再定義されていなかった. + + * eval.c (rb_eval): 初期化されていないローカル変数へのアクセスを引 + 数の無いメソッド呼び出しと解釈する.ただし,(現状では)メソッドが + 定義されていない場合,エラーにせず変数未初期化のwaringを出して + nilを返している.「ruby -pe print」などが実行できるという意味で + はありがたいこの仕様は,しかし今後の検討が必要である.-- メソッ + ド呼び出しとするのを止めるか(以前の仕様),いつもメソッド呼び出し + とする(未定義ならばエラー)か,今の仕様で行くか. + + * eval.c (rb_eval): 初期化されていないローカル変数へのアクセスで + (evalなどで)初期化された事が分かった時には以後初期化されたローカ + ル変数とみなす. + +Wed Jun 7 11:58:12 1995 Yukihiro Matsumoto <matz@ix-02> + + * eval.c (rb_fail): 例外処理後も`$!'をクリアしないように. + (rb_fail): `$!'変数に最後に改行を追加しない. + + * io.c (Fprint): privateメソッドに変更.引数を取らない時の動作を変 + 更(`$_'を出力する). + (Fio_print): 出力先指定のprintメソッド. + (Fio_printf): 出力先指定のprintfメソッド. + + * parse.y: not演算子の追加.優先順位の低い`!'演算子. + +Mon Jun 5 19:00:55 1995 Yukihiro Matsumoto <matz@ix-02> + + * version 0.78 + +Fri Jun 2 17:52:03 1995 Yukihiro Matsumoto <matz@ix-02> + + * ruby.c (proc_options): -Iオプションで`$:'への追加される順番を修 + 正した. + +Fri Jun 2 00:36:34 1995 Yukihiro Matsumoto (matz@dyna) + + * parse.y: while修飾子の動作を通常のwhileと同じにした.ただし, + begin式へのwhile修飾子だけはdo..while型のループとなる. + +Wed May 31 18:36:30 1995 Yukihiro Matsumoto <matz@ix-02> + + * version 0.77 + +Mon May 29 18:39:37 1995 Yukihiro Matsumoto <matz@ix-02> + + * ext/extmk.rb.in (install): 拡張モジュールもインストールできるよ + うに. + +Fri May 26 14:43:01 1995 Yukihiro Matsumoto <matz@ix-02> + + * process.c (Fsystem): 戻り値をサブプロセスの失敗/成功を表す真偽値 + にした.終了ステータスは`$?'で得る. + +Tue May 23 10:58:11 1995 Yukihiro Matsumoto <matz@ix-02> + + * string.c (Fstr_upto): 無限ループに陥らないように. + + * parse.y (cond): `||'などの右辺に制御式が書けるように,条件式がか + ならずしも値を持たなくても良いようにした. + + * ext/marshal/marshal.c: オブジェクトの読み書きをメソッドの再定義 + でコントロールできるように.インスタンスが`_dump_to'というメソッ + ドを定義している時はそちらを使うように. + + * ext/extmk.rb.in: static linkも設定できるような仕様にした. + ext/Setupというファイルにディレクトリ名を記述するとそのディレク + トリに存在するモジュールはstatic linkされる(はず). + + * eval.c (rb_eval): `..'を文法に組み込み,`..'と`...'の動作をperl + に合わせた. + +Sat May 20 01:22:48 1995 Yukihiro Matsumoto (matz@dyna) + + * io.c (select): timeout時と割込み時の動作の明確化. + Fri May 19 15:33:23 1995 Yukihiro Matsumoto <matz@ix-02> * version 0.76 @@ -241,8 +947,7 @@ Wed Feb 22 00:51:38 1995 Yukihiro Matsumoto (matz@dyna) Tue Feb 21 18:56:56 1995 Yukihiro Matsumoto (matz@ix-02) - * io.c(STDIN, STDOUT, STDERR): 定数として定義.今までの$stdinなど - は将来なくなるかも知れない. + * io.c(STDIN, STDOUT, STDERR): 定数として定義. * io.c(select): bug fix. @@ -351,8 +1056,8 @@ Tue Jan 17 11:11:27 1995 Yukihiro Matsumoto (matz@ix-02) 性を継承する.最初の定義の時は今までと同じデフォルト(トップレベ ルで関数的,クラス定義内で通常メソッド). - * object.c(Class::new): オブジェクトの生成時に関数的メ - ソッドinit_objectが必ず呼ばれるように変更. + * object.c(Class::new): オブジェクトの生成時に関数的メソッド + init_objectが必ず呼ばれるように変更. * eval.c: 未定義のメソッドに対してunknownメソッドが呼ばれるように なった.エラー表示が今までと同じになるようにenvを調節している. @@ -1,65 +0,0 @@ -.\" FAQ - -*- Indented-Text -*- created at: Tue Jan 4 12:48:12 JST 1994 -これはRubyに関するFAQ(Frequently Asked Question)をまとめたものです. -Emacsでこのファイルを見ている場合にはM-2 C-x $で質問の部分だけを読むこ -とができます. C-x $で全てを読む状態に戻せます. ------------------------------- -1) Rubyの名称の由来は - - Rubyは何かの略称ではなく, 赤い宝石の名前をとって名付けられました. - perl(pearlは6月の誕生石)の次を目指す(Rubyは7月の誕生石)という意味 - があります. 更に美しく価値あるものであって欲しいという願いも含まれ - ています. ------------------------------- -2) Rubyが影響を受けた言語は? - - Rubyの言語仕様はC, Perl, Eiffelの各言語にこの順に影響を受けていま - す. その他に影響を受けた言語としてはtcl, AWK, bourne shell, CLU, - Icon, Sather, Smalltalk, Emacs Lispなどがあります. ------------------------------- -3) Rubyの特徴は? - - 以下に箇条書にします. - - + インタプリタであり, 手軽にプログラムできる. - + 変数宣言が要らない. - + ファイルやストリームなどへのUNIX的な操作が簡単にできる. - + バイナリファイルも扱える. - + きちんとしたオブジェクト指向言語である. - + 特異メソッドの機能が提供されている. - + モジュールによる機能の共有ができる. - + 例外処理機能がある. - + GCがあるのでメモリ管理を気にしなくて済む. - + 一貫性のある言語仕様. - + 複雑なデータ構造も扱える. - + ダイナミックロードをサポートする(システムによる). - - 逆に欠点は - - - 発展途上で機能が少ない. - - デバッガが(まだ)ない. - - 遅い. - - ことでしょうか. ------------------------------- -4) Rubyに変数宣言がないことのメリットは? - - ある変数がどのスコープに属するかが一目で分かるので, プログラムの読み - 易さ, 理解し易さが増すことが最大のメリットです. またトータルのタイプ - 量が減るので書き易さ, 手軽さにも貢献します. ------------------------------- -5) RubyとPerlの違いは? - - RubyとPerlはテキスト操作に優れたスクリプト言語であるという点で, カ - バーする範囲が近く, 一見するとライバルに見えます. 確かにRubyの設計 - において「Perlにできることはできるように」ということは, 一つの重要 - な目標でした. しかし, Rubyの真の目的はあくまでも「手軽なオブジェク - ト指向言語」であり, また, Ruby設計の3大目標である, 機能性・一貫性・ - 拡張性の結果として, RubyはPerlとは異なる言語になりました. - - RubyはPerlほど「何でもあり」ではありませんし, ほとんどの場合Perlよ - り処理速度が遅いです. しかし, Rubyのオブジェクト指向機能はPerlでは - 満足できない人たちにアピールすると思います. ------------------------------- -Local variables: -fill-column: 70 -end: @@ -1,12 +1,10 @@ -C-IF ChangeLog -FAQ MANIFEST Makefile.in README +README.EXT ToDo array.c -assoc.c bignum.c class.c compar.c @@ -18,10 +16,10 @@ defines.h dir.c dln.c dln.h +dmyext.c enum.c env.h error.c -etc.c eval.c file.c fnmatch.c @@ -29,7 +27,6 @@ fnmatch.h gc.c glob.c hash.c -ident.h inits.c install-sh io.c @@ -48,11 +45,11 @@ re.c re.h regex.c regex.h -ruby.1 ruby.c ruby.h +ruby.texi +sig.h signal.c -spec sprintf.c st.c st.h @@ -64,3 +61,76 @@ util.c variable.c version.c version.h +ext/Setup +ext/extmk.rb.in +lib/base64.rb +lib/find.rb +lib/getopts.rb +lib/mailread.rb +lib/parsearg.rb +lib/parsedate.rb +lib/tk.rb +lib/tkcanvas.rb +lib/tkentry.rb +lib/tktext.rb +lib/tkclass.rb +missing/alloca.c +missing/crypt.c +missing/dup2.c +missing/memmove.c +missing/mkdir.c +missing/nt.c +missing/nt.h +missing/setenv.c +missing/strdup.c +missing/strerror.c +missing/strftime.c +missing/strstr.c +missing/strtol.c +missing/strtoul.c +sample/biorhythm.rb +sample/cbreak.rb +sample/clnt.rb +sample/dbm.rb +sample/dir.rb +sample/evaldef.rb +sample/export.rb +sample/exyacc.rb +sample/fib.awk +sample/fib.pl +sample/fib.rb +sample/fib.scm +sample/freq.rb +sample/from.rb +sample/fullpath.pl +sample/fullpath.rb +sample/getopts.test +sample/io.rb +sample/less.rb +sample/list.rb +sample/list2.rb +sample/list3.rb +sample/marshal.rb +sample/mkproto.rb +sample/mpart.rb +sample/occur.pl +sample/occur.rb +sample/occur2.rb +sample/rcs.awk +sample/rcs.dat +sample/rcs.rb +sample/ruby-mode.el +sample/sieve.rb +sample/svr.rb +sample/test.rb +sample/time.rb +sample/tkbiff.rb +sample/tkbrowse.rb +sample/tkdialog.rb +sample/tkfrom.rb +sample/tkhello.rb +sample/tkline.rb +sample/tktimer.rb +sample/trojan.pl +sample/trojan.rb +sample/uumerge.rb diff --git a/Makefile.in b/Makefile.in index 9f804f1706..81e3feca79 100644 --- a/Makefile.in +++ b/Makefile.in @@ -13,21 +13,23 @@ INSTALL_DATA = @INSTALL_DATA@ PURIFY= @SET_MAKE@ -CFLAGS = @CFLAGS@ +CFLAGS = @CFLAGS@ -I. STATIC = @STATIC@ LDFLAGS = $(CFLAGS) @LDFLAGS@ -LIBS = @LIBS@ +LIBS = -lm @LIBS@ $(EXTLIBS) MISSING = @LIBOBJS@ @ALLOCA@ prefix = @prefix@ binprefix = exec_prefix = @exec_prefix@ bindir = $(exec_prefix)/bin +libdir = @prefix@/lib/ruby #### End of system configuration section. #### +EXTOBJS = dmyext.o + OBJS = array.o \ - assoc.o \ bignum.o \ class.o \ compar.o \ @@ -35,7 +37,6 @@ OBJS = array.o \ dln.o \ enum.o \ error.o \ - etc.o \ eval.o \ file.o \ fnmatch.o \ @@ -65,41 +66,49 @@ OBJS = array.o \ util.o \ variable.o \ version.o \ - $(MISSING) + $(MISSING) \ + $(EXTOBJS) + +PROGRAM = miniruby -PROGRAM = ruby +all: extruby -all: $(PROGRAM) @EXTMAKE@ +extruby: miniruby ext/Setup + @if test -z "$$UNDER_EXTMAKE_RB"; \ + then echo "Compiling ext modules"; \ + UNDER_EXTMAKE_RB=yes; export UNDER_EXTMAKE_RB; \ + cd ext; ../miniruby ./extmk.rb; fi $(PROGRAM): $(OBJS) @rm -f $(PROGRAM) $(PURIFY) $(CC) $(STATIC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM) -extmake:; @echo "Compiling ext modules"; \ - for extdir in ext/*; do \ - test -d $$extdir || continue; \ - test -f $$extdir/MANIFEST || continue; \ - echo `basename $$extdir`; \ - ( cd $$extdir; ../../ruby ../extmk.rb; );\ - done +$(bindir)/ruby: extruby + $(INSTALL_PROGRAM) ruby $(bindir)/ruby + strip $(bindir)/ruby -$(bindir)/$(PROGRAM): $(PROGRAM) - $(INSTALL_PROGRAM) $(PROGRAM) $(bindir)/$(PROGRAM) - -install: $(bindir)/$(PROGRAM) +install: $(bindir)/ruby + cd ext; ../miniruby ./extmk.rb install + $(INSTALL_DATA) lib/*.rb $(libdir) clean:; @rm -f $(OBJS) - @for extdir in ext/*; do \ - test -d $$extdir || continue; \ - test -f $$extdir/MANIFEST || continue; \ - if test -f $$extdir/Makefile; then \ - ( cd $$extdir; make clean ); \ - fi; \ - done + @rm -f ext/extinit.c ext/extinit.o + cd ext; ../ruby ./extmk.rb install realclean: clean - @rm -f ext/extmk.rb - @rm -f core ruby *~ config.* Makefile + @rm -f Makefile ext/extmk.rb + @rm -f config.cache config.h config.log config.status + @rm -f core ruby miniruby *~ + +test:; @-./ruby sample/test.rb > ./ruby_test 2>&1;\ + if grep '^end of test' ./ruby_test > /dev/null; then \ + echo "test succeeded"; \ + else \ + grep '^sample/test.rb' ./ruby_test; \ + grep '^not' ./ruby_test; \ + echo "test failed";\ + fi;\ + rm -f ./ruby_test .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c $< @@ -107,12 +116,21 @@ realclean: clean alloca.o: missing/alloca.c $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/alloca.c +crypt.o: missing/crypt.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/crypt.c + +dup2.o: missing/dup2.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/dup2.c + memmove.o: missing/memmove.c $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/memmove.c mkdir.o: missing/mkdir.c $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/mkdir.c +setenv.o: missing/setenv.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/setenv.c + strerror.o: missing/strerror.c $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strerror.c @@ -131,47 +149,49 @@ strtol.o: missing/strtol.c strtoul.o: missing/strtoul.c $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strtoul.c +nt.o: missing/nt.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/nt.c + # Prevent GNU make v3 from overflowing arg limit on SysV. .NOEXPORT: ### -parse.o : parse.y ruby.h defines.h config.h env.h node.h st.h ident.h regex.h +parse.o : parse.y ruby.h defines.h config.h env.h node.h st.h regex.h ### array.o: array.c ruby.h config.h defines.h bignum.o: bignum.c ruby.h config.h defines.h -class.o: class.c ruby.h config.h defines.h env.h node.h st.h +class.o: class.c ruby.h config.h defines.h node.h st.h compar.o: compar.c ruby.h config.h defines.h -cons.o: cons.c ruby.h config.h defines.h dir.o: dir.c ruby.h config.h defines.h dln.o: dln.c config.h defines.h dln.h +dmyext.o: dmyext.c +dummy.o: dummy.c config.h dln.c defines.h dln.h enum.o: enum.c ruby.h config.h defines.h error.o: error.c ruby.h config.h defines.h env.h -etc.o: etc.c ruby.h config.h defines.h -eval.o: eval.c ruby.h config.h defines.h ident.h env.h node.h st.h dln.h -file.o: file.c ruby.h config.h defines.h io.h +eval.o: eval.c ruby.h config.h defines.h env.h node.h sig.h st.h dln.h +file.o: file.c ruby.h config.h defines.h io.h sig.h fnmatch.o: fnmatch.c config.h fnmatch.h -gc.o: gc.c ruby.h config.h defines.h env.h st.h node.h +gc.o: gc.c ruby.h config.h defines.h env.h st.h node.h re.h regex.h glob.o: glob.c config.h fnmatch.h hash.o: hash.c ruby.h config.h defines.h st.h inits.o: inits.c ruby.h config.h defines.h -io.o: io.c ruby.h config.h defines.h io.h +io.o: io.c ruby.h config.h defines.h io.h sig.h main.o: main.c math.o: math.c ruby.h config.h defines.h numeric.o: numeric.c ruby.h config.h defines.h env.h -object.o: object.c ruby.h config.h defines.h env.h st.h +object.o: object.c ruby.h config.h defines.h st.h pack.o: pack.c ruby.h config.h defines.h -process.o: process.c ruby.h config.h defines.h st.h +process.o: process.c ruby.h config.h defines.h sig.h st.h random.o: random.c ruby.h config.h defines.h range.o: range.c ruby.h config.h defines.h re.o: re.c ruby.h config.h defines.h re.h regex.h regex.o: regex.c config.h defines.h regex.h util.h ruby.o: ruby.c ruby.h config.h defines.h re.h regex.h dln.h -signal.o: signal.c ruby.h config.h defines.h +signal.o: signal.c ruby.h config.h defines.h sig.h sprintf.o: sprintf.c ruby.h config.h defines.h st.o: st.c config.h st.h string.o: string.c ruby.h config.h defines.h re.h regex.h struct.o: struct.c ruby.h config.h defines.h env.h -tclglob.o: tclglob.c time.o: time.c ruby.h config.h defines.h util.o: util.c defines.h config.h util.h -variable.o: variable.c ruby.h config.h defines.h env.h ident.h st.h +variable.o: variable.c ruby.h config.h defines.h env.h st.h version.o: version.c ruby.h config.h defines.h version.h @@ -1,73 +1,153 @@ -.\" README - -*- Text -*- created at: Wed Aug 3 11:57:36 JST 1994 +* Rubyとは -コンパイル・インストール +Rubyはシンプルかつ強力なオブジェクト指向スクリプト言語です. +Rubyは最初から純粋なオブジェクト指向言語として設計されていま +すから,オブジェクト指向プログラミングを手軽に行う事が出来ま +す.もちろん通常の手続き型のプログラミングも可能です. - 1. configureを実行してMakefileを生成する. - 2. (必要ならば)defines.hを編集する. - 3. makeを実行してコンパイルする - 4. make install +Rubyはテキスト処理関係の能力などに優れ,perlと同じくらい強力 +です.さらにシンプルな文法と,例外処理やイテレータなどの機構 +によって,より分かりやすいプログラミングが出来ます. - もし, コンパイル時にエラーが発生した場合にはエラーのログとマ - シン, OSの種類を含むできるだけ詳しいレポートを作者に送ってい - ただきたい. +* Rubyの特長. -機能追加 + + シンプルな文法 + + 普通のオブジェクト指向機能(クラス,メソッドコールなど) + + 特殊なオブジェクト指向機能(Mixin, 特異メソッドなど) + + 演算子オーバーロード + + 例外処理機能 + + イテレータとクロージャ + + ガーベージコレクタ + + ダイナミックローディング (アーキテクチャによる) + + 移植性が高い.多くのUNIX上で動く - Cコードを書くことによって, 簡単にrubyに機能を追加できる. - そのおおまかな手順は以下の通りである. +* 入手法 - * 関数的メソッドを追加する場合 +** ftpで - (1) Cで関数を書く - (2) rb_define_method()でカーネルクラスのメソッドとして関 - 数をrubyに登録する関数を書く - (3) init.cを編集して, 登録する関数を呼び出す +以下の場所においてあります. - * クラスを追加する場合 + ftp://ftp.kk.info.kanagawa-u.ac.jp/pub/languages/ruby/ - (1) クラスを設計する - (2) メソッドをCで記述する - (3) rb_define_class()でクラスを生成するコードを書く - (4) rb_define_method()でメソッドを登録するコードを書く - (5) init.cを編集して, 初期化する関数を呼び出す +** メイルで - 詳しくはC-IFを参照. +以下のアドレスに`send'というSubjectのメイルを送って下さい. -移植 + ruby-archive@caelum.co.jp - UNIXであればconfigureがほとんどの差異を吸収してくれるはず - だが, 思わぬ見落としがあった場合(あるに違いない), 作者にレ - ポートすれば, 解決できるかも知れない. +本文には何を書いても構いません.折り返し,最新版のrubyが送っ +て来ます. - アークテクチャにもっとも依存するのはGC部である. rubyのGCは - 対象のアーキテクチャがsetjmp()によって, 全てのレジスタを - jmp_bufに格納することと, jmp_bufとスタックが32bitアライン - メントされていることを仮定している. 前者が真実でない場合の - 移植は困難を極めるだろう. 後者は割と簡単に解決できる. gc.c - のスタックをマークする部分にアラインメントのバイト数だけず - らしてマークするコードを追加するだけで済む. +* メイリングリスト - sparc以外のレジスタウィンドウを持つCPUでは, レジスタウィン - ドウをフラッシュするコードを追加する必要があるかも知れない. + Rubyに関わる話題のためのメイリングリストを解説しました.ア + ドレスは -配布条件 + ruby-list@caelum.co.jp - Rubyは最終的にはGNU Public Licenseにしたがった条件で配布さ - れる予定だが, 現時点では以下の条件で配布する. + です.このアドレスにメイルを送れば,自動的に登録されます. - * 更新 +* コンパイル・インストール - いかなる目的であれ自由である. ただし, バグ修正は作者への - フィードバックを期待する(強制ではない) +以下の手順で行ってください. - * 他のプログラムへの引用 + 1. configureを実行してMakefileなどを生成する - いかなる目的であれ自由である. ただし, 配布したコードに含 - まれる他の作者によるコードは, それぞれの作者の意向による - 制限が加えられる. + 2. (必要ならば)defines.hを編集する - * 再配布 + 多分,必要無いと思います. - 禁止する. 入手したい人は作者に直接連絡をとること. これは - Rubyの言語仕様が不安定な状態のまま広く流布するのを制限す - るためであり, 言語仕様が安定した時点で再配布自由とする. + 3. (必要ならば)ext/Setupに静的にリンクする拡張モジュールを + 指定する + + ext/Setupに記述したモジュールは静的にリンクされます. + + ダイナミックローディングをサポートしていないアーキテク + チャではSetupの1行目の「option nodynamic」という行のコ + メントを外す必要があります.また,このアーキテクチャで + 拡張モジュールを利用するためには,あらかじめ静的にリン + クしておく必要があります. + + 4. makeを実行してコンパイルする + + 5. make testでテストを行う. + + 「test succeeded」と表示されれば成功です. + + 6. make install + +もし,コンパイル時にエラーが発生した場合にはエラーのログとマ +シン,OSの種類を含むできるだけ詳しいレポートを作者に送ってく +ださると他の方のためにもなります. + +* 移植 + +UNIXであればconfigureがほとんどの差異を吸収してくれるはずで +すが,思わぬ見落としがあった場合(あるに違いない),作者にその +ことをレポートすれば,解決できるかも知れません. + +アークテクチャにもっとも依存するのはGC部です.rubyのGCは対象 +のアーキテクチャがsetjmp()によって,全てのレジスタを jmp_buf +に格納することと,jmp_bufとスタックが32bitアラインメントされ +ていることを仮定しています.前者が成立しない場合の対応は困難 +を極めるでしょう.後者の解決は比較的簡単で,gc.cでスタックを +マークしている部分にアラインメントのバイト数だけずらしてマー +クするコードを追加するだけで済みます.「defined(THINK_C)」で +括られている部分を参考にしてください + +# 実際にはrubyはThink Cでコンパイルできません. + +sparc以外のレジスタウィンドウを持つCPUでは,レジスタウィンド +ウをフラッシュするコードを追加する必要があるかも知れません. + +* 配布条件 + +Rubyの配布に関して著作権保持者である作者<matz@caelum.co.jp> +は以下の条件をつけます. + + + 更新 + + いかなる目的であれ自由です.ただし,機能拡張やバグ修正は + 作者へのフィードバックを期待します(もちろん強制ではあり + ません). + + + 他のプログラムへの引用 + + いかなる目的であれ自由です.ただし,rubyに含まれる他の作 + 者によるコードは,それぞれの作者の意向による制限が加えら + れます.具体的にはgc.c(一部),regex.[ch],fnmatch.[ch], + glob.c, st.[ch]とmissingディレクトリ下のファイル群が該当 + します. + + + 再配布 + + 配布した状態を維持する限り自由です.変更を行ったものを再 + 配布することを希望する時には作者に連絡してください.オリ + ジナルのrubyと明確に区別できるようであれば,再配布を認め + る方針です. + + 変更を行なわないrubyをコンパイルしたバイナリの配布は禁止 + しませんが,コンパイル条件に起因するトラブルを減らすため + に,コンパイル時の情報をできるだけ詳しく明記する事を希望 + します. + + + Rubyスクリプトの権利 + + 全てのrubyスクリプトの権利はそれぞれの著作者に属します. + 作者はこれらに関して一切の権利を主張しません.またrubyに + 組み込むための拡張モジュールに関しても同様です. + + + 無保証 + + Rubyは無保証です.作者はrubyをサポートする意志はあります + が,ruby自身のバグあるいはrubyスクリプトのバグなどから発 + 生するいかなる損害に対しても責任を持ちません. + +* 著者 + +コメント,バグレポートその他は matz@caelum.co.jp まで. +------------------------------------------------------- +created at: Thu Aug 3 11:57:36 JST 1995 +Local variables: +mode: indented-text +end: diff --git a/README.EXT b/README.EXT new file mode 100644 index 0000000000..efa627a24a --- /dev/null +++ b/README.EXT @@ -0,0 +1,308 @@ +.\" README.EXT - -*- Text -*- created at: Mon Aug 7 16:45:54 JST 1995 + +Rubyを拡張するための方法を解説する. + +RubyはCコードを書くことによって,簡単に機能を追加できる.おおまかな手 +順は以下の通りである. + + 1. ファイルを用意する + + extディレクトリの下に拡張モジュール用のディレクトリを用意して,そ + の配下に以下のファイルを用意する必要がある. + + + MANIFEST.必要なファイルの一覧. + + 必ず必要.一時的なファイル以外の全てのファイル名を1行1ファイル + の形式で記述すること. + + + Cのソースファイル. + + モジュールが1ファイルだけからなる時はモジュール名と同じ名前のファ + イル名(モジュール.c)をつける.逆にモジュールが複数からなる時は + モジュール名のついたソースファイルは避けること. + + + extconf.rb(optional).設定用ファイル. + + 関数やライブラリ,ヘッダの存在チェックをしたり,モジュール名な + どを設定する.このファイルが無ければ全てデフォルトでコンパイル + される. + + + depend(optional).Makefileにインクルードするためのファ + イルの依存関係を記述したファイル. + + `gcc -MM *.c > depend'とすれば自動的に生成できる. + + 2. Cのソースファイルを用意する + + 必ず「Init_モジュール名」という関数を用意し,その中で,変数やクラ + スの定義や,クラスへのメソッドの登録などの初期化を行うこと.この + 関数の呼び出しはインタプリタの初期化時(静的リンクの場合)かモジュー + ルのロード時(動的リンクの場合)に自動的に行われる. + +* Ruby API + +C言語からRubyの機能を利用するAPIは以下の通りである. + +** 型 + + VALUE + + Rubyオブジェクトを表現する型.必要に応じてキャストして用いる.組み + 込み型を表現するCの型はruby.hに記述してあるRで始まる構造体である. + VALUE型をこれらにキャストするためにRで始まる構造体名を全て大文字に + した名前のマクロが用意されている. + +** 変数・定数 + + Qnil + + 定数: nilオブジェクト + + Qself + + 変数: 現在のselfオブジェクトの値.一般にメソッドにはselfを指す引数 + が与えられるので, この変数にアクセスする必要はない.この変数の値を + 変更する時は以後のselfの値そのものが変わってしまうので, 特別な事情 + がない限り代入してはならない. + + TRUE + + 定数: tオブジェクト(真のデフォルト値) + + FALSE + + 定数: nilオブジェクト + +** Cデータのカプセル化 + + VALUE data_new(void *sval, void (*mark)(), void (*free)()) + + Cの任意のポインタをカプセル化したrubyオブジェクトを返す.このポイン + タがrubyからアクセスされなくなった時,freeで指定した関数が呼ばれる. + また,このポインタの指すデータが他のrubyオブジェクトを指している場 + 合,markに指定する関数でマークする必要がある. + + Make_Data_Struct(obj, iv, type, mark, free, sval) + + type型のメモリをmallocし,変数svalに代入した後,それをカプセル化し + たデータをobjのインスタンス変数ivに代入するマクロ. + + Get_Data_Struct(obj, iv, type, sval) + + objのインスタンス変数ivが指すデータからtype型のポインタを取り出し + 変数svalに代入するマクロ. + +** クラス/モジュール定義 + + VALUE rb_define_class(char *name, VALUE super) + + superのサブクラスとして新しいRubyクラスを定義する. + + VALUE rb_define_module(char *name) + + 新しいRubyモジュールを定義する. + + void rb_include_module(VALUE class, VALUE module) + + モジュールをインクルードする.classがすでにmoduleをインクルードして + いる時には何もしない(多重インクルードの禁止). + + void rb_extend_object(VALUE object, VALUE module) + + オブジェクトをモジュール(で定義されているメソッド)で拡張する. + +** 大域変数定義 + + void rb_define_variable(char *name, VALUE *var) + + RubyとCとで共有するグローバル変数を定義する.変数名が`$'で始まらな + い時には自動的に追加される.nameとしてrubyの識別子として許されない + 文字(例えば` ')を含む場合にはrubyプログラムからは見えなくなる. + + void rb_define_readonly_variable(char *name, VALUE *var) + + RubyとCとで共有するread onlyのグローバル変数を定義する.read onlyで + あること以外はrb_define_variable()と同じ. + + void rb_define_virtual_variable(char *name, + VALUE (*getter)(), VALUE (*setter)()) + + 関数によって実現されるRuby変数を定義する.変数が参照された時には + getterが,関数に値がセットされた時にはsetterが呼ばれる. + + void rb_define_hooked_variable(char *name, VALUE *var, + VALUE (*getter)(), VALUE (*setter)()) + + 関数によってhookのつけられたグローバル変数を定義する.変数が参照さ + れた時にはgetterが,関数に値がセットされた時にはsetterが呼ばれる. + getterやsetterに0を指定した時にはhookを指定しないのと同じ事になる. + + void rb_global_variable(VALUE *var) + + GCのため,Rubyプログラムからはアクセスされないが, Rubyオブジェクト + を含む大域変数をマークする. + +** クラス定数 + + void rb_define_const(VALUE class, char *name, VALUE val) + + クラス定数を定義する. + +** メソッド定義 + + rb_define_method(VALUE class, char *name, VALUE (*func)(), int argc) + + メソッドを定義する.argcはselfを除く引数の数.argcが-1の時, 関数に + は引数の数(selfを含まない)を第1引数, 引数の配列を第2引数とする形式 + で与えられる(第3引数はself).argcが-2の時, 引数はself, args(argsは + 引数を含むrubyの配列)という形式で与えられる. + + rb_define_private_method(VALUE class, char *name, VALUE (*func)(), int argc) + + privateメソッドを定義する.引数はrb_define_method()と同じ. + + rb_define_singleton_method(VALUE class, char *name, VALUE (*func)(), int argc) + + 特異メソッドを定義する.引数はrb_define_method()と同じ. + + rb_scan_args(int atgc, VALUE *argv, char *fmt, ...) + + argc,argv形式で与えられた引数を分解する.fmtは必須引数の数, 付加引 + 数の数, 残りの引数があるかを指定する文字列で, "数字数字*"という形式 + である. 2 番目の数字と"*"はそれぞれ省略可能である.必須引数が一つ + もない場合は0を指定する.第3引数以降は変数へのポインタで, 該当する + 要素がその変数に格納される.付加引数に対応する引数が与えられていな + い場合は変数にQnilが代入される. + +** Rubyメソッド呼び出し + + VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) + + メソッド呼び出し.文字列からmidを得るためにはrb_intern()を使う. + + VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv) + + メソッド呼び出し.引数をargc,argv形式で渡す. + + VALUE rb_eval_string(char *str) + + 文字列をrubyとスクリプトしてコンパイル・実行する. + + ID rb_intern(char *name) + + 文字列に対応するIDを返す. + + char *rb_id2name(ID id) + + IDに対応する文字列を返す(デバッグ用). + + char *rb_class2name(VALUE class) + + classの名前を返す(デバッグ用).classが名前を持たない時には, 一番近 + い名前を持つクラスの名前を返す. + +** インスタンス変数 + + VALUE rb_iv_get(VALUE obj, char *name) + + objのインスタンス変数の値を得る.`@'で始まらないインスタンス変数は + Rubyプログラムからアクセスできない「隠れた」インスタンス変数になる. + + VALUE rb_iv_set(VALUE obj, char *name, VALUE val) + + objのインスタンス変数をvalにセットする. + +** 制御構造 + + VALUE rb_iterate(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2) + + func2をブロックとして設定し, func1をイテレータとして呼ぶ. func1に + は arg1が引数として渡され, func2には第1引数にイテレータから与えられ + た値, 第2引数にarg2が渡される. + + VALUE rb_yield(VALUE val) + + valを値としてイテレータブロックを呼び出す. + + VALUE rb_rescue(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2) + + 関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生した時に + は func2をarg2を引数として呼ぶ.戻り値は例外が発生しなかった時は + func1の戻り値, 例外が発生した時にはfunc2の戻り値である. + + VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2) + + 関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発生して + も) func2をarg2を引数として実行する.戻り値はfunc1の戻り値である(例 + 外が発生した時は戻らない). + +** 例外・エラー + + void Warning(char *fmt, ...) + + verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ. + + void Fail(char *fmt, ...) + + 例外を発生させる.引数はprintf()と同じ. + + void Fatal(char *fmt, ...) + + 致命的例外を発生させる.通常の例外処理は行なわれず, インタープリタ + が終了する(ただしensureで指定されたコードは終了前に実行される). + + void Bug(char *fmt, ...) + + インタープリタなどプログラムのバグでしか発生するはずのない状況の時 + 呼ぶ.インタープリタはコアダンプし直ちに終了する.例外処理は一切行 + なわれない. + +** rubyの初期化・実行 + +Rubyをアプリケーションに埋め込む場合には以下のインタフェースを使う.通 +常の拡張モジュールには必要ない. + + void ruby_init(int argc, char **argv, char **envp) + + rubyインタプリタの初期化を行なう. + + void ruby_run() + + rubyインタプリタを実行する. + + void ruby_script(char *name) + + rubyのスクリプト名($0)を設定する. + +* extconf.rbの記述 + +拡張モジュールのディレクトリに`extconf.rb'というファイルが存在する時に +は,それが実行され,モジュールのコンパイルに必要な条件のチェックなどを +行う事が出来る.extconf.rbの中では以下の関数を使う事ができる. + + have_library(lib, func) + + 関数funcを定義しているライブラリlibの存在をチェックする.ライブラリ + が存在する時,TRUEを返す. + + have_func(func) + + 関数funcの存在をチェックする.funcが標準ではリンクされないライブラ + リ内のものである時には先にhave_libraryでそのライブラリをチェックし + ておく事.関数が存在する時,TRUEを返す. + + have_header(header) + + ヘッダファイルの存在をチェックする.ヘッダファイルが存在する時TRUE + を返す. + + create_makefile(target) + + 拡張モジュール用のMakefileを生成する.この関数を呼ばなければそのモ + ジュールはコンパイルされない. + +/* + * Local variables: + * fill-column: 70 + * end: + */ @@ -1,10 +1,9 @@ -* Process Class +* thread対応 +* Hand written parser(recursive decent) * クラスライブラリの見直し(UNIX依存を減らす) * format機能 * here document * perlのようなsetuid check -* 適切なsignal handling -* rubyで定義する変数hookの実現 * write debugger for ruby * re-write regex code for speed * byte code interpretor @@ -12,7 +12,7 @@ #include "ruby.h" -VALUE C_Array; +VALUE cArray; VALUE rb_to_a(); @@ -20,14 +20,15 @@ VALUE rb_to_a(); VALUE ary_new2(len) + int len; { NEWOBJ(ary, struct RArray); - OBJSETUP(ary, C_Array, T_ARRAY); + OBJSETUP(ary, cArray, T_ARRAY); ary->len = 0; ary->capa = len; if (len == 0) - ary->ptr = Qnil; + ary->ptr = 0; else ary->ptr = ALLOC_N(VALUE, len); @@ -49,7 +50,7 @@ ary_new3(n, va_alist) { va_list ar; struct RArray* ary; - int len, i; + int i; if (n < 0) { Fail("Negative number of items(%d)", n); @@ -80,8 +81,22 @@ ary_new4(n, elts) return (VALUE)ary; } +VALUE +assoc_new(car, cdr) + VALUE car, cdr; +{ + struct RArray* ary; + + ary = (struct RArray*)ary_new2(2); + ary->ptr[0] = car; + ary->ptr[1] = cdr; + ary->len = 2; + + return (VALUE)ary; +} + static VALUE -Sary_new(class) +ary_s_new(class) VALUE class; { NEWOBJ(ary, struct RArray); @@ -95,7 +110,7 @@ Sary_new(class) } static VALUE -Sary_create(argc, argv, class) +ary_s_create(argc, argv, class) int argc; VALUE *argv; VALUE class; @@ -106,7 +121,7 @@ Sary_create(argc, argv, class) ary->len = argc; ary->capa = argc; if (argc == 0) { - ary->ptr = Qnil; + ary->ptr = 0; } else { ary->ptr = ALLOC_N(VALUE, argc); @@ -150,7 +165,7 @@ ary_push(ary, item) } static VALUE -Fary_append(ary, item) +ary_append(ary, item) struct RArray *ary; VALUE item; { @@ -178,7 +193,7 @@ ary_shift(ary) ary->len--; /* sliding items */ - memmove(ary->ptr, ary->ptr+1, sizeof(VALUE)*(ary->len)); + MEMMOVE(ary->ptr, ary->ptr+1, VALUE, ary->len); return top; } @@ -186,16 +201,15 @@ ary_shift(ary) VALUE ary_unshift(ary, item) struct RArray *ary; + int item; { - VALUE top; - if (ary->len >= ary->capa) { ary->capa+=ARY_DEFAULT_SIZE; REALLOC_N(ary->ptr, VALUE, ary->capa); } /* sliding items */ - memmove(ary->ptr+1, ary->ptr, sizeof(VALUE)*(ary->len)); + MEMMOVE(ary->ptr+1, ary->ptr, VALUE, ary->len); ary->len++; return ary->ptr[0] = item; @@ -224,7 +238,6 @@ ary_subseq(ary, beg, len) int beg, len; { struct RArray *ary2; - VALUE *ptr; if (beg < 0) { beg = ary->len + beg; @@ -247,37 +260,42 @@ ary_subseq(ary, beg, len) return (VALUE)ary2; } -extern VALUE C_Range; - -static void -range_beg_end(range, begp, lenp, len) +static VALUE +beg_len(range, begp, lenp, len) VALUE range; int *begp, *lenp; int len; { int beg, end; - beg = rb_iv_get(range, "start"); beg = NUM2INT(beg); - end = rb_iv_get(range, "end"); end = NUM2INT(end); + if (!range_beg_end(range, &beg, &end)) return FALSE; + if (beg < 0) { beg = len + beg; if (beg < 0) beg = 0; } - if (end < 0) { - end = len + end; - if (end < 0) end = 0; + *begp = beg; + if (beg > len) { + *lenp = 0; } - if (beg > end) { - int tmp; - - Warning("start %d is bigger than end %d", beg, end); - tmp = beg; beg = end; end = tmp; + else { + if (end < 0) { + end = len + end; + if (end < 0) end = 0; + } + if (len < end) end = len; + if (beg < end) { + *lenp = 0; + } + else { + *lenp = end - beg +1; + } } - *begp = beg; *lenp = end - beg + 1; + return TRUE; } static VALUE -Fary_aref(argc, argv, ary) +ary_aref(argc, argv, ary) int argc; VALUE *argv; struct RArray *ary; @@ -301,18 +319,18 @@ Fary_aref(argc, argv, ary) } /* check if idx is Range */ - if (obj_is_kind_of(arg1, C_Range)) { + { int beg, len; - range_beg_end(arg1, &beg, &len, ary->len); - return ary_subseq(ary, beg, len); + if (beg_len(arg1, &beg, &len, ary->len)) { + return ary_subseq(ary, beg, len); + } } - return ary_entry(ary, NUM2INT(arg1)); } static VALUE -Fary_index(ary, val) +ary_index(ary, val) struct RArray *ary; VALUE val; { @@ -322,11 +340,11 @@ Fary_index(ary, val) if (rb_equal(ary->ptr[i], val)) return INT2FIX(i); } - return Qnil; + return Qnil; /* should be FALSE? */ } static VALUE -Fary_indexes(ary, args) +ary_indexes(ary, args) struct RArray *ary, *args; { VALUE *p, *pend; @@ -348,7 +366,7 @@ Fary_indexes(ary, args) } static VALUE -Fary_aset(argc, argv, ary) +ary_aset(argc, argv, ary) int argc; VALUE *argv; struct RArray *ary; @@ -397,46 +415,46 @@ Fary_aset(argc, argv, ary) REALLOC_N(ary->ptr, VALUE, ary->capa); } - memmove(ary->ptr+beg+arg3->len, ary->ptr+beg+len, - sizeof(VALUE)*(ary->len-(beg+len))); - memcpy(ary->ptr+beg, arg3->ptr, sizeof(VALUE)*arg3->len); + MEMMOVE(ary->ptr+beg+arg3->len, ary->ptr+beg+len, + VALUE, ary->len-(beg+len)); + MEMCPY(ary->ptr+beg, arg3->ptr, VALUE, arg3->len); ary->len = alen; } return (VALUE)arg3; } /* check if idx is Range */ - if (obj_is_kind_of(arg1, C_Range)) { + { int beg, len; - Check_Type(arg2, T_ARRAY); - range_beg_end(arg1, &beg, &len, ary->len); - if (ary->len < beg) { - len = beg + RARRAY(arg2)->len; - if (len >= ary->capa) { - ary->capa=len; - REALLOC_N(ary->ptr, VALUE, ary->capa); + if (beg_len(arg1, &beg, &len, ary->len)) { + Check_Type(arg2, T_ARRAY); + if (ary->len < beg) { + len = beg + RARRAY(arg2)->len; + if (len >= ary->capa) { + ary->capa=len; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + MEMZERO(ary->ptr+ary->len, VALUE, beg-ary->len); + MEMCPY(ary->ptr+beg, RARRAY(arg2)->ptr, VALUE, RARRAY(arg2)->len); + ary->len = len; } - MEMZERO(ary->ptr+ary->len, VALUE, beg-ary->len); - MEMCPY(ary->ptr+beg, RARRAY(arg2)->ptr, VALUE, RARRAY(arg2)->len); - ary->len = len; - } - else { - int alen; - - alen = ary->len + RARRAY(arg2)->len - len; - if (alen >= ary->capa) { - ary->capa=alen; - REALLOC_N(ary->ptr, VALUE, ary->capa); + else { + int alen; + + alen = ary->len + RARRAY(arg2)->len - len; + if (alen >= ary->capa) { + ary->capa=alen; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + + MEMMOVE(ary->ptr+beg+RARRAY(arg2)->len, ary->ptr+beg+len, + VALUE, ary->len-(beg+len)); + MEMCPY(ary->ptr+beg, RARRAY(arg2)->ptr, VALUE, RARRAY(arg2)->len); + ary->len = alen; } - - memmove(ary->ptr+beg+RARRAY(arg2)->len, ary->ptr+beg+len, - sizeof(VALUE)*(ary->len-(beg+len))); - memcpy(ary->ptr+beg, RARRAY(arg2)->ptr, - sizeof(VALUE)*RARRAY(arg2)->len); - ary->len = alen; + return arg2; } - return arg2; } offset = NUM2INT(arg1); @@ -448,7 +466,7 @@ Fary_aset(argc, argv, ary) } static VALUE -Fary_each(ary) +ary_each(ary) struct RArray *ary; { int i; @@ -457,6 +475,7 @@ Fary_each(ary) for (i=0; i<ary->len; i++) { rb_yield(ary->ptr[i]); } + return Qnil; } else { return (VALUE)ary; @@ -464,7 +483,7 @@ Fary_each(ary) } static VALUE -Fary_each_index(ary) +ary_each_index(ary) struct RArray *ary; { int i; @@ -472,10 +491,11 @@ Fary_each_index(ary) for (i=0; i<ary->len; i++) { rb_yield(INT2FIX(i)); } + return Qnil; } static VALUE -Fary_length(ary) +ary_length(ary) struct RArray *ary; { return INT2FIX(ary->len); @@ -505,7 +525,7 @@ ary_join(ary, sep) if (ary->len == 0) return str_new(0, 0); if (TYPE(ary->ptr[0]) == T_STRING) - result = str_clone(ary->ptr[0]); + result = str_dup(ary->ptr[0]); else result = obj_as_string(ary->ptr[0]); @@ -528,7 +548,7 @@ ary_join(ary, sep) } static VALUE -Fary_join(argc, argv, ary) +ary_join_method(argc, argv, ary) int argc; VALUE *argv; struct RArray *ary; @@ -545,7 +565,7 @@ Fary_join(argc, argv, ary) } VALUE -Fary_to_s(ary) +ary_to_s(ary) VALUE ary; { VALUE str = ary_join(ary, OFS); @@ -554,7 +574,7 @@ Fary_to_s(ary) } VALUE -Fary_print_on(ary, port) +ary_print_on(ary, port) struct RArray *ary; VALUE port; { @@ -569,46 +589,31 @@ Fary_print_on(ary, port) return port; } -#define INSPECT_MAX 10 - static VALUE -Fary_inspect(ary) +ary_inspect(ary) struct RArray *ary; { int i, len; - VALUE str; + VALUE s, str; char *p; - ary = (struct RArray*)ary_clone(ary); + if (ary->len == 0) return str_new2("[]"); + str = str_new2("["); + len = 1; - len = ary->len; - for (i=0; i<len; i++) { - if (i > INSPECT_MAX) break; - ary->ptr[i] = rb_funcall(ary->ptr[i], rb_intern("_inspect"), 0, Qnil); + for (i=0; i<ary->len; i++) { + s = rb_funcall(ary->ptr[i], rb_intern("inspect"), 0, 0); + if (i > 0) str_cat(str, ", ", 2); + str_cat(str, RSTRING(s)->ptr, RSTRING(s)->len); + len += RSTRING(s)->len + 2; } - - str = str_new2(", "); - str = ary_join(ary, str); - if (str == Qnil) return str_new2("[]"); - len = RSTRING(str)->len; - if (ary->len > INSPECT_MAX) - str_grow(str, len+5); - else - str_grow(str, len+2); - - p = RSTRING(str)->ptr; - memmove(p+1, p, len); - p[0] = '['; - if (ary->len > INSPECT_MAX) - strcpy(p+len, "...]"); - else - p[len+1] = ']'; + str_cat(str, "]", 1); return str; } static VALUE -Fary_to_a(ary) +ary_to_a(ary) VALUE ary; { return ary; @@ -626,8 +631,8 @@ rb_to_a(obj) return obj; } -static VALUE -Fary_reverse(ary) +VALUE +ary_reverse(ary) struct RArray *ary; { VALUE ary2 = ary_new2(ary->len); @@ -663,7 +668,7 @@ sort_2(a, b) } VALUE -Fary_sort(ary) +ary_sort(ary) struct RArray *ary; { qsort(ary->ptr, ary->len, sizeof(VALUE), iterator_p()?sort_1:sort_2); @@ -671,7 +676,7 @@ Fary_sort(ary) } static VALUE -Fary_delete(ary, item) +ary_delete(ary, item) struct RArray *ary; VALUE item; { @@ -690,7 +695,7 @@ Fary_delete(ary, item) } static VALUE -Fary_delete_if(ary) +ary_delete_if(ary) struct RArray *ary; { int i1, i2; @@ -708,7 +713,7 @@ Fary_delete_if(ary) } static VALUE -Fary_clear(ary) +ary_clear(ary) struct RArray *ary; { ary->len = 0; @@ -716,7 +721,7 @@ Fary_clear(ary) } static VALUE -Fary_fill(argc, argv, ary) +ary_fill(argc, argv, ary) int argc; VALUE *argv; struct RArray *ary; @@ -726,8 +731,8 @@ Fary_fill(argc, argv, ary) VALUE *p, *pend; rb_scan_args(argc, argv, "12", &item, &arg1, &arg2); - if (arg2 == Qnil && obj_is_kind_of(arg1, C_Range)) { - range_beg_end(arg1, &beg, &len, ary->len); + if (arg2 == Qnil && beg_len(arg1, &beg, &len, ary->len)) { + /* beg and len set already */ } else { beg = NUM2INT(arg1); @@ -762,7 +767,7 @@ Fary_fill(argc, argv, ary) } static VALUE -Fary_plus(x, y) +ary_plus(x, y) struct RArray *x, *y; { struct RArray *z; @@ -784,7 +789,7 @@ Fary_plus(x, y) } static VALUE -Fary_times(ary, times) +ary_times(ary, times) struct RArray *ary; VALUE times; { @@ -803,7 +808,7 @@ Fary_times(ary, times) } VALUE -Fary_assoc(ary, key) +ary_assoc(ary, key) struct RArray *ary; VALUE key; { @@ -811,15 +816,16 @@ Fary_assoc(ary, key) p = ary->ptr; pend = p + ary->len; while (p < pend) { - if (TYPE(*p) == T_ASSOC - && rb_equal(RASSOC(*p)->car, key)) + if (TYPE(*p) == T_ARRAY + && RARRAY(*p)->len > 1 + && rb_equal(RARRAY(*p)->ptr[0], key)) return *p; } - return Qnil; + return Qnil; /* should be FALSE? */ } VALUE -Fary_rassoc(ary, value) +ary_rassoc(ary, value) struct RArray *ary; VALUE value; { @@ -827,15 +833,16 @@ Fary_rassoc(ary, value) p = ary->ptr; pend = p + ary->len; while (p < pend) { - if (TYPE(*p) == T_ASSOC - && rb_equal(RASSOC(*p)->cdr, value)) + if (TYPE(*p) == T_ARRAY + && RARRAY(*p)->len > 2 + && rb_equal(RARRAY(*p)->ptr[1], value)) return *p; } - return Qnil; + return Qnil; /* should be FALSE? */ } static VALUE -Fary_equal(ary1, ary2) +ary_equal(ary1, ary2) struct RArray *ary1, *ary2; { int i; @@ -850,7 +857,7 @@ Fary_equal(ary1, ary2) } static VALUE -Fary_hash(ary) +ary_hash(ary) struct RArray *ary; { int i, h; @@ -858,14 +865,14 @@ Fary_hash(ary) h = 0; for (i=0; i<ary->len; i++) { - h += rb_funcall(ary->ptr[i], hash, 0); + h ^= rb_funcall(ary->ptr[i], hash, 0); } h += ary->len; return INT2FIX(h); } static VALUE -Fary_includes(ary, item) +ary_includes(ary, item) struct RArray *ary; VALUE item; { @@ -879,34 +886,34 @@ Fary_includes(ary, item) } static VALUE -Fary_diff(ary1, ary2) +ary_diff(ary1, ary2) struct RArray *ary1, *ary2; { VALUE ary3; - int i, j; + int i; Check_Type(ary2, T_ARRAY); ary3 = ary_new(); for (i=0; i<ary1->len; i++) { - if (Fary_includes(ary2, ary1->ptr[i])) continue; - if (Fary_includes(ary3, ary1->ptr[i])) continue; + if (ary_includes(ary2, ary1->ptr[i])) continue; + if (ary_includes(ary3, ary1->ptr[i])) continue; ary_push(ary3, ary1->ptr[i]); } return ary3; } static VALUE -Fary_and(ary1, ary2) +ary_and(ary1, ary2) struct RArray *ary1, *ary2; { VALUE ary3; - int i, j; + int i; Check_Type(ary2, T_ARRAY); ary3 = ary_new(); for (i=0; i<ary1->len; i++) { - if (Fary_includes(ary2, ary1->ptr[i]) - && !Fary_includes(ary3, ary1->ptr[i])) { + if (ary_includes(ary2, ary1->ptr[i]) + && !ary_includes(ary3, ary1->ptr[i])) { ary_push(ary3, ary1->ptr[i]); } } @@ -914,77 +921,78 @@ Fary_and(ary1, ary2) } static VALUE -Fary_or(ary1, ary2) +ary_or(ary1, ary2) struct RArray *ary1, *ary2; { VALUE ary3; int i; if (TYPE(ary2) != T_ARRAY) { - if (Fary_includes(ary1, ary2)) return (VALUE)ary1; - else return Fary_plus(ary1, ary2); + if (ary_includes(ary1, ary2)) return (VALUE)ary1; + else return ary_plus(ary1, ary2); } ary3 = ary_new(); for (i=0; i<ary1->len; i++) { - if (!Fary_includes(ary3, ary1->ptr[i])) + if (!ary_includes(ary3, ary1->ptr[i])) ary_push(ary3, ary1->ptr[i]); } for (i=0; i<ary2->len; i++) { - if (!Fary_includes(ary3, ary2->ptr[i])) + if (!ary_includes(ary3, ary2->ptr[i])) ary_push(ary3, ary2->ptr[i]); } return ary3; } -extern VALUE C_Kernel; -extern VALUE M_Enumerable; +extern VALUE cKernel; +extern VALUE mEnumerable; +void Init_Array() { - C_Array = rb_define_class("Array", C_Object); - rb_include_module(C_Array, M_Enumerable); - - rb_define_single_method(C_Array, "new", Sary_new, 0); - rb_define_single_method(C_Array, "[]", Sary_create, -1); - rb_define_method(C_Array, "to_s", Fary_to_s, 0); - rb_define_method(C_Array, "_inspect", Fary_inspect, 0); - rb_define_method(C_Array, "to_a", Fary_to_a, 0); - - rb_define_method(C_Array, "print_on", Fary_print_on, 1); - - rb_define_method(C_Array, "==", Fary_equal, 1); - rb_define_method(C_Array, "hash", Fary_hash, 0); - rb_define_method(C_Array, "[]", Fary_aref, -1); - rb_define_method(C_Array, "[]=", Fary_aset, -1); - rb_define_method(C_Array, "<<", Fary_append, 1); - rb_define_method(C_Array, "push", ary_push, 1); - rb_define_method(C_Array, "pop", ary_pop, 0); - rb_define_method(C_Array, "shift", ary_shift, 0); - rb_define_method(C_Array, "unshift", ary_unshift, 1); - rb_define_method(C_Array, "each", Fary_each, 0); - rb_define_method(C_Array, "each_index", Fary_each_index, 0); - rb_define_method(C_Array, "length", Fary_length, 0); - rb_define_alias(C_Array, "size", "length"); - rb_define_method(C_Array, "index", Fary_index, 1); - rb_define_method(C_Array, "indexes", Fary_indexes, -2); - rb_define_method(C_Array, "clone", ary_clone, 0); - rb_define_method(C_Array, "join", Fary_join, -1); - rb_define_method(C_Array, "reverse", Fary_reverse, 0); - rb_define_method(C_Array, "sort", Fary_sort, 0); - rb_define_method(C_Array, "delete", Fary_delete, 1); - rb_define_method(C_Array, "delete_if", Fary_delete_if, 0); - rb_define_method(C_Array, "clear", Fary_clear, 0); - rb_define_method(C_Array, "fill", Fary_fill, -1); - rb_define_method(C_Array, "includes", Fary_includes, 1); - - rb_define_method(C_Array, "assoc", Fary_assoc, 1); - rb_define_method(C_Array, "rassoc", Fary_rassoc, 1); - - rb_define_method(C_Array, "+", Fary_plus, 1); - rb_define_method(C_Array, "*", Fary_times, 1); - - rb_define_method(C_Array, "-", Fary_diff, 1); - rb_define_method(C_Array, "&", Fary_and, 1); - rb_define_method(C_Array, "|", Fary_or, 1); + cArray = rb_define_class("Array", cObject); + rb_include_module(cArray, mEnumerable); + + rb_define_singleton_method(cArray, "new", ary_s_new, 0); + rb_define_singleton_method(cArray, "[]", ary_s_create, -1); + rb_define_method(cArray, "to_s", ary_to_s, 0); + rb_define_method(cArray, "inspect", ary_inspect, 0); + rb_define_method(cArray, "to_a", ary_to_a, 0); + + rb_define_method(cArray, "print_on", ary_print_on, 1); + + rb_define_method(cArray, "==", ary_equal, 1); + rb_define_method(cArray, "hash", ary_hash, 0); + rb_define_method(cArray, "[]", ary_aref, -1); + rb_define_method(cArray, "[]=", ary_aset, -1); + rb_define_method(cArray, "<<", ary_append, 1); + rb_define_method(cArray, "push", ary_push, 1); + rb_define_method(cArray, "pop", ary_pop, 0); + rb_define_method(cArray, "shift", ary_shift, 0); + rb_define_method(cArray, "unshift", ary_unshift, 1); + rb_define_method(cArray, "each", ary_each, 0); + rb_define_method(cArray, "each_index", ary_each_index, 0); + rb_define_method(cArray, "length", ary_length, 0); + rb_define_alias(cArray, "size", "length"); + rb_define_method(cArray, "index", ary_index, 1); + rb_define_method(cArray, "indexes", ary_indexes, -2); + rb_define_method(cArray, "clone", ary_clone, 0); + rb_define_method(cArray, "join", ary_join_method, -1); + rb_define_method(cArray, "reverse", ary_reverse, 0); + rb_define_method(cArray, "sort", ary_sort, 0); + rb_define_method(cArray, "delete", ary_delete, 1); + rb_define_method(cArray, "delete_if", ary_delete_if, 0); + rb_define_method(cArray, "clear", ary_clear, 0); + rb_define_method(cArray, "fill", ary_fill, -1); + rb_define_method(cArray, "includes", ary_includes, 1); + + rb_define_method(cArray, "assoc", ary_assoc, 1); + rb_define_method(cArray, "rassoc", ary_rassoc, 1); + + rb_define_method(cArray, "+", ary_plus, 1); + rb_define_method(cArray, "*", ary_times, 1); + + rb_define_method(cArray, "-", ary_diff, 1); + rb_define_method(cArray, "&", ary_and, 1); + rb_define_method(cArray, "|", ary_or, 1); } diff --git a/assoc.c b/assoc.c deleted file mode 100644 index f3172b3ca0..0000000000 --- a/assoc.c +++ /dev/null @@ -1,150 +0,0 @@ -/************************************************ - - assoc.c - - - $Author: matz $ - $Date: 1995/01/10 10:30:37 $ - created at: Fri Jan 6 10:10:36 JST 1995 - - Copyright (C) 1993-1995 Yukihiro Matsumoto - -************************************************/ - -#include "ruby.h" - -static VALUE C_Assoc; - -static ID eq; - -VALUE rb_to_a(); - -VALUE -assoc_new(car, cdr) - VALUE car, cdr; -{ - NEWOBJ(assoc, struct RAssoc); - OBJSETUP(assoc, C_Assoc, T_ASSOC); - - assoc->car = car; - assoc->cdr = cdr; - - return (VALUE)assoc; -} - -static VALUE -Fassoc_car(assoc) - struct RAssoc *assoc; -{ - return assoc->car; -} - -static VALUE -Fassoc_cdr(assoc) - struct RAssoc *assoc; -{ - return assoc->cdr; -} - -static VALUE -Fassoc_set_car(assoc, val) - struct RAssoc *assoc; - VALUE val; -{ - return assoc->car = val; -} - -static VALUE -Fassoc_set_cdr(assoc, val) - struct RAssoc *assoc; - VALUE val; -{ - return assoc->cdr = val; -} - -static VALUE -Fassoc_equal(assoc1, assoc2) - struct RAssoc *assoc1, *assoc2; -{ - if (TYPE(assoc2) != T_ASSOC) return FALSE; - if (!rb_equal(assoc1->car, assoc2->car)) return FALSE; - return rb_equal(assoc1->cdr, assoc2->cdr); -} - -static VALUE -Fassoc_hash(assoc) - struct RAssoc *assoc; -{ - static ID hash; - int key; - - if (!hash) hash = rb_intern("hash"); - key = rb_funcall(assoc->car, hash, 0, 0); - key ^= rb_funcall(assoc->cdr, hash, 0, 0); - return INT2FIX(key); -} - -static VALUE -Fassoc_to_s(assoc) - struct RAssoc *assoc; -{ - VALUE str1, str2; - static ID to_s; - - if (!to_s) to_s = rb_intern("to_s"); - - str1 = rb_funcall(assoc->car, to_s, 0); - assoc = RASSOC(assoc->cdr); - while (assoc) { - if (TYPE(assoc) != T_ASSOC) { - str2 = rb_funcall(assoc, to_s, 0); - str_cat(str1, RSTRING(str2)->ptr, RSTRING(str2)->len); - break; - } - str2 = rb_funcall(assoc->car, to_s, 0); - str_cat(str1, RSTRING(str2)->ptr, RSTRING(str2)->len); - assoc = RASSOC(assoc->cdr); - } - - return str1; -} - -static VALUE -Fassoc_inspect(assoc) - struct RAssoc *assoc; -{ - VALUE str1, str2; - static ID inspect; - - if (!inspect) inspect = rb_intern("_inspect"); - - str1 = rb_funcall(assoc->car, inspect, 0, 0); - str2 = rb_funcall(assoc->cdr, inspect, 0, 0); - str_cat(str1, "::", 2); - str_cat(str1, RSTRING(str2)->ptr, RSTRING(str2)->len); - - return str1; -} - -extern VALUE C_Kernel; - -Init_Assoc() -{ - C_Assoc = rb_define_class("Assoc", C_Object); - - rb_undef_method(CLASS_OF(C_Assoc), "new"); - rb_undef_method(C_Assoc, "clone"); - - rb_define_method(C_Assoc, "car", Fassoc_car, 0); - rb_define_method(C_Assoc, "cdr", Fassoc_cdr, 0); - - rb_define_method(C_Assoc, "car=", Fassoc_set_car, 1); - rb_define_method(C_Assoc, "cdr=", Fassoc_set_cdr, 1); - - rb_define_method(C_Assoc, "==", Fassoc_equal, 1); - rb_define_method(C_Assoc, "hash", Fassoc_hash, 0); - - rb_define_method(C_Assoc, "to_s", Fassoc_to_s, 0); - rb_define_method(C_Assoc, "_inspect", Fassoc_inspect, 0); - - rb_define_method(C_Kernel, "::", assoc_new, 1); -} @@ -12,8 +12,8 @@ #include <ctype.h> #include <math.h> -extern VALUE C_Integer; -VALUE C_Bignum; +extern VALUE cInteger; +VALUE cBignum; #define BDIGITS(x) RBIGNUM(x)->digits #define BITSPERDIG (sizeof(USHORT)*CHAR_BIT) @@ -23,9 +23,6 @@ VALUE C_Bignum; #define BIGDN(x) ((x) >> BITSPERDIG) #define BIGLO(x) ((x) & (BIGRAD-1)) -#define MAX(a,b) ((a)>(b)?(a):(b)) -#define MIN(a,b) ((a)<(b)?(a):(b)) - static VALUE bignew_1(class, len, sign) VALUE class; @@ -33,7 +30,7 @@ bignew_1(class, len, sign) char sign; { NEWOBJ(big, struct RBignum); - OBJSETUP(big, C_Bignum, T_BIGNUM); + OBJSETUP(big, cBignum, T_BIGNUM); big->sign = sign; big->len = len; BDIGITS(big) = ALLOC_N(USHORT, len); @@ -41,10 +38,10 @@ bignew_1(class, len, sign) return (VALUE)big; } -#define bignew(len,sign) bignew_1(C_Bignum,len,sign) +#define bignew(len,sign) bignew_1(cBignum,len,sign) static VALUE -Sbig_new(class, y) +big_s_new(class, y) VALUE class; struct RBignum *y; { @@ -201,7 +198,7 @@ str2inum(str, base) } if (len <= (sizeof(VALUE)*CHAR_BIT)) { - int result = strtoul(str, Qnil, base); + int result = strtoul(str, 0, base); if (!sign) result = -result; if (FIXABLE(result)) return INT2FIX(result); @@ -283,6 +280,8 @@ big2str(x, base) hbase = 020; } else { + j = 0; + hbase = 0; Fail("bignum cannot treat base %d", base); } @@ -318,7 +317,7 @@ big2str(x, base) } static VALUE -Fbig_to_s(x) +big_to_s(x) struct RBignum *x; { return big2str(x, 10); @@ -345,7 +344,7 @@ big2int(x) } VALUE -Fbig_to_i(x) +big_to_i(x) VALUE x; { int v = big2int(x); @@ -396,14 +395,14 @@ big2dbl(x) } VALUE -Fbig_to_f(x) +big_to_f(x) VALUE x; { return float_new(big2dbl(x)); } static VALUE -Fbig_uminus(x) +big_uminus(x) struct RBignum *x; { VALUE z = big_clone(x); @@ -423,7 +422,12 @@ bigadd(x, y, sign) long num; UINT i, len; - len = MAX(x->len, y->len) + 1; + if (x->len > y->len) { + len = x->len + 1; + } + else { + len = y->len + 1; + } z = (struct RBignum*)bignew(len, sign==y->sign); zds = BDIGITS(z); @@ -486,7 +490,7 @@ bigadd(x, y, sign) } VALUE -Fbig_plus(x, y) +big_plus(x, y) VALUE x, y; { VALUE z; @@ -501,7 +505,7 @@ Fbig_plus(x, y) } VALUE -Fbig_minus(x, y) +big_minus(x, y) VALUE x, y; { if (FIXNUM_P(y)) y = int2big(FIX2INT(y)); @@ -514,7 +518,7 @@ Fbig_minus(x, y) } VALUE -Fbig_mul(x, y) +big_mul(x, y) struct RBignum *x, *y; { UINT i = 0, j; @@ -674,7 +678,7 @@ bigdivmod(x, y, div, mod) } static VALUE -Fbig_div(x, y) +big_div(x, y) VALUE x, y; { VALUE z; @@ -683,13 +687,13 @@ Fbig_div(x, y) else { Check_Type(y, T_BIGNUM); } - bigdivmod(x, y, &z, Qnil); + bigdivmod(x, y, &z, 0); return z; } static VALUE -Fbig_mod(x, y) +big_mod(x, y) VALUE x, y; { VALUE z; @@ -698,13 +702,13 @@ Fbig_mod(x, y) else { Check_Type(y, T_BIGNUM); } - bigdivmod(x, y, Qnil, &z); + bigdivmod(x, y, 0, &z); return z; } static VALUE -Fbig_divmod(x, y) +big_divmod(x, y) VALUE x, y; { VALUE div, mod; @@ -719,10 +723,9 @@ Fbig_divmod(x, y) } VALUE -Fbig_pow(x, y) +big_pow(x, y) VALUE x, y; { - double d1, d2; VALUE z; int n; @@ -739,15 +742,15 @@ Fbig_pow(x, y) while (--n) { while (!(n % 2)) { n = n /2; - x = Fbig_mul(x, x); + x = big_mul(x, x); } - z = Fbig_mul(z, x); + z = big_mul(z, x); } return z; } VALUE -Fbig_and(x, y) +big_and(x, y) struct RBignum *x, *y; { VALUE z; @@ -798,7 +801,7 @@ Fbig_and(x, y) } VALUE -Fbig_or(x, y) +big_or(x, y) struct RBignum *x, *y; { VALUE z; @@ -850,7 +853,7 @@ Fbig_or(x, y) } VALUE -Fbig_xor(x, y) +big_xor(x, y) struct RBignum *x, *y; { VALUE z; @@ -904,7 +907,7 @@ Fbig_xor(x, y) } static VALUE -Fbig_neg(x) +big_neg(x) struct RBignum *x; { VALUE z = big_clone(x); @@ -919,22 +922,22 @@ Fbig_neg(x) return bignorm(z); } -static VALUE Fbig_rshift(); +static VALUE big_rshift(); VALUE -Fbig_lshift(x, y) +big_lshift(x, y) struct RBignum *x; VALUE y; { USHORT *xds, *zds; - UINT shift = NUM2INT(y); + int shift = NUM2INT(y); UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT); UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT); VALUE z; unsigned long num = 0; UINT len, i; - if (shift < 0) return Fbig_rshift(x, INT2FIX(-shift)); + if (shift < 0) return big_rshift(x, INT2FIX(-shift)); xds = BDIGITS(x); len = x->len; z = bignew(len+s1+1, x->sign); @@ -952,19 +955,19 @@ Fbig_lshift(x, y) } static VALUE -Fbig_rshift(x, y) +big_rshift(x, y) struct RBignum *x; VALUE y; { USHORT *xds, *zds; - UINT shift = NUM2INT(y); + int shift = NUM2INT(y); UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT); UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT); VALUE z; unsigned long num = 0; UINT i = x->len, j; - if (shift < 0) return Fbig_lshift(x, INT2FIX(-shift)); + if (shift < 0) return big_lshift(x, INT2FIX(-shift)); if (s1 > x->len) { if (x->sign) return INT2FIX(0); @@ -984,7 +987,7 @@ Fbig_rshift(x, y) } static VALUE -Fbig_aref(x, y) +big_aref(x, y) struct RBignum *x; VALUE y; { @@ -1011,12 +1014,17 @@ Fbig_aref(x, y) } static VALUE -Fbig_cmp(x, y) +big_cmp(x, y) struct RBignum *x, *y; { int xlen = x->len; - Check_Type(x, T_BIGNUM); + if (FIXNUM_P(y)) { + y = (struct RBignum*)int2big(FIX2INT(y)); + } + else { + Check_Type(y, T_BIGNUM); + } if (x->sign > y->sign) return INT2FIX(1); if (x->sign < y->sign) return INT2FIX(-1); if (xlen < y->len) @@ -1032,7 +1040,7 @@ Fbig_cmp(x, y) } static VALUE -Fbig_hash(x) +big_hash(x) struct RBignum *x; { int i, len, key; @@ -1046,7 +1054,7 @@ Fbig_hash(x) } static VALUE -Fbig_coerce(x, y) +big_coerce(x, y) struct RBignum *x; VALUE y; { @@ -1061,7 +1069,7 @@ Fbig_coerce(x, y) } static VALUE -Fbig_abs(x) +big_abs(x) struct RBignum *x; { if (!x->sign) { @@ -1071,32 +1079,33 @@ Fbig_abs(x) return (VALUE)x; } +void Init_Bignum() { - C_Bignum = rb_define_class("Bignum", C_Integer); - rb_define_single_method(C_Bignum, "new", Sbig_new, 1); - - rb_define_method(C_Bignum, "to_s", Fbig_to_s, 0); - rb_define_method(C_Bignum, "coerce", Fbig_coerce, 1); - rb_define_method(C_Bignum, "-@", Fbig_uminus, 0); - rb_define_method(C_Bignum, "+", Fbig_plus, 1); - rb_define_method(C_Bignum, "-", Fbig_minus, 1); - rb_define_method(C_Bignum, "*", Fbig_mul, 1); - rb_define_method(C_Bignum, "/", Fbig_div, 1); - rb_define_method(C_Bignum, "%", Fbig_mod, 1); - rb_define_method(C_Bignum, "divmod", Fbig_divmod, 1); - rb_define_method(C_Bignum, "**", Fbig_pow, 1); - rb_define_method(C_Bignum, "&", Fbig_and, 1); - rb_define_method(C_Bignum, "|", Fbig_or, 1); - rb_define_method(C_Bignum, "^", Fbig_xor, 1); - rb_define_method(C_Bignum, "~", Fbig_neg, 0); - rb_define_method(C_Bignum, "<<", Fbig_lshift, 1); - rb_define_method(C_Bignum, ">>", Fbig_rshift, 1); - rb_define_method(C_Bignum, "[]", Fbig_aref, 1); - - rb_define_method(C_Bignum, "<=>", Fbig_cmp, 1); - rb_define_method(C_Bignum, "hash", Fbig_hash, 0); - rb_define_method(C_Bignum, "to_i", Fbig_to_i, 0); - rb_define_method(C_Bignum, "to_f", Fbig_to_f, 0); - rb_define_method(C_Bignum, "abs_f", Fbig_abs, 0); + cBignum = rb_define_class("Bignum", cInteger); + rb_define_singleton_method(cBignum, "new", big_s_new, 1); + + rb_define_method(cBignum, "to_s", big_to_s, 0); + rb_define_method(cBignum, "coerce", big_coerce, 1); + rb_define_method(cBignum, "-@", big_uminus, 0); + rb_define_method(cBignum, "+", big_plus, 1); + rb_define_method(cBignum, "-", big_minus, 1); + rb_define_method(cBignum, "*", big_mul, 1); + rb_define_method(cBignum, "/", big_div, 1); + rb_define_method(cBignum, "%", big_mod, 1); + rb_define_method(cBignum, "divmod", big_divmod, 1); + rb_define_method(cBignum, "**", big_pow, 1); + rb_define_method(cBignum, "&", big_and, 1); + rb_define_method(cBignum, "|", big_or, 1); + rb_define_method(cBignum, "^", big_xor, 1); + rb_define_method(cBignum, "~", big_neg, 0); + rb_define_method(cBignum, "<<", big_lshift, 1); + rb_define_method(cBignum, ">>", big_rshift, 1); + rb_define_method(cBignum, "[]", big_aref, 1); + + rb_define_method(cBignum, "<=>", big_cmp, 1); + rb_define_method(cBignum, "hash", big_hash, 0); + rb_define_method(cBignum, "to_i", big_to_i, 0); + rb_define_method(cBignum, "to_f", big_to_f, 0); + rb_define_method(cBignum, "abs_f", big_abs, 0); } @@ -11,22 +11,21 @@ ************************************************/ #include "ruby.h" -#include "env.h" #include "node.h" #include "st.h" struct st_table *new_idhash(); extern st_table *rb_class_tbl; -extern VALUE C_Class; -extern VALUE C_Module; +extern VALUE cClass; +extern VALUE cModule; VALUE class_new(super) struct RClass *super; { NEWOBJ(cls, struct RClass); - OBJSETUP(cls, C_Class, T_CLASS); + OBJSETUP(cls, cClass, T_CLASS); cls->super = super; cls->m_tbl = new_idhash(); @@ -35,7 +34,7 @@ class_new(super) } VALUE -single_class_new(super) +singleton_class_new(super) struct RClass *super; { struct RClass *cls = (struct RClass*)class_new(super); @@ -56,13 +55,13 @@ clone_method(mid, body, tbl) } VALUE -single_class_clone(class) +singleton_class_clone(class) struct RClass *class; { if (!FL_TEST(class, FL_SINGLE)) return (VALUE)class; else { - /* copy single(unnamed) class */ + /* copy singleton(unnamed) class */ NEWOBJ(clone, struct RClass); CLONESETUP(clone, class); @@ -81,9 +80,11 @@ rb_define_class_id(id, super) { struct RClass *cls = (struct RClass*)class_new(super); + if (!super) super = (struct RBasic*)cClass; + cls = (struct RClass*)class_new(super); rb_name_class(cls, id); /* make metaclass */ - RBASIC(cls)->class = single_class_new(super?super->class:C_Class); + RBASIC(cls)->class = singleton_class_new(super->class); return (VALUE)cls; } @@ -104,6 +105,7 @@ rb_define_class(name, super) return class; } +VALUE rb_define_class_under(under, name, super) VALUE under; char *name; @@ -124,7 +126,7 @@ VALUE module_new() { NEWOBJ(mdl, struct RClass); - OBJSETUP(mdl, C_Module, T_MODULE); + OBJSETUP(mdl, cModule, T_MODULE); mdl->super = Qnil; mdl->m_tbl = new_idhash(); @@ -158,6 +160,7 @@ rb_define_module(name) return module; } +VALUE rb_define_module_under(under, name) VALUE under; char *name; @@ -176,10 +179,8 @@ static struct RClass * include_class_new(module, super) struct RClass *module, *super; { - struct RClass *p; - NEWOBJ(cls, struct RClass); - OBJSETUP(cls, C_Class, T_ICLASS); + OBJSETUP(cls, cClass, T_ICLASS); cls->m_tbl = module->m_tbl; cls->iv_tbl = module->iv_tbl; @@ -202,8 +203,15 @@ rb_include_module(class, module) if (!module) return; - Check_Type(module, T_MODULE); + switch (TYPE(module)) { + case T_MODULE: + case T_CLASS: + break; + default: + Check_Type(module, T_MODULE); + } + if (class == module) return; if (BUILTIN_TYPE(class) == T_CLASS) { rb_clear_cache(class); } @@ -226,6 +234,16 @@ rb_include_module(class, module) } void +rb_define_method_id(class, name, func, argc) + struct RClass *class; + ID name; + VALUE (*func)(); + int argc; +{ + rb_add_method(class, name, NEW_CFUNC(func, argc), NOEX_PUBLIC); +} + +void rb_define_method(class, name, func, argc) struct RClass *class; char *name; @@ -254,7 +272,7 @@ rb_define_private_method(class, name, func, argc) } VALUE -rb_single_class(obj) +rb_singleton_class(obj) struct RBasic *obj; { switch (TYPE(obj)) { @@ -264,24 +282,24 @@ rb_single_class(obj) case T_STRUCT: break; default: - Fail("can't define single method for built-in class"); + Fail("can't define singleton method for built-in class"); break; } if (FL_TEST(obj->class, FL_SINGLE)) { return (VALUE)obj->class; } - return obj->class = single_class_new(obj->class); + return obj->class = singleton_class_new(obj->class); } void -rb_define_single_method(obj, name, func, argc) +rb_define_singleton_method(obj, name, func, argc) VALUE obj; char *name; VALUE (*func)(); int argc; { - rb_define_method(rb_single_class(obj), name, func, argc); + rb_define_method(rb_singleton_class(obj), name, func, argc); } void @@ -292,7 +310,7 @@ rb_define_module_function(module, name, func, argc) int argc; { rb_define_private_method(module, name, func, argc); - rb_define_single_method(module, name, func, argc); + rb_define_singleton_method(module, name, func, argc); } void @@ -304,37 +322,30 @@ rb_define_alias(class, name1, name2) } void -rb_define_attr(class, name, pub) +rb_define_attr(class, id, pub) struct RClass *class; - char *name; + ID id; int pub; { + char *name; char *buf; ID attr, attreq, attriv; + name = rb_id2name(id); attr = rb_intern(name); buf = ALLOCA_N(char,strlen(name)+2); sprintf(buf, "%s=", name); attreq = rb_intern(buf); sprintf(buf, "@%s", name); attriv = rb_intern(buf); - if (rb_method_boundp(class, attr) == Qnil) { + if (rb_method_boundp(class, attr) == FALSE) { rb_add_method(class, attr, NEW_IVAR(attriv), 0); } - if (pub && rb_method_boundp(class, attreq) == Qnil) { + if (pub && rb_method_boundp(class, attreq) == FALSE) { rb_add_method(class, attreq, NEW_ATTRSET(attriv), 0); } } -void -rb_define_single_attr(obj, name, pub) - VALUE obj; - char *name; - int pub; -{ - rb_define_attr(rb_single_class(obj), name, pub); -} - #include <varargs.h> #include <ctype.h> @@ -409,4 +420,5 @@ rb_scan_args(argc, argv, fmt, va_alist) error: Fail("bad scan arg format: %s", fmt); + return 0; } @@ -12,12 +12,12 @@ #include "ruby.h" -VALUE M_Comparable; +VALUE mComparable; static ID cmp; static VALUE -Fcmp_eq(x, y) +cmp_eq(x, y) VALUE x, y; { VALUE c = rb_funcall(x, cmp, 1, y); @@ -28,7 +28,7 @@ Fcmp_eq(x, y) } static VALUE -Fcmp_gt(x, y) +cmp_gt(x, y) VALUE x, y; { VALUE c = rb_funcall(x, cmp, 1, y); @@ -39,7 +39,7 @@ Fcmp_gt(x, y) } static VALUE -Fcmp_ge(x, y) +cmp_ge(x, y) VALUE x, y; { VALUE c = rb_funcall(x, cmp, 1, y); @@ -50,7 +50,7 @@ Fcmp_ge(x, y) } static VALUE -Fcmp_lt(x, y) +cmp_lt(x, y) VALUE x, y; { VALUE c = rb_funcall(x, cmp, 1, y); @@ -61,7 +61,7 @@ Fcmp_lt(x, y) } static VALUE -Fcmp_le(x, y) +cmp_le(x, y) VALUE x, y; { VALUE c = rb_funcall(x, cmp, 1, y); @@ -72,7 +72,7 @@ Fcmp_le(x, y) } static VALUE -Fcmp_between(x, min, max) +cmp_between(x, min, max) VALUE x, min, max; { VALUE c = rb_funcall(x, cmp, 1, min); @@ -85,15 +85,16 @@ Fcmp_between(x, min, max) return TRUE; } +void Init_Comparable() { - M_Comparable = rb_define_module("Comparable"); - rb_define_method(M_Comparable, "==", Fcmp_eq, 1); - rb_define_method(M_Comparable, ">", Fcmp_gt, 1); - rb_define_method(M_Comparable, ">=", Fcmp_ge, 1); - rb_define_method(M_Comparable, "<", Fcmp_lt, 1); - rb_define_method(M_Comparable, "<=", Fcmp_le, 1); - rb_define_method(M_Comparable, "between", Fcmp_between, 2); + mComparable = rb_define_module("Comparable"); + rb_define_method(mComparable, "==", cmp_eq, 1); + rb_define_method(mComparable, ">", cmp_gt, 1); + rb_define_method(mComparable, ">=", cmp_ge, 1); + rb_define_method(mComparable, "<", cmp_lt, 1); + rb_define_method(mComparable, "<=", cmp_le, 1); + rb_define_method(mComparable, "between?", cmp_between, 2); cmp = rb_intern("<=>"); } diff --git a/configure.in b/configure.in index 77153b6ed0..fc87754cb8 100644 --- a/configure.in +++ b/configure.in @@ -4,6 +4,23 @@ AC_INIT(ruby.h) PROGS="ruby" AC_SUBST(PROGS)dnl +dnl checks for alternative programs +AC_ARG_WITH(gcc, [--without-gcc never use gcc], [ + case $withval in + no) CC=cc + without_gcc=yes;; + yes) CC=gcc + without_gcc=no;; + *) CC=$withval + without_gcc=$withval;; + esac], [without_gcc=no]) +dnl If the user switches compilers, we can't believe the cache +if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC" +then + AC_ERROR(cached CC is different -- throw away $cache_file +(it is also a good idea to do 'make clean' before compiling)) +fi + dnl Checks for programs. AC_PROG_CC AC_PROG_GCC_TRADITIONAL @@ -11,16 +28,21 @@ AC_PROG_YACC AC_PROG_INSTALL AC_PROG_MAKE_SET +# checks for UNIX variants that set C preprocessor variables +AC_AIX +AC_MINIX + dnl Checks for libraries. -AC_CHECK_LIB(m, pow) AC_CHECK_LIB(crypt, crypt) -AC_CHECK_LIB(dl, dlopen, [:]) +AC_CHECK_LIB(dl, dlopen, [:]) # Dynamic linking for SunOS/Solaris and SYSV +AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h limits.h sys/file.h sys/ioctl.h sys/time.h unistd.h\ - stdlib.h syscall.h a.out.h string.h utime.h) +AC_CHECK_HEADERS(limits.h sys/file.h sys/ioctl.h pwd.h\ + sys/time.h sys/times.h sys/param.h unistd.h\ + syscall.h a.out.h string.h utime.h memory.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T @@ -34,12 +56,12 @@ AC_TYPE_GETGROUPS AC_TYPE_SIGNAL AC_FUNC_ALLOCA AC_FUNC_VFORK -AC_REPLACE_FUNCS(memmove mkdir strerror strftime\ - strstr strtoul strdup) +AC_REPLACE_FUNCS(dup2 setenv memmove mkdir strerror strftime\ + strstr strtoul strdup crypt) AC_CHECK_FUNCS(fmod killpg random wait4 waitpid syscall getcwd\ + truncate chsize times utimes fcntl\ setruid seteuid setreuid setrgid setegid setregid\ - getpriority sigprocmask dlopen utimes) -AC_CHECK_FUNC(setenv, [], AC_CHECK_FUNCS(putenv)) + getgroups getpriority sigprocmask dlopen) if test "$ac_cv_func strftime" = no; then AC_STRUCT_TIMEZONE AC_TRY_LINK([], @@ -47,89 +69,125 @@ if test "$ac_cv_func strftime" = no; then fi AC_C_BIGENDIAN -AC_MSG_CHECKING(std stdio) -AC_CACHE_VAL(rb_cv_stdstdio, -[AC_TRY_COMPILE([#include <stdio.h>], - [stdin->_cnt > 0;], - rb_cv_stdstdio=yes, - rb_cv_stdstdio=no)]) -AC_MSG_RESULT($rb_cv_stdstdio) -if test "$rb_cv_stdstdio" = yes; then - AC_DEFINE(STDSTDIO) + +AC_MSG_CHECKING([count field in FILE structures]) +AC_CACHE_VAL(rb_cv_fcnt, +[AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->_cnt = 0;], rb_cv_fcnt="_cnt", ) +if test "$rb_cv_fcnt=" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->__cnt = 0;], rb_cv_fcnt="__cnt", ) +fi +if test "$rb_cv_fcnt=" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->_r = 0;], rb_cv_fcnt="_r", ) +fi +if test "$rb_cv_fcnt=" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->readCount = 0;], rb_cv_fcnt="readCount", ) +fi]) +if test "$rb_cv_fcnt"; then + AC_MSG_RESULT($rb_cv_fcnt) + AC_DEFINE_UNQUOTED(FILE_COUNT, $rb_cv_fcnt) +else + AC_MSG_RESULT([not found(OK if using GNU libc)]) fi -AC_MSG_CHECKING(struct passwd) -AC_EGREP_HEADER(pw_change, pwd.h, AC_DEFINE(PW_CHANGE)) -AC_EGREP_HEADER(pw_quota, pwd.h, AC_DEFINE(PW_QUOTA)) -AC_EGREP_HEADER(pw_age, pwd.h, AC_DEFINE(PW_AGE)) -AC_EGREP_HEADER(pw_class, pwd.h, AC_DEFINE(PW_CLASS)) -AC_EGREP_HEADER(pw_comment, pwd.h, AC_DEFINE(PW_COMMENT)) -AC_EGREP_HEADER(pw_expire, pwd.h, AC_DEFINE(PW_EXPIRE)) -AC_MSG_RESULT(done) - -if test "$ac_cv_header_a_out_h" = yes; then + +if test "$ac_cv_func_getpwent" = yes; then + AC_MSG_CHECKING(struct passwd) + AC_EGREP_HEADER(pw_change, pwd.h, AC_DEFINE(PW_CHANGE)) + AC_EGREP_HEADER(pw_quota, pwd.h, AC_DEFINE(PW_QUOTA)) + AC_EGREP_HEADER(pw_age, pwd.h, AC_DEFINE(PW_AGE)) + AC_EGREP_HEADER(pw_class, pwd.h, AC_DEFINE(PW_CLASS)) + AC_EGREP_HEADER(pw_comment, pwd.h, AC_DEFINE(PW_COMMENT)) + AC_EGREP_HEADER(pw_expire, pwd.h, AC_DEFINE(PW_EXPIRE)) + AC_MSG_RESULT(done) +fi + +dnl wheather use dln_a_out ot not +AC_ARG_WITH(dln-a-out, [--with-dln-a-out use dln_a_out if possible], [ + case $withval in + yes) with_dln_a_out=yes;; + *) with_dln_a_out=no;; + esac], [with_dln_a_out=no]) + +if test "$with_dln_a_out" = yes && test "$ac_cv_header_a_out_h" = yes; then + AC_MSG_CHECKING(whether matz's dln works) - cp confdefs.h config.h - AC_CACHE_VAL(rb_cv_mydln, + cat confdefs.h > config.h + AC_CACHE_VAL(rb_cv_dln_a_out, [AC_TRY_COMPILE([ -#define USE_MY_DLN +#define USE_DLN_A_OUT #include "dln.c" ], [], - rb_cv_mydln=yes, - rb_cv_mydln=no)]) - AC_MSG_RESULT($rb_cv_mydln) - if test "$rb_cv_mydln" = yes; then - AC_DEFINE(USE_MY_DLN) + rb_cv_dln_a_out=yes, + rb_cv_dln_a_out=no)]) + AC_MSG_RESULT($rb_cv_dln_a_out) + if test "$rb_cv_dln_a_out" = yes; then + AC_DEFINE(USE_DLN_A_OUT) fi +else + rb_cv_dln_a_out=no fi AC_SUBST(STATIC)dnl AC_SUBST(CCDLFLAGS)dnl -AC_SUBST(LDDLFLAGS)dnl +AC_SUBST(LDSHARED)dnl AC_SUBST(DLEXT)dnl -AC_SUBST(EXTMAKE)dnl -EXTMAKE= STATIC= -if test "$rb_cv_mydln" = yes; then - EXTMAKE=extmake - if test "$HOSTTYPE" = sparc; then - if test "$ac_cv_prog_CC" = gcc; then - STATIC=-static - else - STATIC=-Bstatic - fi +if test "$rb_cv_dln_a_out" = yes; then + if test "$GCC" = yes; then + STATIC=-static + else + STATIC=-Bstatic fi DLEXT=o AC_DEFINE(DLEXT, ".o") CCDLFLAGS= LDCMD= -elif test "$ac_cv_lib_dl" = yes || test "$ac_cv_func_dlopen" = yes; then + +else + AC_CANONICAL_HOST - echo "please mail this value to matz -- $host_os" - EXTMAKE=extmake - DLEXT=so - AC_DEFINE(DLEXT, ".so") - if test "$ac_cv_prog_CC" = gcc; then + case "$host_os" in + hpux*) DLEXT=sl + AC_DEFINE(DLEXT, ".sl");; + *) DLEXT=so + AC_DEFINE(DLEXT, ".so");; + esac + + if test "$GCC" = yes; then CCDLFLAGS=-fpic else case "$host_os" in - hpux*) CCDLFLAGS='+z' ;; + hpux*) CCDLFLAGS='+z';; solaris*|irix*) CCDLFLAGS='-K pic' ;; sunos*) CCDLFLAGS='-pic' ;; svr4*|esix*) CCDLFLAGS='-Kpic' ;; *) CCDLFLAGS='' ;; esac fi + case "$host_os" in - hpux*) LDDLFLAGS='-b' ;; - solaris*) LDDLFLAGS='-G' ;; - sunos*) LDDLFLAGS='-assert nodefinitions' ;; - svr4*|esix*) LDDLFLAGS="-G $LDFLAGS" ;; - *) LDDLFLAGS="" ;; + hpux*) LDSHARED='ld -b' ;; + solaris*) LDSHARED='ld -G' ;; + sunos*) LDSHARED='ld -assert nodefinitions' ;; + svr4*|esix*) LDSHARED="ld -G" ;; + linux*) LDSHARED="gcc-elf -shared" ;; + *) LDSHARED='ld' ;; esac fi -cp confdefs.h config.h +if test "$prefix" = NONE; then + AC_DEFINE_UNQUOTED(RUBY_LIB, ".:${ac_default_prefix}/lib/ruby") +else + AC_DEFINE_UNQUOTED(RUBY_LIB, ".:${prefix}/lib/ruby") +fi + +echo "creating config.h" +cat confdefs.h > config.h + AC_OUTPUT(Makefile ext/extmk.rb) @@ -16,20 +16,10 @@ #define EUC #undef SJIS +#define SAFE_SIGHANDLE -/* define USE_DL to load object file(.o). */ -#define USE_DL - -/* a.out.h or dlopen() needed to load object */ -#if !defined(HAVE_DLOPEN) && !defined(HAVE_A_OUT_H) -# undef USE_DL -#endif - -#ifdef USE_MY_DLN -# define LIBC_NAME "libc.a" -# define DLN_DEFAULT_PATH "/lib:/usr/lib:." +#ifdef NT +#include "missing/nt.h" #endif -#define SAFE_SIGHANDLE - #endif @@ -10,7 +10,6 @@ ************************************************/ -#include <sys/param.h> #include "ruby.h" #include <sys/types.h> @@ -18,10 +17,10 @@ #include <unistd.h> #endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> #else -char *getenv(); +# define MAXPATHLEN 1024 #endif #if HAVE_DIRENT_H @@ -39,9 +38,14 @@ char *getenv(); # if HAVE_NDIR_H # include <ndir.h> # endif +# ifdef NT +# include "missing/dirent.h" +# endif #endif -static VALUE C_Dir; +char *getenv(); + +static VALUE cDir; static ID id_dir; static void @@ -52,7 +56,7 @@ free_dir(dir) } static VALUE -Sdir_open(dir_class, dirname) +dir_s_open(dir_class, dirname) VALUE dir_class; struct RString *dirname; { @@ -66,7 +70,7 @@ Sdir_open(dir_class, dirname) obj = obj_alloc(dir_class); if (!id_dir) id_dir = rb_intern("dir"); - Make_Data_Struct(obj, id_dir, DIR*, Qnil, free_dir, d); + Make_Data_Struct(obj, id_dir, DIR*, 0, free_dir, d); *d = dirp; return obj; @@ -87,7 +91,7 @@ closeddir() } static VALUE -Fdir_each(dir) +dir_each(dir) VALUE dir; { extern VALUE rb_lastline; @@ -103,7 +107,7 @@ Fdir_each(dir) } static VALUE -Fdir_tell(dir) +dir_tell(dir) VALUE dir; { DIR *dirp; @@ -115,7 +119,7 @@ Fdir_tell(dir) } static VALUE -Fdir_seek(dir, pos) +dir_seek(dir, pos) VALUE dir, pos; { DIR *dirp; @@ -126,7 +130,7 @@ Fdir_seek(dir, pos) } static VALUE -Fdir_rewind(dir) +dir_rewind(dir) VALUE dir; { DIR *dirp; @@ -137,7 +141,7 @@ Fdir_rewind(dir) } static VALUE -Fdir_close(dir) +dir_close(dir) VALUE dir; { DIR **dirpp; @@ -151,7 +155,7 @@ Fdir_close(dir) } static VALUE -Sdir_chdir(argc, argv, obj) +dir_s_chdir(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -178,7 +182,7 @@ Sdir_chdir(argc, argv, obj) } static VALUE -Sdir_getwd(dir) +dir_s_getwd(dir) VALUE dir; { extern char *getwd(); @@ -194,7 +198,7 @@ Sdir_getwd(dir) } static VALUE -Sdir_chroot(dir, path) +dir_s_chroot(dir, path) VALUE dir, path; { Check_Type(path, T_STRING); @@ -206,7 +210,7 @@ Sdir_chroot(dir, path) } static VALUE -Sdir_mkdir(argc, argv, obj) +dir_s_mkdir(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -229,7 +233,7 @@ Sdir_mkdir(argc, argv, obj) } static VALUE -Sdir_rmdir(obj, dir) +dir_s_rmdir(obj, dir) VALUE obj; struct RString *dir; { @@ -262,7 +266,7 @@ push_globs(ary, s) free(fnames); } -static int +static void push_braces(ary, s) VALUE ary; char *s; @@ -272,11 +276,11 @@ push_braces(ary, s) char *lbrace, *rbrace; p = s; - lbrace = rbrace = Qnil; + lbrace = rbrace = 0; while (*p) { if (*p == '{' && !lbrace) lbrace = p; if (*p == '}' && lbrace) rbrace = p; - *p++; + p++; } if (lbrace) { @@ -300,7 +304,7 @@ push_braces(ary, s) } static VALUE -Sdir_glob(dir, str) +dir_s_glob(dir, str) VALUE dir; struct RString *str; { @@ -342,31 +346,32 @@ Sdir_glob(dir, str) return ary; } +void Init_Dir() { - extern VALUE M_Enumerable; + extern VALUE mEnumerable; - C_Dir = rb_define_class("Dir", C_Object); + cDir = rb_define_class("Dir", cObject); - rb_include_module(C_Dir, M_Enumerable); + rb_include_module(cDir, mEnumerable); - rb_define_single_method(C_Dir, "open", Sdir_open, 1); + rb_define_singleton_method(cDir, "open", dir_s_open, 1); - rb_define_method(C_Dir,"each", Fdir_each, 0); - rb_define_method(C_Dir,"rewind", Fdir_rewind, 0); - rb_define_method(C_Dir,"tell", Fdir_tell, 0); - rb_define_method(C_Dir,"seek", Fdir_seek, 1); - rb_define_method(C_Dir,"close", Fdir_close, 0); + rb_define_method(cDir,"each", dir_each, 0); + rb_define_method(cDir,"rewind", dir_rewind, 0); + rb_define_method(cDir,"tell", dir_tell, 0); + rb_define_method(cDir,"seek", dir_seek, 1); + rb_define_method(cDir,"close", dir_close, 0); - rb_define_single_method(C_Dir,"chdir", Sdir_chdir, -1); - rb_define_single_method(C_Dir,"getwd", Sdir_getwd, 0); - rb_define_single_method(C_Dir,"pwd", Sdir_getwd, 0); - rb_define_single_method(C_Dir,"chroot", Sdir_chroot, 1); - rb_define_single_method(C_Dir,"mkdir", Sdir_mkdir, -1); - rb_define_single_method(C_Dir,"rmdir", Sdir_rmdir, 1); - rb_define_single_method(C_Dir,"delete", Sdir_rmdir, 1); - rb_define_single_method(C_Dir,"unlink", Sdir_rmdir, 1); + rb_define_singleton_method(cDir,"chdir", dir_s_chdir, -1); + rb_define_singleton_method(cDir,"getwd", dir_s_getwd, 0); + rb_define_singleton_method(cDir,"pwd", dir_s_getwd, 0); + rb_define_singleton_method(cDir,"chroot", dir_s_chroot, 1); + rb_define_singleton_method(cDir,"mkdir", dir_s_mkdir, -1); + rb_define_singleton_method(cDir,"rmdir", dir_s_rmdir, 1); + rb_define_singleton_method(cDir,"delete", dir_s_rmdir, 1); + rb_define_singleton_method(cDir,"unlink", dir_s_rmdir, 1); - rb_define_single_method(C_Dir,"glob", Sdir_glob, 1); - rb_define_single_method(C_Dir,"[]", Sdir_glob, 1); + rb_define_singleton_method(cDir,"glob", dir_s_glob, 1); + rb_define_singleton_method(cDir,"[]", dir_s_glob, 1); } @@ -14,19 +14,25 @@ #include "defines.h" #include "dln.h" +char *dln_argv0; + #if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) #include <alloca.h> #endif +void *xmalloc(); +void *xcalloc(); +void *xrealloc(); + #include <stdio.h> -#include <sys/param.h> #include <sys/file.h> +#include <sys/types.h> #include <sys/stat.h> -#ifdef HAVE_STDLIB_H -# include <stdlib.h> +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> #else -char *getenv(); +# define MAXPATHLEN 1024 #endif #ifdef HAVE_UNISTD_H @@ -35,15 +41,29 @@ char *getenv(); #if defined (HAVE_STRING_H) # include <string.h> -#else /* !HAVE_STRING_H */ +#else # include <strings.h> -#endif /* !HAVE_STRING_H */ +#endif + +char *strdup(); + +char *getenv(); -#ifdef RUBY int eaccess(); + +#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) +/* dynamic load with dlopen() */ +# define USE_DLN_DLOPEN +#endif + +#ifndef FUNCNAME_PATTERN +# if defined(hpux) || defined(__NetBSD__) || defined(__BORLANDC__) +# define FUNCNAME_PATTERN "_Init_%.200s" +# else +# define FUNCNAME_PATTERN "Init_%.200s" +# endif #endif -#ifdef USE_DL static void init_funcname(buf, file) char *buf, *file; @@ -54,7 +74,7 @@ init_funcname(buf, file) for (p = file, slash = p-1; *p; p++) /* Find position of last '/' */ if (*p == '/') slash = p; - sprintf(buf, "init_%s", slash + 1); + sprintf(buf, FUNCNAME_PATTERN, slash + 1); for (p = buf; *p; p++) { /* Delete suffix it it exists */ if (*p == '.') { *p = '\0'; break; @@ -62,83 +82,27 @@ init_funcname(buf, file) } } -# if defined(HAVE_DLOPEN) && !defined(USE_MY_DLN) - -/* dynamic load with dlopen() */ -#include <dlfcn.h> +#ifdef USE_DLN_A_OUT -int -dln_init(file) - char *file; -{ - return 0; -} - -int -dln_load(file) - char *file; -{ - void *handle; - char buf[MAXPATHLEN]; - void (*init_fct)(); - int len = strlen(file); -#ifndef RTLD_LAZY -# define RTLD_LAZY 1 +#ifndef LIBC_NAME +# define LIBC_NAME "libc.a" #endif - strcpy(buf, file); - if (len > 3 - && (buf[len-1] == 'o' || buf[len-1] == 'a') - && buf[len-2] == '.') { - buf[len-1] = 's'; buf[len] = 'o'; buf[len+1] = '\0'; - } - - /* Load file */ - if ((handle = dlopen(buf, RTLD_LAZY)) == NULL) { - return -1; - } - - /* Load the file as an object one */ - init_funcname(buf, file); - - if ((init_fct = (void(*)())dlsym(handle, buf)) == NULL) { - buf[0] = 'I'; /* try Init_.. */ - if ((init_fct = (void(*)())dlsym(handle, buf)) == NULL) { - return -1; - } - } - /* Call the init code */ - (*init_fct)(); - - return 0; -} - -char * -dln_strerror() -{ - return dlerror(); -} - -int -dln_load_lib(lib) - char *lib; -{ - return 0; -} - -# else +#ifndef DLN_DEFAULT_PATH +# define DLN_DEFAULT_PATH "/lib:/usr/lib:." +#endif #include <errno.h> static int dln_errno; #define DLN_ENOEXEC ENOEXEC /* Exec format error */ -#define DLN_ECONFL 101 /* Symbol name conflict */ -#define DLN_ENOINIT 102 /* No inititalizer given */ -#define DLN_EUNDEF 103 /* Undefine symbol remains */ -#define DLN_ENOTLIB 104 /* Not a library file */ -#define DLN_EBADLIB 105 /* Malformed library file */ -#define DLN_EINIT 106 /* Not initialized */ +#define DLN_ECONFL 201 /* Symbol name conflict */ +#define DLN_ENOINIT 202 /* No inititalizer given */ +#define DLN_EUNDEF 203 /* Undefine symbol remains */ +#define DLN_ENOTLIB 204 /* Not a library file */ +#define DLN_EBADLIB 205 /* Malformed library file */ +#define DLN_EINIT 206 /* Not initialized */ static int dln_init_p = 0; @@ -157,8 +121,10 @@ static int dln_init_p = 0; static st_table *sym_tbl; static st_table *undef_tbl; +static int load_lib(); + static int -dln_load_header(fd, hdrp, disp) +load_header(fd, hdrp, disp) int fd; struct exec *hdrp; long disp; @@ -178,13 +144,30 @@ dln_load_header(fd, hdrp, disp) return 0; } +#if defined(sequent) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr) +#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr) +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#endif + +/* Default macros */ +#ifndef RELOC_ADDRESS +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#endif + #if defined(sun) && defined(sparc) /* Sparc (Sun 4) macros */ # undef relocation_info # define relocation_info reloc_info_sparc -# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type]) -# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type]) -# define R_LENGTH(r) (reloc_r_length[(r)->r_type]) +# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type]) +# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type]) +# define R_LENGTH(r) (reloc_r_length[(r)->r_type]) static int reloc_r_rightshift[] = { 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, }; @@ -197,14 +180,24 @@ static int reloc_r_length[] = { # define R_PCREL(r) \ ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) # define R_SYMBOL(r) ((r)->r_index) -#else -# define R_LENGTH(r) ((r)->r_length) -# define R_PCREL(r) ((r)->r_pcrel) -# define R_SYMBOL(r) ((r)->r_symbolnum) +#endif + +#if defined(sequent) +#define R_SYMBOL(r) ((r)->r_symbolnum) +#define R_MEMORY_SUB(r) ((r)->r_bsr) +#define R_PCREL(r) ((r)->r_pcrel || (r)->r_bsr) +#define R_LENGTH(r) ((r)->r_length) +#endif + +#ifndef R_SYMBOL +# define R_SYMBOL(r) ((r)->r_symbolnum) +# define R_MEMORY_SUB(r) 0 +# define R_PCREL(r) ((r)->r_pcrel) +# define R_LENGTH(r) ((r)->r_length) #endif static struct relocation_info * -dln_load_reloc(fd, hdrp, disp) +load_reloc(fd, hdrp, disp) int fd; struct exec *hdrp; long disp; @@ -231,7 +224,7 @@ dln_load_reloc(fd, hdrp, disp) } static struct nlist * -dln_load_sym(fd, hdrp, disp) +load_sym(fd, hdrp, disp) int fd; struct exec *hdrp; long disp; @@ -241,7 +234,6 @@ dln_load_sym(fd, hdrp, disp) struct nlist * end; long displ; int size; - st_table *tbl; lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0); if (read(fd, &size, sizeof(int)) != sizeof(int)) { @@ -276,7 +268,7 @@ dln_load_sym(fd, hdrp, disp) } static st_table * -dln_sym_hash(hdrp, syms) +sym_hash(hdrp, syms) struct exec *hdrp; struct nlist *syms; { @@ -297,31 +289,29 @@ dln_sym_hash(hdrp, syms) return tbl; } -int +static int dln_init(prog) - char *prog; { char *file; - int fd, size; + int fd; struct exec hdr; struct nlist *syms; - if (dln_init_p == 1) return; + if (dln_init_p == 1) return 0; file = dln_find_exe(prog, NULL); - if (file == NULL) return -1; - if ((fd = open(file, O_RDONLY)) < 0) { + if (file == NULL || (fd = open(file, O_RDONLY)) < 0) { dln_errno = errno; return -1; } - if (dln_load_header(fd, &hdr, 0) == -1) return -1; - syms = dln_load_sym(fd, &hdr, 0); + if (load_header(fd, &hdr, 0) == -1) return -1; + syms = load_sym(fd, &hdr, 0); if (syms == NULL) { close(fd); return -1; } - sym_tbl = dln_sym_hash(&hdr, syms); + sym_tbl = sym_hash(&hdr, syms); if (sym_tbl == NULL) { /* file may be start with #! */ char c = '\0'; char buf[MAXPATHLEN]; @@ -370,8 +360,8 @@ dln_init(prog) return -1; } -long -dln_load_text_data(fd, hdrp, bss, disp) +static long +load_text_data(fd, hdrp, bss, disp) int fd; struct exec *hdrp; int bss; @@ -423,16 +413,14 @@ dln_print_undef() st_foreach(undef_tbl, undef_print, NULL); } -static +static void dln_undefined() { - fprintf(stderr, "dln: Calling undefined function\n"); - dln_print_undef(); -#ifdef RUBY - rb_exit(1); -#else - exit(1); -#endif + if (undef_tbl->num_entries > 0) { + fprintf(stderr, "dln: Calling undefined function\n"); + dln_print_undef(); + rb_exit(1); + } } struct undef { @@ -504,7 +492,7 @@ reloc_undef(no, undef, arg) #if defined(sun) && defined(sparc) datum += undef->reloc.r_addend; datum >>= R_RIGHTSHIFT(&(undef->reloc)); - mask = 1 << R_BITSIZE(&(undef->reloc)) - 1; + mask = (1 << R_BITSIZE(&(undef->reloc))) - 1; mask |= mask -1; datum &= mask; switch (R_LENGTH(&(undef->reloc))) { @@ -527,13 +515,19 @@ reloc_undef(no, undef, arg) #else switch (R_LENGTH(&(undef->reloc))) { case 0: /* byte */ - *address = undef->u.c + datum; + if (R_MEMORY_SUB(&(undef->reloc))) + *address = datum - *address; + else *address = undef->u.c + datum; break; case 1: /* word */ - *(short *)address = undef->u.s + datum; + if (R_MEMORY_SUB(&(undef->reloc))) + *(short*)address = datum - *(short*)address; + else *(short*)address = undef->u.s + datum; break; case 2: /* long */ - *(long *)address = undef->u.l + datum; + if (R_MEMORY_SUB(&(undef->reloc))) + *(long*)address = datum - *(long*)address; + else *(long*)address = undef->u.l + datum; break; } #endif @@ -542,7 +536,7 @@ reloc_undef(no, undef, arg) return ST_DELETE; } -static int +static void unlink_undef(name, value) char *name; long value; @@ -555,7 +549,7 @@ unlink_undef(name, value) } static int -dln_load_1(fd, disp, need_init) +load_1(fd, disp, need_init) int fd; long disp; char *need_init; @@ -571,14 +565,14 @@ dln_load_1(fd, disp, need_init) int init_p = 0; char buf[256]; - if (dln_load_header(fd, &hdr, disp) == -1) return -1; + if (load_header(fd, &hdr, disp) == -1) return -1; if (INVALID_OBJECT(hdr)) { dln_errno = DLN_ENOEXEC; return -1; } - reloc = dln_load_reloc(fd, &hdr, disp); + reloc = load_reloc(fd, &hdr, disp); if (reloc == NULL) return -1; - syms = dln_load_sym(fd, &hdr, disp); + syms = load_sym(fd, &hdr, disp); if (syms == NULL) return -1; sym = syms; @@ -624,7 +618,7 @@ dln_load_1(fd, disp, need_init) sym++; } - block = dln_load_text_data(fd, &hdr, hdr.a_bss + new_common, disp); + block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp); if (block == 0) goto err_exit; sym = syms; @@ -637,6 +631,7 @@ dln_load_1(fd, disp, need_init) sym->n_value += hdr.a_text + hdr.a_data; case N_TEXT|N_EXT: case N_DATA|N_EXT: + sym->n_value += block; if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0 @@ -655,6 +650,12 @@ dln_load_1(fd, disp, need_init) *new_sym = *sym; new_sym->n_un.n_name = strdup(sym->n_un.n_name); st_insert(sym_tbl, new_sym->n_un.n_name, new_sym); + break; + + case N_TEXT: + case N_DATA: + sym->n_value += block; + break; } sym++; } @@ -693,7 +694,7 @@ dln_load_1(fd, disp, need_init) } } /* end.. look it up */ else { /* is static */ - switch (R_SYMBOL(rel) & N_TYPE) { + switch (R_SYMBOL(rel)) { case N_TEXT: case N_DATA: datum = block; @@ -710,7 +711,7 @@ dln_load_1(fd, disp, need_init) #if defined(sun) && defined(sparc) datum += rel->r_addend; datum >>= R_RIGHTSHIFT(rel); - mask = 1 << R_BITSIZE(rel) - 1; + mask = (1 << R_BITSIZE(rel)) - 1; mask |= mask -1; datum &= mask; @@ -748,37 +749,33 @@ dln_load_1(fd, disp, need_init) if (need_init) { int len; + char **libs_to_be_linked = 0; if (undef_tbl->num_entries > 0) { - if (dln_load_lib(libc) == -1) goto err_exit; + if (load_lib(libc) == -1) goto err_exit; } init_funcname(buf, need_init); len = strlen(buf); -#if 1 - sym = syms; - while (sym < end) { + for (sym = syms; sym<end; sym++) { char *name = sym->n_un.n_name; - if (name[0] == '_' && sym->n_value >= block - && ((bcmp (name, "_Init_", 6) == 0 - || bcmp (name, "_init_", 6) == 0) && name[6] != '_')) { - init_p = 1; - ((int (*)())sym->n_value)(); + if (name[0] == '_' && sym->n_value >= block) { + if (strcmp(name+1, "libs_to_be_linked") == 0) { + libs_to_be_linked = (char**)sym->n_value; + } + if (strcmp(name+1, buf) == 0) { + init_p = 1; + ((int (*)())sym->n_value)(); + } } - sym++; } -#else - for (sym = syms; sym<end; sym++) { - char *name = sym->n_un.n_name; - if (name[0] == '_' && sym->n_value >= block - && (name[1] == 'i' || name[1] == 'I') - && bcmp(name+2, buf+1, len-1) == 0) { - init_p = 1; - ((int (*)())sym->n_value)(); + if (libs_to_be_linked && undef_tbl->num_entries > 0) { + while (*libs_to_be_linked) { + load_lib(*libs_to_be_linked); + libs_to_be_linked++; } } -#endif } free(reloc); free(syms); @@ -788,7 +785,7 @@ dln_load_1(fd, disp, need_init) return -1; } if (undef_tbl->num_entries > 0) { - if (dln_load_lib(libc) == -1) goto err_exit; + if (load_lib(libc) == -1) goto err_exit; if (undef_tbl->num_entries > 0) { dln_errno = DLN_EUNDEF; return -1; @@ -804,38 +801,6 @@ dln_load_1(fd, disp, need_init) return -1; } -int -dln_load(file) - char *file; -{ - int fd; - int result; - - if (dln_init_p == 0) { - dln_errno = DLN_ENOINIT; - return -1; - } - result = strlen(file); - if (file[result-1] == 'a') { - return dln_load_lib(file); - } - - fd = open(file, O_RDONLY); - if (fd == -1) { - dln_errno = errno; - return -1; - } - result = dln_load_1(fd, 0, file); - close(fd); - - return result; -} - -struct symdef { - int str_index; - int lib_offset; -}; - static int target_offset; static int search_undef(key, value, lib_tbl) @@ -854,10 +819,15 @@ search_undef(key, value, lib_tbl) return ST_STOP; } +struct symdef { + int str_index; + int lib_offset; +}; + char *dln_library_path = DLN_DEFAULT_PATH; -int -dln_load_lib(lib) +static int +load_lib(lib) char *lib; { char *path, *file; @@ -927,7 +897,7 @@ dln_load_lib(lib) target_offset = -1; st_foreach(undef_tbl, search_undef, lib_tbl); if (target_offset == -1) break; - if (dln_load_1(fd, target_offset, 0) == -1) { + if (load_1(fd, target_offset, 0) == -1) { st_free_table(lib_tbl); free(data); goto badlib; @@ -957,9 +927,9 @@ dln_load_lib(lib) goto badlib; } offset += sizeof(ahdr); - if (dln_load_header(fd, &hdr, offset) == -1) + if (load_header(fd, &hdr, offset) == -1) goto badlib; - syms = dln_load_sym(fd, &hdr, offset); + syms = load_sym(fd, &hdr, offset); if (syms == NULL) goto badlib; sym = syms; end = syms + (hdr.a_syms / sizeof(struct nlist)); @@ -973,7 +943,7 @@ dln_load_lib(lib) if (sym < end) { found++; free(syms); - if (dln_load_1(fd, offset, 0) == -1) { + if (load_1(fd, offset, 0) == -1) { goto badlib; } } @@ -993,6 +963,32 @@ dln_load_lib(lib) return -1; } +static int +load(file) + char *file; +{ + int fd; + int result; + + if (dln_init_p == 0) { + if (dln_init(dln_argv0) == -1) return -1; + } + result = strlen(file); + if (file[result-1] == 'a') { + return load_lib(file); + } + + fd = open(file, O_RDONLY); + if (fd == -1) { + dln_errno = errno; + return -1; + } + result = load_1(fd, 0, file); + close(fd); + + return result; +} + void* dln_sym(name) char *name; @@ -1004,9 +1000,32 @@ dln_sym(name) return NULL; } -char * +#endif /* USE_DLN_A_OUT */ + +#ifdef USE_DLN_DLOPEN +# ifdef __NetBSD__ +# include <nlist.h> +# include <link.h> +# else +# include <dlfcn.h> +# endif +#endif + +#ifdef hpux +#include <errno.h> +#include "dl.h" +#endif + +#ifdef _AIX +#include <ctype.h> /* for isdigit() */ +#include <errno.h> /* for global errno */ +#include <string.h> /* for strerror() */ +#endif + +static char * dln_strerror() { +#ifdef USE_DLN_A_OUT char *strerror(); switch (dln_errno) { @@ -1025,18 +1044,151 @@ dln_strerror() default: return strerror(dln_errno); } +#endif + +#ifdef USE_DLN_DLOPEN + return dlerror(); +#endif } -# endif -void -dln_perror(str) - char *str; +#ifdef _AIX +static void +aix_loaderror(char *pathname) { - fprintf(stderr, "%s: %s\n", str, dln_strerror()); + char *message[8], errbuf[1024]; + int i,j; + + struct errtab { + int errno; + char *errstr; + } load_errtab[] = { + {L_ERROR_TOOMANY, "too many errors, rest skipped."}, + {L_ERROR_NOLIB, "can't load library:"}, + {L_ERROR_UNDEF, "can't find symbol in library:"}, + {L_ERROR_RLDBAD, + "RLD index out of range or bad relocation type:"}, + {L_ERROR_FORMAT, "not a valid, executable xcoff file:"}, + {L_ERROR_MEMBER, + "file not an archive or does not contain requested member:"}, + {L_ERROR_TYPE, "symbol table mismatch:"}, + {L_ERROR_ALIGN, "text allignment in file is wrong."}, + {L_ERROR_SYSTEM, "System error:"}, + {L_ERROR_ERRNO, NULL} + }; + +#define LOAD_ERRTAB_LEN (sizeof(load_errtab)/sizeof(load_errtab[0])) +#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1) + + sprintf(errbuf, "load failed - %.200s ", pathname); + + if (!loadquery(1, &message[0], sizeof(message))) + ERRBUF_APPEND(strerror(errno)); + for(i = 0; message[i] && *message[i]; i++) { + int nerr = atoi(message[i]); + for (j=0; j<LOAD_ERRTAB_LEN ; j++) { + if (nerr == load_errtab[i].errno && load_errtab[i].errstr) + ERRBUF_APPEND(load_errtab[i].errstr); + } + while (isdigit(*message[i])) message[i]++ ; + ERRBUF_APPEND(message[i]); + ERRBUF_APPEND("\n"); + } + errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ + Fail(errbuf); +return; } +#endif + +void +dln_load(file) + char *file; +{ +#ifdef USE_DLN_A_OUT + if (load(file) == -1) { + goto failed; + } + return; +#else -#endif /* USE_DL */ + char buf[MAXPATHLEN]; + /* Load the file as an object one */ + init_funcname(buf, file); + +#ifdef USE_DLN_DLOPEN +#define DLN_DEFINED + { + void *handle; + void (*init_fct)(); + int len = strlen(file); + +# ifndef RTLD_LAZY +# define RTLD_LAZY 1 +# endif + + /* Load file */ + if ((handle = dlopen(file, RTLD_LAZY)) == NULL) { + goto failed; + } + + if ((init_fct = (void(*)())dlsym(handle, buf)) == NULL) { + goto failed; + } + /* Call the init code */ + (*init_fct)(); + return; + } +#endif /* USE_DLN_DLOPEN */ + +#ifdef hpux +#define DLN_DEFINED + { + shl_t lib = NULL; + int flags; + void (*init_fct)(); + + flags = BIND_DEFERRED; + lib = shl_load(file, flags, 0); + if (lib == NULL) { + char buf[256]; + Fail("Failed to load %.200s", file); + } + shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct); + if (init_fct == NULL) { + shl_findsym(&lib, buf, TYPE_DATA, (void*)&init_fct); + if (init_fct == NULL) { + rb_sys_fail(file); + } + } + (*init_fct)(); + return; + } +#endif /* hpux */ + +#ifdef _AIX +#define DLN_DEFINED + { + void (*init_fct)(); + + init_fct = (void(*)())load(file, 1, 0); + if (init_fct == NULL) { + aix_loaderror(file); + } + (*init_fct)(); + return; + } +#endif /* _AIX */ + +#ifndef DLN_DEFINED + Fail("dynamic link not supported"); +#endif + +#endif /* USE_DLN_A_OUT */ +#ifndef _AIX + failed: + Fail("%s - %s", dln_strerror(), file); +#endif +} static char *dln_find_1(); @@ -1073,6 +1225,8 @@ dln_find_1(fname, path, exe_flag) struct stat st; if (fname[0] == '/') return fname; + if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0) + return fname; for (dp = path;; dp = ++ep) { @@ -1144,22 +1298,7 @@ dln_find_1(fname, path, exe_flag) if (stat(fbuf, &st) == 0) { if (exe_flag == 0) return fbuf; /* looking for executable */ -#ifdef RUBY if (eaccess(fbuf, X_OK) == 0) return fbuf; -#else - { - uid_t uid = getuid(); - gid_t gid = getgid(); - - if (uid == st.st_uid && - (st.st_mode & S_IEXEC) || - gid == st.st_gid && - (st.st_mode & (S_IEXEC>>3)) || - st.st_mode & (S_IEXEC>>6)) { - return fbuf; - } - } -#endif } /* if not, and no other alternatives, life is bleak */ if (*ep == '\0') { @@ -11,16 +11,12 @@ #ifndef DLN_H #define DLN_H -#include <sys/errno.h> - char *dln_find_exe(); char *dln_find_file(); -int dln_init(); -int dln_load(); -int dln_load_lib(); - -char *dln_strerror(); -void dln_perror(); +#ifdef USE_DLN_A_OUT +extern char *dln_argv0; +#endif +void dln_load(); #endif diff --git a/dmyext.c b/dmyext.c new file mode 100644 index 0000000000..4120d493c3 --- /dev/null +++ b/dmyext.c @@ -0,0 +1,4 @@ +void +Init_ext() +{ +} @@ -12,7 +12,7 @@ #include "ruby.h" -VALUE M_Enumerable; +VALUE mEnumerable; static ID id_each, id_match, id_cmp; void @@ -20,11 +20,11 @@ rb_each(obj) VALUE obj; { if (!id_each) id_each = rb_intern("each"); - rb_funcall(obj, id_each, 0, Qnil); + rb_funcall(obj, id_each, 0, 0); } static void -enum_grep(i, arg) +grep_i(i, arg) VALUE i, *arg; { if (!id_match) id_match = rb_intern("=~"); @@ -34,7 +34,7 @@ enum_grep(i, arg) } static void -enum_grep_iter(i, pat) +grep_iter_i(i, pat) VALUE i, pat; { if (!id_match) id_match = rb_intern("=~"); @@ -44,25 +44,25 @@ enum_grep_iter(i, pat) } static VALUE -Fenum_grep(obj, pat) - VALUE obj; +enum_grep(obj, pat) + VALUE obj, pat; { if (iterator_p()) { - rb_iterate(rb_each, obj, enum_grep_iter, pat); + rb_iterate(rb_each, obj, grep_iter_i, pat); return obj; } else { VALUE tmp, arg[2]; arg[0] = pat; arg[1] = tmp = ary_new(); - rb_iterate(rb_each, obj, enum_grep, arg); + rb_iterate(rb_each, obj, grep_i, arg); return tmp; } } static void -enum_find(i, foundp) +find_i(i, foundp) VALUE i; int *foundp; { @@ -73,19 +73,19 @@ enum_find(i, foundp) } static VALUE -Fenum_find(obj) +enum_find(obj) VALUE obj; { int enum_found; enum_found = FALSE; - rb_iterate(rb_each, obj, enum_find, &enum_found); + rb_iterate(rb_each, obj, find_i, &enum_found); return enum_found; } static void -enum_find_all(i, tmp) - VALUE i; +find_all_i(i, tmp) + VALUE i, tmp; { if (rb_yield(i)) { ary_push(tmp, i); @@ -93,20 +93,20 @@ enum_find_all(i, tmp) } static VALUE -Fenum_find_all(obj) +enum_find_all(obj) VALUE obj; { VALUE tmp; tmp = ary_new(); - rb_iterate(rb_each, obj, enum_find_all, Qnil); + rb_iterate(rb_each, obj, find_all_i, 0); return tmp; } static void -enum_collect(i, tmp) - VALUE i; +collect_i(i, tmp) + VALUE i, tmp; { VALUE retval; @@ -117,32 +117,32 @@ enum_collect(i, tmp) } static VALUE -Fenum_collect(obj) +enum_collect(obj) VALUE obj; { VALUE tmp; tmp = ary_new(); - rb_iterate(rb_each, obj, enum_collect, tmp); + rb_iterate(rb_each, obj, collect_i, tmp); return tmp; } static void -enum_reverse(i, tmp) +reverse_i(i, tmp) VALUE i, tmp; { ary_unshift(tmp, i); } static VALUE -Fenum_reverse(obj) +enum_reverse(obj) VALUE obj; { VALUE tmp; tmp = ary_new(); - rb_iterate(rb_each, obj, enum_reverse, tmp); + rb_iterate(rb_each, obj, reverse_i, tmp); return tmp; } @@ -155,7 +155,7 @@ enum_all(i, ary) } static VALUE -Fenum_to_a(obj) +enum_to_a(obj) VALUE obj; { VALUE ary; @@ -167,18 +167,14 @@ Fenum_to_a(obj) } static VALUE -Fenum_sort(obj) +enum_sort(obj) VALUE obj; { - VALUE ary; - - ary = Fenum_to_a(obj); - Fary_sort(ary); - return ary; + return ary_sort(enum_to_a(obj)); } static void -enum_min(i, min) +min_i(i, min) VALUE i, *min; { VALUE cmp; @@ -194,17 +190,17 @@ enum_min(i, min) } static VALUE -Fenum_min(obj) +enum_min(obj) VALUE obj; { VALUE min = Qnil; - rb_iterate(rb_each, obj, enum_min, &min); + rb_iterate(rb_each, obj, min_i, &min); return min; } static void -enum_max(i, max) +max_i(i, max) VALUE i, *max; { VALUE cmp; @@ -220,12 +216,12 @@ enum_max(i, max) } static VALUE -Fenum_max(obj) +enum_max(obj) VALUE obj; { VALUE max = Qnil; - rb_iterate(rb_each, obj, enum_max, &max); + rb_iterate(rb_each, obj, max_i, &max); return max; } @@ -236,7 +232,7 @@ struct i_v_pair { }; static void -enum_index(item, iv) +index_i(item, iv) VALUE item; struct i_v_pair *iv; { @@ -250,21 +246,21 @@ enum_index(item, iv) } static VALUE -Fenum_index(obj, val) - VALUE obj; +enum_index(obj, val) + VALUE obj, val; { struct i_v_pair iv; iv.i = 0; iv.v = val; iv.found = 0; - rb_iterate(rb_each, obj, enum_index, &iv); + rb_iterate(rb_each, obj, index_i, &iv); if (iv.found) return INT2FIX(iv.i); return Qnil; /* not found */ } static void -enum_includes(item, iv) +member_i(item, iv) VALUE item; struct i_v_pair *iv; { @@ -275,20 +271,20 @@ enum_includes(item, iv) } static VALUE -Fenum_includes(obj, val) - VALUE obj; +enum_member(obj, val) + VALUE obj, val; { struct i_v_pair iv; iv.i = 0; iv.v = val; - rb_iterate(rb_each, obj, enum_includes, &iv); + rb_iterate(rb_each, obj, member_i, &iv); if (iv.i) return TRUE; return FALSE; } static void -enum_length(i, length) +length_i(i, length) VALUE i; int *length; { @@ -296,29 +292,31 @@ enum_length(i, length) } static VALUE -Fenum_length(obj) +enum_length(obj) VALUE obj; { int length = 0; - rb_iterate(rb_each, obj, enum_length, &length); + rb_iterate(rb_each, obj, length_i, &length); return INT2FIX(length); } +void Init_Enumerable() { - M_Enumerable = rb_define_module("Enumerable"); - - rb_define_method(M_Enumerable,"to_a", Fenum_to_a, 0); - - rb_define_method(M_Enumerable,"grep", Fenum_grep, 1); - rb_define_method(M_Enumerable,"find", Fenum_find, 0); - rb_define_method(M_Enumerable,"find_all", Fenum_find_all, 0); - rb_define_method(M_Enumerable,"collect", Fenum_collect, 0); - rb_define_method(M_Enumerable,"reverse", Fenum_reverse, 0); - rb_define_method(M_Enumerable,"min", Fenum_min, 0); - rb_define_method(M_Enumerable,"max", Fenum_max, 0); - rb_define_method(M_Enumerable,"index", Fenum_index, 1); - rb_define_method(M_Enumerable,"includes", Fenum_includes, 1); - rb_define_method(M_Enumerable,"length", Fenum_length, 0); + mEnumerable = rb_define_module("Enumerable"); + + rb_define_method(mEnumerable,"to_a", enum_to_a, 0); + + rb_define_method(mEnumerable,"sort", enum_sort, 0); + rb_define_method(mEnumerable,"grep", enum_grep, 1); + rb_define_method(mEnumerable,"find", enum_find, 0); + rb_define_method(mEnumerable,"find_all", enum_find_all, 0); + rb_define_method(mEnumerable,"collect", enum_collect, 0); + rb_define_method(mEnumerable,"reverse", enum_reverse, 0); + rb_define_method(mEnumerable,"min", enum_min, 0); + rb_define_method(mEnumerable,"max", enum_max, 0); + rb_define_method(mEnumerable,"index", enum_index, 1); + rb_define_method(mEnumerable,"member?", enum_member, 1); + rb_define_method(mEnumerable,"length", enum_length, 0); } @@ -11,28 +11,36 @@ #ifndef ENV_H #define ENV_H -extern struct ENVIRON { +extern struct FRAME { int argc; VALUE *argv; ID last_func; struct RClass *last_class; - struct ENVIRON *prev; -} *the_env; + struct FRAME *prev; + char *file; + int line; +} *the_frame; extern struct SCOPE { struct RBasic super; ID *local_tbl; VALUE *local_vars; - int flags; + int flag; } *the_scope; -#define SCOPE_MALLOCED (1<<0) +#define SCOPE_ALLOCA 0 +#define SCOPE_MALLOC 1 extern int rb_in_eval; extern struct RClass *the_class; -#define NOEX_PUBLIC 0 -#define NOEX_PRIVATE 1 +struct RVarmap { + struct RBasic super; + ID id; + VALUE val; + struct RVarmap *next; +}; +extern struct RVarmap *the_dyna_vars; #endif /* ENV_H */ @@ -59,18 +59,6 @@ err_print(fmt, args) } void -yyerror(msg) - char *msg; -{ - static char *f; - static int line; - - if (line == sourceline && strcmp(f, sourcefile) == 0) - return; - f = sourcefile; line = sourceline; - Error("%s", msg); -} - Error(fmt, va_alist) char *fmt; va_dcl @@ -83,6 +71,21 @@ Error(fmt, va_alist) nerrs++; } +int +yyerror(msg) + char *msg; +{ + static char *f; + static int line; + + if (line == sourceline && strcmp(f, sourcefile) == 0) + return; + f = sourcefile; line = sourceline; + Error("%s", msg); + return 0; +} + +void Warning(fmt, va_alist) char *fmt; va_dcl @@ -99,6 +102,7 @@ Warning(fmt, va_alist) va_end(args); } +void Fatal(fmt, va_alist) char *fmt; va_dcl @@ -111,6 +115,7 @@ Fatal(fmt, va_alist) rb_exit(1); } +void Bug(fmt, va_alist) char *fmt; va_dcl @@ -126,6 +131,7 @@ Bug(fmt, va_alist) abort(); } +void Fail(fmt, va_alist) char *fmt; va_dcl @@ -140,16 +146,18 @@ Fail(fmt, va_alist) rb_fail(str_new2(buf)); } +void rb_sys_fail(mesg) char *mesg; { + char *strerror(); char buf[BUFSIZ]; extern int errno; if (mesg == Qnil) - sprintf(buf, "%s\n", strerror(errno)); + sprintf(buf, "%s", strerror(errno)); else - sprintf(buf, "%s - %s\n", strerror(errno), mesg); + sprintf(buf, "%s - %s", strerror(errno), mesg); errno = 0; rb_fail(str_new2(buf)); @@ -167,14 +175,13 @@ static char *builtin_types[] = { "Array", "Fixnum", "Hash", - "Data", - "Method", "Struct", "Bignum", - "Assoc", "Data", + "Match", }; +void WrongType(x, t) VALUE x; int t; @@ -11,9 +11,9 @@ ************************************************/ #include "ruby.h" -#include "ident.h" #include "env.h" #include "node.h" +#include "sig.h" #include <stdio.h> #include <setjmp.h> @@ -24,13 +24,11 @@ # include <string.h> #else char *strchr(); +char *strrchr(); #endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#else -char *getenv(); -#endif +VALUE cProc; +static VALUE proc_call(); static void rb_clear_cache_body(); static void rb_clear_cache_entry(); @@ -48,6 +46,7 @@ struct cache_entry { /* method hash table. */ ID mid; /* method's id */ struct RClass *class; /* receiver's class */ struct RClass *origin; /* where method defined */ + int nargs; /* # of args */ NODE *method; int noex; }; @@ -63,7 +62,7 @@ rb_add_method(class, mid, node, noex) { NODE *body; - if (class == Qnil) class = (struct RClass*)C_Object; + if (class == Qnil) class = (struct RClass*)cObject; if (st_lookup(class->m_tbl, mid, &body)) { Warning("redefine %s", rb_id2name(mid)); rb_clear_cache_body(body); @@ -97,14 +96,13 @@ rb_get_method_body(classp, idp, noexp) ID *idp; int *noexp; { - int pos, i; ID id = *idp; struct RClass *class = *classp; NODE *body; struct RClass *origin; struct cache_entry *ent; - if ((body = search_method(class, id, &origin)) == Qnil) { + if ((body = search_method(class, id, &origin)) == FALSE) { return Qnil; } if (body->nd_body == Qnil) return Qnil; @@ -186,16 +184,30 @@ rb_export_method(class, name, noex) } } -VALUE -rb_method_boundp(class, id) +static VALUE +method_boundp(class, id, ex) struct RClass *class; ID id; + int ex; { - if (rb_get_method_body(&class, &id, 0)) + int noex; + + if (rb_get_method_body(&class, &id, &noex)) { + if (ex && noex == NOEX_PRIVATE) + return FALSE; return TRUE; + } return FALSE; } +VALUE +rb_method_boundp(class, id) + struct RClass *class; + ID id; +{ + return method_boundp(class, id, 0); +} + static void rb_clear_cache_body(body) NODE *body; @@ -251,44 +263,51 @@ extern int nerrs; extern VALUE TopSelf; VALUE Qself; -#define PUSH_SELF(s) { \ - VALUE __saved_self__ = Qself; \ - Qself = s; \ +#define PUSH_SELF(s) { \ + VALUE __saved_self__ = Qself; \ + Qself = s; \ #define POP_SELF() Qself = __saved_self__; } -struct ENVIRON *the_env, *top_env; -struct SCOPE *the_scope, *top_scope; +struct FRAME *the_frame; +struct SCOPE *the_scope; +static struct FRAME *top_frame; +static struct SCOPE *top_scope; -#define PUSH_ENV() { \ - struct ENVIRON _env; \ - _env.prev = the_env; \ - the_env = &_env; \ +#define PUSH_FRAME() { \ + struct FRAME _frame; \ + _frame.prev = the_frame; \ + _frame.file = sourcefile; \ + _frame.line = sourceline; \ + the_frame = &_frame; \ -#define POP_ENV() the_env = _env.prev; } +#define POP_FRAME() the_frame = _frame.prev; } struct BLOCK { NODE *var; NODE *body; VALUE self; - struct ENVIRON env; + struct FRAME frame; struct SCOPE *scope; int level; - VALUE block; int iter; + struct RVarmap *d_vars; struct BLOCK *prev; } *the_block; #define PUSH_BLOCK(v,b) { \ struct BLOCK _block; \ _block.level = tag_level; \ - _block.var=v; \ + _block.var = v; \ _block.body = b; \ _block.self = Qself; \ - _block.env = *the_env; \ + _block.frame = *the_frame; \ + _block.frame.file = sourcefile; \ + _block.frame.line = sourceline; \ _block.scope = the_scope; \ - _block.block = Qnil; \ + _block.d_vars = the_dyna_vars; \ _block.prev = the_block; \ + _block.iter = iter->iter; \ the_block = &_block; \ #define PUSH_BLOCK2(b) { \ @@ -297,6 +316,51 @@ struct BLOCK { #define POP_BLOCK() the_block = the_block->prev; } +struct RVarmap *the_dyna_vars; +#define PUSH_VARS() { \ + struct RVarmap *_old; \ + _old = the_dyna_vars; + +#define POP_VARS() the_dyna_vars = _old; } + +VALUE +dyna_var_ref(id) + ID id; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars) { + if (vars->id == id) return vars->val; + vars = vars->next; + } + return Qnil; +} + +VALUE +dyna_var_asgn(id, value) + ID id; + VALUE value; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars) { + if (vars->id == id) { + vars->val = value; + return; + } + vars = vars->next; + } + { + NEWOBJ(_vars, struct RVarmap); + OBJSETUP(_vars, Qnil, T_VARMAP); + _vars->id = id; + _vars->val = value; + _vars->next = the_dyna_vars; + the_dyna_vars = _vars; + } + return value; +} + static struct iter { int iter; struct iter *prev; @@ -322,7 +386,7 @@ static struct tag { jmp_buf buf; struct gc_list *gclist; VALUE self; - struct ENVIRON *env; + struct FRAME *frame; struct iter *iter; struct tag *prev; } *prot_tag; @@ -331,15 +395,16 @@ static struct tag { struct tag _tag; \ _tag.level= ++tag_level; \ _tag.self = Qself; \ - _tag.env = the_env; \ + _tag.frame = the_frame; \ _tag.iter = iter; \ _tag.prev = prot_tag; \ prot_tag = &_tag; \ #define EXEC_TAG() (setjmp(prot_tag->buf)) + #define JUMP_TAG(val) { \ Qself = prot_tag->self; \ - the_env = prot_tag->env; \ + the_frame = prot_tag->frame; \ iter = prot_tag->iter; \ longjmp(prot_tag->buf,(val)); \ } @@ -372,8 +437,8 @@ struct class_link { class_link = &_link \ #define POP_CLASS() \ - the_class = class_link->class; \ - class_link = _link.prev; } + the_class = class_link->class; \ + class_link = _link.prev; } #define PUSH_SCOPE() { \ struct SCOPE *_old; \ @@ -382,16 +447,24 @@ struct class_link { _old = the_scope; \ the_scope = _scope; \ -#define POP_SCOPE() the_scope = _old; } +#define POP_SCOPE() \ + if (the_scope->flag == SCOPE_ALLOCA) {\ + the_scope->local_vars = 0;\ + the_scope->local_tbl = 0;\ + }\ + the_scope = _old;\ +} static VALUE rb_eval(); -static VALUE Feval(); +static VALUE f_eval(); static VALUE rb_call(); VALUE rb_apply(); VALUE rb_xstring(); void rb_fail(); +VALUE rb_rescue(); + static void module_setup(); static VALUE masign(); @@ -404,20 +477,15 @@ extern VALUE rb_stderr; extern int sourceline; extern char *sourcefile; -VALUE -rb_self() -{ - return Qself; -} - static ID last_func; static void -error_print() +error_print(last_func) + ID last_func; { if (errat) { fwrite(RSTRING(errat)->ptr, 1, RSTRING(errat)->len, stderr); if (last_func) { - fprintf(stderr, ":in method `%s': ", rb_id2name(last_func)); + fprintf(stderr, ":in `%s': ", rb_id2name(last_func)); } else { fprintf(stderr, ": "); @@ -426,34 +494,43 @@ error_print() if (errstr) { fwrite(RSTRING(errstr)->ptr, 1, RSTRING(errstr)->len, stderr); + if (RSTRING(errstr)->ptr[RSTRING(errstr)->len - 1] != '\n') { + putc('\n', stderr); + } } else { fprintf(stderr, "unhandled failure.\n"); } - rb_trap_exit(); - exit(1); } +extern char **environ; +char **origenviron; + void ruby_init(argc, argv, envp) int argc; char **argv, **envp; { int state; - static struct ENVIRON env; - the_env = top_env = &env; + static struct FRAME frame; + the_frame = top_frame = &frame; + + origenviron = environ; +#ifdef NT + NtInitialize(&argc, &argv); +#endif init_heap(); PUSH_SCOPE(); - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; + the_scope->local_vars = 0; + the_scope->local_tbl = 0; top_scope = the_scope; PUSH_TAG(); PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); - the_class = (struct RClass*)C_Object; + the_class = (struct RClass*)cObject; ruby_options(argc, argv, envp); } POP_ITER(); @@ -466,37 +543,27 @@ ruby_init(argc, argv, envp) exit(FIX2UINT(last_val)); } if (state) { - PUSH_TAG(); - error_print(); - POP_TAG(); + error_print(last_func); } } -VALUE rb_readonly_hook(); - static VALUE Eval() { - VALUE result; + VALUE result = Qnil; NODE *tree; int state; if (!eval_tree) return Qnil; tree = eval_tree; - eval_tree = Qnil; - sourcefile = tree->file; - - PUSH_TAG(); - if ((state = EXEC_TAG()) == 0) { - result = rb_eval(tree); - } - POP_TAG(); - if (state) JUMP_TAG(state); + eval_tree = 0; + result = rb_eval(tree); return result; } +void ruby_run() { int state; @@ -504,13 +571,14 @@ ruby_run() if (nerrs > 0) exit(nerrs); init_stack(); - rb_define_variable("$!", &errstr, Qnil, Qnil, 0); + rb_define_variable("$!", &errstr); errat = Qnil; /* clear for execution */ PUSH_TAG(); PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { Eval(); + rb_trap_exit(); } POP_ITER(); POP_TAG(); @@ -534,12 +602,10 @@ ruby_run() Fatal("retry outside of protect clause"); break; case TAG_FAIL: - PUSH_TAG(); - error_print(); - POP_TAG(); + error_print(last_func); + exit(1); break; case TAG_EXIT: - rb_trap_exit(); exit(FIX2UINT(last_val)); break; default: @@ -566,11 +632,12 @@ rb_eval_string(str) char *str; { char *oldsrc = sourcefile; - VALUE result; lex_setsrc("(eval)", str, strlen(str)); - eval_tree = Qnil; + eval_tree = 0; + PUSH_VARS(); yyparse(); + POP_VARS(); sourcefile = oldsrc; if (nerrs == 0) { return Eval(); @@ -582,22 +649,30 @@ rb_eval_string(str) } void -rb_trap_eval(cmd) - VALUE cmd; +rb_eval_cmd(cmd, arg) + VALUE cmd, arg; { int state; struct SCOPE *saved_scope; + if (TYPE(cmd) != T_STRING) { + if (TYPE(cmd) == T_OBJECT + && obj_is_kind_of(cmd, cProc)) { + proc_call(cmd, arg); + return; + } + } + PUSH_SELF(TopSelf); PUSH_CLASS(); PUSH_TAG(); saved_scope = the_scope; the_scope = top_scope; - the_class = (struct RClass*)C_Object; + the_class = (struct RClass*)cObject; if ((state = EXEC_TAG()) == 0) { - Feval(Qself, cmd); + f_eval(Qself, cmd); } the_scope = saved_scope; @@ -624,21 +699,40 @@ rb_trap_eval(cmd) Fatal("retry outside of protect clause"); break; default: + JUMP_TAG(state); + break; + } +} + +void +rb_trap_eval(cmd, sig) + VALUE cmd; + int sig; +{ #ifdef SAFE_SIGHANDLE + int state; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig))); + } + POP_TAG(); + if (state) { trap_immediate = 0; -#endif JUMP_TAG(state); - break; } +#else + rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig))); +#endif } -#define SETUP_ARGS do {\ +#define SETUP_ARGS {\ NODE *n = node->nd_args;\ if (!n) {\ argc = 0;\ - argv = Qnil;\ + argv = 0;\ }\ - else if (/*nd_type(n) == NODE_LIST ||*/ nd_type(n) == NODE_ARRAY) {\ + else if (nd_type(n) == NODE_ARRAY) {\ argc=n->nd_alen;\ if (argc > 0) {\ int i;\ @@ -649,6 +743,10 @@ rb_trap_eval(cmd) n=n->nd_next;\ }\ }\ + else {\ + argc = 0;\ + argv = 0;\ + }\ }\ else {\ VALUE args = rb_eval(n);\ @@ -658,45 +756,36 @@ rb_trap_eval(cmd) argv = ALLOCA_N(VALUE, argc);\ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\ }\ -} while (0) +} + +#define RETURN(v) do { result = (v); goto finish; } while (0) static VALUE rb_eval(node) register NODE *node; { int state; - int go_out; - VALUE result; + VALUE result = Qnil; again: - if (node == Qnil) return Qnil; + if (node == Qnil) RETURN(Qnil); sourceline = node->line; - -#ifdef SAFE_SIGHANDLE - { - extern int trap_pending; - - if (trap_pending) { - rb_trap_exec(); - } - } -#endif + sourcefile = node->file; switch (nd_type(node)) { case NODE_BLOCK: - while (node->nd_next) { - rb_eval(node->nd_head); + while (node) { + result = rb_eval(node->nd_head); node = node->nd_next; } - node = node->nd_head; - goto again; + break; case NODE_SELF: - return Qself; + RETURN(Qself); case NODE_NIL: - return Qnil; + RETURN(Qnil); case NODE_IF: if (rb_eval(node->nd_cond)) { @@ -706,7 +795,7 @@ rb_eval(node) node = node->nd_else; } if (node) goto again; - return Qnil; + RETURN(Qnil); case NODE_CASE: { @@ -720,18 +809,18 @@ rb_eval(node) while (tag) { if (rb_funcall(rb_eval(tag->nd_head), match, 1, val)){ - return rb_eval(node->nd_body); + RETURN(rb_eval(node->nd_body)); } tag = tag->nd_next; } } else { - return rb_eval(node); + RETURN(rb_eval(node)); } node = node->nd_next; } } - return Qnil; + RETURN(Qnil); case NODE_WHILE: PUSH_TAG(); @@ -742,23 +831,26 @@ rb_eval(node) while_redo: rb_eval(node->nd_body); } - go_out = 0; break; case TAG_REDO: + state = 0; goto while_redo; case TAG_CONTINUE: + state = 0; goto while_cont; default: - go_out = 1; break; + } + POP_TAG(); + switch (state) { + case 0: case TAG_BREAK: - go_out = 0; + break; + default: + JUMP_TAG(state); break; } - while_out: - POP_TAG(); - if (go_out) JUMP_TAG(state); - return Qnil; + RETURN(Qnil); case NODE_WHILE2: PUSH_TAG(); @@ -769,25 +861,31 @@ rb_eval(node) while2_redo: rb_eval(node->nd_body); } while (rb_eval(node->nd_cond)); - go_out = 0; break; case TAG_REDO: + state = 0; goto while2_redo; case TAG_CONTINUE: + state = 0; goto while2_cont; default: - go_out = 1; case TAG_BREAK: break; } - while2_out: POP_TAG(); - if (go_out) JUMP_TAG(state); - return Qnil; + switch (state) { + case 0: + case TAG_BREAK: + break; + default: + JUMP_TAG(state); + } + RETURN(Qnil); case NODE_ITER: case NODE_FOR: { + iter_retry: PUSH_BLOCK(node->nd_var, node->nd_body); PUSH_TAG(); @@ -803,7 +901,7 @@ rb_eval(node) recv = rb_eval(node->nd_iter); PUSH_ITER(ITER_PRE); - result = rb_call(CLASS_OF(recv),recv,each,0,Qnil,0); + result = rb_call(CLASS_OF(recv),recv,each,0,0,0); POP_ITER(); } } @@ -812,13 +910,16 @@ rb_eval(node) switch (state) { case 0: break; + + case TAG_RETRY: + goto iter_retry; + case IN_BLOCK|TAG_BREAK: if (target_level != tag_level) { JUMP_TAG(state); } result = Qnil; break; - case IN_BLOCK|TAG_RETRY: case IN_BLOCK|TAG_RETURN: if (target_level == tag_level) { state &= ~IN_BLOCK; @@ -828,91 +929,100 @@ rb_eval(node) JUMP_TAG(state); } } - return result; + break; case NODE_FAIL: { VALUE mesg = rb_eval(node->nd_stts); if (mesg) Check_Type(mesg, T_STRING); rb_fail(mesg); - return Qnil; /* not reached */ } + break; case NODE_YIELD: - { - VALUE val; - - val = rb_eval(node->nd_stts); - result = rb_yield(val); - } - return result; + result = rb_yield(rb_eval(node->nd_stts)); + break; case NODE_BEGIN: - PUSH_TAG(); - switch (state = EXEC_TAG()) { - case 0: - retry_entry: - result = rb_eval(node->nd_head); - break; + if (node->nd_resq == Qnil && node->nd_ensr == Qnil) { + node = node->nd_head; + goto again; + } + else { + VALUE (*r_proc)(); - case TAG_FAIL: - if (node->nd_resq) { - if (node->nd_resq == (NODE*)1) { - state = 0; - } - else { - PUSH_TAG(); - state = EXEC_TAG(); - if (state == 0) result = rb_eval(node->nd_resq); - POP_TAG(); - if (state == TAG_RETRY) { - goto retry_entry; - } + if (node->nd_resq == (NODE*)1) { + r_proc = 0; + } + else { + r_proc = rb_eval; + } + if (node->nd_ensr) { + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_rescue(rb_eval, node->nd_head, r_proc, node->nd_resq); } - if (state == 0) { - errstr = errat = Qnil; - last_func = 0; + POP_TAG(); + /* ensure clause */ + rb_eval(node->nd_ensr); + if (state) { + JUMP_TAG(state); } } - break; - } - POP_TAG(); - - /* ensure clause */ - rb_eval(node->nd_ensr); - - if (state != 0) { - JUMP_TAG(state); + else { + result = rb_rescue(rb_eval, node->nd_head, r_proc, node->nd_resq); + } } - return result; + break; case NODE_AND: - if ((result = rb_eval(node->nd_1st)) == Qnil) return result; + if ((result = rb_eval(node->nd_1st)) == FALSE) RETURN(result); node = node->nd_2nd; goto again; case NODE_OR: - if ((result = rb_eval(node->nd_1st)) != Qnil) return result; + if ((result = rb_eval(node->nd_1st)) != FALSE) RETURN(result); node = node->nd_2nd; goto again; case NODE_NOT: - if (rb_eval(node->nd_body)) return FALSE; - return TRUE; + if (rb_eval(node->nd_body)) result = FALSE; + else result = TRUE; + break; + case NODE_DOT2: case NODE_DOT3: + RETURN(range_new(rb_eval(node->nd_beg), rb_eval(node->nd_end))); + + case NODE_FLIP2: /* like AWK */ + if (node->nd_state == 0) { + if (rb_eval(node->nd_beg)) { + node->nd_state = rb_eval(node->nd_end)?0:1; + result = TRUE; + } + result = FALSE; + } + else { + if (rb_eval(node->nd_end)) { + node->nd_state = 0; + } + result = TRUE; + } + break; + + case NODE_FLIP3: /* like SED */ if (node->nd_state == 0) { if (rb_eval(node->nd_beg)) { node->nd_state = 1; - return TRUE; + result = TRUE; } - return FALSE; + result = FALSE; } else { if (rb_eval(node->nd_end)) { node->nd_state = 0; } - return TRUE; + result = TRUE; } break; @@ -946,7 +1056,7 @@ rb_eval(node) recv = rb_eval(node->nd_recv); SETUP_ARGS; POP_ITER(); - return rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0); + result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0); } break; @@ -957,19 +1067,18 @@ rb_eval(node) PUSH_ITER(ITER_NOT); SETUP_ARGS; POP_ITER(); - return rb_call(CLASS_OF(Qself),Qself,node->nd_mid,argc,argv,1); + result = rb_call(CLASS_OF(Qself),Qself,node->nd_mid,argc,argv,1); } break; case NODE_SUPER: case NODE_ZSUPER: { - int i; int argc; VALUE *argv; /* used in SETUP_ARGS */ if (nd_type(node) == NODE_ZSUPER) { - argc = the_env->argc; - argv = the_env->argv; + argc = the_frame->argc; + argv = the_frame->argv; } else { PUSH_ITER(ITER_NOT); @@ -978,16 +1087,14 @@ rb_eval(node) } PUSH_ITER(iter->iter?ITER_PRE:ITER_NOT); - result = rb_call(the_env->last_class->super, Qself, - the_env->last_func, argc, argv, 1); + result = rb_call(the_frame->last_class->super, Qself, + the_frame->last_func, argc, argv, 1); POP_ITER(); } - return result; + break; case NODE_SCOPE: { - VALUE result; - PUSH_SCOPE(); PUSH_TAG(); if (node->nd_cnt > 0) { @@ -996,22 +1103,17 @@ rb_eval(node) the_scope->local_tbl = node->nd_tbl; } else { - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; + the_scope->local_vars = 0; + the_scope->local_tbl = 0; } if ((state = EXEC_TAG()) == 0) { result = rb_eval(node->nd_body); } POP_TAG(); - if (!(the_scope->flags & SCOPE_MALLOCED)) { - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; - } POP_SCOPE(); if (state != 0) JUMP_TAG(state); - - return result; } + break; case NODE_OP_ASGN1: { @@ -1026,8 +1128,9 @@ rb_eval(node) val = rb_funcall(val, node->nd_mid, 1, rb_eval(rval)); ary_push(args, val); rb_apply(recv, aset, args); - return val; + result = val; } + break; case NODE_OP_ASGN2: { @@ -1036,24 +1139,27 @@ rb_eval(node) recv = rb_funcall(rb_eval(node->nd_recv), id, 0); - id &= ~ID_SCOPE_MASK; - id |= ID_ATTRSET; + id = id_attrset(id); val = rb_eval(node->nd_value); rb_funcall(recv, id, 1, val); - return val; + result = val; } + break; case NODE_MASGN: - { - VALUE val = rb_eval(node->nd_value); - return masign(node, val); - } + result = masign(node, rb_eval(node->nd_value)); + break; case NODE_LASGN: - if (the_scope->local_vars == Qnil) + if (the_scope->local_vars == 0) Bug("unexpected local variable asignment"); - return the_scope->local_vars[node->nd_cnt] = rb_eval(node->nd_value); + result = the_scope->local_vars[node->nd_cnt] = rb_eval(node->nd_value); + break; + + case NODE_DASGN: + result = dyna_var_asgn(node->nd_vid, rb_eval(node->nd_value)); + break; case NODE_GASGN: { @@ -1061,51 +1167,47 @@ rb_eval(node) val = rb_eval(node->nd_value); rb_gvar_set(node->nd_entry, val); - return val; + result = val; } + break; + case NODE_IASGN: { VALUE val; val = rb_eval(node->nd_value); - rb_ivar_set(node->nd_vid, val); - return val; + rb_ivar_set(Qself, node->nd_vid, val); + result = val; } + break; + case NODE_CASGN: { VALUE val; val = rb_eval(node->nd_value); rb_const_set(the_class, node->nd_vid, val); - return val; + result = val; } break; case NODE_LVAR: - if (the_scope->local_vars == Qnil) + if (the_scope->local_vars == 0) Bug("unexpected local variable"); - return the_scope->local_vars[node->nd_cnt]; - - case NODE_LVAR2: - if (the_scope->flags & SCOPE_MALLOCED) { - ID id = node->nd_vid, *tbl = the_scope->local_tbl; - int i, len = tbl[0]; + result = the_scope->local_vars[node->nd_cnt]; + break; - tbl++; - for (i=0; i<len; i++) { - if (id == tbl[i]) break; - } - if (i<len) { - return the_scope->local_vars[i]; - } - } - Warning("local var %s not initialized", rb_id2name(node->nd_vid)); - return Qnil; + case NODE_DVAR: + result = dyna_var_ref(node->nd_vid); + break; case NODE_GVAR: - return rb_gvar_get(node->nd_entry); + result = rb_gvar_get(node->nd_entry); + break; + case NODE_IVAR: - return rb_ivar_get(node->nd_vid); + result = rb_ivar_get(Qself, node->nd_vid); + break; case NODE_CVAR: { @@ -1114,14 +1216,54 @@ rb_eval(node) val = rb_const_get(node->nd_rval->nd_clss, node->nd_vid); nd_set_type(node, NODE_CONST); node->nd_cval = val; - return val; + result = val; } + break; case NODE_CONST: - return node->nd_cval; + result = node->nd_cval; + break; + + case NODE_COLON2: + { + VALUE cls; + + cls = rb_eval(node->nd_head); + switch (TYPE(cls)) { + case T_CLASS: + case T_MODULE: + break; + default: + Check_Type(cls, T_CLASS); + break; + } + result = rb_const_get(cls, node->nd_mid); + } + break; +#define MATCH_DATA the_scope->local_vars[node->nd_cnt] case NODE_NTH_REF: - return re_nth_match(node->nd_nth); + result = reg_nth_match(node->nd_nth, MATCH_DATA); + break; + + case NODE_BACK_REF: + switch (node->nd_nth) { + case '&': + result = reg_last_match(MATCH_DATA); + break; + case '`': + result = reg_match_pre(MATCH_DATA); + break; + case '\'': + result = reg_match_post(MATCH_DATA); + break; + case '+': + result = reg_match_last(MATCH_DATA); + break; + default: + Bug("unexpected back-ref"); + } + break; case NODE_HASH: { @@ -1133,24 +1275,24 @@ rb_eval(node) while (list) { key = rb_eval(list->nd_head); list = list->nd_next; - if (list == Qnil) + if (list == 0) Bug("odd number list for Hash"); val = rb_eval(list->nd_head); list = list->nd_next; - Fhash_aset(hash, key, val); + hash_aset(hash, key, val); } - return hash; + result = hash; } break; case NODE_ZARRAY: /* zero length list */ - return ary_new(); + result = ary_new(); + break; case NODE_ARRAY: { VALUE ary; int i; - NODE *list; i = node->nd_alen; ary = ary_new2(i); @@ -1159,12 +1301,13 @@ rb_eval(node) RARRAY(ary)->len = i; } - return ary; + result = ary; } break; case NODE_STR: - return str_new3(node->nd_lit); + result = str_new3(node->nd_lit); + break; case NODE_STR2: case NODE_XSTR2: @@ -1188,26 +1331,32 @@ rb_eval(node) list = list->nd_next; } if (nd_type(node) == NODE_DREGX) { - VALUE re = regexp_new(RSTRING(str)->ptr, RSTRING(str)->len, - node->nd_cflag); - return re; + VALUE re = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, + node->nd_cflag); + result = re; + } + else if (nd_type(node) == NODE_XSTR2) { + result = rb_xstring(str); } - if (nd_type(node) == NODE_XSTR2) { - return rb_xstring(str); + else { + result = str; } - return str; } + break; case NODE_XSTR: - return rb_xstring(node->nd_lit); + result = rb_xstring(node->nd_lit); + break; case NODE_LIT: - return node->nd_lit; + result = node->nd_lit; + break; case NODE_ATTRSET: - if (the_env->argc != 1) - Fail("Wrong # of arguments(%d for 1)", the_env->argc); - return rb_ivar_set(node->nd_vid, the_env->argv[0]); + if (the_frame->argc != 1) + Fail("Wrong # of arguments(%d for 1)", the_frame->argc); + result = rb_ivar_set(Qself, node->nd_vid, the_frame->argv[0]); + break; case NODE_DEFN: if (node->nd_defn) { @@ -1216,7 +1365,7 @@ rb_eval(node) int noex; body = search_method(the_class, node->nd_mid, &origin); - if (verbose && origin != (VALUE)the_class + if (body && verbose && origin != (VALUE)the_class && body->nd_noex != node->nd_noex) { Warning("change method %s's scope", rb_id2name(node->nd_mid)); } @@ -1225,8 +1374,9 @@ rb_eval(node) else noex = node->nd_noex; /* default(1 for toplevel) */ rb_add_method(the_class, node->nd_mid, node->nd_defn, noex); + result = Qnil; } - return Qnil; + break; case NODE_DEFS: if (node->nd_defn) { @@ -1236,20 +1386,23 @@ rb_eval(node) Fail("Can't define method \"%s\" for nil", rb_id2name(node->nd_mid)); } - rb_funcall(recv, rb_intern("single_method_added"), + rb_funcall(recv, rb_intern("singleton_method_added"), 1, INT2FIX(node->nd_mid)); - rb_add_method(rb_single_class(recv),node->nd_mid,node->nd_defn, + rb_add_method(rb_singleton_class(recv),node->nd_mid,node->nd_defn, NOEX_PUBLIC); + result = Qnil; } - return Qnil; + break; case NODE_UNDEF: rb_add_method(the_class, node->nd_mid, Qnil, NOEX_PUBLIC); - return Qnil; + result = Qnil; + break; case NODE_ALIAS: rb_alias(the_class, node->nd_new, node->nd_old); - return Qnil; + result = Qnil; + break; case NODE_CLASS: { @@ -1257,17 +1410,16 @@ rb_eval(node) struct RClass *tmp; if (node->nd_super) { - super = rb_const_get(the_class, node->nd_super); - if (super == Qnil) { - Fail("undefined superclass %s", - rb_id2name(node->nd_super)); + super = rb_eval(node->nd_super); + if (super == Qnil || TYPE(super) != T_CLASS) { + Fail("superclass undefined"); } } else { super = Qnil; } - if (rb_const_bound(the_class, node->nd_cname)) { + if (rb_const_defined(the_class, node->nd_cname)) { class = rb_const_get(the_class, node->nd_cname); if (super) { if (TYPE(class) != T_CLASS) @@ -1280,27 +1432,28 @@ rb_eval(node) tmp = RCLASS(tmp)->super; } if (tmp != RCLASS(super)) - Fail("%s's superclass differs", + Fail("superclass mismatch for %s", rb_id2name(node->nd_cname)); } Warning("extending class %s", rb_id2name(node->nd_cname)); } else { - if (super == Qnil) super = C_Object; + if (super == Qnil) super = cObject; class = rb_define_class_id(node->nd_cname, super); rb_const_set(the_class, node->nd_cname, class); rb_set_class_path(class,the_class,rb_id2name(node->nd_cname)); } module_setup(class, node->nd_body); - return class; + result = class; } + break; case NODE_MODULE: { VALUE module; - if (rb_const_bound(the_class, node->nd_cname)) { + if (rb_const_defined(the_class, node->nd_cname)) { module = rb_const_get(the_class, node->nd_cname); if (TYPE(module) != T_MODULE) Fail("%s is not a module", rb_id2name(node->nd_cname)); @@ -1313,13 +1466,141 @@ rb_eval(node) } module_setup(module, node->nd_body); - return module; + result = module; } + break; + + case NODE_DEFINED: + { + VALUE obj; + + node = node->nd_head; + switch (nd_type(node)) { + case NODE_SUPER: + case NODE_ZSUPER: + if (the_frame->last_func == 0) result = FALSE; + else { + result = method_boundp(the_frame->last_class->super, + the_frame->last_func, 1); + } + break; + + case NODE_FCALL: + obj = CLASS_OF(Qself); + goto check_bound; + + case NODE_CALL: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + obj = rb_eval(node->nd_recv); + } + POP_TAG(); + if (state == TAG_FAIL) { + result = FALSE; + break; + } + else { + if (state) JUMP_TAG(state); + obj = CLASS_OF(obj); + check_bound: + if (method_boundp(obj, node->nd_mid, + nd_type(node)== NODE_CALL)) { + result = TRUE; + } + else result = FALSE; + } + break; + + case NODE_YIELD: + result = iterator_p(); + break; + + case NODE_BREAK: + case NODE_CONTINUE: + case NODE_REDO: + case NODE_RETRY: + + case NODE_SELF: + case NODE_NIL: + case NODE_FAIL: + case NODE_ATTRSET: + case NODE_DEFINED: + + case NODE_OP_ASGN1: + case NODE_OP_ASGN2: + case NODE_MASGN: + case NODE_LASGN: + case NODE_DASGN: + case NODE_GASGN: + case NODE_IASGN: + case NODE_CASGN: + case NODE_LVAR: + case NODE_DVAR: + result = TRUE; + break; + + case NODE_GVAR: + result = rb_gvar_defined(node->nd_entry); + break; + + case NODE_IVAR: + result = rb_ivar_defined(node->nd_vid); + break; + + case NODE_CVAR: + result = rb_const_defined(node->nd_rval->nd_clss, node->nd_vid); + break; + + case NODE_CONST: + result = TRUE; + break; + + case NODE_COLON2: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + obj = rb_eval(node->nd_head); + } + POP_TAG(); + if (state == TAG_FAIL) result = FALSE; + else { + if (state) JUMP_TAG(state); + result = rb_const_defined(obj, node->nd_mid); + } + break; + + case NODE_NTH_REF: + result = reg_nth_defined(node->nd_nth, MATCH_DATA); + break; + + case NODE_BACK_REF: + result = reg_nth_defined(0, MATCH_DATA); + break; + + default: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval(node); + } + POP_TAG(); + if (state == TAG_FAIL) result = FALSE; + else { + if (state) JUMP_TAG(state); + result = TRUE; + } + } + } + break; default: Bug("unknown node type %d", nd_type(node)); } - return Qnil; /* not reached */ + finish: +#ifdef SAFE_SIGHANDLE + if (trap_pending) { + rb_trap_exec(); + } +#endif + return result; /* not reached */ } static void @@ -1337,7 +1618,6 @@ module_setup(module, node) the_class = (struct RClass*)module; PUSH_SELF((VALUE)the_class); PUSH_SCOPE(); - PUSH_TAG(); if (node->nd_cnt > 0) { the_scope->local_vars = ALLOCA_N(VALUE, node->nd_cnt); @@ -1345,20 +1625,15 @@ module_setup(module, node) the_scope->local_tbl = node->nd_tbl; } else { - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; + the_scope->local_vars = 0; + the_scope->local_tbl = 0; } + PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { rb_eval(node->nd_body); } - POP_TAG(); - if (!(the_scope->flags & SCOPE_MALLOCED)) { - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; - } - if (state != 0) JUMP_TAG(state); POP_SCOPE(); POP_SELF(); POP_CLASS(); @@ -1366,20 +1641,10 @@ module_setup(module, node) } VALUE -obj_responds_to(obj, msg) +rb_responds_to(obj, id) VALUE obj; - struct RString *msg; -{ ID id; - - if (FIXNUM_P(msg)) { - id = FIX2INT(msg); - } - else { - Check_Type(msg, T_STRING); - id = rb_intern(msg->ptr); - } - +{ if (rb_method_boundp(CLASS_OF(obj), id)) { return TRUE; } @@ -1393,11 +1658,12 @@ rb_exit(status) last_val = INT2FIX(status); if (prot_tag) JUMP_TAG(TAG_EXIT); + rb_trap_exit(); exit(FIX2UINT(last_val)); } static VALUE -Fexit(argc, argv, obj) +f_exit(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -1440,25 +1706,22 @@ rb_fail(mesg) { char buf[BUFSIZ]; - if (errat == Qnil || sourcefile) { - if (the_env->last_func) { - last_func = the_env->last_func; + if (errat == Qnil && mesg == Qnil) { + errstr = Qnil; + } + + if (errat == Qnil && sourcefile) { + if (the_frame->last_func) { + last_func = the_frame->last_func; } sprintf(buf, "%s:%d", sourcefile, sourceline); errat = str_new2(buf); } if (mesg) { - if (RSTRING(mesg)->ptr[RSTRING(mesg)->len - 1] == '\n') { - errstr = mesg; - } - else { - errstr = str_clone(mesg); - str_cat(errstr, "\n", 1); - } + errstr = mesg; } - - if (prot_tag->level == 0) error_print(); + if (prot_tag->level == 0) error_print(last_func); JUMP_TAG(TAG_FAIL); } @@ -1470,48 +1733,54 @@ iterator_p() } static VALUE -Fiterator_p() +f_iterator_p() { if (iter->prev && iter->prev->iter) return TRUE; return FALSE; } VALUE -rb_yield(val) - VALUE val; +rb_yield_0(val, self) + VALUE val, self; { struct BLOCK *block; NODE *node; int state; - VALUE result; - struct ENVIRON *old_env; - struct SCOPE *old_scope; + VALUE result = Qnil; + struct SCOPE *old_scope; + struct FRAME frame; if (!iterator_p()) { Fail("yield called out of iterator"); } + PUSH_VARS(); block = the_block; - old_env = the_env; - the_env = &(block->env); + frame = block->frame; + frame.prev = the_frame; + the_frame = &(frame); old_scope = the_scope; the_scope = block->scope; the_block = block->prev; + the_dyna_vars = block->d_vars; if (block->var) { if (nd_type(block->var) == NODE_MASGN) masign(block->var, val); else asign(block->var, val); } + node = block->body; PUSH_ITER(block->iter); - PUSH_SELF(block->self); + PUSH_SELF(self?self:block->self); PUSH_TAG(); - node = block->body; switch (state = EXEC_TAG()) { redo: case 0: - if (nd_type(node) == NODE_CFUNC) { + if (!node) { + result = Qnil; + } + else if (nd_type(node) == NODE_CFUNC) { result = (*node->nd_cfnc)(val,node->nd_argc); } else { @@ -1523,24 +1792,40 @@ rb_yield(val) case TAG_CONTINUE: state = 0; break; - case TAG_RETRY: case TAG_BREAK: case TAG_RETURN: target_level = block->level; state = IN_BLOCK|state; break; + default: + break; } POP_TAG(); POP_SELF(); POP_ITER(); + POP_VARS(); the_block = block; - the_env = old_env; + the_frame = the_frame->prev; the_scope = old_scope; if (state) JUMP_TAG(state); return result; } +VALUE +rb_yield(val) + VALUE val; +{ + return rb_yield_0(val, 0); +} + +static VALUE +f_loop() +{ + for (;;) { rb_yield(Qnil); } + return Qnil; +} + static VALUE masign(node, val) NODE *node; @@ -1590,15 +1875,19 @@ asign(lhs, val) break; case NODE_IASGN: - rb_ivar_set(lhs->nd_vid, val); + rb_ivar_set(Qself, lhs->nd_vid, val); break; case NODE_LASGN: - if (the_scope->local_vars == Qnil) + if (the_scope->local_vars == 0) Bug("unexpected iterator variable asignment"); the_scope->local_vars[lhs->nd_cnt] = val; break; + case NODE_DASGN: + dyna_var_asgn(lhs->nd_vid, val); + break; + case NODE_CASGN: rb_const_set(the_class, lhs->nd_vid, val); break; @@ -1631,13 +1920,13 @@ asign(lhs, val) VALUE rb_iterate(it_proc, data1, bl_proc, data2) VALUE (*it_proc)(), (*bl_proc)(); - char *data1, *data2; + void *data1, *data2; { int state; - VALUE retval; + VALUE retval = Qnil; NODE *node = NEW_CFUNC(bl_proc, data2); - struct BLOCK block; + iter_retry: PUSH_ITER(ITER_PRE); PUSH_BLOCK(Qnil, node); PUSH_TAG(); @@ -1654,13 +1943,17 @@ rb_iterate(it_proc, data1, bl_proc, data2) switch (state) { case 0: break; + + case TAG_RETRY: + goto iter_retry; + case IN_BLOCK|TAG_BREAK: if (target_level != tag_level) { JUMP_TAG(state); } retval = Qnil; break; - case IN_BLOCK|TAG_RETRY: + case IN_BLOCK|TAG_RETURN: if (target_level == tag_level) { state &= ~IN_BLOCK; @@ -1674,23 +1967,24 @@ rb_iterate(it_proc, data1, bl_proc, data2) } VALUE -rb_resque(b_proc, data1, r_proc, data2) +rb_rescue(b_proc, data1, r_proc, data2) VALUE (*b_proc)(), (*r_proc)(); - char *data1, *data2; + void *data1, *data2; { int state; - int go_out; - VALUE result; + VALUE result = Qnil; + volatile SIGHANDLE handle; PUSH_TAG(); switch (state = EXEC_TAG()) { case 0: + handle = sig_beg(); retry_entry: result = (*b_proc)(data1); - go_out = 0; break; case TAG_FAIL: + sig_end(handle); if (r_proc) { PUSH_TAG(); state = EXEC_TAG(); @@ -1698,28 +1992,25 @@ rb_resque(b_proc, data1, r_proc, data2) result = (*r_proc)(data2); } POP_TAG(); - switch (state) { - case TAG_RETRY: + if (state == TAG_RETRY) { goto retry_entry; - case 0: - go_out = 0; - break; - default: - go_out = 1; - break; } } + else { + state = 0; + } if (state == 0) { - errstr = errat = Qnil; + errat = Qnil; + last_func = 0; } break; default: - go_out = 1; + sig_end(handle); break; } POP_TAG(); - if (go_out) JUMP_TAG(state); + if (state) JUMP_TAG(state); return result; } @@ -1727,10 +2018,10 @@ rb_resque(b_proc, data1, r_proc, data2) VALUE rb_ensure(b_proc, data1, e_proc, data2) VALUE (*b_proc)(), (*e_proc)(); - char *data1, *data2; + void *data1, *data2; { int state; - VALUE result; + VALUE result = Qnil; PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { @@ -1748,7 +2039,7 @@ rb_ensure(b_proc, data1, e_proc, data2) static int last_noex; static VALUE -Fmissing(argc, argv, obj) +f_missing(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -1756,31 +2047,33 @@ Fmissing(argc, argv, obj) VALUE desc; ID id; char *format; - struct ENVIRON *env; + struct FRAME *frame; id = FIX2INT(argv[0]); argc--; argv++; - desc = obj_as_string(obj); - if (RSTRING(desc)->len > 160) { - desc = Fkrn_to_s(obj); + if (TYPE(obj) == T_STRING) { + desc = krn_inspect(obj); + } + else { + desc = obj_as_string(obj); } if (last_noex) - format = "method `%s' not available for \"%s\"(%s)"; + format = "method `%s' not available for %s(%s)"; else - format = "undefined method `%s' for \"%s\"(%s)"; + format = "undefined method `%s' for %s(%s)"; - /* fake environment */ - PUSH_ENV(); - env = the_env->prev; - MEMCPY(the_env, env->prev, struct ENVIRON, 1); - the_env->prev = env; + /* fake frame */ + PUSH_FRAME(); + frame = the_frame->prev; + *the_frame = *frame->prev; + the_frame->prev = frame; Fail(format, rb_id2name(id), RSTRING(desc)->ptr, rb_class2name(CLASS_OF(obj))); - POP_ENV(); + POP_FRAME(); } static VALUE @@ -1793,7 +2086,6 @@ rb_undefined(obj, id, argc, argv, noex) { VALUE *nargv; - argc; nargv = ALLOCA_N(VALUE, argc+1); nargv[0] = INT2FIX(id); MEMCPY(nargv+1, argv, VALUE, argc); @@ -1803,6 +2095,9 @@ rb_undefined(obj, id, argc, argv, noex) return rb_funcall2(obj, rb_intern("method_missing"), argc+1, nargv); } +#define STACK_LEVEL_MAX 10000 +static int stack_level; + static VALUE rb_call(class, recv, mid, argc, argv, scope) struct RClass *class; @@ -1814,7 +2109,7 @@ rb_call(class, recv, mid, argc, argv, scope) { NODE *body; int noex; - VALUE result; + VALUE result = Qnil; struct cache_entry *ent; int itr; enum node_type type; @@ -1830,7 +2125,7 @@ rb_call(class, recv, mid, argc, argv, scope) else { ID id = mid; - if ((body = rb_get_method_body(&class, &id, &noex)) == Qnil) { + if ((body = rb_get_method_body(&class, &id, &noex)) == FALSE) { return rb_undefined(recv, mid, argc, argv, 0); } mid = id; @@ -1861,13 +2156,16 @@ rb_call(class, recv, mid, argc, argv, scope) return rb_call(class->super, recv, mid, argc, argv, scope?scope:1); } + if (stack_level++ > STACK_LEVEL_MAX) + Fail("stack level too deep"); + PUSH_ITER(itr); PUSH_SELF(recv); - PUSH_ENV(); - the_env->last_func = mid; - the_env->last_class = class; - the_env->argc = argc; - the_env->argv = argv; + PUSH_FRAME(); + the_frame->last_func = mid; + the_frame->last_class = class; + the_frame->argc = argc; + the_frame->argv = argv; switch (type) { case NODE_CFUNC: @@ -1985,14 +2283,14 @@ rb_call(class, recv, mid, argc, argv, scope) /* for attr get/set */ case NODE_ATTRSET: case NODE_IVAR: - return rb_eval(body); + result = rb_eval(body); + break; default: { int state; VALUE *local_vars; - sourcefile = body->file; PUSH_SCOPE(); if (body->nd_cnt > 0) { @@ -2002,47 +2300,59 @@ rb_call(class, recv, mid, argc, argv, scope) the_scope->local_vars = local_vars; } else { - local_vars = the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; + local_vars = the_scope->local_vars = 0; + the_scope->local_tbl = 0; } body = body->nd_body; - if (nd_type(body) == NODE_BLOCK) { - NODE *node = body->nd_head; - NODE *local; - int i; - - if (nd_type(node) != NODE_ARGS) { - Bug("no argument-node"); - } + PUSH_TAG(); + state = EXEC_TAG(); + if (state == 0) { + if (nd_type(body) == NODE_BLOCK) { + NODE *node = body->nd_head; + int i; - body = body->nd_next; - i = node->nd_cnt; - if (i > argc || (node->nd_rest == -1 && i < argc)) - Fail("Wrong # of arguments(%d for %d)", argc, i); + if (nd_type(node) != NODE_ARGS) { + Bug("no argument-node"); + } - if (local_vars) { - if (i > 0) { - MEMCPY(local_vars, argv, VALUE, i); + body = body->nd_next; + i = node->nd_cnt; + if (i > argc + || (node->nd_rest == -1 + && i+(node->nd_opt?node->nd_opt->nd_alen:0)<argc)){ + Fail("Wrong # of arguments(%d for %d)", argc, i); } - if (node->nd_rest >= 0) { - if (argc == 0) - local_vars[node->nd_rest] = ary_new(); - else - local_vars[node->nd_rest] = ary_new4(argc-i, argv+i); + + if (local_vars) { + if (i > 0) { + MEMCPY(local_vars, argv, VALUE, i); + } + argv += i; argc -= i; + if (node->nd_opt) { + NODE *opt = node->nd_opt; + + while (opt && argc) { + asign(opt->nd_head, *argv); + argv++; argc--; + opt = opt->nd_next; + } + rb_eval(opt); + } + if (node->nd_rest >= 0) { + if (argc > 0) + local_vars[node->nd_rest]=ary_new4(argc,argv); + else + local_vars[node->nd_rest] = ary_new2(0); + } } } - } - PUSH_TAG(); - state = EXEC_TAG(); - if (state == 0) { + else if (nd_type(body) == NODE_ARGS) { + body = 0; + } result = rb_eval(body); } POP_TAG(); - if (!(the_scope->flags & SCOPE_MALLOCED)) { - the_scope->local_vars = Qnil; - the_scope->local_tbl = Qnil; - } POP_SCOPE(); switch (state) { case 0: @@ -2056,20 +2366,23 @@ rb_call(class, recv, mid, argc, argv, scope) case TAG_REDO: Fatal("unexpected redo"); break; - case TAG_RETRY: - Fatal("retry outside of resque clause"); - break; case TAG_RETURN: result = last_val; break; + case TAG_RETRY: + if (!iterator_p()) { + Fatal("retry outside of rescue clause"); + } default: + stack_level--; JUMP_TAG(state); } } } - POP_ENV(); + POP_FRAME(); POP_SELF(); POP_ITER(); + stack_level--; return result; } @@ -2089,7 +2402,7 @@ rb_apply(recv, mid, args) } static VALUE -Fapply(argc, argv, recv) +f_send(argc, argv, recv) int argc; VALUE *argv; VALUE recv; @@ -2133,7 +2446,7 @@ rb_funcall(recv, mid, n, va_alist) va_end(ar); } else { - argv = Qnil; + argv = 0; } return rb_call(CLASS_OF(recv), recv, mid, n, argv, 1); @@ -2149,17 +2462,69 @@ rb_funcall2(recv, mid, argc, argv) return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1); } +static VALUE +f_caller(argc, argv) + int argc; + VALUE *argv; +{ + VALUE level; + struct FRAME *frame = the_frame; + int lev, n; + char buf[BUFSIZ]; + + rb_scan_args(argc, argv, "01", &level); + if (level == Qnil) lev = 1; + else lev = NUM2INT(level); + n = lev; + if (n < 0) Fail("negative level(%d)", n); + else { + while (n-- > 0) { + frame = frame->prev; + if (!frame) return Qnil; + } + if (!frame->file) return Qnil; + if (frame->prev && frame->prev->last_func) { + sprintf(buf, "%s:%d:in `%s'", + frame->file, frame->line, + rb_id2name(frame->prev->last_func)); + } + else { + sprintf(buf, "%s:%d", frame->file, frame->line); + } + } + return str_new2(buf); +} + +void +rb_backtrace() +{ + VALUE c, lev; + int n = 0; + + lev = INT2FIX(n); + while (c = f_caller(1, &lev)) { + printf("%s\n", RSTRING(c)->ptr); + n++; + lev = INT2FIX(n); + } +} + +ID +rb_frame_last_func() +{ + return the_frame->last_func; +} + int rb_in_eval = 0; static VALUE -Feval(obj, src) +f_eval(obj, src) VALUE obj; struct RString *src; { - VALUE result; + VALUE result = Qnil; int state; NODE *node; - char *oldsrc = sourcefile; Check_Type(src, T_STRING); PUSH_TAG(); @@ -2173,11 +2538,12 @@ Feval(obj, src) if ((state = EXEC_TAG()) == 0) { lex_setsrc("(eval)", src->ptr, src->len); - eval_tree = Qnil; + eval_tree = 0; + PUSH_VARS(); yyparse(); - sourcefile = oldsrc; + POP_VARS(); if (nerrs == 0) { - result = Eval(0); + result = Eval(); } } eval_tree = node; @@ -2221,22 +2587,22 @@ find_file(file) } VALUE -Fload(obj, fname) +f_load(obj, fname) VALUE obj; struct RString *fname; { int state, in_eval = rb_in_eval; - NODE *node; - char *file; + char *file, *src; Check_Type(fname, T_STRING); file = find_file(fname->ptr); - if (!file) Fail("No such file to load -- %s", file); + if (!file) Fail("No such file to load -- %s", fname->ptr); PUSH_SELF(TopSelf); PUSH_TAG(); PUSH_CLASS(); - the_class = (struct RClass*)C_Object; + the_class = (struct RClass*)cObject; + PUSH_SCOPE(); the_scope->local_vars = top_scope->local_vars; the_scope->local_tbl = top_scope->local_tbl; rb_in_eval = 1; @@ -2244,9 +2610,11 @@ Fload(obj, fname) if (state == 0) { rb_load_file(file); if (nerrs == 0) { - Eval(0); + Eval(); } } + top_scope->flag = the_scope->flag; + POP_SCOPE(); POP_CLASS(); POP_TAG(); POP_SELF(); @@ -2259,98 +2627,175 @@ Fload(obj, fname) return TRUE; } -static VALUE rb_loadfiles; +static VALUE rb_features; -Frequire(obj, fname) - VALUE obj; - struct RString *fname; +static VALUE +rb_provided(feature) + char *feature; { - char *file; VALUE *p, *pend; + char *f; + int len; - Check_Type(fname, T_STRING); - file = find_file(fname->ptr); - if (!file) { - char *buf = ALLOCA_N(char, strlen(fname->ptr) + 4); - sprintf(buf, "%s.rb", fname->ptr); - file = find_file(buf); -#ifdef USE_DL - if (!file) { - sprintf(buf, "%s%s", fname->ptr, DLEXT); - file = find_file(buf); - } -#endif - if (!file) Fail("No such file to load -- %s", file); - } - - p = RARRAY(rb_loadfiles)->ptr; - pend = p + RARRAY(rb_loadfiles)->len; + p = RARRAY(rb_features)->ptr; + pend = p + RARRAY(rb_features)->len; while (p < pend) { Check_Type(*p, T_STRING); - if (strcmp(RSTRING(*p)->ptr, file) == 0) return FALSE; + f = RSTRING(*p)->ptr; + if (strcmp(f, feature) == 0) return TRUE; + len = strlen(feature); + if (strncmp(f, feature, len) == 0 + && (strcmp(f+len, ".rb") == 0 ||strcmp(f+len, ".o") == 0)) { + return TRUE; + } + p++; } - fname = (struct RString*)str_new2(file); - ary_push(rb_loadfiles, fname); - file = fname->ptr; + return FALSE; +} -#ifdef USE_DL - { - int len = strlen(file), extsiz = sizeof(DLEXT); +void +rb_provide(feature) + char *feature; +{ + if (!rb_provided(feature)) + ary_push(rb_features, str_new2(feature)); +} - if (len > extsiz) { - int i; - for (i=1;i<extsiz;i++) { - if (file[len-i] != DLEXT[extsiz-i-1]) break; +VALUE +f_require(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *ext, *file, *feature, *buf; + VALUE load; + + Check_Type(fname, T_STRING); + if (rb_provided(fname->ptr)) return FALSE; + + ext = strrchr(fname->ptr, '.'); + if (ext) { + if (strcmp(".rb", ext) == 0) { + feature = file = fname->ptr; + file = find_file(file); + if (file) goto rb_load; + } + else if (strcmp(".o", ext) == 0) { + feature = fname->ptr; + if (strcmp(".o", DLEXT) != 0) { + buf = ALLOCA_N(char, strlen(fname->ptr) + 3); + strcpy(buf, feature); + ext = strrchr(buf, '.'); + strcpy(ext, DLEXT); + file = find_file(buf); } - if (i==extsiz) { - static int rb_dln_init = 0; - extern char *rb_dln_argv0; + if (file) goto dyna_load; + } + else if (strcmp(DLEXT, ext) == 0) { + feature = fname->ptr; + file = find_file(feature); + if (file) goto dyna_load; + } + } + buf = ALLOCA_N(char, strlen(fname->ptr) + 4); + sprintf(buf, "%s.rb", fname->ptr); + file = find_file(buf); + if (file) { + fname = (struct RString*)str_new2(file); + feature = buf; + goto rb_load; + } + sprintf(buf, "%s%s", fname->ptr, DLEXT); + file = find_file(buf); + if (file) { + feature = buf; + goto dyna_load; + } + Fail("No such file to load -- %s", fname->ptr); + + dyna_load: + load = str_new2(file); + file = RSTRING(load)->ptr; + dln_load(file); + rb_provide(feature); + return TRUE; - if (rb_dln_init == 0 && dln_init(rb_dln_argv0) == -1) { - Fail("%s: %s", rb_dln_argv0, dln_strerror()); - } + rb_load: + f_load(obj, fname); + rb_provide(feature); + return TRUE; +} - if (dln_load(file) == -1) - Fail(dln_strerror()); +static void +set_method_visibility(argc, argv, ex) + int argc; + VALUE *argv; + int ex; +{ + VALUE self = Qself; + int i; + ID id; - return TRUE; - } + for (i=0; i<argc; i++) { + if (FIXNUM_P(argv[i])) { + id = FIX2INT(argv[i]); } + else { + Check_Type(argv[i], T_STRING); + id = rb_intern(RSTRING(argv[i])->ptr); + } + rb_export_method(self, id, ex); } -#endif - return Fload(obj, fname); } -#ifndef RUBY_LIB -#define RUBY_LIB "/usr/local/lib/ruby:." -#endif - -#define RUBY_LIB_SEP ':' +static VALUE +mod_public(argc, argv) + int argc; + VALUE *argv; +{ + set_method_visibility(argc, argv, NOEX_PUBLIC); + return Qnil; +} -static void -addpath(path) - char *path; +static VALUE +mod_private(argc, argv) + int argc; + VALUE *argv; { - char *p, *s; + set_method_visibility(argc, argv, NOEX_PRIVATE); + return Qnil; +} - if (path == Qnil) return; +static VALUE +mod_modfunc(argc, argv, module) + int argc; + VALUE *argv; + VALUE module; +{ + int i; + ID id; + NODE *body, *old; - p = s = path; - while (*p) { - while (*p == RUBY_LIB_SEP) p++; - if (s = strchr(p, RUBY_LIB_SEP)) { - ary_push(rb_load_path, str_new(p, (int)(s-p))); - p = s + 1; + set_method_visibility(argc, argv, NOEX_PRIVATE); + for (i=0; i<argc; i++) { + if (FIXNUM_P(argv[i])) { + id = FIX2INT(argv[i]); } else { - ary_push(rb_load_path, str_new2(p)); - break; + Check_Type(argv[i], T_STRING); + id = rb_intern(RSTRING(argv[i])->ptr); } + body = search_method(module, id, 0); + if (body == 0 || body->nd_body == 0) { + Fail("undefined method `%s' for module `%s'", + rb_id2name(id), rb_class2name(module)); + } + rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC); } + return Qnil; } static VALUE -Fmod_include(argc, argv, module) +mod_include(argc, argv, module) int argc; VALUE *argv; struct RClass *module; @@ -2358,33 +2803,43 @@ Fmod_include(argc, argv, module) int i; for (i=0; i<argc; i++) { + Check_Type(argv[i], T_MODULE); rb_include_module(module, argv[i]); } return (VALUE)module; } static VALUE -Ftop_include(argc, argv) +top_include(argc, argv) + int argc; + VALUE *argv; { - return Fmod_include(argc, argv, C_Object); + return mod_include(argc, argv, cObject); } static VALUE -Fobj_extend(argc, argv, obj) +obj_extend(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; { - return Fmod_include(argc, argv, rb_single_class(obj)); + return mod_include(argc, argv, rb_singleton_class(obj)); } void rb_extend_object(obj, module) VALUE obj, module; { - rb_include_module(rb_single_class(obj), module); + rb_include_module(rb_singleton_class(obj), module); } -extern VALUE C_Kernel; -extern VALUE C_Module; +extern VALUE cKernel; +extern VALUE cModule; +VALUE f_trace_var(); +VALUE f_untrace_var(); + +void Init_eval() { match = rb_intern("=~"); @@ -2395,126 +2850,137 @@ Init_eval() rb_global_variable(&top_scope); rb_global_variable(&eval_tree); - rb_define_private_method(C_Kernel, "exit", Fexit, -1); - rb_define_private_method(C_Kernel, "eval", Feval, 1); - rb_define_private_method(C_Kernel, "iterator_p", Fiterator_p, 0); - rb_define_method(C_Kernel, "apply", Fapply, -1); - rb_define_method(C_Kernel, "method_missing", Fmissing, -1); - - rb_define_method(C_Module, "include", Fmod_include, -1); - rb_define_method(CLASS_OF(TopSelf), "include", Ftop_include, -1); - rb_define_method(C_Object, "extend", Fobj_extend, -1); + rb_global_variable(&the_dyna_vars); + rb_define_private_method(cKernel, "exit", f_exit, -1); + rb_define_private_method(cKernel, "eval", f_eval, 1); + rb_define_private_method(cKernel, "iterator?", f_iterator_p, 0); + rb_define_private_method(cKernel, "method_missing", f_missing, -1); + rb_define_private_method(cKernel, "loop", f_loop, 0); + rb_define_private_method(cKernel, "caller", f_caller, -1); + + rb_define_method(cKernel, "send", f_send, -1); + + rb_define_method(cModule, "include", mod_include, -1); + rb_define_method(cModule, "public", mod_public, -1); + rb_define_method(cModule, "private", mod_private, -1); + rb_define_method(cModule, "module_function", mod_modfunc, -1); + + rb_define_method(CLASS_OF(TopSelf), "include", top_include, -1); + rb_define_method(cObject, "extend", obj_extend, -1); + + rb_define_private_method(cKernel, "trace_var", f_trace_var, -1); + rb_define_private_method(cKernel, "untrace_var", f_untrace_var, 1); } +VALUE f_autoload(); + +void Init_load() { - char *path; - rb_load_path = ary_new(); - rb_define_variable("$:", &rb_load_path, Qnil, rb_readonly_hook, 0); - rb_define_variable("$LOAD_PATH", &rb_load_path, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$:", &rb_load_path); + rb_define_readonly_variable("$LOAD_PATH", &rb_load_path); - rb_loadfiles = ary_new(); - rb_define_variable("$\"", &rb_load_path, Qnil, rb_readonly_hook, 0); - rb_define_variable("$LOAD_FILES", &rb_load_path, Qnil, rb_readonly_hook,0); - addpath(getenv("RUBYLIB")); - addpath(RUBY_LIB); + rb_features = ary_new(); + rb_define_readonly_variable("$\"", &rb_features); - rb_define_private_method(C_Kernel, "load", Fload, 1); - rb_define_private_method(C_Kernel, "require", Frequire, 1); + rb_define_private_method(cKernel, "load", f_load, 1); + rb_define_private_method(cKernel, "require", f_require, 1); + rb_define_private_method(cKernel, "autoload", f_autoload, 2); } -void +static void scope_dup(scope) struct SCOPE *scope; { ID *tbl; VALUE *vars; - if (scope->flags & SCOPE_MALLOCED) return; - if (!scope->local_tbl) return; + if (scope->flag == SCOPE_MALLOC) return; - tbl = scope->local_tbl; - scope->local_tbl = ALLOC_N(ID, tbl[0]+1); - MEMCPY(scope->local_tbl, tbl, ID, tbl[0]+1); - vars = scope->local_vars; - scope->local_vars = ALLOC_N(VALUE, tbl[0]); - MEMCPY(scope->local_vars, vars, VALUE, tbl[0]); - scope->flags |= SCOPE_MALLOCED; + if (scope->local_tbl) { + tbl = scope->local_tbl; + scope->local_tbl = ALLOC_N(ID, tbl[0]+1); + MEMCPY(scope->local_tbl, tbl, ID, tbl[0]+1); + vars = scope->local_vars; + scope->local_vars = ALLOC_N(VALUE, tbl[0]); + MEMCPY(scope->local_vars, vars, VALUE, tbl[0]); + scope->flag = SCOPE_MALLOC; + } } -VALUE C_Block; static ID blkdata; static void blk_mark(data) struct BLOCK *data; { - gc_mark_env(&data->env); + gc_mark_frame(&data->frame); gc_mark(data->scope); gc_mark(data->var); gc_mark(data->body); gc_mark(data->self); + gc_mark(data->d_vars); } static void blk_free(data) struct BLOCK *data; { - free(data->env.argv); + free(data->frame.argv); } static VALUE -Sblk_new(class) +proc_s_new(class) + VALUE class; { - VALUE blk; + VALUE proc; struct BLOCK *data; - struct SCOPE *scope; - if (!iterator_p() && !Fiterator_p()) { - Fail("tryed to create Block out of iterator"); + if (!iterator_p() && !f_iterator_p()) { + Fail("tryed to create Procedure-Object out of iterator"); } - if (the_block->block) return the_block->block; - blk = obj_alloc(class); + proc = obj_alloc(class); if (!blkdata) blkdata = rb_intern("blk"); - Make_Data_Struct(blk, blkdata, struct BLOCK, Qnil, blk_free, data); + Make_Data_Struct(proc, blkdata, struct BLOCK, blk_mark, blk_free, data); MEMCPY(data, the_block, struct BLOCK, 1); - data->env.argv = ALLOC_N(VALUE, data->env.argc); - MEMCPY(data->env.argv, the_block->env.argv, VALUE, data->env.argc); + data->frame.argv = ALLOC_N(VALUE, data->frame.argc); + MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc); scope_dup(data->scope); - the_block->block = blk; - return blk; + return proc; } VALUE -block_new() +f_lambda() { - return Sblk_new(C_Block); + return proc_s_new(cProc); } static VALUE -Fblk_call(blk, args) - VALUE blk, args; +proc_call(proc, args) + VALUE proc, args; { struct BLOCK *data; - VALUE result; + VALUE result = Qnil; int state; - switch (RARRAY(args)->len) { - case 0: - args = Qnil; - break; - case 1: - args = RARRAY(args)->ptr[0]; - break; + if (TYPE(args) == T_ARRAY) { + switch (RARRAY(args)->len) { + case 0: + args = 0; + break; + case 1: + args = RARRAY(args)->ptr[0]; + break; + } } - Get_Data_Struct(blk, blkdata, struct BLOCK, data); + Get_Data_Struct(proc, blkdata, struct BLOCK, data); /* PUSH BLOCK from data */ PUSH_BLOCK2(data); @@ -2525,18 +2991,14 @@ Fblk_call(blk, args) if (state == 0) { result = rb_yield(args); } - POP_TAG(); + POP_ITER(); POP_BLOCK(); switch (state) { case 0: break; - case TAG_RETRY: - case IN_BLOCK|TAG_RETRY: - Fail("retry from block-closure"); - break; case TAG_BREAK: case IN_BLOCK|TAG_BREAK: Fail("break from block-closure"); @@ -2552,11 +3014,14 @@ Fblk_call(blk, args) return result; } -Init_Block() +void +Init_Proc() { - C_Block = rb_define_class("Block", C_Object); + cProc = rb_define_class("Proc", cObject); - rb_define_single_method(C_Block, "new", Sblk_new, 0); + rb_define_singleton_method(cProc, "new", proc_s_new, 0); - rb_define_method(C_Block, "call", Fblk_call, -2); + rb_define_method(cProc, "call", proc_call, -2); + rb_define_private_method(cKernel, "lambda", f_lambda, 0); + rb_define_private_method(cKernel, "proc", f_lambda, 0); } diff --git a/ext/MANIFEST b/ext/MANIFEST deleted file mode 100644 index 9580b61544..0000000000 --- a/ext/MANIFEST +++ /dev/null @@ -1,3 +0,0 @@ -MANIFEST -extmk.rb.in - diff --git a/ext/Setup b/ext/Setup new file mode 100644 index 0000000000..93586ea0e6 --- /dev/null +++ b/ext/Setup @@ -0,0 +1,7 @@ +#option nodynamic + +#dbm +#etc +#marshal +#socket +tkutil diff --git a/ext/dbm/MANIFEST b/ext/dbm/MANIFEST new file mode 100644 index 0000000000..141b8dd601 --- /dev/null +++ b/ext/dbm/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +dbm.c +dbm.doc +depend +extconf.rb diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c new file mode 100644 index 0000000000..dbdd99c0ca --- /dev/null +++ b/ext/dbm/dbm.c @@ -0,0 +1,435 @@ +/************************************************ + + dbm.c - + + $Author: matz $ + $Date: 1995/01/10 10:42:24 $ + created at: Mon Jan 24 15:59:52 JST 1994 + + Copyright (C) 1995 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +#include <ndbm.h> +#include <fcntl.h> +#include <errno.h> + +VALUE cDBM; +static ID id_dbm; + +extern VALUE mEnumerable; + +static void +closeddbm() +{ + Fail("closed DBM file"); +} + +#define GetDBM(obj, dbmp) {\ + DBM **_dbm;\ + Get_Data_Struct(obj, id_dbm, DBM*, _dbm);\ + dbmp = *_dbm;\ + if (dbmp == Qnil) closeddbm();\ +} + +static void +free_dbm(dbmp) + DBM **dbmp; +{ + if (*dbmp) dbm_close(*dbmp); +} + +#define MakeDBM(obj, dp) {\ + DBM **_dbm;\ + if (!id_dbm) id_dbm = rb_intern("dbm");\ + Make_Data_Struct(obj,id_dbm,DBM*,Qnil,free_dbm,_dbm);\ + *_dbm=dp;\ +} + +static VALUE +fdbm_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE file, vmode; + DBM *dbm, **dbm2; + int mode; + VALUE obj; + + if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) { + mode = 0666; /* default value */ + } + else if (NIL_P(vmode)) { + mode = -1; /* return nil if DB not exist */ + } + else { + mode = NUM2INT(vmode); + } + Check_Type(file, T_STRING); + + dbm = Qnil; + if (mode >= 0) + dbm = dbm_open(RSTRING(file)->ptr, O_RDWR|O_CREAT, mode); + if (!dbm) + dbm = dbm_open(RSTRING(file)->ptr, O_RDWR, mode); + if (!dbm) + dbm = dbm_open(RSTRING(file)->ptr, O_RDONLY, mode); + + if (!dbm) { + if (mode == -1) return Qnil; + rb_sys_fail(RSTRING(file)->ptr); + } + + obj = obj_alloc(class); + MakeDBM(obj, dbm); + + return obj; +} + +static VALUE +fdbm_close(obj) + VALUE obj; +{ + DBM **dbmp; + + Get_Data_Struct(obj, id_dbm, DBM*, dbmp); + if (*dbmp == Qnil) Fail("already closed DBM file"); + dbm_close(*dbmp); + *dbmp = Qnil; + + return Qnil; +} + +static VALUE +fdbm_fetch(obj, keystr) + VALUE obj, keystr; +{ + datum key, value; + DBM *dbm; + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbm); + value = dbm_fetch(dbm, key); + if (value.dptr == Qnil) { + return Qnil; + } + return str_new(value.dptr, value.dsize); +} + +static VALUE +fdbm_indexes(obj, args) + VALUE obj; + struct RArray *args; +{ + VALUE *p, *pend; + struct RArray *new; + int i = 0; + + if (!args || args->len == 1 && TYPE(args->ptr) != T_ARRAY) { + args = (struct RArray*)rb_to_a(args->ptr[0]); + } + + new = (struct RArray*)ary_new2(args->len); + + p = args->ptr; pend = p + args->len; + while (p < pend) { + new->ptr[i++] = fdbm_fetch(obj, *p++); + new->len = i; + } + return (VALUE)new; +} + +static VALUE +fdbm_delete(obj, keystr) + VALUE obj, keystr; +{ + datum key; + DBM *dbm; + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbm); + if (dbm_delete(dbm, key)) { + Fail("dbm_delete failed"); + } + return obj; +} + +static VALUE +fdbm_shift(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + VALUE keystr, valstr; + + GetDBM(obj, dbm); + + key = dbm_firstkey(dbm); + if (!key.dptr) return Qnil; + val = dbm_fetch(dbm, key); + dbm_delete(dbm, key); + + keystr = str_new(key.dptr, key.dsize); + valstr = str_new(val.dptr, val.dsize); + return assoc_new(keystr, valstr); +} + +static VALUE +fdbm_delete_if(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + VALUE keystr, valstr; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + keystr = str_new(key.dptr, key.dsize); + valstr = str_new(val.dptr, val.dsize); + if (rb_yield(assoc_new(keystr, valstr)) + && dbm_delete(dbm, key)) { + Fail("dbm_delete failed"); + } + } + return obj; +} + +static VALUE +fdbm_clear(obj) + VALUE obj; +{ + datum key; + DBM *dbm; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + if (dbm_delete(dbm, key)) { + Fail("dbm_delete failed"); + } + } + return obj; +} + +static VALUE +fdbm_store(obj, keystr, valstr) + VALUE obj, keystr, valstr; +{ + datum key, val; + DBM *dbm; + + if (valstr == Qnil) { + fdbm_delete(obj, keystr); + return Qnil; + } + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + Check_Type(valstr, T_STRING); + val.dptr = RSTRING(valstr)->ptr; + val.dsize = RSTRING(valstr)->len; + + GetDBM(obj, dbm); + if (dbm_store(dbm, key, val, DBM_REPLACE)) { + dbm_clearerr(dbm); + if (errno == EPERM) rb_sys_fail(Qnil); + Fail("dbm_store failed"); + } + return valstr; +} + +static VALUE +fdbm_length(obj) + VALUE obj; +{ + datum key; + DBM *dbm; + int i = 0; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + i++; + } + return INT2FIX(i); +} + +static VALUE +fdbm_each_value(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + rb_yield(str_new(val.dptr, val.dsize)); + } + return obj; +} + +static VALUE +fdbm_each_key(obj) + VALUE obj; +{ + datum key; + DBM *dbm; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + rb_yield(str_new(key.dptr, key.dsize)); + } + return obj; +} + +static VALUE +fdbm_each_pair(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + VALUE keystr, valstr; + + GetDBM(obj, dbm); + + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + keystr = str_new(key.dptr, key.dsize); + valstr = str_new(val.dptr, val.dsize); + rb_yield(assoc_new(keystr, valstr)); + } + + return obj; +} + +static VALUE +fdbm_keys(obj) + VALUE obj; +{ + datum key; + DBM *dbm; + VALUE ary; + + ary = ary_new(); + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + ary_push(ary, str_new(key.dptr, key.dsize)); + } + + return ary; +} + +static VALUE +fdbm_values(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + VALUE ary; + + ary = ary_new(); + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + ary_push(ary, str_new(val.dptr, val.dsize)); + } + + return ary; +} + +static VALUE +fdbm_has_key(obj, keystr) + VALUE obj, keystr; +{ + datum key, val; + DBM *dbm; + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbm); + val = dbm_fetch(dbm, key); + if (val.dptr) return TRUE; + return FALSE; +} + +static VALUE +fdbm_has_value(obj, valstr) + VALUE obj, valstr; +{ + datum key, val; + DBM *dbm; + + Check_Type(valstr, T_STRING); + val.dptr = RSTRING(valstr)->ptr; + val.dsize = RSTRING(valstr)->len; + + GetDBM(obj, dbm); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + if (val.dsize == RSTRING(valstr)->len && + memcmp(val.dptr, RSTRING(valstr)->ptr, val.dsize) == 0) + return TRUE; + } + return FALSE; +} + +static VALUE +fdbm_to_a(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + VALUE ary; + + GetDBM(obj, dbm); + + ary = ary_new(); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + ary_push(ary, assoc_new(str_new(key.dptr, key.dsize), + str_new(val.dptr, val.dsize))); + } + + return ary; +} + +Init_dbm() +{ + cDBM = rb_define_class("DBM", cObject); + rb_include_module(cDBM, mEnumerable); + + rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1); + rb_define_method(cDBM, "close", fdbm_close, 0); + rb_define_method(cDBM, "[]", fdbm_fetch, 1); + rb_define_method(cDBM, "[]=", fdbm_store, 2); + rb_define_method(cDBM, "indexes", fdbm_indexes, -2); + rb_define_method(cDBM, "length", fdbm_length, 0); + rb_define_alias(cDBM, "size", "length"); + rb_define_method(cDBM, "each", fdbm_each_pair, 0); + rb_define_method(cDBM, "each_value", fdbm_each_value, 0); + rb_define_method(cDBM, "each_key", fdbm_each_key, 0); + rb_define_method(cDBM, "each_pair", fdbm_each_pair, 0); + rb_define_method(cDBM, "keys", fdbm_keys, 0); + rb_define_method(cDBM, "values", fdbm_values, 0); + rb_define_method(cDBM, "shift", fdbm_shift, 1); + rb_define_method(cDBM, "delete", fdbm_delete, 1); + rb_define_method(cDBM, "delete_if", fdbm_delete_if, 0); + rb_define_method(cDBM, "clear", fdbm_clear, 0); + rb_define_method(cDBM, "includes", fdbm_has_key, 1); + rb_define_method(cDBM, "has_key", fdbm_has_key, 1); + rb_define_method(cDBM, "has_value", fdbm_has_value, 1); + + rb_define_method(cDBM, "to_a", fdbm_to_a, 0); +} diff --git a/ext/dbm/dbm.doc b/ext/dbm/dbm.doc new file mode 100644 index 0000000000..45f174b7aa --- /dev/null +++ b/ext/dbm/dbm.doc @@ -0,0 +1,107 @@ +.\" dbm.doc - -*- Indented-Text -*- created at: Thu Mar 23 20:28:31 JST 1995 + +** DBM(クラス) + +NDBMファイルをアクセスするクラス.キー,データともに文字列でなければな +らないという制限と,データがファイルに保存されるという点を除いては +Dictクラスと全く同様に扱うことができる.NDBMを備えていないシステムでは +このクラスは定義されない. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + + self [key] + + keyをキーとする値を返す. + + self [key]= value + + keyをキーとして,valueを格納する.valueとしてnilを指定すると, + keyに対する項目の削除となる. + + clear + + DBMファイルの中身を空にする. + + close + + DBMファイルをクローズする.以後の操作は例外を発生させる. + + delete(key) + + keyをキーとする組を削除する. + + delete_if + + 要素を削除するイテレータ.key::valueというペアを与えて,ブロッ + クを評価した値が真の時,該当する項目を削除する. + + each + each_pair + + key::valueなるペアを与えるイテレータ. + + each_key + + 全てのkeyに対して繰り返すイテレータ. + + each_value + + 全てのvalueに対して繰り返すイテレータ. + + has_key(key) + includes(key) + + keyがデータベース中に存在する時,真を返す + + has_value(value) + + valueを値とする組がデータベース中に存在する時,真を + 返す + + indexes(ary) + indexes(key-1, ..., key-n) + + 1番目の形式では文字列の配列を引数として受けて,その要素をキー + とする要素を含む配列を返す.2番目の形式では各引数の値をキーと + する要素を含む配列を返す. + + keys + + データベース中に存在するキー全てを含む配列を返す. + + length + size + + データベース中の要素の数を返す.(注意:現在の実現では要素数を数 + えるためにデータベースを全部検索するので,結構コストが高い.気 + をつけて使うこと.) + + shift + + データベース中の要素を一つ取り出し(データベースから削除する), + key::valueというペアを返す. + + to_a + + データベース中のkey-valueペアを要素とする配列を返す. + + values + + データベース中に存在する値全てを含む配列を返す. + +Single Methods: + + open(dbname[, mode]) + + dbnameで指定したデータベースをモードをmodeに設定してオープンす + る.modeの省略値は0666である.modeとしてnilを指定するとデータ + ベースが既に存在しない時には新たにオープンせず,nilを返す. + +------------------------------------------------------- +Local variables: +fill-column: 70 +end: diff --git a/ext/dbm/depend b/ext/dbm/depend new file mode 100644 index 0000000000..40139962a7 --- /dev/null +++ b/ext/dbm/depend @@ -0,0 +1 @@ +dbm.o: dbm.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/dbm/extconf.rb b/ext/dbm/extconf.rb new file mode 100644 index 0000000000..5105cd662f --- /dev/null +++ b/ext/dbm/extconf.rb @@ -0,0 +1,4 @@ +have_library("dbm", "dbm_open") +if have_func("dbm_open") + create_makefile("dbm") +end diff --git a/ext/etc/MANIFEST b/ext/etc/MANIFEST new file mode 100644 index 0000000000..a0f521b386 --- /dev/null +++ b/ext/etc/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +etc.c +etc.doc +depend +extconf.rb diff --git a/ext/etc/depend b/ext/etc/depend new file mode 100644 index 0000000000..5c95ef117a --- /dev/null +++ b/ext/etc/depend @@ -0,0 +1 @@ +etc.o : etc.c ../../ruby.h ../../config.h ../../defines.h @@ -9,62 +9,77 @@ ************************************************/ #include "ruby.h" + +#ifdef HAVE_GETPWENT #include <pwd.h> +#endif + +#ifdef HAVE_GETGRENT #include <grp.h> +#endif -char *getlogin(); +static VALUE sPasswd, sGroup; static VALUE -Fetc_getlogin(obj) +etc_getlogin(obj) VALUE obj; { +#ifdef HAVE_GETLOGIN + char *getlogin(); char *login = getlogin(); +#else + char *getenv(); + char *login = getenv("USER"); +#endif if (login) return str_new2(login); return Qnil; } +#ifdef HAVE_GETPWENT static VALUE setup_passwd(pwd) struct passwd *pwd; { if (pwd == Qnil) rb_sys_fail("/etc/passwd"); - return struct_new("passwd", - "name", str_new2(pwd->pw_name), - "passwd", str_new2(pwd->pw_passwd), - "uid", INT2FIX(pwd->pw_uid), - "gid", INT2FIX(pwd->pw_gid), - "gecos", str_new2(pwd->pw_gecos), - "dir", str_new2(pwd->pw_dir), - "shell", str_new2(pwd->pw_shell), + return struct_new(sPasswd, + str_new2(pwd->pw_name), + str_new2(pwd->pw_passwd), + INT2FIX(pwd->pw_uid), + INT2FIX(pwd->pw_gid), + str_new2(pwd->pw_gecos), + str_new2(pwd->pw_dir), + str_new2(pwd->pw_shell), #ifdef PW_CHANGE - "change", INT2FIX(pwd->pw_change), + INT2FIX(pwd->pw_change), #endif #ifdef PW_QUOTA - "quota", INT2FIX(pwd->pw_quota), + INT2FIX(pwd->pw_quota), #endif #ifdef PW_AGE - "age", INT2FIX(pwd->pw_age), + INT2FIX(pwd->pw_age), #endif #ifdef PW_CLASS - "class", str_new2(pwd->pw_class), + str_new2(pwd->pw_class), #endif #ifdef PW_COMMENT - "comment", str_new2(pwd->pw_comment), + str_new2(pwd->pw_comment), #endif #ifdef PW_EXPIRE - "expire", INT2FIX(pwd->pw_expire), + INT2FIX(pwd->pw_expire), #endif Qnil); } +#endif static VALUE -Fetc_getpwuid(argc, argv, obj) +etc_getpwuid(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { +#ifdef HAVE_GETPWENT VALUE id; int uid; struct passwd *pwd; @@ -78,24 +93,32 @@ Fetc_getpwuid(argc, argv, obj) pwd = getpwuid(uid); if (pwd == Qnil) Fail("can't find user for %d", uid); return setup_passwd(pwd); +#else + return Qnil; +#endif } static VALUE -Fetc_getpwnam(obj, nam) +etc_getpwnam(obj, nam) VALUE obj, nam; { +#ifdef HAVE_GETPWENT struct passwd *pwd; Check_Type(nam, T_STRING); pwd = getpwnam(RSTRING(nam)->ptr); if (pwd == Qnil) Fail("can't find user for %s", RSTRING(nam)->ptr); return setup_passwd(pwd); +#else + return Qnil; +#endif } static VALUE -Fetc_passwd(obj) +etc_passwd(obj) VALUE obj; { +#ifdef HAVE_GETPWENT struct passwd *pw; if (iterator_p()) { @@ -109,8 +132,12 @@ Fetc_passwd(obj) pw = getpwent(); if (pw == Qnil) Fail("can't fetch next -- /etc/passwd"); return setup_passwd(pw); +#else + return Qnil; +#endif } +#ifdef HAVE_GETGRENT static VALUE setup_group(grp) struct group *grp; @@ -124,18 +151,20 @@ setup_group(grp) ary_push(mem, str_new2(*tbl)); tbl++; } - return struct_new("group", - "name", str_new2(grp->gr_name), - "passwd", str_new2(grp->gr_passwd), - "gid", INT2FIX(grp->gr_gid), - "mem", mem, + return struct_new(sGroup, + str_new2(grp->gr_name), + str_new2(grp->gr_passwd), + INT2FIX(grp->gr_gid), + mem, Qnil); } +#endif static VALUE -Fetc_getgrgid(obj, id) +etc_getgrgid(obj, id) VALUE obj, id; { +#ifdef HAVE_GETGRENT int gid; struct group *grp; @@ -143,24 +172,32 @@ Fetc_getgrgid(obj, id) grp = getgrgid(gid); if (grp == Qnil) Fail("can't find group for %d", gid); return setup_group(grp); +#else + return Qnil; +#endif } static VALUE -Fetc_getgrnam(obj, nam) +etc_getgrnam(obj, nam) VALUE obj, nam; { +#ifdef HAVE_GETGRENT struct group *grp; Check_Type(nam, T_STRING); grp = getgrnam(RSTRING(nam)->ptr); if (grp == Qnil) Fail("can't find group for %s", RSTRING(nam)->ptr); return setup_group(grp); +#else + return Qnil; +#endif } static VALUE -Fetc_group(obj) +etc_group(obj) VALUE obj; { +#ifdef HAVE_GETGRENT struct group *grp; if (iterator_p()) { @@ -172,22 +209,52 @@ Fetc_group(obj) return obj; } return setup_group(getgrent()); +#else + return Qnil; +#endif } -VALUE M_Etc; +VALUE mEtc; -Init_Etc() +void +Init_etc() { - M_Etc = rb_define_module("Etc"); - rb_extend_object(M_Etc, M_Etc); + mEtc = rb_define_module("Etc"); + + rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); - rb_define_method(M_Etc, "getlogin", Fetc_getlogin, 0); + rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); + rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1); + rb_define_module_function(mEtc, "passwd", etc_passwd, 0); - rb_define_method(M_Etc, "getpwuid", Fetc_getpwuid, -1); - rb_define_method(M_Etc, "getpwnam", Fetc_getpwnam, 1); - rb_define_method(M_Etc, "passwd", Fetc_passwd, 0); + rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, 1); + rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); + rb_define_module_function(mEtc, "group", etc_group, 0); - rb_define_method(M_Etc, "getgrgid", Fetc_getgrgid, 1); - rb_define_method(M_Etc, "getgrnam", Fetc_getgrnam, 1); - rb_define_method(M_Etc, "group", Fetc_group, 0); + sPasswd = struct_define("Passwd", + "name", "passwd", "uid", "gid", + "gecos", "dir", "shell", +#ifdef PW_CHANGE + "change", +#endif +#ifdef PW_QUOTA + "quota", +#endif +#ifdef PW_AGE + "age", +#endif +#ifdef PW_CLASS + "class", +#endif +#ifdef PW_COMMENT + "comment", +#endif +#ifdef PW_EXPIRE + "expire", +#endif + Qnil); + +#ifdef HAVE_GETGRENT + sGroup = struct_define("Group", "name", "passwd", "gid", "mem", Qnil); +#endif } diff --git a/ext/etc/etc.doc b/ext/etc/etc.doc new file mode 100644 index 0000000000..2af895c9de --- /dev/null +++ b/ext/etc/etc.doc @@ -0,0 +1,73 @@ +.\" etc.doc - -*- Indented-Text -*- created at: Fri Jul 14 00:47:15 JST 1995 + +** Etc(モジュール) + +/etcディレクトリ以下の情報を得るためのモジュール.クラスにインクルード +して使うこともできる. + +Methods: +Single Methods: + + getlogin + + 自分のlogin名を返す.これが失敗した場合はgetpwuid()を用いると + 良い. + + getpwnam(name) + + /etc/passwdファイル(あるいはDBMファイルやNISデータベース)を検 + 索し,nameの名前を持つpasswdエントリを返す.戻り値はpasswd構造 + 体で以下のメンバを持つ. + + struct passwd + name # ユーザ名(文字列) + passwd # パスワード(文字列) + uid # ユーザID(整数) + gid # グループID(整数) + gecos # gecosフィールド(文字列) + dir # ホームディレクトリ(文字列) + shell # ログインシェル(文字列) + # 以降のメンバはシステムによっては提供されない. + change # パスワード変更時間(整数) + quota # クォータ(整数) + age # エージ(整数) + class # ユーザアクセスクラス(文字列) + comment # コメント(文字列) + expire # アカウント有効期限(整数) + end + + 詳細はgetpwnam(3)を参照のこと. + + getpwuid([uid]) + + uidをユーザIDとするpasswdエントリを返す.戻り値はgetpwnam()と + 同様である.引数を省略した場合にはgetuid()の値を用いる.詳細は + getpwuid(3)を参照のこと. + + getgrgid(gid) + + /etc/groupファイル(あるいは…getpwnam参照)を検索し,gidをグルー + プIDとするグループエントリを返す.戻り値はgroup構造体で以下の + メンバを持つ. + + struct group + name # グループ名(文字列) + passwd # グループのパスワード(文字列) + gid # グループID(整数) + mem # グループメンバ名の配列 + end + + 詳細はgetgrgid(3)を参照のこと. + + getgrnam(name) + + nameという名前のグループエントリを返す.戻り値はgetgrgid()と同 + 様である.詳細はgetgrnam(3)を参照. + + group + + 全てのグループエントリを順にアクセスするためのイテレータ. + + passwd + + 全てのpasswdエントリを順にアクセスするためのイテレータ. diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb new file mode 100644 index 0000000000..884de93ec8 --- /dev/null +++ b/ext/etc/extconf.rb @@ -0,0 +1,7 @@ +have_library("sun", "getpwnam") # NIS (== YP) interface for IRIX 4 +a = have_func("getlogin") +b = have_func("getpwent") +c = have_func("getgrent") +if a or b or c + create_makefile("etc") +end diff --git a/ext/extmk.rb.in b/ext/extmk.rb.in index 25bf6b9027..b61ccd222f 100644 --- a/ext/extmk.rb.in +++ b/ext/extmk.rb.in @@ -1,10 +1,40 @@ #! /usr/local/bin/ruby +if $ARGV[0] == 'install' + $install = TRUE + $ARGV.shift +end + +if $ARGV[0] == 'clean' + $clean = TRUE + $ARGV.shift +end + +$cache_mod = FALSE; +$lib_cache = {} +$func_cache = {} +$hdr_cache = {} + +if File.exists?("config.cache") then + f = open("config.cache", "r") + while f.gets + case $_ + when /^lib: ([\w_]+) (yes|no)/ + $lib_cache[$1] = $2 + when /^func: ([\w_]+) (yes|no)/ + $func_cache[$1] = $2 + when /^hdr: (.+) (yes|no)/ + $hdr_cache[$1] = $2 + end + end + f.close +end + def older(file1, file2) - if !File.exists(file1) then + if !File.exists?(file1) then return TRUE end - if !File.exists(file2) then + if !File.exists?(file2) then return FALSE end if File.mtime(file1) < File.mtime(file2) @@ -13,77 +43,144 @@ def older(file1, file2) return FALSE end -if !File.exists("./Makefile") || - older("./Makefile", "../extmk.rb") || - older("./Makefile", "./extconf.rb") then +LINK = "@CC@ -o conftest %s %s conftest.c %s > /dev/null 2>&1" +CPP = "@CPP@ @CPPFLAGS@ %s conftest.c > /dev/null 2>&1" - LINK = "@CC@ -o conftest @CFLAGS@ @LDFLAGS@ conftest.c %s > /dev/null 2>&1" - $defs = [] +def have_library(lib, func) + if $lib_cache[lib] + if $lib_cache[lib] == "yes" + if $libs + $libs = $libs + " -l" + lib + else + $libs = "-l" + lib + end + return TRUE + else + return FALSE + end + end - def have_library(func, lib) - cfile = open("conftest.c", "w") - printf cfile, "\ + cfile = open("conftest.c", "w") + printf cfile, "\ int main() { return 0; } int t() { %s(); return 0; } ", func - cfile.close - - begin - if system(format(LINK, "-l" + lib)) != 0 - return FALSE - end - ensure - system "/bin/rm -f conftest*" - end + cfile.close + begin if $libs - $libs = $libs + " -l" + lib + libs = "-l" + lib + " " + $libs else - $libs = "-l" + lib + libs = "-l" + lib end - $defs.push(format("-DHAVE_LIB%s", lib.toupper)) - return TRUE + if !system(format(LINK, $CFLAGS, $LDFLAGS, libs)) + $lib_cache[lib] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "/bin/rm -f conftest*" end - def have_func(func) + $libs = libs + $lib_cache[lib] = 'yes' + $cache_mod = TRUE + return TRUE +end - cfile = open("conftest.c", "w") - printf cfile, "\ +def have_func(func) + if $func_cache[func] + if $func_cache[func] == "yes" + $defs.push(format("-DHAVE_%s", func.upcase)) + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + printf cfile, "\ char %s(); int main() { return 0; } int t() { %s(); return 0; } ", func, func - cfile.close + cfile.close - libs = $libs - libs = "" if libs == nil + libs = $libs + libs = "" if libs == nil - begin - if system(format(LINK, libs)) != 0 - return FALSE - end - ensure - system "/bin/rm -f conftest*" + begin + if !system(format(LINK, $CFLAGS, $LDFLAGS, libs)) + $func_cache[func] = 'no' + $cache_mod = TRUE + return FALSE end - $defs.push(format("-DHAVE_%s", func.toupper)) - return TRUE + ensure + system "/bin/rm -f conftest*" + end + $defs.push(format("-DHAVE_%s", func.upcase)) + $func_cache[func] = 'yes' + $cache_mod = TRUE + return TRUE +end +def have_header(header) + if $hdr_cache[header] + if $hdr_cache[header] == "yes" + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + return TRUE + else + return FALSE + end end - def create_header() - if $defs.length > 0 - hfile = open("extconf.h", "w") - for line in $defs - line =~ /^-D(.*)/ - printf hfile, "#define %s 1\n", $1 - end - hfile.close + cfile = open("conftest.c", "w") + printf cfile, "\ +#include <%s> +", header + cfile.close + + begin + if !system(format(CPP, $CFLAGS)) + $hdr_cache[header] = 'no' + $cache_mod = TRUE + return FALSE end + ensure + system "/bin/rm -f conftest*" end + $hdr_cache[header] = 'yes' + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + $cache_mod = TRUE + return TRUE +end - def create_makefile(target) - mfile = open("Makefile", "w") - printf mfile, "\ +def create_header() + if $defs.length > 0 + hfile = open("extconf.h", "w") + for line in $defs + line =~ /^-D(.*)/ + printf hfile, "#define %s 1\n", $1 + end + hfile.close + end +end + +def create_makefile(target) + + if $libs and "@DLEXT@" == "o" + libs = $libs.split + for lib in libs + lib.sub!(/-l(.*)/, '"lib\1.a"') + end + $defs.push(format("-DEXTLIB='%s'", libs.join(","))) + end + $libs = "" if not $libs + + mfile = open("Makefile", "w") + printf mfile, "\ SHELL = /bin/sh #### Start of system configuration section. #### @@ -93,84 +190,205 @@ VPATH = @srcdir@ CC = @CC@ -CFLAGS = -I../.. @CCDLFLAGS@ @CFLAGS@ %s -LDDLFLAGS = @LDDLFLAGS@ -", $defs.join(" ") +CFLAGS = %s #$CFLAGS %s +LDSHARED = @LDSHARED@ +", if $static then "" else "@CCDLFLAGS@" end, $defs.join(" ") - printf mfile, "\ + printf mfile, "\ prefix = @prefix@ binprefix = exec_prefix = @exec_prefix@ bindir = $(exec_prefix)/bin +libdir = @prefix@/lib/ruby @SET_MAKE@ #### End of system configuration section. #### " - printf mfile, "OBJS = " - if !$objs then - $objs = Dir["*.c"] - for f in $objs - f.sub(/\.c$/, ".o") - end + printf mfile, "LIBS = %s\n", $libs + printf mfile, "OBJS = " + if !$objs then + $objs = Dir["*.c"] + for f in $objs + f.sub!(/\.c$/, ".o") end - printf mfile, $objs.join(" ") - printf mfile, "\n" + end + printf mfile, $objs.join(" ") + printf mfile, "\n" - printf mfile, "\ -TARGET = %s.@DLEXT@ + printf mfile, "\ +TARGET = %s.%s + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ all: $(TARGET) -clean:; @rm -f *.o *.so +clean:; @rm -f *.o *.so *.sl @rm -f Makefile extconf.h conftest.* @rm -f core ruby *~ realclean: clean -", target +", target, if $static then "o" else "@DLEXT@" end + + if !$static + printf mfile, "\ + +install: $(libdir)/$(TARGET) + +$(libdir)/$(TARGET): $(TARGET) + @test -d $(libdir) || mkdir $(libdir) + $(INSTALL_DATA) $(TARGET) $(libdir)/$(TARGET) +" + else + printf mfile, "\ - if "@DLEXT@" == "so" - printf mfile, "\ -.SUFFIXES: .so $(SUFFIXES) +install:; +" + end -$(TARGET).so: $(OBJS) - ld $(LDDLFLAGS) -o $*.so $(OBJS) + if !$static && "@DLEXT@" != "o" + printf mfile, "\ +$(TARGET): $(OBJS) + $(LDSHARED) -o $(TARGET) $(OBJS) $(LIBS) " - elsif !File.exists(target + ".c") - printf mfile, "\ -$(TARGET).o: $(OBJS) - ld $(LDDLFLAGS) -r $*.o $(OBJS) + elsif !File.exists?(target + ".c") + printf mfile, "\ +$(TARGET): $(OBJS) + ld $(LDDLFLAGS) -r $(TARGET) $(OBJS) " + end + + if File.exists?("depend") + dfile = open("depend", "r") + printf mfile, "###\n" + while line = dfile.gets() + printf mfile, "%s", line end + dfile.close + end + mfile.close + if $static + $extinit += format("\ +\tInit_%s();\n\ +\trb_provide(\"%s.o\");\n\ +", target, target) + $extobjs += format("ext/%s/%s.o ", $static, target) + end +end - if File.exists("depend") - dfile = open("depend", "r") - printf mfile, "###\n" - while line = dfile.gets() - printf mfile, "%s", line +def extmake(target) + if $static_ext[target] + $static = target + else + $static = FALSE + end + + return if $nodynamic and not $static + + $libs = nil + $objs = nil + $CFLAGS = "-I../.. @CFLAGS@" + $LDFLAGS = "@STATIC@ @LDFLAGS@" + + begin + Dir.chdir target + if $static_ext.size > 0 || + !File.exists?("./Makefile") || + older("./Makefile", "../Setup") || + older("./Makefile", "../extmk.rb") || + older("./Makefile", "./extconf.rb") + then + $defs = [] + if File.exists?("extconf.rb") + load "extconf.rb" + else + create_makefile(target); + end + end + if File.exists?("./Makefile") + if $install + system "make install" + elsif $clean + system "make clean" + else + system "make all" end - dfile.close end - mfile.close + $extlibs += " " + $libs if $static && $libs + ensure + Dir.chdir ".." end +end - if File.exists("configure") && - (!File.exists("config.status") || - File.mtime("config.status") < File.mtime("configure")) then +# get static-link modules +$static_ext = {} +if File.file? "./Setup" + f = open("./Setup") + while f.gets() + $_.chop! + sub!(/#.*$/, '') + continue if /^\s*$/ + if /^option +nodynamic/ + $nodynamic = TRUE + continue + end + $static_ext[$_.split[0]] = TRUE + end + f.close +end - system "./configure" +for d in Dir["*"] + File.directory?(d) || continue + File.file?(d + "/MANIFEST") || continue + + d = $1 if d =~ /\/([\/]*)$/ + print "compiling ", d, "\n" + extmake(d) +end + +if $cache_mod + f = open("config.cache", "w") + for k,v in $lib_cache + printf f, "lib: %s %s\n", k, v + end + for k,v in $func_cache + printf f, "func: %s %s\n", k, v end + for k,v in $hdr_cache + printf f, "hdr: %s %s\n", k, v + end + f.close +end - if File.exists("extconf.rb") - load "extconf.rb" - else - Dir.pwd =~ /[^\/]+$/ - create_makefile($&); +exit if $install +if $extobjs + if older("extinit.c", "Setup") + f = open("extinit.c", "w") + printf f, "void Init_ext() {\n" + printf f, $extinit + printf f, "}\n" + f.close + end + if older("extinit.o", "extinit.c") + cmd = "@CC@ @CFLAGS@ -c extinit.c" + print cmd, "\n" + system cmd or exit 1 end + Dir.chdir ".." + $extobjs = "ext/extinit.o " + $extobjs + + if older("ruby", "ext/Setup") or older("ruby", "miniruby") + `rm -f ruby` + end + system format('make ruby PROGRAM=ruby EXTOBJS="%s" EXTLIBS="%s"', $extobjs, $extlibs) +else + Dir.chdir ".." + `rm -f ruby` + `cp miniruby ruby` end -system "make all" if File.exists("./Makefile") #Local variables: # mode: ruby diff --git a/ext/marshal/MANIFEST b/ext/marshal/MANIFEST new file mode 100644 index 0000000000..53b0849484 --- /dev/null +++ b/ext/marshal/MANIFEST @@ -0,0 +1,4 @@ +MANIFEST +depend +marshal.c +marshal.doc diff --git a/ext/marshal/depend b/ext/marshal/depend new file mode 100644 index 0000000000..c955eb2d59 --- /dev/null +++ b/ext/marshal/depend @@ -0,0 +1,2 @@ +marshal.o: marshal.c ../../ruby.h ../../config.h ../../defines.h ../../io.h \ + ../../st.h diff --git a/ext/marshal/marshal.c b/ext/marshal/marshal.c new file mode 100644 index 0000000000..0b29ad5ab8 --- /dev/null +++ b/ext/marshal/marshal.c @@ -0,0 +1,565 @@ +/************************************************ + + marshal.c - + + $Author$ + $Revision$ + $Date$ + created at: Thu Apr 27 16:30:01 JST 1995 + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include "st.h" + +#define TYPE_NIL '0' +#define TYPE_FIXNUM 'i' + +#define TYPE_OBJECT 'o' +#define TYPE_LINK '@' +#define TYPE_FLOAT 'f' +#define TYPE_BIGNUM 'l' +#define TYPE_STRING '"' +#define TYPE_REGEXP '/' +#define TYPE_ARRAY '[' +#define TYPE_HASH '{' +#define TYPE_STRUCT 'S' + +char *rb_class2path(); +VALUE rb_path2class(); + +static ID s_dump, s_load; + +#define w_byte(c, fp) putc((c), fp) +#define w_bytes(s, n, fp) (w_long((n), fp),fwrite(s, 1, n, fp)) + +static void +w_short(x, fp) + int x; + FILE *fp; +{ + w_byte( x & 0xff, fp); + w_byte((x>> 8) & 0xff, fp); +} + +static void +w_long(x, fp) + long x; + FILE *fp; +{ + w_byte((int)( x & 0xff), fp); + w_byte((int)((x>> 8) & 0xff), fp); + w_byte((int)((x>>16) & 0xff), fp); + w_byte((int)((x>>24) & 0xff), fp); +} + +static void +w_float(d, fp) + double d; + FILE *fp; +{ + char buf[100]; + + sprintf(buf, "%.12g", d); + w_bytes(buf, strlen(buf), fp); +} + +static void +w_symbol(id, fp) + ID id; + FILE *fp; +{ + char *sym = rb_id2name(id); + + w_bytes(sym, strlen(sym), fp); +} + +static void w_object(); +extern VALUE cBignum, cStruct; + +static int +hash_each(key, value, fp) + VALUE key, value; + FILE *fp; +{ + w_object(key, fp); + w_object(value, fp); + return ST_CONTINUE; +} + +static int +obj_each(id, value, fp) + ID id; + VALUE value; + FILE *fp; +{ + w_symbol(id, fp); + w_object(value, fp); + return ST_CONTINUE; +} + +struct st_table *new_idhash(); + +static void +w_object(obj, fp, port, table) + VALUE obj, port; + FILE *fp; + st_table *table; +{ + if (obj == Qnil) { + w_byte(TYPE_NIL, fp); + } + else if (FIXNUM_P(obj)) { + w_byte(TYPE_FIXNUM, fp); + w_long(FIX2INT(obj), fp); + } + else if (st_lookup(table, obj, 0)) { + w_byte(TYPE_LINK, fp); + w_long(obj, fp); + } + else { + st_insert(table, obj, 0); + switch (BUILTIN_TYPE(obj)) { + case T_FLOAT: + w_byte(TYPE_FLOAT, fp); + w_long(obj, fp); + w_float(RFLOAT(obj)->value, fp); + break; + + case T_BIGNUM: + w_byte(TYPE_BIGNUM, fp); + w_long(obj, fp); + { + char sign = RBIGNUM(obj)->sign?'+':'-'; + int len = RBIGNUM(obj)->len; + USHORT *d = RBIGNUM(obj)->digits; + + w_byte(sign, fp); + w_long(len, fp); + while (len--) { + w_short(d, fp); + d++; + } + } + break; + + case T_STRING: + w_byte(TYPE_STRING, fp); + w_long(obj, fp); + w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp); + break; + + case T_REGEXP: + w_byte(TYPE_REGEXP, fp); + w_long(obj, fp); + w_bytes(RREGEXP(obj)->str, RREGEXP(obj)->len, fp); + w_byte(FL_TEST(obj, FL_USER1), fp); + break; + + case T_ARRAY: + w_byte(TYPE_ARRAY, fp); + w_long(obj, fp); + { + int len = RARRAY(obj)->len; + VALUE *ptr = RARRAY(obj)->ptr; + + w_long(len, fp); + while (len--) { + w_object(*ptr, fp, port, table); + ptr++; + } + } + break; + + case T_HASH: + w_byte(TYPE_HASH, fp); + w_long(obj, fp); + w_long(RHASH(obj)->tbl->num_entries, fp); + st_foreach(RHASH(obj)->tbl, hash_each, fp); + break; + + case T_STRUCT: + w_byte(TYPE_STRUCT, fp); + w_long(obj, fp); + { + int len = RSTRUCT(obj)->len; + char *path = rb_class2path(CLASS_OF(obj)); + VALUE mem; + int i; + + w_bytes(path, strlen(path), fp); + w_long(len, fp); + mem = rb_ivar_get(CLASS_OF(obj), rb_intern("__member__")); + if (mem == Qnil) { + Fail("non-initialized struct"); + } + for (i=0; i<len; i++) { + w_symbol(FIX2INT(RARRAY(mem)->ptr[i]), fp); + w_object(RSTRUCT(obj)->ptr[i], fp, port, table); + } + } + break; + + case T_OBJECT: + w_byte(TYPE_OBJECT, fp); + w_long(obj, fp); + { + VALUE class = CLASS_OF(obj); + char *path = rb_class2path(class); + + w_bytes(path, strlen(path), fp); + if (rb_responds_to(obj, s_dump)) { + w_long(-1, fp); + rb_funcall(obj, s_dump, 1, port); + } + else if (ROBJECT(obj)->iv_tbl) { + w_long(ROBJECT(obj)->iv_tbl->num_entries, fp); + st_foreach(ROBJECT(obj)->iv_tbl, obj_each, fp); + } + else { + w_long(0, fp); + } + } + break; + + default: + Fail("can't dump %s", rb_class2name(CLASS_OF(obj))); + break; + } + } +} + +static VALUE +marshal_dump(self, obj, port) + VALUE self, obj, port; +{ + extern VALUE cIO; + FILE *fp; + OpenFile *fptr; + st_table *table; + + if (obj_is_kind_of(port, cIO)) { + GetOpenFile(port, fptr); + if (!(fptr->mode & FMODE_WRITABLE)) { + Fail("not opened for writing"); + } + fp = (fptr->f2) ? fptr->f2 : fptr->f; + } + else { + Fail("instance of IO needed"); + } + + table = new_idhash(); + + w_object(obj, fp, port, table); + + st_free_table(table); + return Qnil; +} + +static VALUE +marshal_dumps(self, obj) + VALUE self, obj; +{ + VALUE str = str_new(0, 0); + VALUE port; + FILE *fp = Qnil; + char buf[BUFSIZ]; + int n; + + sprintf(buf, "/tmp/rb-mrsr-%x", getpid()^(int)buf); + port = file_open(buf, "w"); + if (!port) rb_sys_fail("tmp file"); + fp = fopen(buf, "r"); + if (!fp) rb_sys_fail("tmp file(read)"); + unlink(buf); + + marshal_dump(self, obj, port); + io_close(port); + + while (n = fread(buf, 1, BUFSIZ, fp)) { + str_cat(str, buf, n); + } + + return str; +} + +#define r_byte(fp) getc(fp) + +static int +r_short(fp) + FILE *fp; +{ + register short x; + x = r_byte(fp); + x |= r_byte(fp) << 8; + /* XXX If your short is > 16 bits, add sign-extension here!!! */ + return x; +} + +static long +r_long(fp) + FILE *fp; +{ + register long x; + x = r_byte(fp); + x |= (long)r_byte(fp) << 8; + x |= (long)r_byte(fp) << 16; + x |= (long)r_byte(fp) << 24; + /* XXX If your long is > 32 bits, add sign-extension here!!! */ + return x; +} +#define r_bytes(s, fp) r_bytes0(&s, fp) +static int +r_bytes0(s, fp) + char **s; + FILE *fp; +{ + int len = r_long(fp); + *s = ALLOC_N(char, len+1); + + fread(*s, 1, len, fp); + (*s)[len] = '\0'; + return len; +} + +static ID +r_symbol(fp) + FILE *fp; +{ + char *buf; + ID id; + + r_bytes(buf, fp); + id = rb_intern(buf); + free(buf); + return id; +} + +static VALUE +r_object(fp, port, table) + FILE *fp; + VALUE port; + st_table *table; +{ + VALUE v; + int type = r_byte(fp); + int id; + + switch (type) { + case EOF: + Fail("EOF read where object expected"); + return Qnil; + + case TYPE_NIL: + return Qnil; + + case TYPE_LINK: + if (st_lookup(table, r_long(fp), &v)) { + return v; + } + Fail("corrupted marshal file"); + break; + + case TYPE_FIXNUM: + { + int i = r_long(fp); + return INT2FIX(i); + } + } + + id = r_long(fp); + switch (type) { + case TYPE_FLOAT: + { + double atof(); + char *buf; + + r_bytes(buf, fp); + v = float_new(atof(buf)); + free(buf); + } + break; + + case TYPE_BIGNUM: + { + int len; + USHORT *digits; + + NEWOBJ(big, struct RBignum); + OBJSETUP(big, cBignum, T_BIGNUM); + big->sign = (r_byte(fp) == '+'); + big->len = len = r_long(fp); + big->digits = digits = ALLOC_N(USHORT, len); + while (len--) { + *digits++ = r_short(fp); + } + v = (VALUE)big; + } + break; + + case TYPE_STRING: + { + char *buf; + int len = r_bytes(buf, fp); + v = str_new(buf, len); + free(buf); + } + break; + + case TYPE_REGEXP: + { + char *buf; + int len = r_bytes(buf, fp); + int ci = r_byte(fp); + v = reg_new(buf, len, ci); + free(buf); + } + break; + + case TYPE_ARRAY: + { + int len = r_long(fp); + v = ary_new2(len); + while (len--) { + ary_push(v, r_object(fp, port, table)); + } + } + break; + + case TYPE_HASH: + { + int len = r_long(fp); + + v = hash_new(); + while (len--) { + VALUE key = r_object(fp, port, table); + VALUE value = r_object(fp, port, table); + hash_aset(v, key, value); + } + } + break; + + case TYPE_STRUCT: + { + VALUE class, mem, values; + char *path; + int i, len; + + r_bytes(path, fp); + class = rb_path2class(path); + free(path); + mem = rb_ivar_get(class, rb_intern("__member__")); + if (mem == Qnil) { + Fail("non-initialized struct"); + } + len = r_long(fp); + + values = ary_new(); + i = 0; + while (len--) { + ID slot = r_symbol(fp); + if (RARRAY(mem)->ptr[i++] != INT2FIX(slot)) + Fail("struct not compatible"); + ary_push(values, r_object(fp, port, table)); + } + v = struct_alloc(class, values); + } + break; + + case TYPE_OBJECT: + { + VALUE class; + int len; + char *path; + + r_bytes(path, fp); + class = rb_path2class(path); + free(path); + len = r_long(fp); + if (len == -1) { + if (rb_responds_to(class, s_load)) { + v = rb_funcall(class, s_load, 1, port); + } + else { + Fail("class %s needs to have method `_load_from'", + rb_class2name(class)); + } + } + else { + v = obj_alloc(class); + if (len > 0) { + while (len--) { + ID id = r_symbol(fp); + VALUE val = r_object(fp, port, table); + rb_ivar_set(v, id, val); + } + } + } + } + break; + + default: + Fail("dump format error(0x%x)", type); + break; + } + st_insert(table, id, v); + return v; +} + +static VALUE +marshal_load(self, port) + VALUE self, port; +{ + extern VALUE cIO; + void *fp; + VALUE v; + OpenFile *fptr; + st_table *table; + + if (TYPE(port) == T_STRING) { + char buf[32]; + + sprintf(buf, "/tmp/rb-mrsw-%x", getpid()^(int)buf); + fp = fopen(buf, "w"); + if (!fp) rb_sys_fail("tmp file"); + v = file_open(buf, "r"); + if (!v) rb_sys_fail("tmp file(read)"); + unlink(buf); + + fwrite(RSTRING(port)->ptr, RSTRING(port)->len, 1, fp); + fclose(fp); + port = v; + } + if (obj_is_kind_of(port, cIO)) { + GetOpenFile(port, fptr); + if (!(fptr->mode & FMODE_READABLE)) { + Fail("not opened for reading"); + } + fp = fptr->f; + } + else { + Fail("instance of IO needed"); + } + + table = new_idhash(); + + v = r_object(fp, port, table); + + st_free_table(table); + + return v; +} + +Init_marshal() +{ + VALUE mMarshal = rb_define_module("Marshal"); + + s_dump = rb_intern("_dump_to"); + s_load = rb_intern("_load_from"); + rb_define_module_function(mMarshal, "dump", marshal_dump, 2); + rb_define_module_function(mMarshal, "dumps", marshal_dumps, 1); + rb_define_module_function(mMarshal, "load", marshal_load, 1); + rb_define_module_function(mMarshal, "restore", marshal_load, 1); +} diff --git a/ext/marshal/marshal.doc b/ext/marshal/marshal.doc new file mode 100644 index 0000000000..8c3b63072e --- /dev/null +++ b/ext/marshal/marshal.doc @@ -0,0 +1,45 @@ +.\" marshal.doc - -*- Indented-Text -*- created at: Tue May 16 12:18:08 JST 1995 + +** Marshal(モジュール) + +rubyオブジェクトをファイルに書き出したり,読みも度したりする機能を提供 +するモジュール.大部分のクラスのインスタンスを書き出す事ができるが,ファ +イルへの不可能なクラスも存在し(例:IO),そのようなクラスを書き出そうと +すると例外を発生させる. + +Methods: +Single Methods: + + dump(obj, port) + + objを再帰的にファイルに書き出す.ファイルに書き出せないクラスのイ + ンスタンスをファイルに書き出そうとすると例外を発生させる.ファイル + に書き出せないクラスは以下の通り. + + Class, Module, Data + + また,これらのクラスを間接的に指すクラス(例えばIOのサブクラス)など + も書き出せない.portはIO(またはそのサブクラス)のインスタンスを指定 + する. + + 出力するオブジェクトがメソッド`_dump_to'を定義している場合には,ファ + イル出力はそのメソッドを使って行われる.メソッド`_dump_to'は引数と + して出力先のファイルオブジェクトを受け取る.インスタンスがメソッド + `_dump_to'を持つクラスは必ず同じフォーマットを読み戻す特異メソッド + `_load_from'を定義する必要がある. + + + dumps(obj) + + dump()がファイルに書き出すのと同じ内容を含む文字列を返す. + + load(port) + + portからオブジェクトを読み込んで来て,元のオブジェクトと同じ状態を + もつオブジェクトを生成する.portは文字列かIO(またはそのサブクラス) + のインスタンスである. + +------------------------------------------------------- +Local variables: +fill-column: 70 +end: diff --git a/ext/socket/MANIFEST b/ext/socket/MANIFEST new file mode 100644 index 0000000000..836caada41 --- /dev/null +++ b/ext/socket/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +depend +extconf.rb +socket.c +socket.doc diff --git a/ext/socket/depend b/ext/socket/depend new file mode 100644 index 0000000000..e6ede5a411 --- /dev/null +++ b/ext/socket/depend @@ -0,0 +1 @@ +socket.o : socket.c ../../ruby.h ../../config.h ../../defines.h ../../io.h ../../sig.h diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb new file mode 100644 index 0000000000..60d6deeb84 --- /dev/null +++ b/ext/socket/extconf.rb @@ -0,0 +1,6 @@ +have_library("inet", "gethostbyname") +have_library("socket", "socket") +have_header("sys/un.h") +if have_func("socket") + create_makefile("socket") +end diff --git a/ext/socket/socket.c b/ext/socket/socket.c new file mode 100644 index 0000000000..5671b2762c --- /dev/null +++ b/ext/socket/socket.c @@ -0,0 +1,785 @@ +/************************************************ + + socket.c - + + $Author: matz $ + $Date: 1995/01/10 10:42:55 $ + created at: Thu Mar 31 12:21:29 JST 1994 + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#else +#undef AF_UNIX +#endif + +extern VALUE cIO; +VALUE cBasicSocket; +VALUE cTCPsocket; +VALUE cTCPserver; +#ifdef AF_UNIX +VALUE cUNIXsocket; +VALUE cUNIXserver; +#endif +VALUE cSocket; + +FILE *rb_fdopen(); +char *strdup(); + +#ifdef NT +static void +sock_finalize(fptr) + OpenFile *fptr; +{ + SOCKET s = fileno(fptr->f); + free(fptr->f); + free(fptr->f2); + closesocket(s); +} +#endif + +static VALUE +sock_new(class, fd) + VALUE class; + int fd; +{ + VALUE sock = obj_alloc(class); + OpenFile *fp; + + MakeOpenFile(sock, fp); +#ifdef NT + fp->finalize = sock_finalize; +#endif + fp->f = rb_fdopen(fd, "r"); + setbuf(fp->f, NULL); + fp->f2 = rb_fdopen(fd, "w"); + fp->mode = FMODE_READWRITE|FMODE_SYNC; + + return sock; +} + +static VALUE +bsock_shutdown(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + VALUE howto; + int how; + OpenFile *fptr; + + rb_scan_args(argc, argv, "01", &howto); + if (howto == Qnil) + how = 2; + else { + how = NUM2INT(howto); + if (how < 0 && how > 2) how = 2; + } + GetOpenFile(sock, fptr); + if (shutdown(fileno(fptr->f), how) == -1) + rb_sys_fail(Qnil); + + return INT2FIX(0); +} + +static VALUE +bsock_setopt(sock, lev, optname, val) + VALUE sock, lev, optname; + struct RString *val; +{ + int level, option; + OpenFile *fptr; + + level = NUM2INT(lev); + option = NUM2INT(optname); + Check_Type(val, T_STRING); + + GetOpenFile(sock, fptr); + if (setsockopt(fileno(fptr->f), level, option, val->ptr, val->len) < 0) + rb_sys_fail(fptr->path); + + return INT2FIX(0); +} + +static VALUE +bsock_getopt(sock, lev, optname) + VALUE sock, lev, optname; +{ + int level, option, len; + struct RString *val; + OpenFile *fptr; + + level = NUM2INT(lev); + option = NUM2INT(optname); + len = 256; + val = (struct RString*)str_new(0, len); + Check_Type(val, T_STRING); + + GetOpenFile(sock, fptr); + if (getsockopt(fileno(fptr->f), level, option, val->ptr, &len) < 0) + rb_sys_fail(fptr->path); + val->len = len; + return (VALUE)val; +} + +static VALUE +bsock_getsockname(sock) + VALUE sock; +{ + char buf[1024]; + int len = sizeof buf; + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) + rb_sys_fail("getsockname(2)"); + return str_new(buf, len); +} + +static VALUE +bsock_getpeername(sock) + VALUE sock; +{ + char buf[1024]; + int len = sizeof buf; + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) + rb_sys_fail("getpeername(2)"); + return str_new(buf, len); +} + +static VALUE +open_inet(class, h, serv, server) + VALUE class, h, serv; + int server; +{ + char *host; + struct hostent *hostent, _hostent; + struct servent *servent, _servent; + struct protoent *protoent; + struct sockaddr_in sockaddr; + int fd, status; + int hostaddr, hostaddrPtr[2]; + int servport; + char *syscall; + VALUE sock; + + if (h) { + Check_Type(h, T_STRING); + host = RSTRING(h)->ptr; + hostent = gethostbyname(host); + if (hostent == NULL) { + hostaddr = inet_addr(host); + if (hostaddr == -1) { + if (server && !strlen(host)) + hostaddr = INADDR_ANY; + else + rb_sys_fail(host); + } + _hostent.h_addr_list = (char **)hostaddrPtr; + _hostent.h_addr_list[0] = (char *)&hostaddr; + _hostent.h_addr_list[1] = NULL; + _hostent.h_length = sizeof(hostaddr); + _hostent.h_addrtype = AF_INET; + hostent = &_hostent; + } + } + servent = NULL; + if (FIXNUM_P(serv)) { + servport = FIX2UINT(serv); + goto setup_servent; + } + Check_Type(serv, T_STRING); + servent = getservbyname(RSTRING(serv)->ptr, "tcp"); + if (servent == NULL) { + servport = strtoul(RSTRING(serv)->ptr, Qnil, 0); + if (servport == -1) Fail("no such servce %s", RSTRING(serv)->ptr); + setup_servent: + _servent.s_port = servport; + _servent.s_proto = "tcp"; + servent = &_servent; + } + protoent = getprotobyname(servent->s_proto); + if (protoent == NULL) Fail("no such proto %s", servent->s_proto); + + fd = socket(PF_INET, SOCK_STREAM, protoent->p_proto); + + sockaddr.sin_family = AF_INET; + if (h == Qnil) { + sockaddr.sin_addr.s_addr = INADDR_ANY; + } + else { + memcpy((char *)&(sockaddr.sin_addr.s_addr), + (char *) hostent->h_addr_list[0], + (size_t) hostent->h_length); + } + sockaddr.sin_port = servent->s_port; + + if (server) { + status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + syscall = "bind(2)"; + } + else { + status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + syscall = "connect(2)"; + } + + if (status < 0) { + close (fd); + rb_sys_fail(syscall); + } + if (server) listen(fd, 5); + + /* create new instance */ + sock = sock_new(class, fd); + + return sock; +} + +static VALUE +tcp_s_sock_open(class, host, serv) + VALUE class, host, serv; +{ + Check_Type(host, T_STRING); + return open_inet(class, host, serv, 0); +} + +static VALUE +tcp_svr_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE arg1, arg2; + + if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) + return open_inet(class, arg1, arg2, 1); + else + return open_inet(class, Qnil, arg1, 1); +} + +static VALUE +s_accept(class, fd, sockaddr, len) + VALUE class; + int fd; + struct sockaddr *sockaddr; + int *len; +{ + int fd2; + + retry: + fd2 = accept(fd, sockaddr, len); + if (fd2 < 0) { + if (errno == EINTR) goto retry; + rb_sys_fail(Qnil); + } + return sock_new(class, fd2); +} + +static VALUE +tcp_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in from; + int fromlen; + + GetOpenFile(sock, fptr); + fromlen = sizeof(struct sockaddr_in); + return s_accept(cTCPsocket, fileno(fptr->f), + (struct sockaddr*)&from, &fromlen); +} + +#ifdef AF_UNIX +static VALUE +open_unix(class, path, server) + VALUE class; + struct RString *path; + int server; +{ + struct sockaddr_un sockaddr; + int fd, status; + char *syscall; + VALUE sock; + OpenFile *fptr; + + Check_Type(path, T_STRING); + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) rb_sys_fail("socket(2)"); + + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path->ptr, sizeof(sockaddr.sun_path)-1); + sockaddr.sun_path[sizeof(sockaddr.sun_path)-1] = '\0'; + + if (server) { + status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + syscall = "bind(2)"; + } + else { + status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + syscall = "connect(2)"; + } + + if (status < 0) { + close (fd); + rb_sys_fail(syscall); + } + + if (server) listen(fd, 5); + + sock = sock_new(class, fd); + GetOpenFile(sock, fptr); + fptr->path = strdup(path->ptr); + + return sock; +} +#endif + +static VALUE +tcpaddr(sockaddr) + struct sockaddr_in *sockaddr; +{ + VALUE family, port, addr; + VALUE ary; + struct hostent *hostent; + + family = str_new2("AF_INET"); + hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr, + sizeof(sockaddr->sin_addr), + AF_INET); + if (hostent) { + addr = str_new2(hostent->h_name); + } + else { + char buf[16]; + char *a = (char*)&sockaddr->sin_addr; + sprintf(buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); + addr = str_new2(buf); + } + port = INT2FIX(sockaddr->sin_port); + ary = ary_new3(3, family, port, addr); + + return ary; +} + +static VALUE +tcp_addr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return tcpaddr(&addr); +} + +static VALUE +tcp_peeraddr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return tcpaddr(&addr); +} + +#ifdef AF_UNIX +static VALUE +unix_s_sock_open(sock, path) + VALUE sock, path; +{ + return open_unix(sock, path, 0); +} + +static VALUE +unix_path(sock) + VALUE sock; +{ + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (fptr->path == Qnil) { + struct sockaddr_un addr; + int len = sizeof(addr); + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail(Qnil); + fptr->path = strdup(addr.sun_path); + } + return str_new2(fptr->path); +} + +static VALUE +unix_svr_s_open(class, path) + VALUE class, path; +{ + return open_unix(class, path, 1); +} + +static VALUE +unix_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un from; + int fromlen; + + GetOpenFile(sock, fptr); + fromlen = sizeof(struct sockaddr_un); + return s_accept(cUNIXsocket, fileno(fptr->f), + (struct sockaddr*)&from, &fromlen); +} + +static VALUE +unixaddr(sockaddr) + struct sockaddr_un *sockaddr; +{ + return assoc_new(str_new2("AF_UNIX"),str_new2(sockaddr->sun_path)); +} + +static VALUE +unix_addr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return unixaddr(&addr); +} + +static VALUE +unix_peeraddr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return unixaddr(&addr); +} +#endif + +static void +setup_domain_and_type(domain, dv, type, tv) + VALUE domain, type; + int *dv, *tv; +{ + char *ptr; + + if (TYPE(domain) == T_STRING) { + ptr = RSTRING(domain)->ptr; + if (strcmp(ptr, "PF_INET") == 0) + *dv = PF_INET; +#ifdef PF_UNIX + else if (strcmp(ptr, "PF_UNIX") == 0) + *dv = PF_UNIX; +#endif +#ifdef PF_IMPLINK + else if (strcmp(ptr, "PF_IMPLINK") == 0) + *dv = PF_IMPLINK; +#endif +#ifdef PF_AX25 + else if (strcmp(ptr, "PF_AX25") == 0) + *dv = PF_AX25; +#endif +#ifdef PF_IPX + else if (strcmp(ptr, "PF_IPX") == 0) + *dv = PF_IPX; +#endif + else + Fail("Unknown socket domain %s", ptr); + } + else { + *dv = NUM2INT(domain); + } + if (TYPE(type) == T_STRING) { + ptr = RSTRING(type)->ptr; + if (strcmp(ptr, "SOCK_STREAM") == 0) + *tv = SOCK_STREAM; + else if (strcmp(ptr, "SOCK_DGRAM") == 0) + *tv = SOCK_DGRAM; +#ifdef SOCK_RAW + else if (strcmp(ptr, "SOCK_RAW") == 0) + *tv = SOCK_RAW; +#endif +#ifdef SOCK_SEQPACKET + else if (strcmp(ptr, "SOCK_SEQPACKET") == 0) + *tv = SOCK_SEQPACKET; +#endif +#ifdef SOCK_RDM + else if (strcmp(ptr, "SOCK_RDM") == 0) + *tv = SOCK_RDM; +#endif +#ifdef SOCK_PACKET + else if (strcmp(ptr, "SOCK_PACKET") == 0) + *tv = SOCK_PACKET; +#endif + else + Fail("Unknown socket type %s", ptr); + } + else { + *tv = NUM2INT(type); + } +} + +static VALUE +sock_s_open(class, domain, type, protocol) + VALUE class, domain, type, protocol; +{ + int fd; + int d, t; + + setup_domain_and_type(domain, &d, type, &t); + fd = socket(d, t, NUM2INT(protocol)); + if (fd < 0) rb_sys_fail("socke(2)"); + return sock_new(class, fd); +} + +static VALUE +sock_s_for_fd(class, fd) + VALUE class, fd; +{ + return sock_new(class, NUM2INT(fd)); +} + +static VALUE +sock_s_socketpair(class, domain, type, protocol) + VALUE class, domain, type, protocol; +{ + int fd; + int d, t, sp[2]; + + setup_domain_and_type(domain, &d, type, &t); + if (socketpair(d, t, NUM2INT(protocol), sp) < 0) + rb_sys_fail("socketpair(2)"); + + return assoc_new(sock_new(class, sp[0]), sock_new(class, sp[1])); +} + +static VALUE +sock_connect(sock, addr) + VALUE sock; + struct RString *addr; +{ + OpenFile *fptr; + + Check_Type(addr, T_STRING); + str_modify(addr); + + GetOpenFile(sock, fptr); + if (connect(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0) + rb_sys_fail("connect(2)"); + + return INT2FIX(0); +} + +static VALUE +sock_bind(sock, addr) + VALUE sock; + struct RString *addr; +{ + OpenFile *fptr; + + Check_Type(addr, T_STRING); + str_modify(addr); + + GetOpenFile(sock, fptr); + if (bind(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0) + rb_sys_fail("bind(2)"); + + return INT2FIX(0); +} + +static VALUE +sock_listen(sock, log) + VALUE sock, log; +{ + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (listen(fileno(fptr->f), NUM2INT(log)) < 0) + rb_sys_fail("listen(2)"); + + return INT2FIX(0); +} + +static VALUE +sock_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + VALUE addr, sock2; + char buf[1024]; + int len = sizeof buf; + + GetOpenFile(sock, fptr); + sock2 = s_accept(cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len); + + return assoc_new(sock2, str_new(buf, len)); +} + +static VALUE +sock_send(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + struct RString *msg, *to; + VALUE flags; + OpenFile *fptr; + FILE *f; + int fd, n; + + rb_scan_args(argc, argv, "21", &msg, &flags, &to); + + Check_Type(msg, T_STRING); + + GetOpenFile(sock, fptr); + f = fptr->f2?fptr->f2:fptr->f; + fd = fileno(f); + if (to) { + Check_Type(to, T_STRING); + n = sendto(fd, msg->ptr, msg->len, NUM2INT(flags), + (struct sockaddr*)to->ptr, to->len); + } + else { + n = send(fd, msg->ptr, msg->len, NUM2INT(flags)); + } + if (n < 0) { + rb_sys_fail("send(2)"); + } + return INT2FIX(n); +} + +static VALUE +s_recv(sock, argc, argv, from) + VALUE sock; + int argc; + VALUE *argv; + int from; +{ + OpenFile *fptr; + FILE f; + struct RString *str; + char buf[1024]; + int fd, alen = sizeof buf; + VALUE len, flg; + int flags; + + rb_scan_args(argc, argv, "11", &len, &flg); + + if (flg == Qnil) flags = 0; + else flags = NUM2INT(flg); + + str = (struct RString*)str_new(0, NUM2INT(len)); + + GetOpenFile(sock, fptr); + fd = fileno(fptr->f); + if ((str->len = recvfrom(fd, str->ptr, str->len, flags, + (struct sockaddr*)buf, &alen)) < 0) { + rb_sys_fail("recvfrom(2)"); + } + + if (from) + return assoc_new(str, str_new(buf, alen)); + else + return (VALUE)str; +} + +static VALUE +sock_recv(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 0); +} + +static VALUE +sock_recvfrom(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 1); +} + +Init_socket () +{ + cBasicSocket = rb_define_class("BasicSocket", cIO); + rb_undef_method(cBasicSocket, "new"); + rb_define_method(cBasicSocket, "shutdown", bsock_shutdown, -1); + rb_define_method(cBasicSocket, "setopt", bsock_setopt, 3); + rb_define_method(cBasicSocket, "getopt", bsock_getopt, 2); + rb_define_method(cBasicSocket, "getsockname", bsock_getsockname, 0); + rb_define_method(cBasicSocket, "getpeername", bsock_getpeername, 0); + + cTCPsocket = rb_define_class("TCPsocket", cBasicSocket); + rb_define_singleton_method(cTCPsocket, "open", tcp_s_sock_open, 2); + rb_define_singleton_method(cTCPsocket, "new", tcp_s_sock_open, 2); + rb_define_method(cTCPsocket, "addr", tcp_addr, 0); + rb_define_method(cTCPsocket, "peeraddr", tcp_peeraddr, 0); + + cTCPserver = rb_define_class("TCPserver", cTCPsocket); + rb_define_singleton_method(cTCPserver, "open", tcp_svr_s_open, -1); + rb_define_singleton_method(cTCPserver, "new", tcp_svr_s_open, -1); + rb_define_method(cTCPserver, "accept", tcp_accept, 0); + +#ifdef AF_UNIX + cUNIXsocket = rb_define_class("UNIXsocket", cBasicSocket); + rb_define_singleton_method(cUNIXsocket, "open", unix_s_sock_open, 1); + rb_define_singleton_method(cUNIXsocket, "new", unix_s_sock_open, 1); + rb_define_method(cUNIXsocket, "path", unix_path, 0); + rb_define_method(cUNIXsocket, "addr", unix_addr, 0); + rb_define_method(cUNIXsocket, "peeraddr", unix_peeraddr, 0); + + cUNIXserver = rb_define_class("UNIXserver", cUNIXsocket); + rb_define_singleton_method(cUNIXserver, "open", unix_svr_s_open, 1); + rb_define_singleton_method(cUNIXserver, "new", unix_svr_s_open, 1); + rb_define_method(cUNIXserver, "accept", unix_accept, 0); +#endif + + cSocket = rb_define_class("Socket", cBasicSocket); + rb_define_singleton_method(cSocket, "open", sock_s_open, 3); + rb_define_singleton_method(cSocket, "new", sock_s_open, 3); + rb_define_singleton_method(cSocket, "for_fd", sock_s_for_fd, 1); + + rb_define_method(cSocket, "connect", sock_connect, 1); + rb_define_method(cSocket, "bind", sock_bind, 1); + rb_define_method(cSocket, "listen", sock_listen, 1); + rb_define_method(cSocket, "accept", sock_accept, 0); + + rb_define_method(cSocket, "send", sock_send, -1); + rb_define_method(cSocket, "recv", sock_recv, -1); + rb_define_method(cSocket, "recvfrom", sock_recv, -1); + + rb_define_singleton_method(cSocket, "socketpair", sock_s_socketpair, 3); +} diff --git a/ext/socket/socket.doc b/ext/socket/socket.doc new file mode 100644 index 0000000000..aa5bfedbff --- /dev/null +++ b/ext/socket/socket.doc @@ -0,0 +1,227 @@ +.\" socket.doc - -*- Indented-Text -*- created at: Thu Mar 23 20:29:02 JST 1995 + +** Socket(クラス) + +SuperClass: BasicSocket + +ソケットそのものに対するシステムコールレベルのアクセスを提供するクラス. +Perlのソケットに対するアクセスと同レベルの機能を提供している.このクラ +スではソケットアドレスはpackされた文字列で,指定する.UDPソケットはこ +のクラスを使って利用する. + +Methods: + + accept + + 新しい接続を受け付けて,新しい接続に対するソケットとアドレスの + ペアを返す.accept(2)を参照. + + bind(addr) + + bind(2)と同じ働きをする.addrはpackされたソケットアドレス構造 + 体である. + + connect(addr) + + connect(2)と同じ働きをする.addrはpackされたソケットアドレス構 + 造体である. + + listen(backlog) + + listen(2)と同じ働きをする. + + recv(len[, flags]) + + ソケットからデータを受け取り,文字列として返す.lenは受け取る + 最大の長さを指定する.flagsについてはrecv(2)を参照.flagsのデ + フォルト値は0である. + + recvfrom(len[, flags]) + + recvと同様にソケットからデータを受け取るが,戻り値は文字列と相 + 手ソケットのアドレスのペアである.引数についてはrecvと同様. + + send(mesg, flags[, to]) + + ソケットを介してデータを送る.flagsに関してはsend(2)を参照の事. + connectしていないソケットに対しては送り先であるtoを指定する必 + 要がある.実際に送ったデータの長さを返す. + +Single Methods: + + open(domain, type, protocol) + new(domain, type, protocol) + + 新しいソケットを生成する.domain,type,protocolはインクルード + ファイルで定義されている定数値で指定する.domainとtypeに関して + は,文字列で指定できるが,すべてをカバーしている保証はない. + + socketpair(domain, type, protocol) + + ソケットのペアを返す.引数の指定は openと同じである. + +** BasicSocket(クラス) + +ソケットを表す抽象クラス.具体的なソケット操作はサブクラスで定義される. +例えばインターネットドメインの場合はTCPsocketを用いる. + +SuperClass: IO + +Methods: + + getopt(level, optname) + + ソケットのオプションを取得する.getsockopt(2)を参照のこと.取 + 得したオプションの内容を含む文字列を返す. + + getpeername + + 接続の相手先のソケットの情報を得る.パックされたsockaddr構造体 + をベタにダンプした文字列が返される.getpeername(2)を参照のこと. + + getsockname + + ソケットの情報を得る.パックされたsockaddr構造体をベタにダンプ + した文字列が返される.getsockname(2)を参照のこと. + + setopt(level, optname, optval) + + ソケットのオプションを設定する.setsockopt(2)を参照のこと. + + shutdown(how) + + ソケットの以降の接続を終了させる.howが0である時,以降の受信が, + howが1である時は,以降の送信が拒否される.howが2の時には,それ + 以降の送信,受信ともに拒否される.shutdown(2)を参照. + +** TCPserver(クラス) + +TCP/IPストリーム型接続のサーバ側のソケットのクラス.このクラスによって +簡単にソケットを利用したサーバのプログラミングができる.例えばechoサー +バは以下のようになる. + + gs = TCPserver.open(4444) + socks = [gs] + + while TRUE + nsock = select(socks); + if nsock == nil; continue end + for s in nsock[0] + if s == gs + socks.push(s.accept) + else + if s.eof + s.close + socks.delete(s) + else + str = s.gets + s.write(str) + end + end + end + end + +SuperClass: TCPsocket + +Methods: + + accept + + クライアントからの接続要求を受け付け,接続したTCPsocketのイン + スタンスを返す. + +Single Methods: + + new([host, ]service) + open([host, ]service) + + serviceは/etc/services(またはNIS)に登録されているサービス名か + ポート番号で指定する.hostを指定した時は指定したホストからの接 + 続だけを受け付ける.省略時は全てのホストからの接続要求を受け付 + ける. + +** TCPsocket + +インターネットドメインのストリーム型ソケットのクラス.通常のIOクラスの +サブクラスと同様の入出力ができる.このクラスによってソケットを用いたク +ライアントを簡単に記述できる.ユーザの入力をそのままサーバに転送するプ +ログラムは以下のようになる. + + s = TCPsocket("localhost", 4444) + while gets() + s.write($_) + print(s.read) + end + +SuperClass: BasicSocket + +Methods: + + addr + + ソケットの接続情報を表す配列を返す.その配列の各要素は第1要素 + が文字列 "AF_INET",第2要素がport番号,第3要素がホストを表す文 + 字列である. + + peeraddr + + 接続相手先ソケットの情報を表す配列を返す.その配列の各要素は + addrメソッドが返す配列と同じである. + +Single Methods: + + open(host, service) + new(host, service) + + hostで指定したホストのserviceで指定したポートと接続したソケッ + トを返す.hostはホスト名,またはインターネットアドレスを示す文 + 字列,serviceは/etc/services(またはNIS)に登録されているサービ + ス名かポート番号である. + +** UNIXserver + +UNIXストリーム型接続のサーバ側のソケットのクラス. + +SuperClass: UNIXsocket + +Methods: + + accept + + クライアントからの接続要求を受け付け,接続したUNIXsocketのイン + スタンスを返す. + +** UNIXsocket + +UNIXドメインのストリーム型ソケットのクラス.通常のIOクラスのサブクラス +と同様の入出力ができる. + +SuperClass: BasicSocket + +Methods: + + addr + + ソケットの接続情報を表す配列を返す.その配列の各要素は第1要素 + が文字列 "AF_UNIX",第2要素がpathである. + + path + + UNIXソケットのパスを返す. + + peeraddr + + 接続相手先ソケットの情報を表す配列を返す.その配列の各要素は + addrメソッドが返す配列と同じである. + +Single Methods: + + open(path) + new(path) + + pathで指定したパス名を用いて接続したソケットを返す. + +------------------------------------------------------- +Local variables: +fill-column: 70 +end: diff --git a/ext/tkutil/MANIFEST b/ext/tkutil/MANIFEST new file mode 100644 index 0000000000..98df4663b3 --- /dev/null +++ b/ext/tkutil/MANIFEST @@ -0,0 +1,3 @@ +MANIFEST +extconf.rb +tkutil.c diff --git a/ext/tkutil/extconf.rb b/ext/tkutil/extconf.rb new file mode 100644 index 0000000000..b61a7ac01c --- /dev/null +++ b/ext/tkutil/extconf.rb @@ -0,0 +1,11 @@ +for dir in ENV['PATH'].split(':') + if File.exists? "#{dir}/wish" + $CFLAGS = $CFLAGS + " -DWISHPATH=" + "'\"#{dir}/wish\"'" + have_wish = TRUE + break + end +end + +if have_wish and have_func('pipe') + create_makefile("tkutil") +end diff --git a/ext/tkutil/tkutil.c b/ext/tkutil/tkutil.c new file mode 100644 index 0000000000..2b74b254c2 --- /dev/null +++ b/ext/tkutil/tkutil.c @@ -0,0 +1,54 @@ +/************************************************ + + tk.c - + + $Author: matz $ + $Date: 1995/11/03 00:47:55 $ + created at: Fri Nov 3 00:47:54 JST 1995 + +************************************************/ + +#include "ruby.h" + +static VALUE +tk_eval_cmd(argc, argv) + int argc; + VALUE argv[]; +{ + VALUE cmd, rest; + + rb_scan_args(argc, argv, "1*", &cmd, &rest); + rb_eval_cmd(cmd, rest); + return Qnil; +} + +static VALUE +tk_yield(obj) + VALUE obj; +{ + rb_yield_0(obj, obj); +} + +static VALUE +tk_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj = obj_alloc(class); + + rb_funcall2(obj, rb_intern("initialize"), argc, argv); + if (iterator_p()) tk_yield(obj); + return obj; +} + +Init_tkutil() +{ + VALUE mTK = rb_define_module("TkUtil"); + VALUE cTK = rb_define_class("TkKernel", cObject); + + rb_define_const(mTK, "WISH_PATH", str_new2(WISHPATH)); + rb_define_singleton_method(mTK, "eval_cmd", tk_eval_cmd, -1); + + rb_define_singleton_method(cTK, "new", tk_s_new, -1); +} @@ -10,24 +10,49 @@ ************************************************/ -#include <sys/param.h> -#include <sys/time.h> #include "ruby.h" #include "io.h" + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#else +# define MAXPATHLEN 1024 +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif + #ifdef HAVE_UTIME_H #include <utime.h> #endif +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strrchr(); +#endif char *strdup(); +char *getenv(); -extern VALUE C_IO; -VALUE C_File; -VALUE M_FileTest; +extern VALUE cIO; +VALUE cFile; +VALUE mFileTest; +static VALUE sStat; VALUE time_new(); @@ -38,7 +63,7 @@ file_open(fname, mode) VALUE port; OpenFile *fptr; - port = obj_alloc(C_File); + port = obj_alloc(cFile); MakeOpenFile(port, fptr); fptr->mode = io_mode_flags(mode); @@ -79,7 +104,7 @@ apply2files(func, args, arg) } static VALUE -Ffile_tell(obj) +file_tell(obj) VALUE obj; { OpenFile *fptr; @@ -94,7 +119,7 @@ Ffile_tell(obj) } static VALUE -Ffile_seek(obj, offset, ptrname) +file_seek(obj, offset, ptrname) VALUE obj, offset, ptrname; { OpenFile *fptr; @@ -110,7 +135,7 @@ Ffile_seek(obj, offset, ptrname) } static VALUE -Ffile_set_pos(obj, offset) +file_set_pos(obj, offset) VALUE obj, offset; { OpenFile *fptr; @@ -125,7 +150,7 @@ Ffile_set_pos(obj, offset) } static VALUE -Ffile_rewind(obj) +file_rewind(obj) VALUE obj; { OpenFile *fptr; @@ -138,7 +163,7 @@ Ffile_rewind(obj) } static VALUE -Ffile_eof(obj) +file_eof(obj) VALUE obj; { OpenFile *fptr; @@ -149,7 +174,7 @@ Ffile_eof(obj) } static VALUE -Ffile_path(obj) +file_path(obj) VALUE obj; { OpenFile *fptr; @@ -159,7 +184,7 @@ Ffile_path(obj) } static VALUE -Ffile_isatty(obj) +file_isatty(obj) VALUE obj; { return FALSE; @@ -174,56 +199,55 @@ stat_new(st) struct stat *st; { if (st == Qnil) Bug("stat_new() called with nil"); - return struct_new("stat", - "dev", INT2FIX((int)st->st_dev), - "ino", INT2FIX((int)st->st_ino), - "mode", INT2FIX((int)st->st_mode), - "nlink", INT2FIX((int)st->st_nlink), - "uid", INT2FIX((int)st->st_uid), - "gid", INT2FIX((int)st->st_gid), + return struct_new(sStat, + INT2FIX((int)st->st_dev), + INT2FIX((int)st->st_ino), + INT2FIX((int)st->st_mode), + INT2FIX((int)st->st_nlink), + INT2FIX((int)st->st_uid), + INT2FIX((int)st->st_gid), #ifdef HAVE_ST_RDEV - "rdev", INT2FIX((int)st->st_rdev), + INT2FIX((int)st->st_rdev), #else - "rdev", INT2FIX(0), + INT2FIX(0), #endif - "size", INT2FIX((int)st->st_size), + INT2FIX((int)st->st_size), #ifdef HAVE_ST_BLKSIZE - "blksize", INT2FIX((int)st->st_blksize), + INT2FIX((int)st->st_blksize), #else - "blksize", INT2FIX(0), + INT2FIX(0), #endif #ifdef HAVE_ST_BLOCKS - "blocks", INT2FIX((int)st->st_blocks), + INT2FIX((int)st->st_blocks), #else - "blocks", INT2FIX(0), + INT2FIX(0), #endif - "atime", time_new(st->st_atime, 0), - "mtime", time_new(st->st_mtime, 0), - "ctime", time_new(st->st_ctime, 0), + time_new(st->st_atime, 0), + time_new(st->st_mtime, 0), + time_new(st->st_ctime, 0), Qnil); } -static char lastpath[MAXPATHLEN]; static struct stat laststat; +int cache_stat(path, st) char *path; struct stat *st; { - if (strcmp(lastpath, path) == 0) { + if (strcmp("&", path) == 0) { *st = laststat; return 0; } if (stat(path, st) == -1) return -1; - strcpy(lastpath, path); - laststat = *st; + laststat = *st; return 0; } static VALUE -Sfile_stat(obj, fname) +file_s_stat(obj, fname) VALUE obj; struct RString *fname; { @@ -237,21 +261,20 @@ Sfile_stat(obj, fname) } static VALUE -Ffile_stat(obj) +file_stat(obj) VALUE obj; { OpenFile *fptr; - struct stat st; GetOpenFile(obj, fptr); - if (fstat(fileno(fptr->f), &st) == -1) { + if (fstat(fileno(fptr->f), &laststat) == -1) { rb_sys_fail(fptr->path); } - return stat_new(&st); + return stat_new(&laststat); } static VALUE -Sfile_lstat(obj, fname) +file_s_lstat(obj, fname) VALUE obj; struct RString *fname; { @@ -265,7 +288,7 @@ Sfile_lstat(obj, fname) } static VALUE -Ffile_lstat(obj) +file_lstat(obj) VALUE obj; { OpenFile *fptr; @@ -278,21 +301,18 @@ Ffile_lstat(obj) return stat_new(&st); } -#define HAS_GETGROUPS - static int group_member(gid) GETGROUPS_T gid; { - GETGROUPS_T egid; - +#ifndef NT if (getgid() == gid || getegid() == gid) return TRUE; -#ifdef HAS_GETGROUPS -#ifndef NGROUPS -#define NGROUPS 32 -#endif +# ifdef HAVE_GETGROUPS +# ifndef NGROUPS +# define NGROUPS 32 +# endif { GETGROUPS_T gary[NGROUPS]; int anum; @@ -302,6 +322,7 @@ group_member(gid) if (gary[anum] == gid) return TRUE; } +# endif #endif return FALSE; } @@ -315,7 +336,6 @@ eaccess(path, mode) char *path; int mode; { - extern int group_member (); struct stat st; static int euid = -1; @@ -347,7 +367,7 @@ eaccess(path, mode) } static VALUE -Ftest_d(obj, fname) +test_d(obj, fname) VALUE obj; struct RString *fname; { @@ -364,7 +384,7 @@ Ftest_d(obj, fname) } static VALUE -Ftest_p(obj, fname) +test_p(obj, fname) VALUE obj; struct RString *fname; { @@ -384,7 +404,7 @@ Ftest_p(obj, fname) } static VALUE -Ftest_l(obj, fname) +test_l(obj, fname) VALUE obj; struct RString *fname; { @@ -413,7 +433,8 @@ Ftest_l(obj, fname) return FALSE; } -Ftest_S(obj, fname) +VALUE +test_S(obj, fname) VALUE obj; struct RString *fname; { @@ -443,7 +464,7 @@ Ftest_S(obj, fname) } static VALUE -Ftest_b(obj, fname) +test_b(obj, fname) VALUE obj; struct RString *fname; { @@ -465,7 +486,7 @@ Ftest_b(obj, fname) } static VALUE -Ftest_c(obj, fname) +test_c(obj, fname) VALUE obj; struct RString *fname; { @@ -483,7 +504,7 @@ Ftest_c(obj, fname) } static VALUE -Ftest_e(obj, fname) +test_e(obj, fname) VALUE obj; struct RString *fname; { @@ -495,7 +516,7 @@ Ftest_e(obj, fname) } static VALUE -Ftest_r(obj, fname) +test_r(obj, fname) VALUE obj; struct RString *fname; { @@ -505,7 +526,7 @@ Ftest_r(obj, fname) } static VALUE -Ftest_R(obj, fname) +test_R(obj, fname) VALUE obj; struct RString *fname; { @@ -515,7 +536,7 @@ Ftest_R(obj, fname) } static VALUE -Ftest_w(obj, fname) +test_w(obj, fname) VALUE obj; struct RString *fname; { @@ -525,7 +546,7 @@ Ftest_w(obj, fname) } static VALUE -Ftest_W(obj, fname) +test_W(obj, fname) VALUE obj; struct RString *fname; { @@ -535,7 +556,7 @@ Ftest_W(obj, fname) } static VALUE -Ftest_x(obj, fname) +test_x(obj, fname) VALUE obj; struct RString *fname; { @@ -545,7 +566,7 @@ Ftest_x(obj, fname) } static VALUE -Ftest_X(obj, fname) +test_X(obj, fname) VALUE obj; struct RString *fname; { @@ -555,7 +576,7 @@ Ftest_X(obj, fname) } static VALUE -Ftest_f(obj, fname) +test_f(obj, fname) VALUE obj; struct RString *fname; { @@ -568,7 +589,7 @@ Ftest_f(obj, fname) } static VALUE -Ftest_z(obj, fname) +test_z(obj, fname) VALUE obj; struct RString *fname; { @@ -581,7 +602,7 @@ Ftest_z(obj, fname) } static VALUE -Ftest_s(obj, fname) +test_s(obj, fname) VALUE obj; struct RString *fname; { @@ -594,7 +615,7 @@ Ftest_s(obj, fname) } static VALUE -Ftest_owned(obj, fname) +test_owned(obj, fname) VALUE obj; struct RString *fname; { @@ -607,7 +628,7 @@ Ftest_owned(obj, fname) } static VALUE -Ftest_grpowned(obj, fname) +test_rowned(obj, fname) VALUE obj; struct RString *fname; { @@ -615,7 +636,24 @@ Ftest_grpowned(obj, fname) Check_Type(fname, T_STRING); if (cache_stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_uid == getuid()) return TRUE; + return FALSE; +} + +static VALUE +test_grpowned(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef NT + struct stat st; + + Check_Type(fname, T_STRING); + if (cache_stat(fname->ptr, &st) < 0) return FALSE; if (st.st_gid == getegid()) return TRUE; +#else + Check_Type(fname, T_STRING); +#endif return FALSE; } @@ -634,7 +672,7 @@ check3rdbyte(file, mode) #endif static VALUE -Ftest_suid(obj, fname) +test_suid(obj, fname) VALUE obj; struct RString *fname; { @@ -647,12 +685,12 @@ Ftest_suid(obj, fname) } static VALUE -Ftest_sgid(obj, fname) +test_sgid(obj, fname) VALUE obj; struct RString *fname; { Check_Type(fname, T_STRING); -#ifdef S_ISGID +#ifndef NT return check3rdbyte(fname->ptr, S_ISGID); #else return FALSE; @@ -660,7 +698,7 @@ Ftest_sgid(obj, fname) } static VALUE -Ftest_sticky(obj, fname) +test_sticky(obj, fname) VALUE obj; struct RString *fname; { @@ -673,7 +711,7 @@ Ftest_sticky(obj, fname) } static VALUE -Sfile_type(obj, fname) +file_s_type(obj, fname) VALUE obj; struct RString *fname; { @@ -718,7 +756,7 @@ Sfile_type(obj, fname) } static VALUE -Sfile_atime(obj, fname) +file_s_atime(obj, fname) VALUE obj; struct RString *fname; { @@ -730,7 +768,7 @@ Sfile_atime(obj, fname) } static VALUE -Ffile_atime(obj) +file_atime(obj) VALUE obj; { OpenFile *fptr; @@ -744,7 +782,7 @@ Ffile_atime(obj) } static VALUE -Sfile_mtime(obj, fname) +file_s_mtime(obj, fname) VALUE obj; struct RString *fname; { @@ -756,7 +794,7 @@ Sfile_mtime(obj, fname) } static VALUE -Ffile_mtime(obj) +file_mtime(obj) VALUE obj; { OpenFile *fptr; @@ -770,7 +808,7 @@ Ffile_mtime(obj) } static VALUE -Sfile_ctime(obj, fname) +file_s_ctime(obj, fname) VALUE obj; struct RString *fname; { @@ -782,7 +820,7 @@ Sfile_ctime(obj, fname) } static VALUE -Ffile_ctime(obj) +file_ctime(obj) VALUE obj; { OpenFile *fptr; @@ -805,15 +843,13 @@ chmod_internal(path, mode) } static VALUE -Sfile_chmod(argc, argv, obj) +file_s_chmod(argc, argv) int argc; VALUE *argv; - VALUE obj; { VALUE vmode; VALUE rest; int mode, n; - VALUE path; rb_scan_args(argc, argv, "1*", &vmode, &rest); mode = NUM2INT(vmode); @@ -823,7 +859,7 @@ Sfile_chmod(argc, argv, obj) } static VALUE -Ffile_chmod(obj, vmode) +file_chmod(obj, vmode) VALUE obj, vmode; { OpenFile *fptr; @@ -852,10 +888,9 @@ chown_internal(path, args) } static VALUE -Sfile_chown(argc, argv, obj) +file_s_chown(argc, argv) int argc; VALUE *argv; - VALUE obj; { VALUE o, g, rest; struct chown_args arg; @@ -879,11 +914,11 @@ Sfile_chown(argc, argv, obj) return INT2FIX(n); } -Ffile_chown(obj, owner, group) +VALUE +file_chown(obj, owner, group) VALUE obj, owner, group; { OpenFile *fptr; - int mode; GetOpenFile(obj, fptr); if (fchown(fileno(fptr->f), NUM2INT(owner), NUM2INT(group)) == -1) @@ -894,7 +929,7 @@ Ffile_chown(obj, owner, group) struct timeval *time_timeval(); -#ifdef HAVE_UTIME +#ifdef HAVE_UTIMES static void utime_internal(path, tvp) @@ -906,10 +941,9 @@ utime_internal(path, tvp) } static VALUE -Sfile_utime(argc, argv, obj) +file_s_utime(argc, argv) int argc; VALUE *argv; - VALUE obj; { VALUE atime, mtime, rest; struct timeval tvp[2]; @@ -926,39 +960,35 @@ Sfile_utime(argc, argv, obj) #else +#ifndef HAVE_UTIME_H +# ifdef NT +# include <sys/utime.h> +# else +struct utimbuf { + long actime; + long modtime; +}; +# endif +#endif + static void utime_internal(path, utp) char *path; -#ifdef HAVE_UTIME_H struct utimbuf *utp; -#else - struct { - long actime; - long modtime; - } *utp; -#endif { if (utime(path, utp) < 0) rb_sys_fail(path); } static VALUE -Sfile_utime(argc, argv, obj) +file_s_utime(argc, argv) int argc; VALUE *argv; - VALUE obj; { VALUE atime, mtime, rest; int n; struct timeval *tv; -#ifdef HAVE_UTIME_H struct utimbuf utbuf; -#else - struct { - long actime; - long modtime; - } utbuf; -#endif rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest); @@ -974,7 +1004,7 @@ Sfile_utime(argc, argv, obj) #endif static VALUE -Sfile_link(obj, from, to) +file_s_link(obj, from, to) VALUE obj; struct RString *from, *to; { @@ -987,7 +1017,7 @@ Sfile_link(obj, from, to) } static VALUE -Sfile_symlink(obj, from, to) +file_s_symlink(obj, from, to) VALUE obj; struct RString *from, *to; { @@ -1000,7 +1030,7 @@ Sfile_symlink(obj, from, to) } static VALUE -Sfile_readlink(obj, path) +file_s_readlink(obj, path) VALUE obj; struct RString *path; { @@ -1024,7 +1054,7 @@ unlink_internal(path) } static VALUE -Sfile_unlink(obj, args) +file_s_unlink(obj, args) VALUE obj; struct RArray *args; { @@ -1035,7 +1065,7 @@ Sfile_unlink(obj, args) } static VALUE -Sfile_rename(obj, from, to) +file_s_rename(obj, from, to) VALUE obj; struct RString *from, *to; { @@ -1049,12 +1079,11 @@ Sfile_rename(obj, from, to) } static VALUE -Sfile_umask(argc, argv) +file_s_umask(argc, argv) int argc; VALUE *argv; { - VALUE mask; - int omask; + int omask = 0; if (argc == 0) { int omask = umask(0); @@ -1069,20 +1098,44 @@ Sfile_umask(argc, argv) return INT2FIX(omask); } +#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) static VALUE -Sfile_truncate(obj, path, len) +file_s_truncate(obj, path, len) VALUE obj, len; struct RString *path; { Check_Type(path, T_STRING); +#ifdef HAVE_TRUNCATE if (truncate(path->ptr, NUM2INT(len)) < 0) rb_sys_fail(path->ptr); +#else +# ifdef HAVE_CHSIZE + { + int tmpfd; + +#if defined(NT) + if ((tmpfd = open(path->ptr, O_RDWR)) < 0) { + rb_sys_fail(path->ptr); + } +#else + if ((tmpfd = open(path->ptr, 0)) < 0) { + rb_sys_fail(path->ptr); + } +#endif + if (chsize(tmpfd, NUM2INT(len)) < 0) { + close(tmpfd); + rb_sys_fail(path->ptr); + } + close(tmpfd); + } +# endif +#endif return TRUE; } static VALUE -Ffile_truncate(obj, len) +file_truncate(obj, len) VALUE obj, len; { OpenFile *fptr; @@ -1092,111 +1145,424 @@ Ffile_truncate(obj, len) if (!(fptr->mode & FMODE_WRITABLE)) { Fail("not opened for writing"); } +#ifdef HAVE_TRUNCATE if (ftruncate(fileno(fptr->f), NUM2INT(len)) < 0) rb_sys_fail(fptr->path); +#else +# ifdef HAVE_CHSIZE + if (chsize(fileno(fptr->f), NUM2INT(len)) < 0) + rb_sys_fail(fptr->path); +# endif +#endif return TRUE; } +#endif +#ifdef HAVE_FCNTL static VALUE -Ffile_fcntl(obj, req, arg) +file_fcntl(obj, req, arg) VALUE obj, req; struct RString *arg; { io_ctl(obj, req, arg, 0); return obj; } +#endif + +static VALUE +file_s_expand_path(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *s, *p; + char buf[MAXPATHLEN]; + + Check_Type(fname, T_STRING); + s = fname->ptr; + + p = buf; + if (s[0] == '~') { + if (s[1] == '/' || s[1] == '\0') { + char *dir = getenv("HOME"); + + if (!dir) { + Fail("couldn't find HOME environment -- expanding `%s'", s); + } + strcpy(buf, dir); + p = &buf[strlen(buf)]; + s++; + } + else { +#ifdef HAVE_PWD_H + struct passwd *pwPtr; + s++; +#endif + + while (*s && *s != '/') { + *p++ = *s++; + } + *p = '\0'; +#ifdef HAVE_PWD_H + pwPtr = getpwnam(buf); + if (!pwPtr) { + endpwent(); + Fail("user %s doesn't exist", buf); + } + strcpy(buf, pwPtr->pw_dir); + p = &buf[strlen(buf)]; + endpwent(); +#endif + } + } + else if (s[0] != '/') { +#ifdef HAVE_GETCWD + getcwd(buf, MAXPATHLEN); +#else + getwd(buf)l +#endif + p = &buf[strlen(buf)]; + } + *p = '/'; + + for ( ; *s; s++) { + switch (*s) { + case '.': + if (*(s+1)) { + switch (*++s) { + case '.': + if (*(s+1) == '\0' || *(s+1) == '/') { + /* We must go back to the parent */ + if (*p == '/' && p > buf) p--; + while (p > buf && *p != '/') p--; + } + else { + *++p = '.'; + *++p = '.'; + } + break; + case '/': + if (*p != '/') *++p = '/'; + break; + default: + *++p = '.'; *++p = *s; break; + } + } + break; + case '/': + if (*p != '/') *++p = '/'; break; + default: + *++p = *s; + } + } + + /* Place a \0 at end. If path ends with a "/", delete it */ + if (p == buf || *p != '/') p++; + *p = '\0'; + + return str_new2(buf); +} + +static int +rmext(p, e) + char *p, *e; +{ + int l1, l2; + + l1 = strlen(p); + if (!e) return 0; + + l2 = strlen(e); + if (l1 < l2) return l1; + + if (strcmp(p+l1-l2, e) == 0) { + return l1-l2; + } + return 0; +} + +static VALUE +file_s_basename(argc, argv) + int argc; + VALUE *argv; +{ + struct RString *fname; + struct RString *ext; + char *p; + int f; + + rb_scan_args(argc, argv, "11", &fname, &ext); + Check_Type(fname, T_STRING); + if (ext) Check_Type(ext, T_STRING); + p = strrchr(fname->ptr, '/'); + if (p == Qnil) { + if (ext) { + f = rmext(fname->ptr, ext->ptr); + if (f) return str_new(fname->ptr, f); + } + return (VALUE)fname; + } + p++; /* skip last `/' */ + if (ext) { + f = rmext(p, ext->ptr); + if (f) return str_new(p, f); + } + return str_new2(p); +} + +static VALUE +file_s_dirname(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *p; + Check_Type(fname, T_STRING); + p = strrchr(fname->ptr, '/'); + if (p == Qnil) return (VALUE)fname; + return str_new(fname->ptr, p - fname->ptr); +} + +static void +test_check(n, argc, argv) + int n, argc; + VALUE *argv; +{ + int i; + + n+=1; + if (n < argc) Fail("Wrong # of arguments(%d for %d)", argc, n); + for (i=1; i<n; i++) { + Check_Type(argv[i], T_STRING); + } +} + +#define CHECK(n) test_check((n), argc, argv) + +static VALUE +f_test(argc, argv) + int argc; + VALUE *argv; +{ + int cmd; + + if (argc == 0) Fail("Wrong # of arguments"); + Need_Fixnum(argv[0]); + cmd = FIX2INT(argv[0]); + if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) { + CHECK(1); + switch (cmd) { + case 'b': + return test_b(0, argv[1]); + + case 'c': + return test_c(0, argv[1]); + + case 'd': + return test_d(0, argv[1]); + + case 'a': + case 'e': + return test_e(0, argv[1]); + + case 'f': + return test_f(0, argv[1]); + + case 'g': + return test_sgid(0, argv[1]); + + case 'G': + return test_grpowned(0, argv[1]); + + case 'k': + return test_sticky(0, argv[1]); + + case 'l': + return test_l(0, argv[1]); + case 'o': + return test_owned(0, argv[1]); + + case 'O': + return test_rowned(0, argv[1]); + + case 'p': + return test_p(0, argv[1]); + + case 'r': + return test_r(0, argv[1]); + + case 'R': + return test_R(0, argv[1]); + + case 's': + return test_s(0, argv[1]); + + case 'S': + return test_S(0, argv[1]); + + case 'u': + return test_suid(0, argv[1]); + + case 'w': + return test_w(0, argv[1]); + + case 'W': + return test_W(0, argv[1]); + + case 'x': + return test_x(0, argv[1]); + + case 'X': + return test_X(0, argv[1]); + + case 'z': + return test_z(0, argv[1]); + } + } + + if (strchr("MAC", cmd)) { + struct stat st; + + CHECK(1); + if (cache_stat(RSTRING(argv[1])->ptr, &st) == -1) { + rb_sys_fail(RSTRING(argv[1])->ptr); + } + + switch (cmd) { + case 'A': + return time_new(st.st_atime, 0); + case 'M': + return time_new(st.st_mtime, 0); + case 'C': + return time_new(st.st_ctime, 0); + } + } + + if (strchr("-=<>", cmd)) { + struct stat st1, st2; + + CHECK(2); + if (stat(RSTRING(argv[1])->ptr, &st1) < 0) return FALSE; + if (stat(RSTRING(argv[2])->ptr, &st2) < 0) return FALSE; + + switch (cmd) { + case '-': + if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) + return TRUE; + break; + + case '=': + if (st1.st_mtime == st2.st_mtime) return TRUE; + break; + + case '>': + if (st1.st_mtime > st2.st_mtime) return TRUE; + break; + + case '<': + if (st1.st_mtime < st2.st_mtime) return TRUE; + break; + } + } + return FALSE; +} + +extern VALUE cKernel; + +void Init_File() { - M_FileTest = rb_define_module("FileTest"); - - rb_define_method(M_FileTest, "d", Ftest_d, 1); - rb_define_method(M_FileTest, "isdirectory", Ftest_d, 1); - rb_define_method(M_FileTest, "a", Ftest_e, 1); - rb_define_method(M_FileTest, "e", Ftest_e, 1); - rb_define_method(M_FileTest, "exists", Ftest_e, 1); - rb_define_method(M_FileTest, "r", Ftest_r, 1); - rb_define_method(M_FileTest, "readable", Ftest_r, 1); - rb_define_method(M_FileTest, "R", Ftest_R, 1); - rb_define_method(M_FileTest, "w", Ftest_w, 1); - rb_define_method(M_FileTest, "writable", Ftest_w, 1); - rb_define_method(M_FileTest, "W", Ftest_W, 1); - rb_define_method(M_FileTest, "x", Ftest_x, 1); - rb_define_method(M_FileTest, "executable", Ftest_x, 1); - rb_define_method(M_FileTest, "X", Ftest_X, 1); - rb_define_method(M_FileTest, "f", Ftest_f, 1); - rb_define_method(M_FileTest, "isfile", Ftest_f, 1); - rb_define_method(M_FileTest, "z", Ftest_z, 1); - rb_define_method(M_FileTest, "s", Ftest_s, 1); - rb_define_method(M_FileTest, "size", Ftest_s, 1); - rb_define_method(M_FileTest, "O", Ftest_owned, 1); - rb_define_method(M_FileTest, "owned", Ftest_owned, 1); - rb_define_method(M_FileTest, "G", Ftest_grpowned, 1); - - rb_define_method(M_FileTest, "p", Ftest_p, 1); - rb_define_method(M_FileTest, "ispipe", Ftest_p, 1); - rb_define_method(M_FileTest, "l", Ftest_l, 1); - rb_define_method(M_FileTest, "issymlink", Ftest_l, 1); - rb_define_method(M_FileTest, "S", Ftest_S, 1); - rb_define_method(M_FileTest, "issocket", Ftest_S, 1); - - rb_define_method(M_FileTest, "b", Ftest_b, 1); - rb_define_method(M_FileTest, "c", Ftest_c, 1); - - rb_define_method(M_FileTest, "u", Ftest_suid, 1); - rb_define_method(M_FileTest, "setuid", Ftest_suid, 1); - rb_define_method(M_FileTest, "g", Ftest_sgid, 1); - rb_define_method(M_FileTest, "setgid", Ftest_sgid, 1); - rb_define_method(M_FileTest, "k", Ftest_sticky, 1); - - C_File = rb_define_class("File", C_IO); - rb_extend_object(C_File, M_FileTest); - - rb_define_single_method(C_File, "stat", Sfile_stat, 1); - rb_define_single_method(C_File, "lstat", Sfile_lstat, 1); - rb_define_single_method(C_File, "type", Sfile_type, 1); - - rb_define_single_method(C_File, "atime", Sfile_atime, 1); - rb_define_single_method(C_File, "mtime", Sfile_mtime, 1); - rb_define_single_method(C_File, "ctime", Sfile_ctime, 1); - - rb_define_single_method(C_File, "utime", Sfile_utime, -1); - rb_define_single_method(C_File, "chmod", Sfile_chmod, -1); - rb_define_single_method(C_File, "chown", Sfile_chown, -1); - - rb_define_single_method(C_File, "link", Sfile_link, 2); - rb_define_single_method(C_File, "symlink", Sfile_symlink, 2); - rb_define_single_method(C_File, "readlink", Sfile_readlink, 1); - - rb_define_single_method(C_File, "unlink", Sfile_unlink, -2); - rb_define_single_method(C_File, "delete", Sfile_unlink, -2); - rb_define_single_method(C_File, "rename", Sfile_rename, 2); - rb_define_single_method(C_File, "umask", Sfile_umask, -1); - rb_define_single_method(C_File, "truncate", Sfile_truncate, 2); - - rb_define_method(C_File, "stat", Ffile_stat, 0); - rb_define_method(C_File, "lstat", Ffile_lstat, 0); - - rb_define_method(C_File, "atime", Ffile_atime, 0); - rb_define_method(C_File, "mtime", Ffile_mtime, 0); - rb_define_method(C_File, "ctime", Ffile_ctime, 0); - - rb_define_method(C_File, "chmod", Ffile_chmod, 1); - rb_define_method(C_File, "chown", Ffile_chown, 2); - rb_define_method(C_File, "truncate", Ffile_truncate, 1); - - rb_define_method(C_File, "tell", Ffile_tell, 0); - rb_define_method(C_File, "seek", Ffile_seek, 2); - - - rb_define_method(C_File, "pos", Ffile_tell, 0); - rb_define_method(C_File, "pos=", Ffile_set_pos, 1); - - rb_define_method(C_File, "rewind", Ffile_rewind, 0); - rb_define_method(C_File, "isatty", Ffile_isatty, 0); - rb_define_method(C_File, "eof", Ffile_eof, 0); - - rb_define_method(C_IO, "fcntl", Ffile_fcntl, 2); - - rb_define_method(C_File, "path", Ffile_path, 0); + mFileTest = rb_define_module("FileTest"); + + rb_define_module_function(mFileTest, "directory?", test_d, 1); + rb_define_module_function(mFileTest, "exists?", test_e, 1); + rb_define_module_function(mFileTest, "readable?", test_r, 1); + rb_define_module_function(mFileTest, "readable_real?", test_R, 1); + rb_define_module_function(mFileTest, "writable?", test_w, 1); + rb_define_module_function(mFileTest, "writable_real?", test_W, 1); + rb_define_module_function(mFileTest, "executable?", test_x, 1); + rb_define_module_function(mFileTest, "executable_real?", test_X, 1); + rb_define_module_function(mFileTest, "file?", test_f, 1); + rb_define_module_function(mFileTest, "zero?", test_z, 1); + rb_define_module_function(mFileTest, "size", test_s, 1); + rb_define_module_function(mFileTest, "owned?", test_owned, 1); + rb_define_module_function(mFileTest, "grpowned?", test_grpowned, 1); + + rb_define_module_function(mFileTest, "pipe?", test_p, 1); + rb_define_module_function(mFileTest, "symlink?", test_l, 1); + rb_define_module_function(mFileTest, "socket?", test_S, 1); + + rb_define_module_function(mFileTest, "blockdev?", test_b, 1); + rb_define_module_function(mFileTest, "chardev?", test_c, 1); + + rb_define_module_function(mFileTest, "setuid?", test_suid, 1); + rb_define_module_function(mFileTest, "setgid?", test_sgid, 1); + rb_define_module_function(mFileTest, "sticky?", test_sticky, 1); + + cFile = rb_define_class("File", cIO); + rb_extend_object(cFile, CLASS_OF(mFileTest)); + + rb_define_singleton_method(cFile, "stat", file_s_stat, 1); + rb_define_singleton_method(cFile, "lstat", file_s_lstat, 1); + rb_define_singleton_method(cFile, "type", file_s_type, 1); + + rb_define_singleton_method(cFile, "atime", file_s_atime, 1); + rb_define_singleton_method(cFile, "mtime", file_s_mtime, 1); + rb_define_singleton_method(cFile, "ctime", file_s_ctime, 1); + + rb_define_singleton_method(cFile, "utime", file_s_utime, -1); + rb_define_singleton_method(cFile, "chmod", file_s_chmod, -1); + rb_define_singleton_method(cFile, "chown", file_s_chown, -1); + + rb_define_singleton_method(cFile, "link", file_s_link, 2); + rb_define_singleton_method(cFile, "symlink", file_s_symlink, 2); + rb_define_singleton_method(cFile, "readlink", file_s_readlink, 1); + + rb_define_singleton_method(cFile, "unlink", file_s_unlink, -2); + rb_define_singleton_method(cFile, "delete", file_s_unlink, -2); + rb_define_singleton_method(cFile, "rename", file_s_rename, 2); + rb_define_singleton_method(cFile, "umask", file_s_umask, -1); +#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) + rb_define_singleton_method(cFile, "truncate", file_s_truncate, 2); +#endif + rb_define_singleton_method(cFile, "expand_path", file_s_expand_path, 1); + rb_define_singleton_method(cFile, "basename", file_s_basename, -1); + rb_define_singleton_method(cFile, "dirname", file_s_dirname, 1); + + rb_define_method(cFile, "stat", file_stat, 0); + rb_define_method(cFile, "lstat", file_lstat, 0); + + rb_define_method(cFile, "atime", file_atime, 0); + rb_define_method(cFile, "mtime", file_mtime, 0); + rb_define_method(cFile, "ctime", file_ctime, 0); + + rb_define_method(cFile, "chmod", file_chmod, 1); + rb_define_method(cFile, "chown", file_chown, 2); +#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) + rb_define_method(cFile, "truncate", file_truncate, 1); +#endif + + rb_define_method(cFile, "tell", file_tell, 0); + rb_define_method(cFile, "seek", file_seek, 2); + + rb_define_method(cFile, "pos", file_tell, 0); + rb_define_method(cFile, "pos=", file_set_pos, 1); + + rb_define_method(cFile, "rewind", file_rewind, 0); + rb_define_method(cFile, "isatty", file_isatty, 0); + rb_define_method(cFile, "tty?", file_isatty, 0); + rb_define_method(cFile, "eof", file_eof, 0); + rb_define_method(cFile, "eof?", file_eof, 0); + +#ifdef HAVE_FCNTL + rb_define_method(cIO, "fcntl", file_fcntl, 2); +#endif + + rb_define_method(cFile, "path", file_path, 0); + + rb_define_method(cKernel, "test", f_test, -1); + + sStat = struct_define("Stat", "dev", "ino", "mode", + "nlink", "uid", "gid", "rdev", + "size", "blksize", "blocks", + "atime", "mtime", "ctime", Qnil); } @@ -14,6 +14,7 @@ #include "env.h" #include "st.h" #include "node.h" +#include "re.h" #include <stdio.h> #include <setjmp.h> @@ -115,16 +116,16 @@ Cambridge, MA 02138 static int dont_gc; VALUE -Sgc_enable() +gc_s_enable() { int old = dont_gc; - dont_gc = Qnil; + dont_gc = FALSE; return old; } VALUE -Sgc_disable() +gc_s_disable() { int old = dont_gc; @@ -132,22 +133,7 @@ Sgc_disable() return old; } -#include <sys/types.h> -#include <sys/times.h> - -static -Fgc_begin() -{ - return Qnil; -} - -static -Fgc_end() -{ - return Qnil; -} - -VALUE M_GC; +VALUE mGC; static struct gc_list { int n; @@ -161,7 +147,7 @@ rb_global_variable(var) { struct gc_list *tmp; - tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list)); + tmp = ALLOC(struct gc_list); tmp->next = Global_List; tmp->varptr = var; tmp->n = 1; @@ -186,12 +172,13 @@ typedef struct RVALUE { struct RStruct rstruct; struct RBignum bignum; struct RNode node; - struct RAssoc assoc; + struct RMatch match; + struct RVarmap varmap; struct SCOPE scope; } as; } RVALUE; -RVALUE *freelist = Qnil; +RVALUE *freelist = 0; #define HEAPS_INCREMENT 10 static RVALUE **heaps; @@ -201,6 +188,8 @@ static int heaps_used = 0; #define HEAP_SLOTS 10000 #define FREE_MIN 512 +static RVALUE *himem, *lomem; + static void add_heap() { @@ -212,12 +201,14 @@ add_heap() heaps = (heaps_used)? (RVALUE**)realloc(heaps, heaps_length*sizeof(RVALUE)): (RVALUE**)malloc(heaps_length*sizeof(RVALUE)); - if (heaps == Qnil) Fatal("can't alloc memory"); + if (heaps == 0) Fatal("can't alloc memory"); } p = heaps[heaps_used++] = (RVALUE*)malloc(sizeof(RVALUE)*HEAP_SLOTS); - if (p == Qnil) Fatal("can't alloc memory"); + if (p == 0) Fatal("can't alloc memory"); pend = p + HEAP_SLOTS; + if (lomem == 0 || lomem > p) lomem = p; + if (himem < pend) himem = pend; while (p < pend) { p->as.free.flag = 0; @@ -245,15 +236,15 @@ newobj() } VALUE -data_new(datap, dfree, dmark) - VALUE *datap; +data_new(datap, dmark, dfree) + void *datap; void (*dfree)(); void (*dmark)(); { - extern VALUE C_Data; + extern VALUE cData; struct RData *data = (struct RData*)newobj(); - OBJSETUP(data, C_Data, T_DATA); + OBJSETUP(data, cData, T_DATA); data->data = datap; data->dfree = dfree; data->dmark = dmark; @@ -271,7 +262,10 @@ looks_pointerp(p) register RVALUE *heap_org; register long i; - /* if p looks as a SCM pointer mark location */ + if (p < lomem) return FALSE; + if (p > himem) return FALSE; + + /* check if p looks like a pointer */ for (i=0; i < heaps_used; i++) { heap_org = heaps[i]; if (heap_org <= p && p < heap_org + HEAP_SLOTS @@ -310,7 +304,7 @@ mark_locations(start, end) mark_locations_array(start,n); } -static +static int mark_entry(key, value) ID key; VALUE value; @@ -319,14 +313,14 @@ mark_entry(key, value) return ST_CONTINUE; } -static +static int mark_tbl(tbl) st_table *tbl; { st_foreach(tbl, mark_entry, 0); } -static +static int mark_hashentry(key, value) ID key; VALUE value; @@ -336,7 +330,7 @@ mark_hashentry(key, value) return ST_CONTINUE; } -static +static int mark_hash(tbl) st_table *tbl; { @@ -393,8 +387,8 @@ gc_mark(obj) break; case T_CLASS: - gc_mark(obj->as.class.super); case T_MODULE: + gc_mark(obj->as.class.super); mark_tbl(obj->as.class.m_tbl); if (obj->as.class.iv_tbl) mark_tbl(obj->as.class.iv_tbl); break; @@ -405,7 +399,7 @@ gc_mark(obj) VALUE *ptr = obj->as.array.ptr; for (i=0; i < len; i++) - gc_mark(ptr[i]); + gc_mark(*ptr++); } break; @@ -425,40 +419,37 @@ gc_mark(obj) if (obj->as.object.iv_tbl) mark_tbl(obj->as.object.iv_tbl); break; + case T_MATCH: case T_REGEXP: case T_FLOAT: case T_BIGNUM: break; - case T_STRUCT: - { - int i, len = obj->as.rstruct.len; - struct kv_pair *ptr = obj->as.rstruct.tbl; - - for (i=0; i < len; i++) - gc_mark(ptr[i].value); - } + case T_VARMAP: + gc_mark(obj->as.varmap.next); break; case T_SCOPE: - { - struct SCOPE *scope = (struct SCOPE*)obj; - if (scope->local_vars) { - int n = scope->local_tbl[0]; - VALUE *tbl = scope->local_vars; - - while (n--) { - gc_mark(*tbl); - tbl++; - } + if (obj->as.scope.local_vars) { + int n = obj->as.scope.local_tbl[0]; + VALUE *tbl = obj->as.scope.local_vars; + + while (n--) { + gc_mark(*tbl); + tbl++; } } break; - case T_ASSOC: - gc_mark(obj->as.assoc.car); - obj = (RVALUE*)obj->as.assoc.cdr; - goto Top; + case T_STRUCT: + { + int i, len = obj->as.rstruct.len; + VALUE *ptr = obj->as.rstruct.ptr; + + for (i=0; i < len; i++) + gc_mark(*ptr++); + } + break; default: Bug("gc_mark(): unknown data type %d", obj->as.basic.flags & T_MASK); @@ -475,7 +466,7 @@ gc_sweep() int freed = 0; int i; - freelist = Qnil; + freelist = 0; for (i = 0; i < heaps_used; i++) { RVALUE *p, *pend; RVALUE *nfreelist; @@ -542,14 +533,15 @@ obj_free(obj) if (obj->as.data.dfree) (*obj->as.data.dfree)(DATA_PTR(obj)); free(DATA_PTR(obj)); break; + case T_MATCH: + re_free_registers(obj->as.match.regs); + free(obj->as.match.regs); + if (obj->as.match.ptr) free(obj->as.match.ptr); + break; case T_ICLASS: /* iClass shares table with the module */ case T_FLOAT: - case T_ASSOC: - break; - case T_STRUCT: - free(obj->as.rstruct.name); - free(obj->as.rstruct.tbl); + case T_VARMAP: break; case T_BIGNUM: free(obj->as.bignum.digits); @@ -561,13 +553,14 @@ obj_free(obj) return; /* no need to free iv_tbl */ case T_SCOPE: - { - struct SCOPE *scope = (struct SCOPE*)obj; - if (scope->local_vars) - free(scope->local_vars); - if (scope->local_tbl) - free(scope->local_tbl); - } + if (obj->as.scope.local_vars) + free(obj->as.scope.local_vars); + if (obj->as.scope.local_tbl) + free(obj->as.scope.local_tbl); + break; + + case T_STRUCT: + free(obj->as.rstruct.ptr); break; default: @@ -576,11 +569,11 @@ obj_free(obj) } void -gc_mark_env(env) - struct ENVIRON *env; +gc_mark_frame(frame) + struct FRAME *frame; { - int n = env->argc; - VALUE *tbl = env->argv; + int n = frame->argc; + VALUE *tbl = frame->argv; while (n--) { gc_mark(*tbl); @@ -592,7 +585,7 @@ void gc() { struct gc_list *list; - struct ENVIRON *env; + struct FRAME *frame; jmp_buf save_regs_gc_mark; VALUE stack_end; @@ -603,9 +596,9 @@ gc() alloca(0); #endif - /* mark env stack */ - for (env = the_env; env; env = env->prev) { - gc_mark_env(env); + /* mark frame stack */ + for (frame = the_frame; frame; frame = frame->prev) { + gc_mark_frame(frame); } gc_mark(the_scope); @@ -627,7 +620,6 @@ gc() gc_mark_global_tbl(); mark_tbl(rb_class_tbl); - gc_mark_trap_list(); gc_sweep(); @@ -649,11 +641,12 @@ init_heap() add_heap(); } +void Init_GC() { - M_GC = rb_define_module("GC"); - rb_define_single_method(M_GC, "start", gc, 0); - rb_define_single_method(M_GC, "enable", Sgc_enable, 0); - rb_define_single_method(M_GC, "disable", Sgc_disable, 0); - rb_define_method(M_GC, "garbage_collect", gc, 0); + mGC = rb_define_module("GC"); + rb_define_singleton_method(mGC, "start", gc, 0); + rb_define_singleton_method(mGC, "enable", gc_s_enable, 0); + rb_define_singleton_method(mGC, "disable", gc_s_disable, 0); + rb_define_method(mGC, "garbage_collect", gc, 0); } @@ -21,15 +21,6 @@ #include "config.h" -#if defined (SHELL) -# if defined (HAVE_STDLIB_H) -# include <stdlib.h> -# else -# include "ansi_stdlib.h" -# endif /* HAVE_STDLIB_H */ -# include <config.h> -#endif - #include <sys/types.h> #if !defined (SHELL) && (defined (_POSIX_VERSION) || defined (USGr3)) @@ -13,23 +13,19 @@ #include "ruby.h" #include "st.h" -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#else -char *getenv(); -#endif - #ifdef HAVE_STRING_H # include <string.h> #else char *strchr(); #endif -VALUE C_Hash; +char *getenv(); + +VALUE cHash; static VALUE envtbl; static ID hash; -VALUE Fgetenv(), Fsetenv(); +VALUE f_getenv(), f_setenv(); static VALUE rb_cmp(a, b) @@ -50,7 +46,7 @@ rb_hash(a, mod) #define ASSOC_VAL(a) RASSOC(a)->cdr static VALUE -Shash_new(class) +hash_s_new(class) VALUE class; { NEWOBJ(hash, struct RHash); @@ -61,10 +57,10 @@ Shash_new(class) return (VALUE)hash; } -static VALUE Fhash_clone(); +static VALUE hash_clone(); static VALUE -Shash_create(argc, argv, class) +hash_s_create(argc, argv, class) int argc; VALUE *argv; VALUE class; @@ -86,7 +82,7 @@ Shash_create(argc, argv, class) if (argc % 2 != 0) { Fail("odd number args for Hash"); } - hash = (struct RHash*)Shash_new(class); + hash = (struct RHash*)hash_s_new(class); for (i=0; i<argc; i+=2) { st_insert(hash->tbl, argv[i], argv[i+1]); @@ -98,11 +94,11 @@ Shash_create(argc, argv, class) VALUE hash_new() { - return Shash_new(C_Hash); + return hash_s_new(cHash); } static VALUE -Fhash_clone(hash) +hash_clone(hash) struct RHash *hash; { NEWOBJ(hash2, struct RHash); @@ -114,11 +110,11 @@ Fhash_clone(hash) } static VALUE -Fhash_aref(hash, key) +hash_aref(hash, key) struct RHash *hash; VALUE key; { - VALUE val = Qnil; + VALUE val; if (!st_lookup(hash->tbl, key, &val)) { return Qnil; @@ -127,7 +123,7 @@ Fhash_aref(hash, key) } static VALUE -Fhash_indexes(hash, args) +hash_indexes(hash, args) struct RHash *hash; struct RArray *args; { @@ -151,14 +147,14 @@ Fhash_indexes(hash, args) p = args->ptr; pend = p + args->len; while (p < pend) { - new_hash->ptr[i++] = Fhash_aref(hash, *p++); + new_hash->ptr[i++] = hash_aref(hash, *p++); } new_hash->len = i; return (VALUE)new_hash; } static VALUE -Fhash_delete(hash, key) +hash_delete(hash, key) struct RHash *hash; VALUE key; { @@ -176,7 +172,7 @@ struct shift_var { }; static -hash_shift(key, value, var) +shift_i(key, value, var) VALUE key, value; struct shift_var *var; { @@ -188,20 +184,20 @@ hash_shift(key, value, var) } static VALUE -Fhash_shift(hash) +hash_shift(hash) struct RHash *hash; { struct shift_var var; var.stop = 0; - st_foreach(hash->tbl, hash_shift, &var); + st_foreach(hash->tbl, shift_i, &var); if (var.stop == 0) return Qnil; return assoc_new(var.key, var.val); } static int -hash_delete_if(key, value) +delete_if_i(key, value) VALUE key, value; { if (rb_yield(assoc_new(key, value))) @@ -210,52 +206,55 @@ hash_delete_if(key, value) } static VALUE -Fhash_delete_if(hash) +hash_delete_if(hash) struct RHash *hash; { - st_foreach(hash->tbl, hash_delete_if, Qnil); + st_foreach(hash->tbl, delete_if_i, Qnil); return (VALUE)hash; } -static -hash_clear(key, value) +static int +clear_i(key, value) VALUE key, value; { return ST_DELETE; } static VALUE -Fhash_clear(hash) +hash_clear(hash) struct RHash *hash; { - st_foreach(hash->tbl, hash_clear); + st_foreach(hash->tbl, clear_i); return (VALUE)hash; } VALUE -Fhash_aset(hash, key, val) +hash_aset(hash, key, val) struct RHash *hash; VALUE key, val; { if (val == Qnil) { - Fhash_delete(hash, key); + hash_delete(hash, key); return Qnil; } + if (TYPE(key) == T_STRING) { + key = str_dup_freezed(key); + } st_insert(hash->tbl, key, val); return val; } static VALUE -Fhash_length(hash) +hash_length(hash) struct RHash *hash; { return INT2FIX(hash->tbl->num_entries); } -static -hash_each_value(key, value) +static int +each_value_i(key, value) VALUE key, value; { rb_yield(value); @@ -263,15 +262,15 @@ hash_each_value(key, value) } static VALUE -Fhash_each_value(hash) +hash_each_value(hash) struct RHash *hash; { - st_foreach(hash->tbl, hash_each_value); + st_foreach(hash->tbl, each_value_i); return (VALUE)hash; } -static -hash_each_key(key, value) +static int +each_key_i(key, value) VALUE key, value; { rb_yield(key); @@ -279,15 +278,15 @@ hash_each_key(key, value) } static VALUE -Fhash_each_key(hash) +hash_each_key(hash) struct RHash *hash; { - st_foreach(hash->tbl, hash_each_key); + st_foreach(hash->tbl, each_key_i); return (VALUE)hash; } -static -hash_each_pair(key, value) +static int +each_pair_i(key, value) VALUE key, value; { rb_yield(assoc_new(key, value)); @@ -295,15 +294,15 @@ hash_each_pair(key, value) } static VALUE -Fhash_each_pair(hash) +hash_each_pair(hash) struct RHash *hash; { - st_foreach(hash->tbl, hash_each_pair); + st_foreach(hash->tbl, each_pair_i); return (VALUE)hash; } -static -hash_to_a(key, value, ary) +static int +to_a_i(key, value, ary) VALUE key, value, ary; { ary_push(ary, assoc_new(key, value)); @@ -311,59 +310,59 @@ hash_to_a(key, value, ary) } static VALUE -Fhash_to_a(hash) +hash_to_a(hash) struct RHash *hash; { VALUE ary; ary = ary_new(); - st_foreach(hash->tbl, hash_to_a, ary); + st_foreach(hash->tbl, to_a_i, ary); return ary; } -static -hash_inspect(key, value, str) +static int +inspect_i(key, value, str) VALUE key, value; struct RString *str; { VALUE str2; - ID inspect = rb_intern("_inspect"); + ID inspect = rb_intern("inspect"); if (str->len > 1) { str_cat(str, ", ", 2); } - str2 = rb_funcall(key, inspect, 0, Qnil); + str2 = rb_funcall(key, inspect, 0, 0); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); str_cat(str, "=>", 2); - str2 = rb_funcall(value, inspect, 0, Qnil); + str2 = rb_funcall(value, inspect, 0, 0); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); return ST_CONTINUE; } static VALUE -Fhash_inspect(hash) +hash_inspect(hash) struct RHash *hash; { VALUE str; str = str_new2("{"); - st_foreach(hash->tbl, hash_inspect, str); + st_foreach(hash->tbl, inspect_i, str); str_cat(str, "}", 1); return str; } static VALUE -Fhash_to_s(hash) +hash_to_s(hash) VALUE hash; { - return Fary_to_s(Fhash_to_a(hash)); + return ary_to_s(hash_to_a(hash)); } -static -hash_keys(key, value, ary) +static int +keys_i(key, value, ary) VALUE key, value, ary; { ary_push(ary, key); @@ -371,49 +370,37 @@ hash_keys(key, value, ary) } static VALUE -Fhash_keys(hash) +hash_keys(hash) struct RHash *hash; { VALUE ary; ary = ary_new(); - st_foreach(hash->tbl, hash_keys, ary); + st_foreach(hash->tbl, keys_i, ary); return ary; } -static -hash_values(key, value, ary) +static int +values_i(key, value, ary) VALUE key, value, ary; { - ary_push(ary, key); + ary_push(ary, value); return ST_CONTINUE; } static VALUE -Fhash_values(hash) +hash_values(hash) struct RHash *hash; { VALUE ary; ary = ary_new(); - st_foreach(hash->tbl, hash_values, ary); + st_foreach(hash->tbl, values_i, ary); return ary; } -static VALUE -Fhash_has_key(hash, key) - struct RHash *hash; - VALUE key; -{ - VALUE val; - - if (st_lookup(hash->tbl, key, &val)) - return TRUE; - return FALSE; -} - static int hash_search_value(key, value, data) VALUE key, value, *data; @@ -426,7 +413,18 @@ hash_search_value(key, value, data) } static VALUE -Fhash_has_value(hash, val) +hash_has_key(hash, key) + struct RHash *hash; + VALUE key; +{ + if (st_lookup(hash->tbl, key, 0)) { + return TRUE; + } + return FALSE; +} + +static VALUE +hash_has_value(hash, val) struct RHash *hash; VALUE val; { @@ -444,7 +442,7 @@ struct equal_data { }; static int -hash_equal(key, val1, data) +equal_i(key, val1, data) VALUE key, val1; struct equal_data *data; { @@ -462,7 +460,7 @@ hash_equal(key, val1, data) } static VALUE -Fhash_equal(hash1, hash2) +hash_equal(hash1, hash2) struct RHash *hash1, *hash2; { struct equal_data data; @@ -473,13 +471,13 @@ Fhash_equal(hash1, hash2) data.tbl = hash2->tbl; data.result = TRUE; - st_foreach(hash1->tbl, hash_equal, &data); + st_foreach(hash1->tbl, equal_i, &data); return data.result; } static int -hash_hash(key, val, data) +hash_i(key, val, data) VALUE key, val; int *data; { @@ -489,21 +487,19 @@ hash_hash(key, val, data) } static VALUE -Fhash_hash(hash) +hash_hash(hash) struct RHash *hash; { int h; - st_foreach(hash->tbl, hash_hash, &h); + st_foreach(hash->tbl, hash_i, &h); return INT2FIX(h); } -extern VALUE rb_readonly_hook(); - extern char **environ; static VALUE -Fenv_each(hash) +env_each(hash) VALUE hash; { char **env; @@ -522,12 +518,12 @@ Fenv_each(hash) } static VALUE -Fenv_delete(obj, name) +env_delete(obj, name) VALUE obj; struct RString *name; { int i, len; - char *nam, *val = Qnil; + char *nam, *val = 0; Check_Type(name, T_STRING); nam = name->ptr; @@ -549,7 +545,7 @@ Fenv_delete(obj, name) } VALUE -Fgetenv(obj, name) +f_getenv(obj, name) VALUE obj; struct RString *name; { @@ -568,13 +564,13 @@ Fgetenv(obj, name) } VALUE -Fsetenv(obj, name, value) +f_setenv(obj, name, value) VALUE obj; struct RString *name, *value; { Check_Type(name, T_STRING); if (value == Qnil) { - Fenv_delete(obj, name); + env_delete(obj, name); return Qnil; } @@ -585,86 +581,69 @@ Fsetenv(obj, name, value) if (strlen(value->ptr) != value->len) Fail("Bad environment value"); -#ifdef HAVE_SETENV - if (setenv(name->ptr, value->ptr, 1) == 0) return TRUE; -#else -#ifdef HAVE_PUTENV - { - char *str; - int len; - - str = ALLOC_N(char, name->len + value->len + 2); - sprintf("%s=%s", name->ptr, value->ptr); - if (putenv(str) == 0) return TRUE; - } -#else - Fail("setenv is not supported on this system"); -#endif -#endif - - Fail("setenv failed"); - return FALSE; /* not reached */ + setenv(name->ptr, value->ptr, 1); + return TRUE; } static VALUE -Fenv_to_s() +env_to_s() { return str_new2("$ENV"); } +void Init_Hash() { - extern VALUE C_Kernel; - extern VALUE M_Enumerable; + extern VALUE cKernel; + extern VALUE mEnumerable; hash = rb_intern("hash"); - C_Hash = rb_define_class("Hash", C_Object); + cHash = rb_define_class("Hash", cObject); - rb_include_module(C_Hash, M_Enumerable); + rb_include_module(cHash, mEnumerable); - rb_define_single_method(C_Hash, "new", Shash_new, 0); - rb_define_single_method(C_Hash, "[]", Shash_create, -1); + rb_define_singleton_method(cHash, "new", hash_s_new, 0); + rb_define_singleton_method(cHash, "[]", hash_s_create, -1); - rb_define_method(C_Hash,"clone", Fhash_clone, 0); + rb_define_method(cHash,"clone", hash_clone, 0); - rb_define_method(C_Hash,"to_a", Fhash_to_a, 0); - rb_define_method(C_Hash,"to_s", Fhash_to_s, 0); - rb_define_method(C_Hash,"_inspect", Fhash_inspect, 0); + rb_define_method(cHash,"to_a", hash_to_a, 0); + rb_define_method(cHash,"to_s", hash_to_s, 0); + rb_define_method(cHash,"inspect", hash_inspect, 0); - rb_define_method(C_Hash,"==", Fhash_equal, 1); - rb_define_method(C_Hash,"hash", Fhash_hash, 0); - rb_define_method(C_Hash,"[]", Fhash_aref, 1); - rb_define_method(C_Hash,"[]=", Fhash_aset, 2); - rb_define_method(C_Hash,"indexes", Fhash_indexes, -2); - rb_define_method(C_Hash,"length", Fhash_length, 0); - rb_define_alias(C_Hash, "size", "length"); - rb_define_method(C_Hash,"each", Fhash_each_pair, 0); - rb_define_method(C_Hash,"each_value", Fhash_each_value, 0); - rb_define_method(C_Hash,"each_key", Fhash_each_key, 0); - rb_define_method(C_Hash,"each_pair", Fhash_each_pair, 0); + rb_define_method(cHash,"==", hash_equal, 1); + rb_define_method(cHash,"hash", hash_hash, 0); + rb_define_method(cHash,"[]", hash_aref, 1); + rb_define_method(cHash,"[]=", hash_aset, 2); + rb_define_method(cHash,"indexes", hash_indexes, -2); + rb_define_method(cHash,"length", hash_length, 0); + rb_define_alias(cHash, "size", "length"); + rb_define_method(cHash,"each", hash_each_pair, 0); + rb_define_method(cHash,"each_value", hash_each_value, 0); + rb_define_method(cHash,"each_key", hash_each_key, 0); + rb_define_method(cHash,"each_pair", hash_each_pair, 0); - rb_define_method(C_Hash,"keys", Fhash_keys, 0); - rb_define_method(C_Hash,"values", Fhash_values, 0); + rb_define_method(cHash,"keys", hash_keys, 0); + rb_define_method(cHash,"values", hash_values, 0); - rb_define_method(C_Hash,"shift", Fhash_shift, 0); - rb_define_method(C_Hash,"delete", Fhash_delete, 1); - rb_define_method(C_Hash,"delete_if", Fhash_delete_if, 0); - rb_define_method(C_Hash,"clear", Fhash_clear, 0); + rb_define_method(cHash,"shift", hash_shift, 0); + rb_define_method(cHash,"delete", hash_delete, 1); + rb_define_method(cHash,"delete_if", hash_delete_if, 0); + rb_define_method(cHash,"clear", hash_clear, 0); - rb_define_method(C_Hash,"includes", Fhash_has_key, 1); - rb_define_method(C_Hash,"has_key", Fhash_has_key, 1); - rb_define_method(C_Hash,"has_value", Fhash_has_value, 1); + rb_define_method(cHash,"has_key?", hash_has_key, 1); + rb_define_method(cHash,"has_value?", hash_has_value, 1); - envtbl = obj_alloc(C_Object); - rb_extend_object(envtbl, M_Enumerable); + envtbl = obj_alloc(cObject); + rb_extend_object(envtbl, mEnumerable); - rb_define_single_method(envtbl,"[]", Fgetenv, 1); - rb_define_single_method(envtbl,"[]=", Fsetenv, 2); - rb_define_single_method(envtbl,"each", Fenv_each, 0); - rb_define_single_method(envtbl,"delete", Fenv_delete, 1); - rb_define_single_method(envtbl,"to_s", Fenv_to_s, 0); + rb_define_singleton_method(envtbl,"[]", f_getenv, 1); + rb_define_singleton_method(envtbl,"[]=", f_setenv, 2); + rb_define_singleton_method(envtbl,"each", env_each, 0); + rb_define_singleton_method(envtbl,"delete", env_delete, 1); + rb_define_singleton_method(envtbl,"to_s", env_to_s, 0); - rb_define_variable("$ENV", &envtbl, Qnil, rb_readonly_hook, 0); - rb_define_const(C_Kernel, "ENV", envtbl); + rb_define_readonly_variable("$ENV", &envtbl); + rb_define_const(cKernel, "ENV", envtbl); } diff --git a/ident.h b/ident.h deleted file mode 100644 index 1e28098576..0000000000 --- a/ident.h +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************ - - ident.h - - - $Author: matz $ - $Revision: 1.2 $ - $Date: 1994/08/12 04:47:29 $ - created at: Mon Jan 31 16:23:19 JST 1994 - - Copyright (C) 1993-1995 Yukihiro Matsumoto - -************************************************/ - -#ifndef IDENT_H -#define IDENT_H - -#define ID_SCOPE_SHIFT 3 -#define ID_SCOPE_MASK 0x07 -#define ID_LOCAL 0x00 -#define ID_INSTANCE 0x01 -#define ID_GLOBAL 0x02 -#define ID_ATTRSET 0x03 -#define ID_CONST 0x04 -#define ID_NTHREF 0x05 - -#endif @@ -12,6 +12,7 @@ #include "ruby.h" +void rb_call_inits() { Init_sym(); @@ -23,7 +24,6 @@ rb_call_inits() Init_Enumerable(); Init_Numeric(); Init_Bignum(); - Init_Assoc(); Init_Array(); Init_Hash(); Init_Struct(); @@ -37,12 +37,9 @@ rb_call_inits() Init_Random(); Init_signal(); Init_process(); - Init_Etc(); Init_load(); - Init_Block(); + Init_Proc(); Init_Math(); - /* new Inits comes between here.. */ - - /* .. and here. */ + Init_ext(); Init_version(); } @@ -14,15 +14,27 @@ #include "io.h" #include <ctype.h> #include <errno.h> -#include <sys/time.h> + #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif +#ifdef HAVE_VFORK_H +#include <vfork.h> +#endif + VALUE rb_ad_string(); -VALUE C_IO; -extern VALUE C_File; +VALUE cIO; +extern VALUE cFile; VALUE rb_stdin, rb_stdout, rb_stderr, rb_defout; @@ -46,9 +58,6 @@ io_write(obj, str) VALUE out; int n; - if (TYPE(str) != T_STRING) - str = (struct RString*)obj_as_string(str); - GetOpenFile(obj, fptr); if (!(fptr->mode & FMODE_WRITABLE)) { Fail("not opened for writing"); @@ -57,6 +66,8 @@ io_write(obj, str) f = (fptr->f2) ? fptr->f2 : fptr->f; if (f == NULL) Fail("closed stream"); + if (TYPE(str) != T_STRING) + str = (struct RString*)obj_as_string(str); if (str->len == 0) return INT2FIX(0); n = fwrite(str->ptr, sizeof(char), str->len, f); @@ -71,7 +82,7 @@ io_write(obj, str) } static VALUE -Fio_puts(obj, str) +io_puts(obj, str) VALUE obj, str; { io_write(obj, str); @@ -79,7 +90,7 @@ Fio_puts(obj, str) } static VALUE -Fio_flush(obj) +io_flush(obj) VALUE obj; { OpenFile *fptr; @@ -98,7 +109,7 @@ Fio_flush(obj) } static VALUE -Fio_eof(obj) +io_eof(obj) VALUE obj; { OpenFile *fptr; @@ -126,7 +137,7 @@ Fio_eof(obj) } static VALUE -Fio_sync(obj) +io_sync(obj) VALUE obj; { OpenFile *fptr; @@ -136,7 +147,7 @@ Fio_sync(obj) } static VALUE -Fio_set_sync(obj, mode) +io_set_sync(obj, mode) VALUE obj, mode; { OpenFile *fptr; @@ -152,7 +163,7 @@ Fio_set_sync(obj, mode) } static VALUE -Fio_fileno(obj) +io_fileno(obj) VALUE obj; { OpenFile *fptr; @@ -194,7 +205,7 @@ read_all(port) } static VALUE -Fio_read(argc, argv, obj) +io_read(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -234,7 +245,7 @@ VALUE rb_lastline; static VALUE lineno; static VALUE -Fio_gets(obj) +io_gets(obj) VALUE obj; { OpenFile *fptr; @@ -264,7 +275,7 @@ Fio_gets(obj) rslen = 1; } - if (rslen == 0 && c == '\n') { + if (rslen == 0) { do { TRAP_BEG; c = getc(f); @@ -279,7 +290,6 @@ Fio_gets(obj) { char buf[8192]; char *bp, *bpe = buf + sizeof buf - 3; - char *ptr; int append = 0; again: @@ -323,7 +333,7 @@ Fio_gets(obj) } return_gets: - if (rslen == 0 && c == '\n') { + if (rslen == 0) { while (c != EOF) { TRAP_BEG; c = getc(f); @@ -343,19 +353,19 @@ Fio_gets(obj) } static VALUE -Fio_each(obj) +io_each_line(obj) VALUE obj; { VALUE str; - while (str = Fio_gets(obj)) { + while (str = io_gets(obj)) { rb_yield(str); } return Qnil; } static VALUE -Fio_each_byte(obj) +io_each_byte(obj) VALUE obj; { OpenFile *fptr; @@ -381,7 +391,7 @@ Fio_each_byte(obj) } static VALUE -Fio_getc(obj) +io_getc(obj) VALUE obj; { OpenFile *fptr; @@ -407,43 +417,78 @@ Fio_getc(obj) } static VALUE -Fio_isatty(obj) +io_isatty(obj) VALUE obj; { OpenFile *fptr; +#ifndef NT GetOpenFile(obj, fptr); if (fptr->f == NULL) Fail("closed stream"); if (isatty(fileno(fptr->f)) == 0) return FALSE; - +#endif return TRUE; } -static VALUE -Fio_close(obj) - VALUE obj; -{ +static void +fptr_finalize(fptr) OpenFile *fptr; - - GetOpenFile(obj, fptr); - +{ + if (fptr->f != NULL) { + fclose(fptr->f); + } if (fptr->f2 != NULL) { fclose(fptr->f2); } - if (fptr->f != NULL) { - fclose(fptr->f); + if (fptr->path) { + free(fptr->path); + fptr->path = NULL; } - fptr->f = fptr->f2 = NULL; if (fptr->pid) { rb_syswait(fptr->pid); fptr->pid = 0; } +} + +void +io_fptr_finalize(fptr) + OpenFile *fptr; +{ + if (fptr->finalize) { + (*fptr->finalize)(fptr); + fptr->finalize = 0; + } + else { + fptr_finalize(fptr); + } + fptr->f = fptr->f2 = NULL; +} + +VALUE +io_close(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + io_fptr_finalize(fptr); + return Qnil; } static VALUE -Fio_syswrite(obj, str) +io_closed(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + return fptr->f?FALSE:TRUE; +} + +static VALUE +io_syswrite(obj, str) VALUE obj, str; { OpenFile *fptr; @@ -468,7 +513,7 @@ Fio_syswrite(obj, str) } static VALUE -Fio_sysread(obj, len) +io_sysread(obj, len) VALUE obj, len; { OpenFile *fptr; @@ -496,29 +541,11 @@ Fio_sysread(obj, len) return str; } -void -io_free_OpenFile(fptr) - OpenFile *fptr; -{ - if (fptr->f != NULL) { - fclose(fptr->f); - } - if (fptr->f2 != NULL) { - fclose(fptr->f2); - } - if (fptr->path) { - free(fptr->path); - } - if (fptr->pid) { - rb_syswait(fptr->pid); - } -} - static VALUE -Fio_binmode(obj) +io_binmode(obj) VALUE obj; { -#ifdef MSDOS +#ifdef NT OpenFile *fptr; GetOpenFile(obj, fptr); @@ -530,6 +557,7 @@ Fio_binmode(obj) VALUE obj_alloc(); +int io_mode_flags(mode) char *mode; { @@ -564,8 +592,7 @@ rb_fdopen(fd, mode) f = fdopen(fd, mode); if (f == NULL) { - if (errno = EMFILE) { - gc(); + if (errno == EMFILE) { f = fdopen(fd, mode); } if (f == NULL) { @@ -575,22 +602,45 @@ rb_fdopen(fd, mode) return f; } +#ifdef NT +static void +pipe_finalize(fptr) + OpenFile *fptr; +{ + if (fptr->f != NULL) { + pclose(fptr->f); + } + fptr->f = fptr->f2 = NULL; +} +#endif + static VALUE pipe_open(pname, mode) char *pname, *mode; { + int modef = io_mode_flags(mode); VALUE port; OpenFile *fptr; - int pid, pr[2], pw[2]; - int doexec; +#ifdef NT + FILE *f = popen(pname, mode); + + if (f == NULL) rb_sys_fail(pname); - port = obj_alloc(C_IO); + port = obj_alloc(cIO); MakeOpenFile(port, fptr); - fptr->mode = io_mode_flags(mode); + fptr->finalize = pipe_finalize; + + if (modef & FMODE_READABLE) fptr->f = f; + if (modef & FMODE_WRITABLE) fptr->f2 = f; + fptr->mode = modef | FMODE_SYNC; + return port; +#else + int pid, pr[2], pw[2]; + volatile int doexec; - if (((fptr->mode & FMODE_READABLE) && pipe(pr) == -1) || - ((fptr->mode & FMODE_WRITABLE) && pipe(pw) == -1)) + if (((modef & FMODE_READABLE) && pipe(pr) == -1) || + ((modef & FMODE_WRITABLE) && pipe(pw) == -1)) rb_sys_fail(Qnil); doexec = (strcmp("-", pname) != 0); @@ -603,18 +653,26 @@ pipe_open(pname, mode) retry: switch (pid = (doexec?vfork():fork())) { case 0: /* child */ - if (fptr->mode & FMODE_READABLE) { + if (modef & FMODE_READABLE) { close(pr[0]); dup2(pr[1], 1); close(pr[1]); } - if (fptr->mode & FMODE_WRITABLE) { + if (modef & FMODE_WRITABLE) { close(pw[1]); dup2(pw[0], 0); close(pw[0]); } if (doexec) { + VALUE fd = io_fileno(rb_stderr); + int f = FIX2INT(fd); + + if (f != 2) { + close(2); + dup2(f, 2); + close(f); + } rb_proc_exec(pname); _exit(127); } @@ -622,25 +680,28 @@ pipe_open(pname, mode) case -1: /* fork failed */ if (errno == EAGAIN) { - sleep(5); + sleep(1); goto retry; } + close(pr[0]); close(pw[1]); + rb_sys_fail(pname); break; default: /* parent */ - if (fptr->mode & FMODE_READABLE) close(pr[1]); - if (fptr->mode & FMODE_WRITABLE) close(pw[0]); - } - if (pid == -1) { - close(pr[0]); close(pw[1]); - rb_sys_fail(Qnil); + port = obj_alloc(cIO); + MakeOpenFile(port, fptr); + if (modef & FMODE_READABLE) close(pr[1]); + if (modef & FMODE_WRITABLE) close(pw[0]); + fptr->mode = modef; + fptr->mode |= FMODE_SYNC; } fptr->pid = pid; - if (fptr->mode & FMODE_READABLE) fptr->f = rb_fdopen(pr[0], "r"); - if (fptr->mode & FMODE_WRITABLE) fptr->f2 = rb_fdopen(pw[1], "w"); + if (modef & FMODE_READABLE) fptr->f = rb_fdopen(pr[0], "r"); + if (modef & FMODE_WRITABLE) fptr->f2 = rb_fdopen(pw[1], "w"); return port; +#endif } static VALUE @@ -658,7 +719,7 @@ io_open(fname, mode) } static VALUE -Fopen(argc, argv, self) +f_open(argc, argv, self) int argc; VALUE *argv; VALUE self; @@ -683,7 +744,18 @@ Fopen(argc, argv, self) } static VALUE -Fprintf(argc, argv) +io_printf(argc, argv, out) + int argc; + VALUE argv[]; + VALUE out; +{ + rb_funcall(out, id_write, 1, f_sprintf(argc, argv)); + + return Qnil; +} + +static VALUE +f_printf(argc, argv) int argc; VALUE argv[]; { @@ -693,7 +765,7 @@ Fprintf(argc, argv) if (TYPE(argv[0]) == T_STRING) { out = rb_defout; } - else if (obj_responds_to(argv[0], INT2FIX(id_write))) { + else if (rb_responds_to(argv[0], id_write)) { out = argv[0]; argv++; argc--; @@ -701,63 +773,78 @@ Fprintf(argc, argv) else { Fail("output must responds to `write'"); } - rb_funcall(out, id_write, 1, Fsprintf(argc, argv)); + rb_funcall(out, id_write, 1, f_sprintf(argc, argv)); return Qnil; } static VALUE -Fprint(argc, argv, self) +io_print(argc, argv, out) int argc; VALUE *argv; - VALUE self; + VALUE out; { int i; + VALUE line; - /* if no argument given, print recv */ + /* if no argument given, print `$_' */ if (argc == 0) { - rb_funcall(self, id_print_on, 1, rb_defout); + argc = 1; + if (rb_lastline) + argv = &rb_lastline; + else { + line = str_new(0,0); + argv = &line; + } } - else { - for (i=0; i<argc; i++) { - if (OFS && i>0) { - io_write(rb_defout, OFS); - } - switch (TYPE(argv[i])) { - case T_STRING: - io_write(rb_defout, argv[i]); - break; - case T_ARRAY: - Fary_print_on(argv[i], rb_defout); - break; - default: - rb_funcall(argv[i], id_print_on, 1, rb_defout); - break; - } + for (i=0; i<argc; i++) { + if (OFS && i>0) { + io_write(out, OFS); + } + switch (TYPE(argv[i])) { + case T_STRING: + io_write(out, argv[i]); + break; + case T_ARRAY: + ary_print_on(argv[i], out); + break; + default: + rb_funcall(argv[i], id_print_on, 1, out); + break; } } if (ORS) { - io_write(rb_defout, ORS); + io_write(out, ORS); } return Qnil; } static VALUE -io_defset(obj, val) - VALUE obj, val; +f_print(argc, argv) + int argc; + VALUE *argv; +{ + io_print(argc, argv, rb_defout); + return Qnil; +} + +static VALUE +io_defset(val, id) + VALUE val; + ID id; { if (TYPE(val) == T_STRING) { val = io_open(RSTRING(val)->ptr, "w"); } - if (!obj_is_kind_of(val, C_IO)) { + if (!obj_is_kind_of(val, cIO)) { Fail("$< must be a file, %s given", rb_class2name(CLASS_OF(val))); } return rb_defout = val; } static VALUE -Fprint_on(obj, port) +f_print_on(obj, port) VALUE obj, port; { return io_write(port, obj); @@ -768,7 +855,7 @@ prep_stdio(f, mode) FILE *f; int mode; { - VALUE obj = obj_alloc(C_IO); + VALUE obj = obj_alloc(cIO); OpenFile *fp; MakeOpenFile(obj, fp); @@ -815,18 +902,23 @@ next_argv() else { FILE *fr = fopen(fn, "r"); + if (!fr) rb_sys_fail(fn); if (inplace) { struct stat st, st2; VALUE str; FILE *fw; if (rb_defout != rb_stdout) { - Fio_close(rb_defout); + io_close(rb_defout); } fstat(fileno(fr), &st); if (*inplace) { str = str_new2(fn); +#ifdef NT + add_suffix(str, inplace); +#else str_cat(str, inplace, strlen(inplace)); +#endif if (rename(fn, RSTRING(str)->ptr) < 0) { Warning("Can't rename %s to %s: %s, skipping file", fn, RSTRING(str)->ptr, strerror(errno)); @@ -860,15 +952,15 @@ next_argv() } static VALUE -Fgets() +f_gets() { VALUE line; retry: if (!next_argv()) return Qnil; - line = Fio_gets(file); + line = io_gets(file); if (line == Qnil && next_p != -1) { - Fio_close(file); + io_close(file); next_p = 1; goto retry; } @@ -880,11 +972,11 @@ Fgets() } static VALUE -Feof() +f_eof() { if (init_p == 0 && !next_argv()) return TRUE; - if (Fio_eof(file)) { + if (io_eof(file)) { next_p = 1; return TRUE; } @@ -892,19 +984,19 @@ Feof() } static VALUE -Fgetc() +f_getc() { - return Fio_getc(rb_stdin); + return io_getc(rb_stdin); } static VALUE -Freadlines(obj) +f_readlines(obj) VALUE obj; { VALUE line, ary; ary = ary_new(); - while (line = Fgets(obj)) { + while (line = f_gets(obj)) { ary_push(ary, line); } @@ -912,15 +1004,15 @@ Freadlines(obj) } VALUE -rb_check_str(val, id) +rb_str_setter(val, id, var) VALUE val; ID id; + VALUE *var; { - if (val == Qnil) return TRUE; - if (TYPE(val) != T_STRING) { + if (val && TYPE(val) != T_STRING) { Fail("value of %s must be String", rb_id2name(id)); } - return TRUE; + return *var = val; } VALUE @@ -929,15 +1021,12 @@ rb_xstring(str) { VALUE port, result; OpenFile *fptr; - int mask; Check_Type(str, T_STRING); port = pipe_open(str->ptr, "r"); result = read_all(port); - GetOpenFile(port, fptr); - rb_syswait(fptr->pid); - fptr->pid = 0; + io_close(port); return result; } @@ -945,25 +1034,22 @@ rb_xstring(str) struct timeval *time_timeval(); #ifdef _STDIO_USES_IOSTREAM /* GNU libc */ -# ifdef _other_gbase +# ifdef _IO_fpos_t # define READ_DATA_PENDING(fp) ((fp)->_IO_read_ptr < (fp)->_IO_read_end) # else # define READ_DATA_PENDING(fp) ((fp)->_gptr < (fp)->_egptr) # endif #else -# if __SLBF -# define READ_DATA_PENDING(fp) (fp->_r > 0) +# ifdef FILE_COUNT +# define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0) # else -# if STDSTDIO -# define READ_DATA_PENDING(fp) (fp->_cnt != 0) -# else ---------------> You Lose <-------------- -# endif +extern int ReadDataPending(); +# define READ_DATA_PENDING(fp) ReadDataPending(fp) # endif #endif static VALUE -Fselect(argc, argv, obj) +f_select(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -971,7 +1057,7 @@ Fselect(argc, argv, obj) VALUE read, write, except, timeout, res, list; fd_set rset, wset, eset, pset; fd_set *rp, *wp, *ep; - struct timeval time, *tp, timerec; + struct timeval *tp, timerec; OpenFile *fptr; int i, max = 0, n; int interrupt = 0; @@ -1049,21 +1135,17 @@ Fselect(argc, argv, obj) n = select(max, rp, wp, ep, tp); TRAP_END; if (n < 0) { - if (errno == EINTR) { - if (tp == NULL) goto retry; - interrupt = 1; + if (errno != EINTR) { + rb_sys_fail(Qnil); } - rb_sys_fail(Qnil); + if (tp == NULL) goto retry; + interrupt = 1; } - if (n == 0) return Qnil; res = ary_new2(3); - RARRAY(res)->ptr[0] = rp?ary_new():Qnil; - RARRAY(res)->len++; - RARRAY(res)->ptr[1] = wp?ary_new():Qnil; - RARRAY(res)->len++; - RARRAY(res)->ptr[2] = ep?ary_new():Qnil; - RARRAY(res)->len++; + ary_push(res, rp?ary_new():ary_new2(0)); + ary_push(res, wp?ary_new():ary_new2(0)); + ary_push(res, ep?ary_new():ary_new2(0)); if (interrupt == 0) { @@ -1139,16 +1221,23 @@ io_ctl(obj, req, arg, io_p) } arg->ptr[len] = 17; fd = fileno(fptr->f); +#ifdef HAVE_FCNTL if ((io_p?ioctl(fd, cmd, arg->ptr):fcntl(fd, cmd, arg->ptr))<0) { rb_sys_fail(fptr->path); } +#else + if (!io_p) { + Bug("fcntl() not implemented"); + } + if (ioctl(fd, cmd, arg->ptr)<0) rb_sys_fail(fptr->path); +#endif if (arg->ptr[len] != 17) { Fail("Return value overflowed string"); } } static VALUE -Fio_ioctl(obj, req, arg) +io_ioctl(obj, req, arg) VALUE obj, req; struct RString *arg; { @@ -1157,7 +1246,7 @@ Fio_ioctl(obj, req, arg) } static VALUE -Fsyscall(argc, argv) +f_syscall(argc, argv) int argc; VALUE *argv; { @@ -1251,7 +1340,7 @@ Fsyscall(argc, argv) } static VALUE -Farg_read(obj) +arg_read(obj) VALUE obj; { VALUE str, str2; @@ -1260,9 +1349,9 @@ Farg_read(obj) for (;;) { retry: if (!next_argv()) return Qnil; - str2 = Fio_read(0, Qnil, file); + str2 = io_read(0, Qnil, file); if (str2 == Qnil && next_p != -1) { - Fio_close(file); + io_close(file); next_p = 1; goto retry; } @@ -1274,15 +1363,15 @@ Farg_read(obj) } static VALUE -Farg_getc() +arg_getc() { VALUE byte; retry: if (!next_argv()) return Qnil; - byte = Fio_getc(file); + byte = io_getc(file); if (byte == Qnil && next_p != -1) { - Fio_close(file); + io_close(file); next_p = 1; goto retry; } @@ -1291,144 +1380,151 @@ Farg_getc() } static VALUE -Farg_each() +arg_each_line() { VALUE str; - while (str = Fgets()) { + while (str = f_gets()) { rb_yield(str); } return Qnil; } static VALUE -Farg_each_byte() +arg_each_byte() { VALUE byte; - while (byte = Farg_getc()) { + while (byte = arg_getc()) { rb_yield(byte); } return Qnil; } static VALUE -Farg_filename() +arg_filename() { return filename; } static VALUE -Farg_file() +arg_file() { return file; } -extern VALUE M_Enumerable; -VALUE rb_readonly_hook(); +extern VALUE mEnumerable; +void Init_IO() { - extern VALUE C_Kernel; + extern VALUE cKernel; id_write = rb_intern("write"); id_fd = rb_intern("fd"); id_print_on = rb_intern("print_on"); - rb_define_private_method(C_Kernel, "syscall", Fsyscall, -1); + rb_define_private_method(cKernel, "syscall", f_syscall, -1); - rb_define_private_method(C_Kernel, "open", Fopen, -1); - rb_define_private_method(C_Kernel, "printf", Fprintf, -1); - rb_define_private_method(C_Kernel, "gets", Fgets, 0); - rb_define_alias(C_Kernel,"readline", "gets"); - rb_define_private_method(C_Kernel, "eof", Feof, 0); - rb_define_private_method(C_Kernel, "getc", Fgetc, 0); - rb_define_private_method(C_Kernel, "select", Fselect, -1); + rb_define_private_method(cKernel, "open", f_open, -1); + rb_define_private_method(cKernel, "printf", f_printf, -1); + rb_define_private_method(cKernel, "print", f_print, -1); + rb_define_private_method(cKernel, "gets", f_gets, 0); + rb_define_alias(cKernel,"readline", "gets"); + rb_define_private_method(cKernel, "eof", f_eof, 0); + rb_define_private_method(cKernel, "getc", f_getc, 0); + rb_define_private_method(cKernel, "select", f_select, -1); - rb_define_private_method(C_Kernel, "readlines", Freadlines, 0); + rb_define_private_method(cKernel, "readlines", f_readlines, 0); - rb_define_method(C_Kernel, "print_on", Fprint_on, 1); - rb_define_method(C_Kernel, "print", Fprint, -1); + rb_define_method(cKernel, "print_on", f_print_on, 1); - C_IO = rb_define_class("IO", C_Object); - rb_include_module(C_IO, M_Enumerable); + cIO = rb_define_class("IO", cObject); + rb_include_module(cIO, mEnumerable); - rb_define_variable("$;", &FS, Qnil, rb_check_str, 0); - rb_define_variable("$,", &OFS, Qnil, rb_check_str, 0); + rb_define_hooked_variable("$;", &FS, 0, rb_str_setter); + rb_define_hooked_variable("$,", &OFS, 0, rb_str_setter); RS = str_new2("\n"); - rb_define_variable("$/", &RS, Qnil, rb_check_str, 0); - rb_define_variable("$\\", &ORS, Qnil, rb_check_str, 0); + rb_define_hooked_variable("$/", &RS, 0, rb_str_setter); + rb_define_hooked_variable("$\\", &ORS, 0, rb_str_setter); + + rb_define_variable("$.", &lineno); + rb_define_variable("$_", &rb_lastline); - rb_define_variable("$.", &lineno, Qnil, Qnil, 0); - rb_define_variable("$_", &rb_lastline, Qnil, Qnil, 0); + rb_define_method(cIO, "print", io_print, -1); - rb_define_method(C_IO, "each", Fio_each, 0); - rb_define_method(C_IO, "each_byte", Fio_each_byte, 0); + rb_define_method(cIO, "each", io_each_line, 0); + rb_define_method(cIO, "each_line", io_each_line, 0); + rb_define_method(cIO, "each_byte", io_each_byte, 0); - rb_define_method(C_IO, "syswrite", Fio_syswrite, 1); - rb_define_method(C_IO, "sysread", Fio_sysread, 1); + rb_define_method(cIO, "syswrite", io_syswrite, 1); + rb_define_method(cIO, "sysread", io_sysread, 1); - rb_define_method(C_IO, "fileno", Fio_fileno, 0); - rb_define_alias(C_IO, "to_i", "fileno"); + rb_define_method(cIO, "fileno", io_fileno, 0); + rb_define_alias(cIO, "to_i", "fileno"); - rb_define_method(C_IO, "sync", Fio_sync, 0); - rb_define_method(C_IO, "sync=", Fio_set_sync, 1); + rb_define_method(cIO, "sync", io_sync, 0); + rb_define_method(cIO, "sync=", io_set_sync, 1); - rb_define_alias(C_IO, "readlines", "to_a"); + rb_define_alias(cIO, "readlines", "to_a"); - rb_define_method(C_IO, "read", Fio_read, -2); - rb_define_method(C_IO, "write", io_write, 1); - rb_define_method(C_IO, "gets", Fio_gets, 0); - rb_define_alias(C_IO, "readline", "gets"); - rb_define_method(C_IO, "getc", Fio_getc, 0); - rb_define_method(C_IO, "puts", Fio_puts, 1); - rb_define_method(C_IO, "<<", Fio_puts, 1); - rb_define_method(C_IO, "flush", Fio_flush, 0); - rb_define_method(C_IO, "eof", Fio_eof, 0); + rb_define_method(cIO, "read", io_read, -2); + rb_define_method(cIO, "write", io_write, 1); + rb_define_method(cIO, "gets", io_gets, 0); + rb_define_alias(cIO, "readline", "gets"); + rb_define_method(cIO, "getc", io_getc, 0); + rb_define_method(cIO, "puts", io_puts, 1); + rb_define_method(cIO, "<<", io_puts, 1); + rb_define_method(cIO, "flush", io_flush, 0); + rb_define_method(cIO, "eof", io_eof, 0); - rb_define_method(C_IO, "close", Fio_close, 0); + rb_define_method(cIO, "close", io_close, 0); + rb_define_method(cIO, "closed?", io_closed, 0); - rb_define_method(C_IO, "isatty", Fio_isatty, 0); - rb_define_method(C_IO, "binmode", Fio_binmode, 0); + rb_define_method(cIO, "isatty", io_isatty, 0); + rb_define_method(cIO, "tty?", io_isatty, 0); + rb_define_method(cIO, "binmode", io_binmode, 0); - rb_define_method(C_IO, "ioctl", Fio_ioctl, 2); + rb_define_method(cIO, "ioctl", io_ioctl, 2); rb_stdin = prep_stdio(stdin, FMODE_READABLE); - rb_define_variable("$stdin", &rb_stdin, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$stdin", &rb_stdin); rb_stdout = prep_stdio(stdout, FMODE_WRITABLE); - rb_define_variable("$stdout", &rb_stdout, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$stdout", &rb_stdout); rb_stderr = prep_stdio(stderr, FMODE_WRITABLE); - rb_define_variable("$stderr", &rb_stderr, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$stderr", &rb_stderr); rb_defout = rb_stdout; - rb_define_variable("$>", &rb_defout, Qnil, io_defset, 0); + rb_define_hooked_variable("$>", &rb_defout, 0, io_defset); - rb_define_const(C_Object, "STDIN", rb_stdin); - rb_define_const(C_Object, "STDOUT", rb_stdout); - rb_define_const(C_Object, "STDERR", rb_stderr); + rb_define_const(cObject, "STDIN", rb_stdin); + rb_define_const(cObject, "STDOUT", rb_stdout); + rb_define_const(cObject, "STDERR", rb_stderr); - argf = obj_alloc(C_Object); - rb_extend_object(argf, M_Enumerable); + argf = obj_alloc(cObject); + rb_extend_object(argf, mEnumerable); - rb_define_variable("$<", &argf, Qnil, rb_readonly_hook, 0); - rb_define_variable("$ARGF", &argf, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$<", &argf); + rb_define_readonly_variable("$ARGF", &argf); - rb_define_single_method(argf, "each", Farg_each, 0); - rb_define_single_method(argf, "each_byte", Farg_each_byte, 0); + rb_define_singleton_method(argf, "each", arg_each_line, 0); + rb_define_singleton_method(argf, "each_line", arg_each_line, 0); + rb_define_singleton_method(argf, "each_byte", arg_each_byte, 0); - rb_define_single_method(argf, "read", Farg_read, 0); - rb_define_single_method(argf, "readlines", Freadlines, 0); - rb_define_single_method(argf, "gets", Fgets, 0); - rb_define_single_method(argf, "readline", Fgets, 0); - rb_define_single_method(argf, "getc", Farg_getc, 0); - rb_define_single_method(argf, "eof", Feof, 0); + rb_define_singleton_method(argf, "read", arg_read, 0); + rb_define_singleton_method(argf, "readlines", f_readlines, 0); + rb_define_singleton_method(argf, "gets", f_gets, 0); + rb_define_singleton_method(argf, "readline", f_gets, 0); + rb_define_singleton_method(argf, "getc", arg_getc, 0); + rb_define_singleton_method(argf, "eof", f_eof, 0); + rb_define_singleton_method(argf, "eof?", f_eof, 0); - rb_define_single_method(argf, "to_s", Farg_filename, 0); - rb_define_single_method(argf, "filename", Farg_filename, 0); - rb_define_single_method(argf, "file", Farg_file, 0); + rb_define_singleton_method(argf, "to_s", arg_filename, 0); + rb_define_singleton_method(argf, "filename", arg_filename, 0); + rb_define_singleton_method(argf, "file", arg_file, 0); filename = str_new2("-"); - rb_define_variable("$FILENAME", &filename, Qnil, rb_readonly_hook, 0); + rb_define_readonly_variable("$FILENAME", &filename); file = rb_stdin; rb_global_variable(&file); @@ -14,6 +14,7 @@ #ifndef IO_H #define IO_H +#include "sig.h" #include <stdio.h> #include <errno.h> @@ -24,6 +25,7 @@ typedef struct { int pid; /* child's pid (for pipes) */ int lineno; /* number of lines read */ char *path; /* pathname for file */ + void (*finalize)(); /* finalize proc */ } OpenFile; #define FMODE_READABLE 1 @@ -35,15 +37,16 @@ extern ID id_fd; #define GetOpenFile(obj,fp) Get_Data_Struct(obj, id_fd, OpenFile, fp) -void io_free_OpenFile(); +void io_fptr_finalize(); -#define MakeOpenFile(obj, fp) {\ - Make_Data_Struct(obj, id_fd, OpenFile, Qnil, io_free_OpenFile, fp);\ +#define MakeOpenFile(obj, fp) do {\ + Make_Data_Struct(obj, id_fd, OpenFile, 0, io_fptr_finalize, fp);\ fp->f = fp->f2 = NULL;\ fp->mode = 0;\ fp->pid = 0;\ fp->lineno = 0;\ fp->path = NULL;\ -} + fp->finalize = 0;\ +} while (0) #endif diff --git a/lib/base64.rb b/lib/base64.rb new file mode 100644 index 0000000000..a6bf1adf92 --- /dev/null +++ b/lib/base64.rb @@ -0,0 +1,55 @@ +def decode64(str) + e = -1; + c = "," + string='' + for line in str.split("\n") + line.sub!(/=+$/, '') + line.tr! 'A-Za-z0-9+/', "\000-\377" + line.each_byte { |ch| + n +=1 + e +=1 + if e==0 + c = ch << 2 + elsif e==1 + c |= ch >>4 + string += [c].pack('c') + c = ch << 4 + elsif e == 2 + c |= ch >> 2 + string += [c].pack('c'); + c = ch << 6 + elsif e==3 + c |= ch + string += [c].pack('c') + e = -1 + end + } + end + return string +end + +def j2e(str) + while str =~ /\033\$B([^\033]*)\033\(B/ + s = $1 + pre, post = $`, $' + s.gsub!(/./) { |ch| + (ch[0]|0x80).chr + } + str = pre + s + post + end +# str.gsub!(/\033\$B([^\033]*)\033\(B/) { +# $1.gsub!(/./) { |ch| +# (ch[0]|0x80).chr +# } +# } + str +end + +def decode_b(str) + str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/) { + decode64($1) + } + str.gsub!(/\n/, ' ') + str.gsub!(/\0/, '') + j2e(str) +end diff --git a/lib/find.rb b/lib/find.rb new file mode 100644 index 0000000000..340461c653 --- /dev/null +++ b/lib/find.rb @@ -0,0 +1,38 @@ +# Usage: +# require "find.rb" +# +# Find.find('/foo','/bar') {|f| ...} +# or +# include Find +# find('/foo','/bar') {|f| ...} +# + +module Find + extend Find + + def findpath(path, ary) + ary.push(path) + d = Dir.open(path) + for f in d + continue if f =~ /^\.\.?$/ + f = path + "/" + f + if File.directory? f + findpath(f, ary) + else + ary.push(f) + end + end + end + private :findpath + + def find(*path) + ary = [] + for p in path + findpath(p, ary) + for f in ary + yield f + end + end + end + module_function :find +end diff --git a/sample/getopts.rb b/lib/getopts.rb index 37fd3dc69d..37fd3dc69d 100644 --- a/sample/getopts.rb +++ b/lib/getopts.rb diff --git a/lib/mailread.rb b/lib/mailread.rb new file mode 100644 index 0000000000..4b04445beb --- /dev/null +++ b/lib/mailread.rb @@ -0,0 +1,45 @@ +class Mail + + def Mail.new(f) + if !f.is_kind_of?(IO) + f = open(f, "r") + me = super + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + continue if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end diff --git a/sample/parsearg.rb b/lib/parsearg.rb index e7e2b7a7f3..e7e2b7a7f3 100644 --- a/sample/parsearg.rb +++ b/lib/parsearg.rb diff --git a/lib/parsedate.rb b/lib/parsedate.rb new file mode 100644 index 0000000000..3f4612ebe5 --- /dev/null +++ b/lib/parsedate.rb @@ -0,0 +1,42 @@ +module ParseDate + MONTHS = { + 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, + 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, + 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12 } + MONTHPAT = MONTHS.keys.join('|') + DAYPAT = 'mon|tue|wed|thu|fri|sat|sun' + + def parsedate(date) + if date.sub!(/(#{DAYPAT})/i, ' ') + dayofweek = $1 + end + if date.sub!(/\s+(\d+:\d+(:\d+)?)/, ' ') + time = $1 + end + if date =~ /19(\d\d)/ + year = $1 + end + if date.sub!(/\s*(\d+)\s+(#{MONTHPAT})\S*\s+/i, ' ') + dayofmonth = $1 + monthname = $2 + elsif date.sub!(/\s*(#{MONTHPAT})\S*\s+(\d+)\s+/i, ' ') + monthname = $1 + dayofmonth = $2 + elsif date.sub!(/\s*(#{MONTHPAT})\S*\s+(\d+)\D+/i, ' ') + monthname = $1 + dayofmonth = $2 + elsif date.sub!(/\s*(\d\d?)\/(\d\d?)/, ' ') + month = $1 + dayofmonth = $2 + end + if monthname + month = MONTHS[monthname.downcase] + end + if ! year && date =~ /\d\d/ + year = $& + end + return year, month, dayofmonth + end + + module_function :parsedate +end diff --git a/lib/tk.rb b/lib/tk.rb new file mode 100644 index 0000000000..9c61269881 --- /dev/null +++ b/lib/tk.rb @@ -0,0 +1,1215 @@ +# +# tk.rb - Tk interface for ruby +# $Date: 1995/11/03 08:17:15 $ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tkutil" + +trap "PIPE", proc{exit 0} +trap "EXIT", proc{Tk.tk_exit} + +module Tk + include TkUtil + extend Tk + + $0 =~ /\/(.*$)/ + + PORT = open(format("|%s -n %s", WISH_PATH, $1), "w+"); + def tk_write(*args) + printf PORT, *args; + PORT.print "\n" + PORT.flush + end + tk_write '\ +wm withdraw . +proc rb_out args { + puts [format %%s $args] + flush stdout +} +proc tkerror args { exit } +proc keepalive {} { rb_out alive; after 120000 keepalive} +after 120000 keepalive' + + READABLE = [] + READ_CMD = {} + + def file_readable(port, cmd) + READABLE.push port + READ_CMD[port] = cmd + end + + WRITABLE = [] + WRITE_CMD = {} + def file_writable + WRITABLE.push port + WRITE_CMD[port] = cmd + end + module_function :file_readable, :file_writable + + file_readable PORT, proc { + exit if not PORT.gets + Tk.dispatch($_.chop!) + } + + def tk_exit + PORT.print "exit\n" + PORT.close + end + + def error_at + n = 1 + while c = caller(n) + break if c !~ /tk\.rb:/ + n+=1 + end + c + end + + def tk_tcl2ruby(val) + case val + when /^-?\d+$/ + val.to_i + when /^\./ + $tk_window_list[val] + when /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + when / / + val.split.collect{|elt| + tk_tcl2ruby(elt) + } + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + + def tk_split_list(str) + idx = str.index('{') + return tk_tcl2ruby(str) if not idx + + list = tk_tcl2ruby(str[0,idx]) + str = str[idx+1..-1] + i = -1 + brace = 1 + str.each_byte {|c| + i += 1 + brace += 1 if c == ?{ + brace -= 1 if c == ?} + break if brace == 0 + } + if str[0, i] == ' ' + list.push ' ' + else + list.push tk_split_list(str[0, i]) + end + list += tk_split_list(str[i+1..-1]) + list + end + private :tk_tcl2ruby, :tk_split_list + + def bool(val) + case bool + when "1", 1, 'yes', 'true' + TRUE + else + FALSE + end + end + def number(val) + case val + when /^-?\d+$/ + val.to_i + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + def string(val) + if val == "{}" + '' + elsif val[0] == ?{ + val[1..-2] + else + val + end + end + def list(val) + tk_split_list(val) + end + def window(val) + $tk_window_list[val] + end + def procedure(val) + if val =~ /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + else + nil + end + end + private :bool, :number, :string, :list, :window, :procedure + + # mark for non-given arguments + None = Object.new + def None.to_s + 'None' + end + + $tk_event_queue = [] + def tk_call(*args) + args = args.collect{|s| + continue if s == None + if s == FALSE + s = "0" + elsif s == TRUE + s = "1" + elsif s.is_kind_of?(TkObject) + s = s.path + else + s = s.to_s + s.gsub!(/[{}]/, '\\\\\0') + end + "{#{s}}" + } + str = args.join(" ") + tk_write 'if [catch {%s} var] {puts "!$var"} {puts "=$var@@"};flush stdout', str + while PORT.gets + $_.chop! + if /^=(.*)@@$/ + val = $1 + break + elsif /^=/ + val = $' + "\n" + while TRUE + PORT.gets + fail 'wish closed' if not $_ + if ~/@@$/ + val += $' + return val + else + val += $_ + end + end + elsif /^!/ + $@ = error_at + msg = $' + if msg =~ /unknown option "-(.*)"/ + fail format("undefined method `%s' for %s(%s)'", $1, self, self.type) + else + fail format("%s - %s", self.type, msg) + end + end + $tk_event_queue.push $_ + end + + while ev = $tk_event_queue.shift + Tk.dispatch ev + end + fail 'wish closed' if not $_ +# tk_split_list(val) + val + end + + def hash_kv(keys) + conf = [] + if keys + for k, v in keys + conf.push("-#{k}") + v = install_cmd(v) if v.type == Proc + conf.push(v) + end + end + conf + end + private :tk_call, :error_at, :hash_kv + + $tk_cmdid = "c00000" + def install_cmd(cmd) + return '' if cmd == '' # uninstall cmd + id = $tk_cmdid + $tk_cmdid = $tk_cmdid.next + $tk_cmdtbl[id] = cmd + @cmdtbl = [] if not @cmdtbl + @cmdtbl.push id + return format('rb_out %s', id) + end + def uninstall_cmd(id) + $tk_cmdtbl[id] = nil + end + private :install_cmd, :uninstall_cmd + + $tk_window_list = {} + class Event + def initialize(seq,b,f,h,k,s,t,w,x,y,aa,ee,kk,nn,ww,tt,xx,yy) + @serial = seq + @num = b + @focus = (f == 1) + @height = h + @keycode = k + @state = s + @time = t + @width = w + @x = x + @y = y + @char = aa + @send_event = (ee == 1) + @keysym = kk + @keysym_num = nn + @type = tt + @widget = ww + @x_root = xx + @y_root = yy + end + attr :serial + attr :num + attr :focus + attr :height + attr :keycode + attr :state + attr :time + attr :width + attr :x + attr :y + attr :char + attr :send_event + attr :keysym + attr :keysym_num + attr :type + attr :widget + attr :x_root + attr :y_root + end + + def install_bind(cmd) + id = install_cmd(proc{|args| + TkUtil.eval_cmd cmd, Event.new(*args) + }) + id + " %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y" + end + + def _bind(path, context, cmd) + begin + id = install_bind(cmd) + tk_call 'bind', path, "<#{context}>", id + rescue + $tk_cmdtbl[id] = nil + fail + end + end + private :install_bind, :_bind + + def bind_all(context, cmd=Proc.new) + _bind 'all', context, cmd + end + + $tk_cmdtbl = {} + + def after(ms, cmd=Proc.new) + myid = $tk_cmdid + tk_call 'after', ms, + install_cmd(proc{ + TkUtil.eval_cmd cmd + uninstall_cmd myid + }) + end + + def update(idle=nil) + if idle + tk_call 'update', 'idletasks' + else + tk_call 'update' + end + end + + def dispatch(line) + if line =~ /^c\d+/ + cmd = $& + fail "no command `#{cmd}'" if not $tk_cmdtbl[cmd] + args = tk_split_list($') + TkUtil.eval_cmd $tk_cmdtbl[cmd], *args + elsif line =~ /^alive$/ + # keep alive, do nothing + else + fail "malformed line <#{line}>" + end + end + + def mainloop + begin + tk_write 'after idle {wm deiconify .}' + while TRUE + rf, wf = select(READABLE, WRITABLE) + for f in rf + READ_CMD[f].call(f) if READ_CMD[f] + if f.closed? + READABLE.delete f + READ_CMD[f] = nil + end + end + for f in wf + WRITE_CMD[f].call(f) if WRITE_CMD[f] + if f.closed? + WRITABLE.delete f + WRITE_CMD[f] = nil + end + end + end + rescue + exit if $! =~ /^Interrupt/ + fail + ensure + tk_exit + end + end + + def root + $tk_root + end + + module_function :after, :update, :dispatch, :mainloop, :root + + module Scrollable + def xscrollcommand(cmd) + configure_cmd 'xscrollcommand', cmd + end + def yscrollcommand(cmd) + configure_cmd 'yscrollcommand', cmd + end + end + + module Wm + def aspect(*args) + w = window(tk_call('wm', 'grid', path, *args)) + w.split.collect{|s|s.to_i} if args.length == 0 + end + def client(name=None) + tk_call 'wm', 'client', path, name + end + def colormapwindows(*args) + list(tk_call('wm', 'colormapwindows', path, *args)) + end + def wm_command(value=None) + string(tk_call('wm', 'command', path, value)) + end + def deiconify + tk_call 'wm', 'deiconify', path + end + def focusmodel(*args) + tk_call 'wm', 'focusmodel', path, *args + end + def frame + tk_call 'wm', 'frame', path + end + def geometry(*args) + list(tk_call('wm', 'geometry', path, *args)) + end + def grid(*args) + w = tk_call('wm', 'grid', path, *args) + list(w) if args.size == 0 + end + def group(*args) + tk_call 'wm', 'path', path, *args + end + def iconbitmap(*args) + tk_call 'wm', 'bitmap', path, *args + end + def iconify + tk_call 'wm', 'iconify' + end + def iconmask(*args) + tk_call 'wm', 'iconmask', path, *args + end + def iconname(*args) + tk_call 'wm', 'iconname', path, *args + end + def iconposition(*args) + w = tk_call('wm', 'iconposition', path, *args) + list(w) if args.size == 0 + end + def iconwindow(*args) + tk_call 'wm', 'iconwindow', path, *args + end + def maxsize(*args) + w = tk_call('wm', 'maxsize', path, *args) + list(w) if not args.size == 0 + end + def minsize(*args) + w = tk_call('wm', 'minsize', path, *args) + list(w) if args.size == 0 + end + def overrideredirect(bool=None) + if bool == None + bool(tk_call('wm', 'overrideredirect', path)) + else + tk_call 'wm', 'overrideredirect', path, bool + end + end + def positionfrom(*args) + tk_call 'wm', 'positionfrom', path, *args + end + def protocol(name, func=None) + func = install_cmd(func) if not func == None + tk_call 'wm', 'command', path, name, func + end + def resizable(*args) + w = tk_call('wm', 'resizable', path, *args) + if args.length == 0 + list(w).collect{|e| bool(e)} + end + end + def sizefrom(*args) + list(tk_call('wm', 'sizefrom', path, *args)) + end + def state + tk_call 'wm', 'state', path + end + def title(*args) + tk_call 'wm', 'title', path, *args + end + def transient(*args) + tk_call 'wm', 'transient', path, *args + end + def withdraw + tk_call 'wm', 'withdraw', path + end + end +end + +module TkSelection + include Tk + extend Tk + def clear(win=Tk.root) + tk_call 'selection', 'clear', win.path + end + def get(type=None) + tk_call 'selection', 'get', type + end + def TkSelection.handle(win, func, type=None, format=None) + id = install_cmd(func) + tk_call 'selection', 'handle', win.path, id, type, format + end + def handle(func, type=None, format=None) + TkSelection.handle self, func, type, format + end + def TkSelection.own(win, func=None) + id = install_cmd(func) + tk_call 'selection', 'own', win.path, id + end + def own(func=None) + TkSelection.own self, func + end + + module_function :clear, :get +end + +module TkWinfo + include Tk + extend Tk + def TkWinfo.atom(name) + tk_call 'winfo', name + end + def winfo_atom(name) + TkWinfo.atom name + end + def TkWinfo.atomname(id) + tk_call 'winfo', id + end + def winfo_atomname(id) + TkWinfo.atomname id + end + def TkWinfo.cells(window) + number(tk_call('winfo', window.path)) + end + def winfo_cells + TkWinfo.cells self + end + def TkWinfo.children(window) + c = tk_call('winfo', 'children', window.path) + list(c) + end + def winfo_children + TkWinfo.children self + end + def TkWinfo.classname(window) + tk_call 'winfo', 'class', window.path + end + def winfo_classname + TkWinfo.classname self + end + def TkWinfo.containing(rootX, rootY) + path = tk_call('winfo', 'class', window.path) + window(path) + end + def winfo_containing(x, y) + TkWinfo.containing x, y + end + def TkWinfo.depth(window) + number(tk_call('winfo', 'depth', window.path)) + end + def winfo_depth(window) + TkWinfo.depth self + end + def TkWinfo.exists(window) + bool(tk_call('winfo', 'exists', window.path)) + end + def winfo_exists(window) + TkWinfo.exists self + end + def TkWinfo.fpixels(window, number) + number(tk_call('winfo', 'fpixels', window.path, number)) + end + def winfo_fpixels(window, number) + TkWinfo.fpixels self + end + def TkWinfo.geometry(window) + list(tk_call('winfo', 'geometry', window.path)) + end + def winfo_geometry(window) + TkWinfo.geometry self + end + def TkWinfo.height(window) + number(tk_call('winfo', 'height', window.path)) + end + def winfo_height(window) + TkWinfo.height self + end + def TkWinfo.id(window) + number(tk_call('winfo', 'id', window.path)) + end + def winfo_id(window) + TkWinfo.id self + end + def TkWinfo.ismapped(window) + bool(tk_call('winfo', 'ismapped', window.path)) + end + def winfo_ismapped(window) + TkWinfo.ismapped self + end + def TkWinfo.parent(window) + window(tk_call('winfo', 'parent', window.path)) + end + def winfo_parent(window) + TkWinfo.parent self + end + def TkWinfo.widget(id) + window(tk_call('winfo', 'pathname', id)) + end + def winfo_widget(id) + TkWinfo.widget id + end + def TkWinfo.pixels(window, number) + number(tk_call('winfo', 'pixels', window.path, number)) + end + def winfo_pixels(window, number) + TkWinfo.pixels self, number + end + def TkWinfo.reqheight(window) + number(tk_call('winfo', 'reqheight', window.path)) + end + def winfo_reqheight(window) + TkWinfo.reqheight self + end + def TkWinfo.reqwidth(window) + number(tk_call('winfo', 'reqwidth', window.path)) + end + def winfo_reqwidth(window) + TkWinfo.reqwidth self + end + def TkWinfo.rgb(window, color) + list(tk_call('winfo', 'rgb', window.path, color)) + end + def winfo_rgb(window, color) + TkWinfo.rgb self, color + end + def TkWinfo.rootx(window) + number(tk_call('winfo', 'rootx', window.path)) + end + def winfo_rootx(window) + TkWinfo.rootx self + end + def TkWinfo.rooty(window) + number(tk_call('winfo', 'rooty', window.path)) + end + def winfo_rooty(window) + TkWinfo.rooty self + end + def TkWinfo.screen(window) + tk_call 'winfo', 'screen', window.path + end + def winfo_screen(window) + TkWinfo.screen self + end + def TkWinfo.screencells(window) + number(tk_call('winfo', 'screencells', window.path)) + end + def winfo_screencells(window) + TkWinfo.screencells self + end + def TkWinfo.screendepth(window) + number(tk_call('winfo', 'screendepth', window.path)) + end + def winfo_screendepth(window) + TkWinfo.screendepth self + end + def TkWinfo.screenheight (window) + number(tk_call('winfo', 'screenheight', window.path)) + end + def winfo_screenheight(window) + TkWinfo.screenheight self + end + def TkWinfo.screenmmheight(window) + number(tk_call('winfo', 'screenmmheight', window.path)) + end + def winfo_screenmmheight(window) + TkWinfo.screenmmheight self + end + def TkWinfo.screenmmwidth(window) + number(tk_call('winfo', 'screenmmwidth', window.path)) + end + def winfo_screenmmwidth(window) + TkWinfo.screenmmwidth self + end + def TkWinfo.screenvisual(window) + tk_call 'winfo', 'screenvisual', window.path + end + def winfo_screenvisual(window) + TkWinfo.screenvisual self + end + def TkWinfo.screenwidth(window) + number(tk_call('winfo', 'screenwidth', window.path)) + end + def winfo_screenwidth(window) + TkWinfo.screenwidth self + end + def TkWinfo.toplevel(window) + window(tk_call('winfo', 'toplevel', window.path)) + end + def winfo_toplevel(window) + TkWinfo.toplevel self + end + def TkWinfo.visual(window) + tk_call 'winfo', 'visual', window.path + end + def winfo_visual(window) + TkWinfo.visual self + end + def TkWinfo.vrootheigh(window) + number(tk_call('winfo', 'vrootheight', window.path)) + end + def winfo_vrootheight(window) + TkWinfo.vrootheight self + end + def TkWinfo.vrootwidth(window) + number(tk_call('winfo', 'vrootwidth', window.path)) + end + def winfo_vrootwidth(window) + TkWinfo.vrootwidth self + end + def TkWinfo.vrootx(window) + number(tk_call('winfo', 'vrootx', window.path)) + end + def winfo_vrootx(window) + TkWinfo.vrootx self + end + def TkWinfo.vrooty(window) + number(tk_call('winfo', 'vrooty', window.path)) + end + def winfo_vrooty(window) + TkWinfo.vrooty self + end + def TkWinfo.width(window) + number(tk_call('winfo', 'width', window.path)) + end + def winfo_width(window) + TkWinfo.width self + end + def TkWinfo.x(window) + number(tk_call('winfo', 'x', window.path)) + end + def winfo_x(window) + TkWinfo.x self + end + def TkWinfo.y(window) + number(tk_call('winfo', 'y', window.path)) + end + def winfo_y(window) + TkWinfo.y self + end +end + +module TkPack + include Tk + extend Tk + def configure(win, *args) + if args[-1].is_kind_of(Hash) + keys = args.pop + end + wins = [win.epath] + for i in args + wins.push i.epath + end + tk_call "pack", 'configure', *(wins+hash_kv(keys)) + end + + def forget(*args) + tk_call 'pack', 'forget' *args + end + + def propagate(master, bool=None) + bool(tk_call('pack', 'propagate', mastaer.epath, bool)) + end + module_function :configure, :forget, :propagate +end + +module TkOption + include Tk + extend Tk + def add pat, value, pri=None + tk_call 'option', 'add', pat, value, pri + end + def clear + tk_call 'option', 'clear' + end + def get win, classname, name + tk_call 'option', 'get', classname, name + end + def readfile file, pri=None + tk_call 'option', 'readfile', file, pri + end + module_function :add, :clear, :get, :readfile +end + +class TkObject:TkKernel + include Tk + + def path + return @path + end + + def epath + return @path + end + + def tk_send(cmd, *rest) + tk_call path, cmd, *rest + end + private :tk_send + + def method_missing(id, *args) + if (args.length == 1) + configure id.id2name, args[0] + else + $@ = error_at + super + end + end + + def []=(id, val) + configure id, val + end + + def configure(slot, value) + if value == FALSE + value = "0" + elsif value.type == Proc + value = install_cmd(value) + end + tk_call path, 'configure', "-#{slot}", value + end + + def configure_cmd(slot, value) + configure slot, install_cmd(value) + end + + def bind(context, cmd=Proc.new) + _bind path, context, cmd + end +end + +class TkWindow:TkObject + $tk_window_id = "w00000" + def initialize(parent=nil, keys=nil) + id = $tk_window_id + $tk_window_id = $tk_window_id.next + if !parent or parent == Tk.root + @path = format(".%s", id); + else + @path = format("%s.%s", parent.path, id) + end + $tk_window_list[@path] = self + create_self + if keys + tk_call @path, 'configure', *hash_kv(keys) + end + end + + def create_self + end + private :create_self + + def pack(keys = nil) + tk_call 'pack', epath, *hash_kv(keys) + self + end + + def unpack(keys = nil) + tk_call 'pack', 'forget', epath + self + end + + def focus + tk_call 'focus', path + self + end + + def grab(*args) + if !args or args.length == 0 + tk_call 'grab', 'set', path + elsif args.length == 1 + case args[0] + when 'global' + tk_call 'grab', 'set', '-global', path + else + val = tk_call('grab', arg[0], path) + end + case args[0] + when 'current' + return window(val) + when 'status' + return val + end + else + fail 'wrong # of args' + end + end + + def lower(below=None) + tk_call 'lower', path, below + self + end + def raise(above=None) + tk_call 'raise', path, above + self + end + + def command(cmd) + configure_cmd 'command', cmd + end + + def colormodel model=None + tk_call 'tk', 'colormodel', path, model + self + end + + def destroy + tk_call 'destroy', path + if @cmdtbl + for id in @cmdtbl + uninstall_cmd id + end + end + $tk_window_list[path] = nil + end +end + +class TkRoot:TkWindow + include Wm + def TkRoot.new + return $tk_root if $tk_root + super + end + def path + "." + end + $tk_root = TkRoot.new + $tk_window_list['.'] = $tk_root +end + +class TkToplevel:TkWindow + include Wm + def initialize(parent=nil, screen=nil, classname=nil) + @screen = screen if screen + @classname = classname if classname + super + end + + def create_self + s = [] + s.push "-screen #@screen" if @screen + s.push "-class #@classname" if @classname + tk_call 'toplevel', path, *s + end +end + +class TkFrame:TkWindow + def create_self + tk_call 'frame', @path + end +end + +class TkLabel:TkWindow + def create_self + tk_call 'label', @path + end + def textvariable(v) + vn = @path + v.id2name + vset = format("global {%s}; set {%s}", vn, vn) + tk_call vset, eval(v.id2name).inspect + trace_var v, proc{|val| + tk_call vset, val.inspect + } + configure 'textvariable', vn + end +end + +class TkButton:TkLabel + def create_self + tk_call 'button', @path + end + def invoke + tk_send 'invoke' + end + def flash + tk_send 'flash' + end +end + +class TkRadioButton:TkButton + def create_self + tk_call 'radiobutton', @path + end + def deselect + tk_send 'deselect' + end + def select + tk_send 'select' + end + def variable(v) + vn = v.id2name; vn =~ /^./ + vn = 'btns_selected_' + $' + trace_var v, proc{|val| + tk_call 'set', vn, val + } + @var_id = install_cmd(proc{|name1,| + val = tk_call('set', name1) + eval(format("%s = '%s'", v.id2name, val)) + }) + tk_call 'trace variable', vn, 'w', @var_id + configure 'variable', vn + end + def destroy + tk_call 'trace vdelete', vn, 'w', @var_id + super + end +end + +class TkCheckButton:TkRadioButton + def create_self + tk_call 'checkbutton', @path + end + def toggle + tk_send 'toggle' + end +end + +class TkMessage:TkLabel + def create_self + tk_call 'message', @path + end +end + +class TkScale:TkWindow + def create_self + tk_call 'scale', path + end + + def get + number(tk_send('get')) + end + + def set(val) + tk_send "set", val + end + + def value + get + end + + def value= (val) + set val + end +end + +class TkScrollbar:TkWindow + def create_self + tk_call 'scrollbar', path + end + + def get + ary1 = tk_send('get', path).split + ary2 = [] + for i in ary1 + push number(i) + end + ary2 + end + + def set(first, last) + tk_send "set", first, last + end +end + +# abstract class for Text and Listbox +class TkTextWin:TkWindow + def bbox(index) + tk_send 'bbox', index + end + def delete(first, last=None) + tk_send 'delete', first, last + end + def get(*index) + tk_send 'get', *index + end + def insert(index, *rest) + tk_send 'insert', index, *rest + end + def index(index) + tk_send 'index', index + end + def insert(index, chars, *args) + tk_send 'insert', index, chars, *args + end + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + def see(index) + tk_send 'see', index + end +end + +class TkListbox:TkTextWin + def create_self + tk_call 'listbox', path + end + + def nearest(y) + tk_send 'nearest', y + end + def selection_anchor(index) + tk_send 'selection', 'anchor', index + end + def selection_clear(first, last=None) + tk_send 'selection', 'clear', first, last + end + def selection_includes + bool(tk_send('selection', 'includes')) + end + def selection_set(first, last=None) + tk_send 'selection', 'set', first, last + end + def xview(cmd, index, *more) + tk_send 'xview', cmd, index, *more + end + def yview(cmd, index, *more) + tk_send 'yview', cmd, index, *more + end +end + +class TkMenu:TkWindow + def create_self + tk_call 'menu', path + end + def activate(index) + tk_send 'activate', index + end + def add(type, keys=nil) + tk_send 'add', type, *kv_hash(keys) + end + def index(index) + tk_send 'index', index + end + def invoke + tk_send 'invoke' + end + def insert(index, type, keys=nil) + tk_send 'add', index, type, *kv_hash(keys) + end + def post(x, y) + tk_send 'post', x, y + end + def postcascade(index) + tk_send 'postcascade', index + end + def postcommand(cmd) + configure_cmd 'postcommand', cmd + end + def menutype(index) + tk_send 'type', index + end + def unpost + tk_send 'unpost' + end + def yposition(index) + number(tk_send('yposition', index)) + end +end + +class TkMenubutton:TkLabel + def create_self + tk_call 'menubutton', path + end +end + +module TkComposite + def initialize(parent=nil, *args) + @frame = TkFrame.new(parent) + @path = @epath = @frame.path + initialize_composite(*args) + end + + def epath + @epath + end + + def initialize_composite(*args) end + private :initialize_composite + + def delegate(option, *wins) + @delegates = {} if not @delegates + @delegates['DEFAULT'] = @frame + if option.is_kind_of? String + @delegates[option] = wins + else + for i in option + @delegates[i] = wins + end + end + end + + def configure(slot, value) + if @delegates and @delegates[slot] + for i in @delegates[slot] + if not i + i = @delegates['DEFALUT'] + redo + else + last = i.configure(slot, value) + end + end + last + else + super + end + end +end + +autoload :TkCanvas, 'tkcanvas' +autoload :TkImage, 'tkcanvas' +autoload :TkBitmapImage, 'tkcanvas' +autoload :TkPhotoImage, 'tkcanvas' +autoload :TkEntry, 'tkentry' +autoload :TkText, 'tktext' diff --git a/lib/tkcanvas.rb b/lib/tkcanvas.rb new file mode 100644 index 0000000000..33b28e3eff --- /dev/null +++ b/lib/tkcanvas.rb @@ -0,0 +1,318 @@ +# +# tkcanvas.rb - Tk canvas classes +# $Date: 1995/11/11 11:17:15 $ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tk" + +class TkCanvas:TkWindow + def create_self + tk_call 'canvas', path + end + def tagid(tag) + if tag.is_kind_of?(TkcItem) + tag.id + else + tag + end + end + private :tagid + def addtag(tag, *args) + tk_send 'addtag', tagid(tag), *args + end + def addtag_above(tagOrId) + addtag('above', tagOrId) + end + def addtag_all + addtag('all') + end + def addtag_below(tagOrId) + addtag('below', tagOrId) + end + def addtag_closest(x, y, halo=None, start=None) + addtag('closest', x, y, halo, start) + end + def addtag_enclosed(x1, y1, x2, y2) + addtag('enclosed', x1, y1, x2, y2) + end + def addtag_overlapping(x1, y1, x2, y2) + addtag('overlapping', x1, y1, x2, y2) + end + def addtag_withtag(tagOrId) + addtag('withtag', tagOrId) + end + def bbox(tag) + list(tk_send('bbox', tagid(tag))) + end + def itembind(tag, seq, cmd=Proc.new) + id = install_cmd(cmd) + tk_send 'bind', tagid(tag), "<#{seq}>", id + @cmdtbl.push id + end + def canvasx(x, *args) + tk_send 'canvasx', x, *args + end + def canvasy(y, *args) + tk_send 'canvasy', y, *args + end + def coords(tag, *args) + tk_send 'coords', tagid(tag), *args + end + def dchars(tag, first, last=None) + tk_send 'dchars', tagid(tag), first, last + end + def delete(*args) + tk_send 'delete', *args + end + alias remove delete + def dtag(tag, tag_to_del=None) + tk_send 'dtag', tagid(tag), tag_to_del + end + def find(*args) + tk_send 'find', *args + end + def itemfocus(tag) + tk_send 'find', tagid(tag) + end + def gettags(tag) + tk_send 'gettags', tagid(tag) + end + def icursor(tag, index) + tk_send 'icursor', tagid(tag), index + end + def index(tag) + tk_send 'index', tagid(tag), index + end + def lower(tag, below=None) + tk_send 'lower', tagid(tag), below + end + def move(tag, x, y) + tk_send 'move', tagid(tag), x, y + end + def postscript(keys=None) + tk_call "pack", *hash_kv(keys) + end + def raise(tag, above=None) + tk_send 'raise', tagid(tag), above + end + def scale(tag, x, y, xs, ys) + tk_send 'scale', tagid(tag), x, y, xs, ys + end + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + def select(*args) + tk_send 'select', *args + end + def xview(index) + tk_send 'xview', index + end + def yview(index) + tk_send 'yview', index + end +end + +class TkcItem:TkObject + def initialize(parent, *args) + if not parent.is_kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = parent.path + if args[-1].type == Hash + keys = args.pop + end + @id = create_self(*args) + if keys + tk_call @path, 'itemconfigure', *hash_kv(keys) + end + end + def create_self(*args) end + private :create_self + def id + return @id + end + + def configure(slot, value) + tk_call path, 'itemconfigure', id, "-#{slot}", value + end + + def addtag(tag) + @c.addtag(tag, 'withtag', @id) + end + def bbox + @c.bbox(@id) + end + def bind(seq, cmd=Proc.new) + @c.itembind @id, seq, cmd + end + def coords(*args) + @c.coords @id, *args + end + def dchars(first, last=None) + @c.dchars @id, first, last + end + def dtag(ttd) + @c.dtag @id, ttd + end + def focus + @c.focus @id + end + def gettags + @c.gettags @id + end + def icursor + @c.icursor @id + end + def index + @c.index @id + end + def insert(beforethis, string) + @c.insert @id, beforethis, string + end + def lower(belowthis=None) + @c.lower @id, belowthis + end + def move(xamount, yamount) + @c.move @id, xamount, yamount + end + def raise(abovethis=None) + @c.raise @id, abovethis + end + def scale(xorigin, yorigin, xscale, yscale) + @c.scale @id, xorigin, yorigin, xscale, yscale + end + def type + @c.type @id + end + def destroy + tk_call path, 'delete', @id + end +end + +class TkcArc:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'arc', *args) + end +end +class TkcBitmap:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'bitmap', *args) + end +end +class TkcImage:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'image', *args) + end +end +class TkcLine:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'line', *args) + end +end +class TkcOval:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'oval', *args) + end +end +class TkcPolygon:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'polygon', *args) + end +end +class TkcText:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'text', *args) + end +end +class TkcWindow:TkcItem + def create_self(*args) + tk_call(@path, 'create', 'window', *args) + end +end +class TkcGroup:TkcItem + $tk_group_id = 'tkg00000' + def create_self(*args) + @id = $tk_group_id + $tk_group_id = $tk_group_id.next + end + + def add(*tags) + for i in tags + i.addtag @id + end + end + def add(*tags) + for i in tags + i.addtag @id + end + end + def delete(*tags) + for i in tags + i.delete @id + end + end + def list + @c.find 'withtag', @id + end + alias remove delete +end + + +class TkImage:TkObject + include Tk + + $tk_image_id = 'i00000' + def initialize(keys=nil) + @path = $tk_image_id + $tk_image_id = $tk_image_id.next + tk_call 'image', @type, @path, *hash_kv(keys) + end + + def height + number(tk_call('image', 'height', @path)) + end + def type + tk_call('image', 'type', @path) + end + def width + number(tk_call('image', 'height', @path)) + end + + def TkImage.names + tk_call('image', 'names', @path).split + end + def TkImage.types + tk_call('image', 'types', @path).split + end +end + +class TkBitmapImage:TkImage + def initialize(*args) + @type = 'bitmap' + super + end +end + +class TkPhotoImage:TkImage + def initialize(*args) + @type = 'bitmap' + super + end + + def blank + tk_send 'blank' + end + def cget + tk_send 'cget' + end + def get(x, y) + tk_send 'get', x, y + end + def put(data, to=None) + tk_send 'put', data, to + end +end diff --git a/lib/tkclass.rb b/lib/tkclass.rb new file mode 100644 index 0000000000..10ecc80b20 --- /dev/null +++ b/lib/tkclass.rb @@ -0,0 +1,36 @@ +# +# tkclass.rb - Tk classes +# $Date: 1995/11/11 19:17:15 $ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tk" + +TopLevel = TkToplevel +Frame = TkFrame +Label = TkLabel +Button = TkButton +Radiobutton = TkRadioButton +Checkbutton = TkCheckButton +Message = TkMessage +Entry = TkEntry +Text = TkText +Scale = TkScale +Scrollbar = TkScrollbar +Listbox = TkListbox +Menu = TkMenu +Menubutton = TkMenubutton +Canvas = TkCanvas +Arc = TkcArc +Bitmap = TkcBitmap +Line = TkcLine +Oval = TkcOval +Polygon = TkcPolygon +TextItem = TkcText +WindowItem = TkcWindow +Selection = TkSelection +Winfo = TkWinfo +Pack = TkPack + +def Mainloop + Tk.mainloop +end diff --git a/lib/tkentry.rb b/lib/tkentry.rb new file mode 100644 index 0000000000..dbd848d0ca --- /dev/null +++ b/lib/tkentry.rb @@ -0,0 +1,74 @@ +# +# tkentry.rb - Tk entry classes +# $Date: 1995/12/07 15:01:10 $ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkEntry:TkLabel + def create_self + tk_call 'entry', @path + end + def scrollcommand(cmd) + configure 'scrollcommand', cmd + end + + def delete(s, e=None) + if e + tk_send 'delete', s + else + tk_send 'delete', s, e + end + end + + def cursor + tk_send 'index', 'insert' + end + def cursor=(index) + tk_send 'icursor', index + end + def index(index) + tk_send 'index', index + end + def insert(text, pos=None) + if pos + tk_send 'icursor', pos + end + tk_send 'insert', 'insert', text + end + def mark(pos) + tk_send 'scan', 'mark', pos + end + def dragto(pos) + tk_send 'scan', 'dragto', pos + end + def select_adjust(index) + tk_send 'select', 'adjust', index + end + def select_clear + tk_send 'select', 'clear', 'end' + end + def select_from(index) + tk_send 'select', 'from', index + end + def select_present() + tk_send('select', 'present') == 1 + end + def select_range(s, e) + tk_send 'select', 'range', s, e + end + def select_to(index) + tk_send 'select', 'to', index + end + def xview(*index) + tk_send 'xview', *index + end + + def value + tk_send 'get' + end + def value= (val) + tk_send 'delete', 0, 'end' + tk_send 'insert', 0, val + end +end diff --git a/lib/tktext.rb b/lib/tktext.rb new file mode 100644 index 0000000000..e7a2be950f --- /dev/null +++ b/lib/tktext.rb @@ -0,0 +1,160 @@ +# +# tktext.rb - Tk text classes +# $Date: 1995/12/07 08:37:10 $ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkText:TkTextWin + include Scrollable + def create_self + tk_call 'text', @path + @tags = {} + end + def index(index) + tk_send 'index', index + end + def value + tk_send 'get', "1.0", "end" + end + def value= (val) + tk_send 'delete', "1.0", 'end' + tk_send 'insert', "1.0", val + end + def _addcmd(cmd) + @cmdtbl.push id + end + def _addtag(cmd) + @cmdtbl.push id + end + private :_addcmd, :_addtag + def tag_names + tk_send('tag', 'names').collect{|elt| + if not @tags[elt] + elt + else + @tags[elt] + end + } + end + def window_names + tk_send('window', 'names').collect{|elt| + if not @tags[elt] + elt + else + @tags[elt] + end + } + end + + def destroy + for t in @tags + t.destroy + end + super + end + + def backspace + self.delete 'insert' + end + + def compare(idx1, op, idx2) + bool(tk_send('compare', idx1, op, idx2)) + end + + def debug + bool(tk_send('debug')) + end + def debug=(boolean) + tk_send 'debug', boolean + end + + def yview(*what) + tk_send 'yview', *what + end + def yview_pickplace(*what) + tk_send 'yview', '-pickplace', *what + end +end + +class TkTextTag:TkObject + $tk_text_tag = 'tag0000' + def initialize(parent) + if not parent.is_kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = parent.path + @id = $tk_text_tag + $tk_text_tag = $tk_text_tag.next + @t._addtag id, self + end + def id + return @id + end + + def add(*index) + tk_call path, 'tag', 'add', @id, *index + end + + def configure(slot, value) + tk_call path, 'tag', 'configure', id, "-#{slot}", value + end + + def bind(seq, cmd=Proc.new) + id = install_cmd(cmd) + tk_call path, 'tag', 'bind', tag, "<#{seq}>", id + @t._addcmd cmd + end + + def lower(below=None) + tk_call path, 'tag', 'lower', below + end + + def destroy + tk_call path, 'tag', 'delete', @id + end +end + +class TkTextMark:TkObject + $tk_text_mark = 'mark0000' + def initialize(parent, index) + if not parent.is_kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = parent.path + @id = $tk_text_mark + $tk_text_mark = $tk_text_mark.next + tk_call @t, 'set', @id, index + @t._addtag id, self + end + def id + return @id + end + + def set(where) + tk_call path, 'mark', 'unset', @id, where + end + + def unset + tk_call path, 'mark', 'unset', @id + end + alias destroy unset +end + +class TkTextWindow:TkObject + def initialize(parent, index, *args) + if not parent.is_kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = parent.path + @index = index + tk_call @path, 'window', 'create', index, *args + end + + def configure(slot, value) + tk_call path, 'window', 'configure', @index, "-#{slot}", value + end +end @@ -8,6 +8,7 @@ ************************************************/ +void main(argc, argv, envp) int argc; char **argv, **envp; @@ -13,7 +13,7 @@ #include "ruby.h" #include <math.h> -VALUE M_Math; +VALUE mMath; VALUE float_new(); #define Need_Float(x) \ @@ -29,7 +29,7 @@ if (FIXNUM_P(x)) {\ } static VALUE -Fmath_atan2(obj, x, y) +math_atan2(obj, x, y) VALUE obj; struct RFloat *x, *y; { @@ -38,7 +38,7 @@ Fmath_atan2(obj, x, y) } static VALUE -Fmath_cos(obj, x) +math_cos(obj, x) VALUE obj; struct RFloat *x; { @@ -48,7 +48,7 @@ Fmath_cos(obj, x) } static VALUE -Fmath_sin(obj, x) +math_sin(obj, x) VALUE obj; struct RFloat *x; { @@ -58,7 +58,7 @@ Fmath_sin(obj, x) } static VALUE -Fmath_tan(obj, x) +math_tan(obj, x) VALUE obj; struct RFloat *x; { @@ -68,7 +68,7 @@ Fmath_tan(obj, x) } static VALUE -Fmath_exp(obj, x) +math_exp(obj, x) VALUE obj; struct RFloat *x; { @@ -77,7 +77,7 @@ Fmath_exp(obj, x) } static VALUE -Fmath_log(obj, x) +math_log(obj, x) VALUE obj; struct RFloat *x; { @@ -86,7 +86,7 @@ Fmath_log(obj, x) } static VALUE -Fmath_log10(obj, x) +math_log10(obj, x) VALUE obj; struct RFloat *x; { @@ -95,7 +95,7 @@ Fmath_log10(obj, x) } static VALUE -Fmath_sqrt(obj, x) +math_sqrt(obj, x) VALUE obj; struct RFloat *x; { @@ -105,30 +105,30 @@ Fmath_sqrt(obj, x) return float_new(sqrt(x->value)); } +void Init_Math() { - M_Math = rb_define_module("Math"); - rb_extend_object(M_Math, M_Math); + mMath = rb_define_module("Math"); #ifdef M_PI - rb_define_const(M_Math, "PI", float_new(M_PI)); + rb_define_const(mMath, "PI", float_new(M_PI)); #else - rb_define_const(M_Math, "PI", float_new(atan(1.0)*4.0)); + rb_define_const(mMath, "PI", float_new(atan(1.0)*4.0)); #endif #ifdef M_E - rb_define_const(M_Math, "E", float_new(M_E)); + rb_define_const(mMath, "E", float_new(M_E)); #else - rb_define_const(M_Math, "E", float_new(exp(1.0))); + rb_define_const(mMath, "E", float_new(exp(1.0))); #endif - rb_define_method(M_Math, "atan2", Fmath_atan2, 2); - rb_define_method(M_Math, "cos", Fmath_cos, 1); - rb_define_method(M_Math, "sin", Fmath_sin, 1); - rb_define_method(M_Math, "tan", Fmath_tan, 1); + rb_define_module_function(mMath, "atan2", math_atan2, 2); + rb_define_module_function(mMath, "cos", math_cos, 1); + rb_define_module_function(mMath, "sin", math_sin, 1); + rb_define_module_function(mMath, "tan", math_tan, 1); - rb_define_method(M_Math, "exp", Fmath_exp, 1); - rb_define_method(M_Math, "log", Fmath_log, 1); - rb_define_method(M_Math, "log10", Fmath_log10, 1); - rb_define_method(M_Math, "sqrt", Fmath_sqrt, 1); + rb_define_module_function(mMath, "exp", math_exp, 1); + rb_define_module_function(mMath, "log", math_log, 1); + rb_define_module_function(mMath, "log10", math_log10, 1); + rb_define_module_function(mMath, "sqrt", math_sqrt, 1); } diff --git a/missing/MANIFEST b/missing/MANIFEST deleted file mode 100644 index 52640c2720..0000000000 --- a/missing/MANIFEST +++ /dev/null @@ -1,10 +0,0 @@ -MANIFEST -alloca.c -memmove.c -mkdir.c -strdup.c -strerror.c -strftime.c -strstr.c -strtol.c -strtoul.c diff --git a/missing/crypt.c b/missing/crypt.c new file mode 100644 index 0000000000..9f9b562c36 --- /dev/null +++ b/missing/crypt.c @@ -0,0 +1,276 @@ +/* From Andy Tanenbaum's book "Computer Networks", + rewritten in C +*/ + +struct block { + unsigned char b_data[64]; +}; + +struct ordering { + unsigned char o_data[64]; +}; + +static struct block key; + +static struct ordering InitialTr = { + 58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4, + 62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8, + 57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3, + 61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7, +}; + +static struct ordering FinalTr = { + 40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31, + 38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29, + 36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27, + 34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25, +}; + +static struct ordering swap = { + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, +}; + +static struct ordering KeyTr1 = { + 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, + 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36, + 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, + 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4, +}; + +static struct ordering KeyTr2 = { + 14,17,11,24, 1, 5, 3,28,15, 6,21,10, + 23,19,12, 4,26, 8,16, 7,27,20,13, 2, + 41,52,31,37,47,55,30,40,51,45,33,48, + 44,49,39,56,34,53,46,42,50,36,29,32, +}; + +static struct ordering etr = { + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9,10,11,12,13,12,13,14,15,16,17, + 16,17,18,19,20,21,20,21,22,23,24,25, + 24,25,26,27,28,29,28,29,30,31,32, 1, +}; + +static struct ordering ptr = { + 16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10, + 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25, +}; + +static unsigned char s_boxes[8][64] = { +{ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, +}, + +{ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, +}, + +{ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, +}, + +{ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, +}, + +{ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, +}, + +{ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, +}, + +{ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, +}, + +{ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, +}, +}; + +static int rots[] = { + 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1, +}; + +static void transpose(struct block *data, struct ordering *t, int n) +{ + struct block x; + + x = *data; + + while (n-- > 0) { + data->b_data[n] = x.b_data[t->o_data[n] - 1]; + } +} + +static void rotate(struct block *key) +{ + register unsigned char *p = key->b_data; + register unsigned char *ep = &(key->b_data[55]); + int data0 = key->b_data[0], data28 = key->b_data[28]; + + while (p++ < ep) *(p-1) = *p; + key->b_data[27] = (char) data0; + key->b_data[55] = (char) data28; +} + +static struct ordering *EP = &etr; + +static void f(int i, struct block *key, struct block *a, struct block *x) +{ + struct block e, ikey, y; + int k; + register unsigned char *p, *q, *r; + + e = *a; + transpose(&e, EP, 48); + for (k = rots[i]; k; k--) rotate(key); + ikey = *key; + transpose(&ikey, &KeyTr2, 48); + p = &(y.b_data[48]); + q = &(e.b_data[48]); + r = &(ikey.b_data[48]); + while (p > y.b_data) { + *--p = *--q ^ *--r; + } + q = x->b_data; + for (k = 0; k < 8; k++) { + register int xb, r; + + r = *p++ << 5; + r += *p++ << 3; + r += *p++ << 2; + r += *p++ << 1; + r += *p++; + r += *p++ << 4; + + xb = s_boxes[k][r]; + + *q++ = (char) (xb >> 3) & 1; + *q++ = (char) (xb>>2) & 1; + *q++ = (char) (xb>>1) & 1; + *q++ = (char) (xb & 1); + } + transpose(x, &ptr, 32); +} + +void definekey(char *k) +{ + + key = *((struct block *) k); + transpose(&key, &KeyTr1, 56); +} + +void encrypt(char *blck, int edflag) +{ + register struct block *p = (struct block *) blck; + register int i; + + transpose(p, &InitialTr, 64); + for (i = 15; i>= 0; i--) { + int j = edflag ? i : 15 - i; + register int k; + struct block b, x; + + b = *p; + for (k = 31; k >= 0; k--) { + p->b_data[k] = b.b_data[k + 32]; + } + f(j, &key, p, &x); + for (k = 31; k >= 0; k--) { + p->b_data[k+32] = b.b_data[k] ^ x.b_data[k]; + } + } + transpose(p, &swap, 64); + transpose(p, &FinalTr, 64); +} + +char *crypt(char *pw, char *salt) +{ + + char pwb[66]; + static char result[16]; + register char *p = pwb; + struct ordering new_etr; + register int i; + + while (*pw && p < &pwb[64]) { + register int j = 7; + + while (j--) { + *p++ = (*pw >> j) & 01; + } + pw++; + *p++ = 0; + } + while (p < &pwb[64]) *p++ = 0; + + definekey(p = pwb); + + while (p < &pwb[66]) *p++ = 0; + + new_etr = etr; + EP = &new_etr; + for (i = 0; i < 2; i++) { + register char c = *salt++; + register int j; + + result[i] = c; + if ( c > 'Z') c -= 6 + 7 + '.'; /* c was a lower case letter */ + else if ( c > '9') c -= 7 + '.';/* c was upper case letter */ + else c -= '.'; /* c was digit, '.' or '/'. */ + /* now, 0 <= c <= 63 */ + for (j = 0; j < 6; j++) { + if ((c >> j) & 01) { + int t = 6*i + j; + int temp = new_etr.o_data[t]; + new_etr.o_data[t] = new_etr.o_data[t+24]; + new_etr.o_data[t+24] = (char) temp; + } + } + } + + if (result[1] == 0) result[1] = result[0]; + + for (i = 0; i < 25; i++) encrypt(pwb,0); + EP = &etr; + + p = pwb; + pw = result+2; + while (p < &pwb[66]) { + register int c = 0; + register int j = 6; + + while (j--) { + c <<= 1; + c |= *p++; + } + c += '.'; /* becomes >= '.' */ + if (c > '9') c += 7; /* not in [./0-9], becomes upper */ + if (c > 'Z') c += 6; /* not in [A-Z], becomes lower */ + *pw++ = (char) c; + } + *pw = 0; + return result; +} diff --git a/missing/dup2.c b/missing/dup2.c new file mode 100644 index 0000000000..c541149d4b --- /dev/null +++ b/missing/dup2.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1991, Larry Wall + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + */ + +#include "defines.h" + +#if defined(HAVE_FCNTL) && defined(F_DUPFD) +# include <fcntl.h> +#endif + +int +dup2(oldfd,newfd) +int oldfd; +int newfd; +{ +#if defined(HAVE_FCNTL) && defined(F_DUPFD) + close(newfd); + return fcntl(oldfd, F_DUPFD, newfd); +#else + int fdtmp[256]; + int fdx = 0; + int fd; + + if (oldfd == newfd) + return 0; + close(newfd); + while ((fd = dup(oldfd)) != newfd) /* good enough for low fd's */ + fdtmp[fdx++] = fd; + while (fdx > 0) + close(fdtmp[--fdx]); + return 0; +#endif +} diff --git a/missing/nt.c b/missing/nt.c new file mode 100644 index 0000000000..e553abbe70 --- /dev/null +++ b/missing/nt.c @@ -0,0 +1,1732 @@ +/*
+ * Copyright (c) 1993, Intergraph Corporation
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the perl README file.
+ *
+ * Various Unix compatibility functions and NT specific functions.
+ *
+ * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
+ *
+ */
+
+#include "ruby.h"
+#include <fcntl.h>
+#include <process.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <assert.h>
+
+bool NtSyncProcess = FALSE;
+extern char **environ;
+
+static bool NtHasRedirection (char *);
+static int valid_filename(char *s);
+
+FILE *fdopen(int, char *);
+
+void
+sleep(unsigned int len)
+{
+ time_t end;
+
+ end = time((time_t *)0) + len;
+ while (time((time_t *)0) < end)
+ ;
+}
+
+//
+// Initialization stuff
+//
+void
+NtInitialize(int *argc, char ***argv) {
+
+ WORD version;
+ WSADATA retdata;
+ int ret;
+
+ //
+ // subvert cmd.exe\'s feeble attempt at command line parsing
+ //
+ *argc = NtMakeCmdVector((char *)GetCommandLine(), argv, TRUE);
+
+ //
+ // Now set up the correct time stuff
+ //
+
+ tzset();
+}
+
+
+char *getlogin()
+{
+ char buffer[200];
+ int len = 200;
+ extern char *NTLoginName;
+
+ if (NTLoginName == NULL) {
+ if (GetUserName(buffer, &len)) {
+ NTLoginName = ALLOC_N(char, len+1);
+ strncpy(NTLoginName, buffer, len);
+ NTLoginName[len] = '\0';
+ }
+ else {
+ NTLoginName = "<Unknown>";
+ }
+ }
+ return NTLoginName;
+}
+
+
+
+// popen stuff
+
+//
+// use these so I can remember which index is which
+//
+
+#define NtPipeRead 0 // index of pipe read descriptor
+#define NtPipeWrite 1 // index of pipe write descriptor
+
+#define NtPipeSize 1024 // size of pipe buffer
+
+#define MYPOPENSIZE 256 // size of book keeping structure
+
+struct {
+ int inuse;
+ int pid;
+ FILE *pipe;
+} MyPopenRecord[MYPOPENSIZE];
+
+FILE *
+mypopen (char *cmd, char *mode)
+{
+ FILE *fp;
+ int saved, reading;
+ int pipemode;
+ int pipes[2];
+ int pid;
+ int slot;
+ static initialized = 0;
+
+ //
+ // if first time through, intialize our book keeping structure
+ //
+
+ if (!initialized++) {
+ for (slot = 0; slot < MYPOPENSIZE; slot++)
+ MyPopenRecord[slot].inuse = FALSE;
+ }
+
+ //
+ // find a free popen slot
+ //
+
+ for (slot = 0; slot < MYPOPENSIZE && MyPopenRecord[slot].inuse; slot++)
+ ;
+
+ if (slot > MYPOPENSIZE) {
+ return NULL;
+ }
+
+ //
+ // Figure out what we\'re doing...
+ //
+
+ reading = (*mode == 'r') ? TRUE : FALSE;
+ pipemode = (*(mode+1) == 'b') ? O_BINARY : O_TEXT;
+
+ //
+ // Now get a pipe
+ //
+
+ if (_pipe(pipes, NtPipeSize, pipemode) == -1) {
+ return NULL;
+ }
+
+ if (reading) {
+
+ //
+ // we\'re reading from the pipe, so we must hook up the
+ // write end of the pipe to the new processes stdout.
+ // To do this we must save our file handle from stdout
+ // by _dup\'ing it, then setting our stdout to be the pipe\'s
+ // write descriptor. We must also make the write handle
+ // inheritable so the new process can use it.
+
+ if ((saved = _dup(fileno(stdout))) == -1) {
+ _close(pipes[NtPipeRead]);
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ if (_dup2 (pipes[NtPipeWrite], fileno(stdout)) == -1) {
+ _close(pipes[NtPipeRead]);
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ }
+ else {
+ //
+ // must be writing to the new process. Do the opposite of
+ // the above, i.e. hook up the processes stdin to the read
+ // end of the pipe.
+ //
+
+ if ((saved = _dup(fileno(stdin))) == -1) {
+ _close(pipes[NtPipeRead]);
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ if (_dup2(pipes[NtPipeRead], fileno(stdin)) == -1) {
+ _close(pipes[NtPipeRead]);
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ }
+
+ //
+ // Start the new process. Must set _fileinfo to non-zero value
+ // for file descriptors to be inherited. Reset after the process
+ // is started.
+ //
+
+ if (NtHasRedirection(cmd)) {
+ docmd:
+ pid = spawnlpe(_P_NOWAIT, "cmd.exe", "/c", cmd, 0, environ);
+ if (pid == -1) {
+ _close(pipes[NtPipeRead]);
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ }
+ else {
+ char **vec;
+ int vecc = NtMakeCmdVector(cmd, &vec, FALSE);
+
+ pid = spawnvpe (_P_NOWAIT, vec[0], vec, environ);
+ if (pid == -1) {
+ goto docmd;
+ }
+ Safefree (vec);
+ }
+
+ if (reading) {
+
+ //
+ // We need to close our instance of the inherited pipe write
+ // handle now that it's been inherited so that it will actually close
+ // when the child process ends.
+ //
+
+ if (_close(pipes[NtPipeWrite]) == -1) {
+ _close(pipes[NtPipeRead]);
+ return NULL;
+ }
+ if (_dup2 (saved, fileno(stdout)) == -1) {
+ _close(pipes[NtPipeRead]);
+ return NULL;
+ }
+ _close(saved);
+
+ //
+ // Now get a stream pointer to return to the calling program.
+ //
+
+ if ((fp = (FILE *) fdopen(pipes[NtPipeRead], mode)) == NULL) {
+ return NULL;
+ }
+ }
+ else {
+
+ //
+ // need to close our read end of the pipe so that it will go
+ // away when the write end is closed.
+ //
+
+ if (_close(pipes[NtPipeRead]) == -1) {
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ if (_dup2 (saved, fileno(stdin)) == -1) {
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ _close(saved);
+
+ //
+ // Now get a stream pointer to return to the calling program.
+ //
+
+ if ((fp = (FILE *) fdopen(pipes[NtPipeWrite], mode)) == NULL) {
+ _close(pipes[NtPipeWrite]);
+ return NULL;
+ }
+ }
+
+ //
+ // do the book keeping
+ //
+
+ MyPopenRecord[slot].inuse = TRUE;
+ MyPopenRecord[slot].pipe = fp;
+ MyPopenRecord[slot].pid = pid;
+
+ return fp;
+}
+
+int
+mypclose(FILE *fp)
+{
+ int i;
+ int exitcode;
+
+ for (i = 0; i < MYPOPENSIZE; i++) {
+ if (MyPopenRecord[i].inuse && MyPopenRecord[i].pipe == fp)
+ break;
+ }
+ if (i >= MYPOPENSIZE) {
+ fprintf(stderr,"Invalid file pointer passed to mypclose!\n");
+ abort();
+ }
+
+ //
+ // get the return status of the process
+ //
+
+ if (_cwait(&exitcode, MyPopenRecord[i].pid, WAIT_CHILD) == -1) {
+ if (errno == ECHILD) {
+ fprintf(stderr, "mypclose: nosuch child as pid %x\n",
+ MyPopenRecord[i].pid);
+ }
+ }
+
+ //
+ // close the pipe
+ //
+
+ fclose(fp);
+
+ //
+ // free this slot
+ //
+
+ MyPopenRecord[i].inuse = FALSE;
+
+ return exitcode;
+}
+
+
+/*
+ * The following code is based on the do_exec and do_aexec functions
+ * in file doio.c
+ */
+
+int
+do_spawn(cmd)
+char *cmd;
+{
+ register char **a;
+ register char *s;
+ char **argv;
+ int status;
+ char *shell, *cmd2;
+ int mode = NtSyncProcess ? P_WAIT : P_NOWAIT;
+
+ /* save an extra exec if possible */
+ if ((shell = getenv("COMSPEC")) == 0)
+ shell = "cmd.exe";
+
+ /* see if there are shell metacharacters in it */
+ if (NtHasRedirection(cmd)) {
+ doshell:
+ return spawnle(mode, shell, shell, "/c", cmd, (char*)0, environ);
+ }
+
+ argv = ALLOC_N(char*, strlen(cmd) / 2 + 2);
+ cmd2 = ALOOC_N(char, strlen(cmd) + 1);
+ strcpy(cmd2, cmd);
+ a = argv;
+ for (s = cmd2; *s;) {
+ while (*s && isspace(*s)) s++;
+ if (*s)
+ *(a++) = s;
+ while (*s && !isspace(*s)) s++;
+ if (*s)
+ *s++ = '\0';
+ }
+ *a = Qnil;
+ if (argv[0]) {
+ if ((status = spawnvpe(mode, argv[0], argv, environ)) == -1) {
+ free(argv);
+ free(cmd2);
+ goto doshell;
+ }
+ }
+ free(cmd2);
+ free(argv);
+ return status;
+}
+
+
+
+typedef struct _NtCmdLineElement {
+ struct _NtCmdLineElement *next, *prev;
+ char *str;
+ int len;
+ int flags;
+} NtCmdLineElement;
+
+//
+// Possible values for flags
+//
+
+#define NTGLOB 0x1 // element contains a wildcard
+#define NTMALLOC 0x2 // string in element was malloc'ed
+#define NTSTRING 0x4 // element contains a quoted string
+
+NtCmdLineElement *NtCmdHead = NULL, *NtCmdTail = NULL;
+
+void
+NtFreeCmdLine(void)
+{
+ NtCmdLineElement *ptr;
+
+ while(NtCmdHead) {
+ ptr = NtCmdHead;
+ NtCmdHead = NtCmdHead->next;
+ free(ptr);
+ }
+ NtCmdHead = NtCmdTail = NULL;
+}
+
+//
+// This function expands wild card characters that were spotted
+// during the parse phase. The idea here is to call FindFirstFile and
+// FindNextFile with the wildcard pattern specified, and splice in the
+// resulting list of new names. If the wildcard pattern doesn\'t match
+// any existing files, just leave it in the list.
+//
+
+void
+NtCmdGlob (NtCmdLineElement *patt)
+{
+ WIN32_FIND_DATA fd;
+ HANDLE fh;
+ char buffer[512];
+ NtCmdLineElement *tmphead, *tmptail, *tmpcurr;
+
+ strncpy(buffer, patt->str, patt->len);
+ buffer[patt->len] = '\0';
+ if ((fh = FindFirstFile (buffer, &fd)) == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ tmphead = tmptail = NULL;
+ do {
+ tmpcurr = ALLOC(NtCmdLineElement);
+ if (tmpcurr == NULL) {
+ fprintf(stderr, "Out of Memory in globbing!\n");
+ while (tmphead) {
+ tmpcurr = tmphead;
+ tmphead = tmphead->next;
+ free(tmpcurr->str);
+ free(tmpcurr);
+ }
+ return;
+ }
+ memset (tmpcurr, 0, sizeof(*tmpcurr));
+ tmpcurr->len = strlen(fd.cFileName);
+ tmpcurr->str = ALLOC_N(char, tmpcurr->len+1);
+ if (tmpcurr->str == NULL) {
+ fprintf(stderr, "Out of Memory in globbing!\n");
+ while (tmphead) {
+ tmpcurr = tmphead;
+ tmphead = tmphead->next;
+ free(tmpcurr->str);
+ free(tmpcurr);
+ }
+ return;
+ }
+ strcpy(tmpcurr->str, fd.cFileName);
+ tmpcurr->flags |= NTMALLOC;
+ if (tmptail) {
+ tmptail->next = tmpcurr;
+ tmpcurr->prev = tmptail;
+ tmptail = tmpcurr;
+ }
+ else {
+ tmptail = tmphead = tmpcurr;
+ }
+ } while(FindNextFile(fh, &fd));
+
+ //
+ // ok, now we\'ve got a list of files that matched the wildcard
+ // specification. Put it in place of the pattern structure.
+ //
+
+ tmphead->prev = patt->prev;
+ tmptail->next = patt->next;
+
+ if (tmphead->prev)
+ tmphead->prev->next = tmphead;
+
+ if (tmptail->next)
+ tmptail->next->prev = tmptail;
+
+ //
+ // Now get rid of the pattern structure
+ //
+
+ if (patt->flags & NTMALLOC)
+ free(patt->str);
+ free(patt);
+}
+
+//
+// Check a command string to determine if it has I/O redirection
+// characters that require it to be executed by a command interpreter
+//
+
+static bool
+NtHasRedirection (char *cmd)
+{
+ int inquote = 0;
+ char quote = '\0';
+ char *ptr ;
+
+ //
+ // Scan the string, looking for redirection (< or >) or pipe
+ // characters (|) that are not in a quoted string
+ //
+
+ for (ptr = cmd; *ptr; ptr++) {
+
+ switch (*ptr) {
+
+ case '\'':
+ case '\"':
+ if (inquote) {
+ if (quote == *ptr) {
+ inquote = 0;
+ quote = '\0';
+ }
+ }
+ else {
+ quote = *ptr;
+ inquote++;
+ }
+ break;
+
+ case '>':
+ case '<':
+
+ if (!inquote)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+int
+NtMakeCmdVector (char *cmdline, char ***vec, int InputCmd)
+{
+ int cmdlen = strlen(cmdline);
+ int done, instring, globbing, quoted, len;
+ int newline, need_free = 0, i;
+ int elements, strsz;
+ int slashes = 0;
+ char *ptr, *base, *buffer;
+ char **vptr;
+ char quote;
+ NtCmdLineElement *curr;
+
+ //
+ // just return if we don\'t have a command line
+ //
+
+ if (cmdlen == 0) {
+ *vec = NULL;
+ return 0;
+ }
+
+ //
+ // strip trailing white space
+ //
+
+ ptr = cmdline+(cmdlen - 1);
+ while(ptr >= cmdline && isspace(*ptr))
+ --ptr;
+ *++ptr = '\0';
+
+ //
+ // check for newlines and formfeeds. If we find any, make a new
+ // command string that replaces them with escaped sequences (\n or \f)
+ //
+
+ for (ptr = cmdline, newline = 0; *ptr; ptr++) {
+ if (*ptr == '\n' || *ptr == '\f')
+ newline++;
+ }
+
+ if (newline) {
+ base = ALLOC_N(char, strlen(cmdline) + 1 + newline + slashes);
+ if (base == NULL) {
+ fprintf(stderr, "malloc failed!\n");
+ return 0;
+ }
+ for (i = 0, ptr = base; (unsigned) i < strlen(cmdline); i++) {
+ switch (cmdline[i]) {
+ case '\n':
+ *ptr++ = '\\';
+ *ptr++ = 'n';
+ break;
+ default:
+ *ptr++ = cmdline[i];
+ }
+ }
+ *ptr = '\0';
+ cmdline = base;
+ need_free++;
+ }
+
+ //
+ // Ok, parse the command line, building a list of CmdLineElements.
+ // When we\'ve finished, and it\'s an input command (meaning that it\'s
+ // the processes argv), we\'ll do globing and then build the argument
+ // vector.
+ // The outer loop does one interation for each element seen.
+ // The inner loop does one interation for each character in the element.
+ //
+
+ for (done = 0, ptr = cmdline; *ptr;) {
+
+ //
+ // zap any leading whitespace
+ //
+
+ while(isspace(*ptr))
+ ptr++;
+ base = ptr;
+
+ for (done = newline = globbing = instring = quoted = 0;
+ *ptr && !done; ptr++) {
+
+ //
+ // Switch on the current character. We only care about the
+ // white-space characters, the wild-card characters, and the
+ // quote characters.
+ //
+
+ switch (*ptr) {
+ case ' ':
+ case '\t':
+#if 0
+ case '/': // have to do this for NT/DOS option strings
+
+ //
+ // check to see if we\'re parsing an option switch
+ //
+
+ if (*ptr == '/' && base == ptr)
+ continue;
+#endif
+ //
+ // if we\'re not in a string, then we\'re finished with this
+ // element
+ //
+
+ if (!instring)
+ done++;
+ break;
+
+ case '*':
+ case '?':
+
+ //
+ // record the fact that this element has a wildcard character
+ // N.B. Don\'t glob if inside a single quoted string
+ //
+
+ if (!(instring && quote == '\''))
+ globbing++;
+ break;
+
+ case '\n':
+
+ //
+ // If this string contains a newline, mark it as such so
+ // we can replace it with the two character sequence "\n"
+ // (cmd.exe doesn\'t like raw newlines in strings...sigh).
+ //
+
+ newline++;
+ break;
+
+ case '\'':
+ case '\"':
+
+ //
+ // if we\'re already in a string, see if this is the
+ // terminating close-quote. If it is, we\'re finished with
+ // the string, but not neccessarily with the element.
+ // If we\'re not already in a string, start one.
+ //
+
+ if (instring) {
+ if (quote == *ptr) {
+ instring = 0;
+ quote = '\0';
+ }
+ }
+ else {
+ instring++;
+ quote = *ptr;
+ quoted++;
+ }
+ break;
+ }
+ }
+
+ //
+ // need to back up ptr by one due to last increment of for loop
+ // (if we got out by seeing white space)
+ //
+
+ if (*ptr)
+ ptr--;
+
+ //
+ // when we get here, we\'ve got a pair of pointers to the element,
+ // base and ptr. Base points to the start of the element while ptr
+ // points to the character following the element.
+ //
+
+ curr = ALLOC(NtCmdLineElement);
+ if (curr == NULL) {
+ NtFreeCmdLine();
+ fprintf(stderr, "Out of memory!!\n");
+ *vec = NULL;
+ return 0;
+ }
+ memset (curr, 0, sizeof(*curr));
+
+ len = ptr - base;
+
+ //
+ // if it\'s an input vector element and it\'s enclosed by quotes,
+ // we can remove them.
+ //
+
+ if (InputCmd &&
+ ((base[0] == '\"' && base[len-1] == '\"') ||
+ (base[0] == '\'' && base[len-1] == '\''))) {
+ base++;
+ len -= 2;
+ }
+
+ curr->str = base;
+ curr->len = len;
+ curr->flags |= (globbing ? NTGLOB : 0);
+
+ //
+ // Now put it in the list of elements
+ //
+ if (NtCmdTail) {
+ NtCmdTail->next = curr;
+ curr->prev = NtCmdTail;
+ NtCmdTail = curr;
+ }
+ else {
+ NtCmdHead = NtCmdTail = curr;
+ }
+ }
+
+ if (InputCmd) {
+
+ //
+ // When we get here we\'ve finished parsing the command line. Now
+ // we need to run the list, expanding any globbing patterns.
+ //
+
+ for(curr = NtCmdHead; curr; curr = curr->next) {
+ if (curr->flags & NTGLOB) {
+ NtCmdGlob(curr);
+ }
+ }
+ }
+
+ //
+ // Almost done!
+ // Count up the elements, then allocate space for a vector of pointers
+ // (argv) and a string table for the elements.
+ //
+
+ for (elements = 0, strsz = 0, curr = NtCmdHead; curr; curr = curr->next) {
+ elements++;
+ strsz += (curr->len + 1);
+ }
+
+ len = (elements+1)*sizeof(char *) + strsz;
+ buffer = ALLOC_N(char, len);
+ if (buffer == NULL) {
+ fprintf(stderr, "Out of memory!!\n");
+ NtFreeCmdLine();
+ *vec = NULL;
+ return 0;
+ }
+
+ memset (buffer, 0, len);
+
+ //
+ // make vptr point to the start of the buffer
+ // and ptr point to the area we\'ll consider the string table.
+ //
+
+ vptr = (char **) buffer;
+
+ ptr = buffer + (elements+1) * sizeof(char *);
+
+ for (curr = NtCmdHead; curr; curr = curr->next) {
+ strncpy (ptr, curr->str, curr->len);
+ ptr[curr->len] = '\0';
+ *vptr++ = ptr;
+ ptr += curr->len + 1;
+ }
+ NtFreeCmdLine();
+ *vec = (char **) buffer;
+ return elements;
+}
+
+
+//
+// UNIX compatible directory access functions for NT
+//
+
+//
+// File names are converted to lowercase if the
+// CONVERT_TO_LOWER_CASE variable is defined.
+//
+
+#define CONVERT_TO_LOWER_CASE
+#define PATHLEN 1024
+
+//
+// The idea here is to read all the directory names into a string table
+// (separated by nulls) and when one of the other dir functions is called
+// return the pointer to the current file name.
+//
+
+DIR *
+opendir(char *filename)
+{
+ DIR *p;
+ long len;
+ long idx;
+ char scannamespc[PATHLEN];
+ char *scanname = scannamespc;
+ struct stat sbuf;
+ WIN32_FIND_DATA FindData;
+ HANDLE fh;
+ char root[PATHLEN];
+ char volname[PATHLEN];
+ DWORD serial, maxname, flags;
+ BOOL downcase;
+ char *dummy;
+
+ //
+ // check to see if we\'ve got a directory
+ //
+
+ if (stat (filename, &sbuf) < 0 ||
+ sbuf.st_mode & _S_IFDIR == 0) {
+ return NULL;
+ }
+
+ //
+ // check out the file system characteristics
+ //
+ if (GetFullPathName(filename, PATHLEN, root, &dummy)) {
+ if (dummy = strchr(root, '\\'))
+ *++dummy = '\0';
+ if (GetVolumeInformation(root, volname, PATHLEN,
+ &serial, &maxname, &flags, 0, 0)) {
+ downcase = !(flags & FS_CASE_SENSITIVE);
+ }
+ }
+ else {
+ downcase = TRUE;
+ }
+
+ //
+ // Get us a DIR structure
+ //
+
+ p = xcalloc(sizeof(DIR), 1);
+ if (p == NULL)
+ return NULL;
+
+ //
+ // Create the search pattern
+ //
+
+ strcpy(scanname, filename);
+
+ if (index("/\\", *(scanname + strlen(scanname) - 1)) == NULL)
+ strcat(scanname, "/*");
+ else
+ strcat(scanname, "*");
+
+ //
+ // do the FindFirstFile call
+ //
+
+ fh = FindFirstFile (scanname, &FindData);
+ if (fh == INVALID_HANDLE_VALUE) {
+ return NULL;
+ }
+
+ //
+ // now allocate the first part of the string table for the
+ // filenames that we find.
+ //
+
+ idx = strlen(FindData.cFileName)+1;
+ p->start = ALLOC_N(char, idx);
+ strcpy (p->start, FindData.cFileName);
+ if (downcase)
+ strlwr(p->start);
+ p->nfiles++;
+
+ //
+ // loop finding all the files that match the wildcard
+ // (which should be all of them in this directory!).
+ // the variable idx should point one past the null terminator
+ // of the previous string found.
+ //
+ while (FindNextFile(fh, &FindData)) {
+ len = strlen (FindData.cFileName);
+
+ //
+ // bump the string table size by enough for the
+ // new name and it's null terminator
+ //
+
+ Renew (p->start, idx+len+1, char);
+ if (p->start == NULL) {
+ fatal ("opendir: malloc failed!\n");
+ }
+ strcpy(&p->start[idx], FindData.cFileName);
+ if (downcase)
+ strlwr(&p->start[idx]);
+ p->nfiles++;
+ idx += len+1;
+ }
+ FindClose(fh);
+ p->size = idx;
+ p->curr = p->start;
+ return p;
+}
+
+
+//
+// Readdir just returns the current string pointer and bumps the
+// string pointer to the next entry.
+//
+
+struct direct *
+readdir(DIR *dirp)
+{
+ int len;
+ static int dummy = 0;
+
+ if (dirp->curr) {
+
+ //
+ // first set up the structure to return
+ //
+
+ len = strlen(dirp->curr);
+ strcpy(dirp->dirstr.d_name, dirp->curr);
+ dirp->dirstr.d_namlen = len;
+
+ //
+ // Fake inode
+ //
+ dirp->dirstr.d_ino = dummy++;
+
+ //
+ // Now set up for the next call to readdir
+ //
+
+ dirp->curr += len + 1;
+ if (dirp->curr >= (dirp->start + dirp->size)) {
+ dirp->curr = NULL;
+ }
+
+ return &(dirp->dirstr);
+
+ } else
+ return NULL;
+}
+
+//
+// Telldir returns the current string pointer position
+//
+
+long
+telldir(DIR *dirp)
+{
+ return (long) dirp->curr; /* ouch! pointer to long cast */
+}
+
+//
+// Seekdir moves the string pointer to a previously saved position
+// (Saved by telldir).
+
+void
+seekdir(DIR *dirp, long loc)
+{
+ dirp->curr = (char *) loc; /* ouch! long to pointer cast */
+}
+
+//
+// Rewinddir resets the string pointer to the start
+//
+
+void
+rewinddir(DIR *dirp)
+{
+ dirp->curr = dirp->start;
+}
+
+//
+// This just free\'s the memory allocated by opendir
+//
+
+void
+closedir(DIR *dirp)
+{
+ free(dirp->start);
+ free(dirp);
+}
+
+
+
+//
+// 98.2% of this code was lifted from the OS2 port. (JCW)
+//
+
+/*
+ * Suffix appending for in-place editing under MS-DOS and OS/2 (and now NT!).
+ *
+ * Here are the rules:
+ *
+ * Style 0: Append the suffix exactly as standard perl would do it.
+ * If the filesystem groks it, use it. (HPFS will always
+ * grok it. So will NTFS. FAT will rarely accept it.)
+ *
+ * Style 1: The suffix begins with a '.'. The extension is replaced.
+ * If the name matches the original name, use the fallback method.
+ *
+ * Style 2: The suffix is a single character, not a '.'. Try to add the
+ * suffix to the following places, using the first one that works.
+ * [1] Append to extension.
+ * [2] Append to filename,
+ * [3] Replace end of extension,
+ * [4] Replace end of filename.
+ * If the name matches the original name, use the fallback method.
+ *
+ * Style 3: Any other case: Ignore the suffix completely and use the
+ * fallback method.
+ *
+ * Fallback method: Change the extension to ".$$$". If that matches the
+ * original name, then change the extension to ".~~~".
+ *
+ * If filename is more than 1000 characters long, we die a horrible
+ * death. Sorry.
+ *
+ * The filename restriction is a cheat so that we can use buf[] to store
+ * assorted temporary goo.
+ *
+ * Examples, assuming style 0 failed.
+ *
+ * suffix = ".bak" (style 1)
+ * foo.bar => foo.bak
+ * foo.bak => foo.$$$ (fallback)
+ * foo.$$$ => foo.~~~ (fallback)
+ * makefile => makefile.bak
+ *
+ * suffix = "~" (style 2)
+ * foo.c => foo.c~
+ * foo.c~ => foo.c~~
+ * foo.c~~ => foo~.c~~
+ * foo~.c~~ => foo~~.c~~
+ * foo~~~~~.c~~ => foo~~~~~.$$$ (fallback)
+ *
+ * foo.pas => foo~.pas
+ * makefile => makefile.~
+ * longname.fil => longname.fi~
+ * longname.fi~ => longnam~.fi~
+ * longnam~.fi~ => longnam~.$$$
+ *
+ */
+
+
+static char suffix1[] = ".$$$";
+static char suffix2[] = ".~~~";
+
+#define ext (&buf[1000])
+
+#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
+
+void
+add_suffix(struct RString *str, char *suffix)
+{
+ int baselen;
+ int extlen = strlen(suffix);
+ char *s, *t, *p;
+ int slen;
+ char buf[1024];
+
+ if (str->len > 1000)
+ Fatal("Cannot do inplace edit on long filename (%d characters)", str->len);
+
+ /* Style 0 */
+ slen = str->len;
+ str_cat(str, suffix, extlen);
+ if (valid_filename(str->ptr)) return;
+
+ /* Fooey, style 0 failed. Fix str before continuing. */
+ str->ptr[str->len = slen] = '\0';
+
+ slen = extlen;
+ t = buf; baselen = 0; s = str->ptr;
+ while ( (*t = *s) && *s != '.') {
+ baselen++;
+ if (*s == '\\' || *s == '/') baselen = 0;
+ s++; t++;
+ }
+ p = t;
+
+ t = ext; extlen = 0;
+ while (*t++ = *s++) extlen++;
+ if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; }
+
+ if (*suffix == '.') { /* Style 1 */
+ if (strEQ(ext, suffix)) goto fallback;
+ strcpy(p, suffix);
+ } else if (suffix[1] == '\0') { /* Style 2 */
+ if (extlen < 4) {
+ ext[extlen] = *suffix;
+ ext[++extlen] = '\0';
+ } else if (baselen < 8) {
+ *p++ = *suffix;
+ } else if (ext[3] != *suffix) {
+ ext[3] = *suffix;
+ } else if (buf[7] != *suffix) {
+ buf[7] = *suffix;
+ } else goto fallback;
+ strcpy(p, ext);
+ } else { /* Style 3: Panic */
+fallback:
+ (void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5);
+ }
+ str_grow(str, strlen(buf));
+ memcpy(str->ptr, buf, str->len);
+}
+
+static int
+valid_filename(char *s)
+{
+ int fd;
+
+ //
+ // if the file exists, then it\'s a valid filename!
+ //
+
+ if (_access(s, 0) == 0) {
+ return 1;
+ }
+
+ //
+ // It doesn\'t exist, so see if we can open it.
+ //
+
+ if ((fd = _open(s, _O_CREAT, 0666)) >= 0) {
+ close(fd);
+ _unlink (s); // don\'t leave it laying around
+ return 1;
+ }
+ return 0;
+}
+
+
+//
+// This is a clone of fdopen so that we can handle the
+// brain damaged version of sockets that NT gets to use.
+//
+// The problem is that sockets are not real file handles and
+// cannot be fdopen\'ed. This causes problems in the do_socket
+// routine in doio.c, since it tries to create two file pointers
+// for the socket just created. We\'ll fake out an fdopen and see
+// if we can prevent perl from trying to do stdio on sockets.
+//
+
+FILE *
+fdopen (int fd, char *mode)
+{
+ FILE *fp;
+ char sockbuf[80];
+ int optlen;
+ int retval;
+ extern int errno;
+
+ retval = getsockopt((SOCKET)fd, SOL_SOCKET, SO_TYPE, sockbuf, &optlen);
+ if (retval == SOCKET_ERROR && WSAGetLastError() == WSAENOTSOCK) {
+ return (_fdopen(fd, mode));
+ }
+
+ //
+ // If we get here, then fd is actually a socket.
+ //
+ fp = xcalloc(sizeof(FILE), 1);
+#if _MSC_VER < 800
+ fileno(fp) = fd;
+#else
+ fp->_file = fd;
+#endif
+ if (*mode = 'r')
+ fp->_flag = _IOREAD;
+ else
+ fp->_flag = _IOWRT;
+ return fp;
+}
+
+
+//
+// Since the errors returned by the socket error function
+// WSAGetLastError() are not known by the library routine strerror
+// we have to roll our own.
+//
+
+#undef strerror
+
+char *
+mystrerror(int e)
+{
+ static char buffer[512];
+ extern int sys_nerr;
+ DWORD source = 0;
+
+ if (e < 0 || e > sys_nerr) {
+ if (e < 0)
+ e = GetLastError();
+ if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
+ buffer, 512, NULL) == 0) {
+ strcpy (buffer, "Unknown Error");
+ }
+ return buffer;
+ }
+ return strerror(e);
+
+}
+
+//
+// various stubs
+//
+
+
+// Ownership
+//
+// Just pretend that everyone is a superuser. NT will let us know if
+// we don\'t really have permission to do something.
+//
+
+#define ROOT_UID 0
+#define ROOT_GID 0
+
+UIDTYPE
+getuid(void)
+{
+ return ROOT_UID;
+}
+
+UIDTYPE
+geteuid(void)
+{
+ return ROOT_UID;
+}
+
+GIDTYPE
+getgid(void)
+{
+ return ROOT_GID;
+}
+
+GIDTYPE
+getegid(void)
+{
+ return ROOT_GID;
+}
+
+int
+setuid(int uid)
+{
+ return (uid == ROOT_UID ? 0 : -1);
+}
+
+int
+setgid(int gid)
+{
+ return (gid == ROOT_GID ? 0 : -1);
+}
+
+//
+// File system stuff
+//
+
+int
+ioctl(int i, unsigned int u, char *data)
+{
+ return -1;
+}
+
+
+//
+// Networking trampolines
+// These are used to avoid socket startup/shutdown overhead in case
+// the socket routines aren\'t used.
+//
+
+#undef select
+
+static int NtSocketsInitialized = 0;
+
+long
+myselect (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
+ struct timeval *timeout)
+{
+ long r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = select (nfds, rd, wr, ex, timeout)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+static void
+StartSockets () {
+ WORD version;
+ WSADATA retdata;
+ int ret;
+
+ //
+ // initalize the winsock interface and insure that it\'s
+ // cleaned up at exit.
+ //
+ version = MAKEWORD(1, 1);
+ if (ret = WSAStartup(version, &retdata))
+ fatal ("Unable to locate winsock library!\n");
+ if (LOBYTE(retdata.wVersion) != 1)
+ fatal("could not find version 1 of winsock dll\n");
+
+ if (HIBYTE(retdata.wVersion) != 1)
+ fatal("could not find version 1 of winsock dll\n");
+
+ atexit((void (*)(void)) WSACleanup);
+}
+
+#undef accept
+
+SOCKET
+myaccept (SOCKET s, struct sockaddr *addr, int *addrlen)
+{
+ SOCKET r;
+
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = accept (s, addr, addrlen)) == INVALID_SOCKET)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef bind
+
+int
+mybind (SOCKET s, struct sockaddr *addr, int addrlen)
+{
+ int r;
+
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = bind (s, addr, addrlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef connect
+
+int
+myconnect (SOCKET s, struct sockaddr *addr, int addrlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = connect (s, addr, addrlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+
+#undef getpeername
+
+int
+mygetpeername (SOCKET s, struct sockaddr *addr, int *addrlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getpeername (s, addr, addrlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getsockname
+
+int
+mygetsockname (SOCKET s, struct sockaddr *addr, int *addrlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getsockname (s, addr, addrlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getsockopt
+
+int
+mygetsockopt (SOCKET s, int level, int optname, char *optval, int *optlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getsockopt (s, level, optname, optval, optlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef ioctlsocket
+
+int
+myioctlsocket (SOCKET s, long cmd, u_long *argp)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = ioctlsocket (s, cmd, argp)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef listen
+
+int
+mylisten (SOCKET s, int backlog)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = listen (s, backlog)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef recv
+
+int
+myrecv (SOCKET s, char *buf, int len, int flags)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = recv (s, buf, len, flags)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef recvfrom
+
+int
+myrecvfrom (SOCKET s, char *buf, int len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = recvfrom (s, buf, len, flags, from, fromlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef send
+
+int
+mysend (SOCKET s, char *buf, int len, int flags)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = send (s, buf, len, flags)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef sendto
+
+int
+mysendto (SOCKET s, char *buf, int len, int flags,
+ struct sockaddr *to, int tolen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = sendto (s, buf, len, flags, to, tolen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef setsockopt
+
+int
+mysetsockopt (SOCKET s, int level, int optname, char *optval, int optlen)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = setsockopt (s, level, optname, optval, optlen)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef shutdown
+
+int
+myshutdown (SOCKET s, int how)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = shutdown (s, how)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef socket
+
+SOCKET
+mysocket (int af, int type, int protocol)
+{
+ SOCKET s;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((s = socket (af, type, protocol)) == INVALID_SOCKET)
+ errno = WSAGetLastError();
+ return s;
+}
+
+#undef gethostbyaddr
+
+struct hostent *
+mygethostbyaddr (char *addr, int len, int type)
+{
+ struct hostent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = gethostbyaddr (addr, len, type)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef gethostbyname
+
+struct hostent *
+mygethostbyname (char *name)
+{
+ struct hostent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = gethostbyname (name)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef gethostname
+
+int
+mygethostname (char *name, int len)
+{
+ int r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = gethostname (name, len)) == SOCKET_ERROR)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getprotobyname
+
+struct protoent *
+mygetprotobyname (char *name)
+{
+ struct protoent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getprotobyname (name)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getprotobynumber
+
+struct protoent *
+mygetprotobynumber (int num)
+{
+ struct protoent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getprotobynumber (num)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getservbyname
+
+struct servent *
+mygetservbyname (char *name, char *proto)
+{
+ struct servent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getservbyname (name, proto)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+#undef getservbyport
+
+struct servent *
+mygetservbyport (int port, char *proto)
+{
+ struct servent *r;
+ if (!NtSocketsInitialized++) {
+ StartSockets();
+ }
+ if ((r = getservbyport (port, proto)) == NULL)
+ errno = WSAGetLastError();
+ return r;
+}
+
+//
+// Networking stubs
+//
+
+void endhostent() {}
+void endnetent() {}
+void endprotoent() {}
+void endservent() {}
+
+struct netent *getnetent (void) {return (struct netent *) NULL;}
+
+struct netent *getnetbyaddr(char *name) {return (struct netent *)NULL;}
+
+struct netent *getnetbyname(long net, int type) {return (struct netent *)NULL;}
+
+struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
+
+struct servent *getservent (void) {return (struct servent *) NULL;}
+
+void sethostent (int stayopen) {}
+
+void setnetent (int stayopen) {}
+
+void setprotoent (int stayopen) {}
+
+void setservent (int stayopen) {}
+
+
+pid_t
+waitpid (pid_t pid, int *stat_loc, int options)
+{
+ DWORD timeout;
+
+ if (options == WNOHANG) {
+ timeout = 0;
+ } else {
+ timeout = INFINITE;
+ }
+ if (WaitForSingleObject((HANDLE) pid, timeout) == WAIT_OBJECT_0) {
+ pid = _cwait(stat_loc, pid, 0);
+ return pid;
+ }
+ return 0;
+}
+
+#include <sys/timeb.h>
+
+void _cdecl
+gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ struct timeb tb;
+
+ ftime(&tb);
+ tv->tv_sec = tb.time;
+ tv->tv_usec = tb.millitm * 1000;
+}
+
+char *
+getcwd(buffer, size)
+ char *buffer;
+ int size;
+{
+ int length;
+ char *pb;
+
+ if (_getcwd(buffer, size) == NULL) {
+ return NULL;
+ }
+ length = strlen(buffer);
+ if (length >= size) {
+ return NULL;
+ }
+
+ for (bp = buffer; *bp != '\0'; bp++) {
+ if (*bp == '\\') {
+ *bp = '/';
+ }
+ }
+ return buffer;
+}
diff --git a/missing/nt.h b/missing/nt.h new file mode 100644 index 0000000000..8b208594b6 --- /dev/null +++ b/missing/nt.h @@ -0,0 +1,246 @@ +#ifndef EXT_NT_H +#define EXT_NT_H + +/* + * Copyright (c) 1993, Intergraph Corporation + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the perl README file. + * + */ + +// +// Definitions for NT port of Perl +// + +// +// GRRRR!!!! Windows Nonsense. +// Define the following so we don't get tons of extra stuff +// when we include windows.h +// + +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define OEMRESOURCE +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +//#define NOKERNEL +//#define NOUSER +#define NONLS +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS + + +// +// Ok now we can include the normal include files. +// + +#include <stdarg.h> +#include <windows.h> +// +// We\'re not using Microsoft\'s "extensions" to C for +// Structured Exception Handling (SEH) so we can nuke these +// +#undef try +#undef except +#undef finally +#undef leave +#include <winsock.h> +#include <sys/types.h> +#include <direct.h> +#include <process.h> +#include <io.h> +#include <time.h> +#include <sys/utime.h> + +// +// Grrr... +// + +#define access _access +#define chmod _chmod +#define chsize _chsize +#define close _close +#define creat _creat +#define dup _dup +#define dup2 _dup2 +#define eof _eof +#define filelength _filelength +#define isatty _isatty +#define locking _locking +#define lseek _lseek +#define mktemp _mktemp +#define open _open +#define read _read +#define setmode _setmode +#define sopen _sopen +#define tell _tell +#define umask _umask +#define unlink _unlink +#define write _write +#define execl _execl +#define execle _execle +#define execlp _execlp +#define execlpe _execlpe +#define execv _execv +#define execve _execve +#define execvp _execvp +#define execvpe _execvpe +#define getpid _getpid +#define spawnl _spawnl +#define spawnle _spawnle +#define spawnlp _spawnlp +#define spawnlpe _spawnlpe +#define spawnv _spawnv +#define spawnve _spawnve +#define spawnvp _spawnvp +#define spawnvpe _spawnvpe +#if _MSC_VER < 800 +#define fileno _fileno +#endif +#define utime _utime +#define pipe _pipe + +#define popen mypopen +#define pclose mypclose + +/* these are defined in nt.c */ + +extern int NtMakeCmdVector(char *, char ***, int); +extern void NtInitialize(int *, char ***); + +extern char *NtGetLib(void); +extern char *NtGetBin(void); + +// +// define this so we can do inplace editing +// + +#define SUFFIX + +// +// stubs +// +extern int ioctl (int, unsigned int, char *); +#if 0 +extern void sleep (unsigned int); +#else +#define sleep(x) Sleep(x*1000) +#endif + +extern UIDTYPE getuid (void); +extern UIDTYPE geteuid (void); +extern GIDTYPE getgid (void); +extern GIDTYPE getegid (void); +extern int setuid (int); +extern int setgid (int); + + +// +// Got the idea and some of the code from the MSDOS implementation +// + +/* + * (C) Copyright 1987, 1990 Diomidis Spinellis. + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * Included in the nt header file for use by nt port + * + * $Log: dir.h,v $ + * Revision 4.0.1.1 91/06/07 11:22:10 lwall + * patch4: new copyright notice + * + * Revision 4.0 91/03/20 01:34:20 lwall + * 4.0 baseline. + * + * Revision 3.0.1.1 90/03/27 16:07:08 lwall + * patch16: MSDOS support + * + * Revision 1.1 90/03/18 20:32:29 dds + * Initial revision + * + * + */ +/* + * defines the type returned by the directory(3) functions + */ + +/*Directory entry size */ +#ifdef DIRSIZ +#undef DIRSIZ +#endif +#define DIRSIZ(rp) (sizeof(struct direct)) + +/* need this so that directory stuff will compile! */ +#define DIRENT direct + +/* + * Structure of a directory entry + */ +struct direct { + ino_t d_ino; /* inode number (not used by MS-DOS) */ + int d_namlen; /* Name length */ + char d_name[257]; /* file name */ +}; + +struct _dir_struc { /* Structure used by dir operations */ + char *start; /* Starting position */ + char *curr; /* Current position */ + long size; /* Size of string table */ + long nfiles; /* number if filenames in table */ + struct direct dirstr; /* Directory structure to return */ +}; + +typedef struct _dir_struc DIR; /* Type returned by dir operations */ + +DIR *cdecl opendir(char *filename); +struct direct *readdir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp,long loc); +void rewinddir(DIR *dirp); +void closedir(DIR *dirp); + +extern int sys_nerr; +extern char *sys_errlist[]; +extern char *mystrerror(int); + +#define strerror(e) mystrerror(e) + +#define PIPE_BUF 1024 + +#define HAVE_STDLIB_H 1 +#define HAVE_GETLOGIN 1 +#define HAVE_WAITPID 1 +#define HAVE_GETCWD 1 + +#endif diff --git a/missing/setenv.c b/missing/setenv.c new file mode 100644 index 0000000000..6211bcf02b --- /dev/null +++ b/missing/setenv.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1991, Larry Wall + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + */ + +#include "ruby.h" + +extern char **environ; +extern char **origenviron; + +char *strdup(); + +int +envix(nam) +char *nam; +{ + register int i, len = strlen(nam); + + for (i = 0; environ[i]; i++) { + if (memcmp(environ[i],nam,len) && environ[i][len] == '=') + break; /* strnEQ must come first to avoid */ + } /* potential SEGV's */ + return i; +} + +void +setenv(nam,val) +char *nam, *val; +{ + register int i=envix(nam); /* where does it go? */ + + if (environ == origenviron) { /* need we copy environment? */ + int j; + int max; + char **tmpenv; + + /*SUPPRESS 530*/ + for (max = i; environ[max]; max++) ; + tmpenv = ALLOC_N(char*, max+2); + for (j=0; j<max; j++) /* copy environment */ + tmpenv[j] = strdup(environ[j]); + tmpenv[max] = 0; + environ = tmpenv; /* tell exec where it is now */ + } + if (!val) { + while (environ[i]) { + environ[i] = environ[i+1]; + i++; + } + return; + } + if (!environ[i]) { /* does not exist yet */ + REALLOC_N(environ, char*, i+2); /* just expand it a bit */ + environ[i+1] = 0; /* make sure it's null terminated */ + } + else { + free(environ[i]); + } + environ[i] = ALLOC_N(char, strlen(nam) + strlen(val) + 2); +#ifndef MSDOS + (void)sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */ +#else + /* MS-DOS requires environment variable names to be in uppercase */ + /* [Tom Dinger, 27 August 1990: Well, it doesn't _require_ it, but + * some utilities and applications may break because they only look + * for upper case strings. (Fixed strupr() bug here.)] + */ + strcpy(environ[i],nam); strupr(environ[i]); + (void)sprintf(environ[i] + strlen(nam),"=%s",val); +#endif /* MSDOS */ +} @@ -13,6 +13,8 @@ #ifndef NODE_H #define NODE_H +struct global_entry* rb_global_entry(); + enum node_type { NODE_METHOD, NODE_FBODY, @@ -32,6 +34,7 @@ enum node_type { NODE_NOT, NODE_MASGN, NODE_LASGN, + NODE_DASGN, NODE_GASGN, NODE_IASGN, NODE_CASGN, @@ -52,12 +55,13 @@ enum node_type { NODE_FAIL, NODE_YIELD, NODE_LVAR, - NODE_LVAR2, + NODE_DVAR, NODE_GVAR, NODE_IVAR, NODE_CVAR, NODE_CONST, NODE_NTH_REF, + NODE_BACK_REF, NODE_LIT, NODE_STR, NODE_STR2, @@ -71,11 +75,16 @@ enum node_type { NODE_UNDEF, NODE_CLASS, NODE_MODULE, + NODE_COLON2, NODE_CREF, + NODE_DOT2, NODE_DOT3, + NODE_FLIP2, + NODE_FLIP3, NODE_ATTRSET, NODE_SELF, NODE_NIL, + NODE_DEFINED, }; typedef struct RNode { @@ -148,6 +157,7 @@ typedef struct RNode { #define nd_frml u1.node #define nd_rest u2.argc +#define nd_opt u1.node #define nd_recv u1.node #define nd_mid u2.id @@ -206,19 +216,21 @@ typedef struct RNode { #define NEW_AND(a,b) newnode(NODE_AND,a,b,0) #define NEW_OR(a,b) newnode(NODE_OR,a,b,0) #define NEW_NOT(a) newnode(NODE_NOT,0,a,0) -#define NEW_MASGN(l,r) newnode(NODE_MASGN,l,r,0) +#define NEW_MASGN(l,r) newnode(NODE_MASGN,l,0,r) #define NEW_GASGN(v,val) newnode(NODE_GASGN,v,val,rb_global_entry(v)) #define NEW_LASGN(v,val) newnode(NODE_LASGN,v,val,local_cnt(v)) +#define NEW_DASGN(v,val) newnode(NODE_DASGN,v,val,0); #define NEW_IASGN(v,val) newnode(NODE_IASGN,v,val,0) #define NEW_CASGN(v,val) newnode(NODE_CASGN,v,val,0) #define NEW_OP_ASGN1(p,id,a) newnode(NODE_OP_ASGN1,p,id,a) #define NEW_OP_ASGN2(r,i,val) newnode(NODE_OP_ASGN1,r,val,i) #define NEW_GVAR(v) newnode(NODE_GVAR,v,0,rb_global_entry(v)) #define NEW_LVAR(v) newnode(NODE_LVAR,v,0,local_cnt(v)) -#define NEW_LVAR2(v) newnode(NODE_LVAR2,v,0,0) +#define NEW_DVAR(v) newnode(NODE_DVAR,v,0,0); #define NEW_IVAR(v) newnode(NODE_IVAR,v,0,0) #define NEW_CVAR(v) newnode(NODE_CVAR,v,0,cref_list) -#define NEW_NTH_REF(n) newnode(NODE_NTH_REF,0,n,0) +#define NEW_NTH_REF(n) newnode(NODE_NTH_REF,0,n,local_cnt('~')) +#define NEW_BACK_REF(n) newnode(NODE_BACK_REF,0,n,local_cnt('~')) #define NEW_LIT(l) newnode(NODE_LIT,l,0,0) #define NEW_STR(s) newnode(NODE_STR,s,0,0) #define NEW_STR2(s) newnode(NODE_STR2,s,0,0) @@ -228,20 +240,26 @@ typedef struct RNode { #define NEW_FCALL(m,a) newnode(NODE_FCALL,0,m,a) #define NEW_SUPER(a) newnode(NODE_SUPER,0,0,a) #define NEW_ZSUPER() newnode(NODE_ZSUPER,0,0,0) -#define NEW_ARGS(f,r) newnode(NODE_ARGS,0,r,f) +#define NEW_ARGS(f,o,r) newnode(NODE_ARGS,o,r,f) #define NEW_ALIAS(n,o) newnode(NODE_ALIAS,0,n,o) #define NEW_UNDEF(i) newnode(NODE_UNDEF,0,i,0) #define NEW_CLASS(n,b,s) newnode(NODE_CLASS,n,NEW_CBODY(b),s) #define NEW_MODULE(n,b) newnode(NODE_MODULE,n,NEW_CBODY(b),0) +#define NEW_COLON2(c,i) newnode(NODE_COLON2,c,i,0) #define NEW_CREF0() (cref_list=newnode(NODE_CREF,the_class,0,0)) #define NEW_CREF(b) (cref_list=newnode(NODE_CREF,0,0,cref_list)) #define NEW_CBODY(b) (cref_list->nd_body=NEW_SCOPE(b),cref_list) +#define NEW_DOT2(b,e) newnode(NODE_DOT2,b,e,0) #define NEW_DOT3(b,e) newnode(NODE_DOT3,b,e,0) #define NEW_ATTRSET(a) newnode(NODE_ATTRSET,a,0,0) #define NEW_SELF() newnode(NODE_SELF,0,0,0) #define NEW_NIL() newnode(NODE_NIL,0,0,0) +#define NEW_DEFINED(e) newnode(NODE_DEFINED,e,0,0) NODE *newnode(); VALUE rb_method_booundp(); +#define NOEX_PUBLIC 0 +#define NOEX_PRIVATE 1 + #endif @@ -11,17 +11,17 @@ ************************************************/ #include "ruby.h" -#include "env.h" #include <math.h> static ID coerce; static ID to_i; -VALUE C_Numeric; -VALUE C_Float; -VALUE C_Integer; -VALUE C_Fixnum; +VALUE cNumeric; +VALUE cFloat; +VALUE cInteger; +VALUE cFixnum; +ID rb_frame_last_func(); double big2dbl(); VALUE @@ -29,45 +29,36 @@ float_new(d) double d; { NEWOBJ(flt, struct RFloat); - OBJSETUP(flt, C_Float, T_FLOAT); + OBJSETUP(flt, cFloat, T_FLOAT); flt->value = d; return (VALUE)flt; } -static +static VALUE num_coerce_bin(x, y) VALUE x, y; { return rb_funcall(rb_funcall(y, coerce, 1, x), - the_env->last_func, 1, y); + rb_frame_last_func(), 1, y); } static VALUE -Fnum_uplus(num) +num_uplus(num) VALUE num; { return num; } static VALUE -Fnum_uminus(num) +num_uminus(num) VALUE num; { return rb_funcall(rb_funcall(num, coerce, 1, INT2FIX(0)), 1, num); } static VALUE -Fnum_dot2(left, right) - VALUE left, right; -{ - Need_Fixnum(left); - Need_Fixnum(right); - return range_new(left, right); -} - -static VALUE -Fnum_next(num) +num_next(num) VALUE num; { num = rb_funcall(num, rb_intern("to_i"), 0); @@ -75,7 +66,7 @@ Fnum_next(num) } VALUE -Fnum_upto(from, to) +num_upto(from, to) VALUE from, to; { int i, end; @@ -89,7 +80,7 @@ Fnum_upto(from, to) } static VALUE -Fnum_downto(from, to) +num_downto(from, to) VALUE from, to; { int i, end; @@ -103,8 +94,8 @@ Fnum_downto(from, to) } static VALUE -Fnum_step(from, to, step) - VALUE from, to; +num_step(from, to, step) + VALUE from, to, step; { int i, end, diff; @@ -128,7 +119,7 @@ Fnum_step(from, to, step) } static VALUE -Fnum_dotimes(num) +num_dotimes(num) VALUE num; { int i, end; @@ -141,7 +132,7 @@ Fnum_dotimes(num) } static VALUE -Fnum_divmod(x, y) +num_divmod(x, y) VALUE x, y; { VALUE div, mod; @@ -159,14 +150,14 @@ Fnum_divmod(x, y) } static VALUE -Fnum_is_int(num) +num_int_p(num) VALUE num; { return FALSE; } static VALUE -Fnum_chr(num) +num_chr(num) VALUE num; { char c; @@ -179,7 +170,7 @@ Fnum_chr(num) } static VALUE -Fflo_to_s(flt) +flo_to_s(flt) struct RFloat *flt; { char buf[32]; @@ -190,7 +181,7 @@ Fflo_to_s(flt) } static VALUE -Fflo_coerce(x, y) +flo_coerce(x, y) VALUE x, y; { switch (TYPE(y)) { @@ -199,7 +190,7 @@ Fflo_coerce(x, y) case T_FLOAT: return y; case T_BIGNUM: - return Fbig_to_f(y); + return big_to_f(y); default: Fail("can't coerce %s to Float", rb_class2name(CLASS_OF(y))); } @@ -208,14 +199,14 @@ Fflo_coerce(x, y) } static VALUE -Fflo_uminus(flt) +flo_uminus(flt) struct RFloat *flt; { return float_new(-flt->value); } static VALUE -Fflo_plus(x, y) +flo_plus(x, y) struct RFloat *x, *y; { switch (TYPE(y)) { @@ -226,14 +217,14 @@ Fflo_plus(x, y) case T_FLOAT: return float_new(x->value + y->value); case T_STRING: - return Fstr_plus(obj_as_string(x), y); + return str_plus(obj_as_string(x), y); default: return num_coerce_bin(x, y); } } static VALUE -Fflo_minus(x, y) +flo_minus(x, y) struct RFloat *x, *y; { switch (TYPE(y)) { @@ -249,7 +240,7 @@ Fflo_minus(x, y) } static VALUE -Fflo_mul(x, y) +flo_mul(x, y) struct RFloat *x, *y; { switch (TYPE(y)) { @@ -260,14 +251,14 @@ Fflo_mul(x, y) case T_FLOAT: return float_new(x->value * y->value); case T_STRING: - return Fstr_times(y, INT2FIX((int)x->value)); + return str_times(y, INT2FIX((int)x->value)); default: return num_coerce_bin(x, y); } } static VALUE -Fflo_div(x, y) +flo_div(x, y) struct RFloat *x, *y; { int f_y; @@ -291,7 +282,7 @@ Fflo_div(x, y) } static VALUE -Fflo_mod(x, y) +flo_mod(x, y) struct RFloat *x, *y; { double value; @@ -326,7 +317,8 @@ Fflo_mod(x, y) return float_new(value); } -Fflo_pow(x, y) +VALUE +flo_pow(x, y) struct RFloat *x, *y; { switch (TYPE(y)) { @@ -341,8 +333,34 @@ Fflo_pow(x, y) } } +struct xy { + VALUE x, y; +}; + +static VALUE +eq(arg) + struct xy *arg; +{ + return rb_funcall(arg->y, rb_intern("=="), 1, arg->x); +} + +static VALUE +eq_rescue() +{ + return FALSE; +} + static VALUE -Fflo_eq(x, y) +num_equal(x, y) + VALUE x, y; +{ + struct xy arg; + arg.x = x; arg.y = y; + return rb_rescue(eq, &arg, eq_rescue, Qnil); +} + +static VALUE +flo_eq(x, y) struct RFloat *x, *y; { switch (TYPE(y)) { @@ -356,12 +374,12 @@ Fflo_eq(x, y) case T_FLOAT: return (x->value == y->value)?TRUE:FALSE; default: - return num_coerce_bin(x, y); + return num_equal(x, y); } } static VALUE -Fflo_hash(num) +flo_hash(num) struct RFloat *num; { double d; @@ -378,7 +396,7 @@ Fflo_hash(num) } static VALUE -Fflo_cmp(x, y) +flo_cmp(x, y) struct RFloat *x, *y; { double a, b; @@ -406,7 +424,7 @@ Fflo_cmp(x, y) } static VALUE -Fflo_to_i(num) +flo_to_i(num) struct RFloat *num; { double f = num->value; @@ -420,14 +438,14 @@ Fflo_to_i(num) } static VALUE -Fflo_to_f(num) +flo_to_f(num) VALUE num; { return num; } static VALUE -Fflo_abs(flt) +flo_abs(flt) struct RFloat *flt; { double val = fabs(flt->value); @@ -457,7 +475,6 @@ num2int(val) switch (TYPE(val)) { case T_FIXNUM: return FIX2INT(val); - break; case T_FLOAT: if (RFLOAT(val)->value <= (double) LONG_MAX @@ -466,14 +483,14 @@ num2int(val) } else { Fail("float %g out of rang of integer", RFLOAT(val)->value); + return Qnil; /* not reached */ } - break; case T_BIGNUM: return big2int(val); default: - val = rb_resque(to_integer, val, fail_to_integer, val); + val = rb_rescue(to_integer, val, fail_to_integer, val); return NUM2INT(val); } } @@ -500,14 +517,14 @@ num2fix(val) } static VALUE -Fint_is_int(num) +int_int_p(num) VALUE num; { return TRUE; } static VALUE -Ffix_uminus(num) +fix_uminus(num) VALUE num; { return int2inum(-FIX2INT(num)); @@ -531,14 +548,14 @@ fix2str(x, base) } VALUE -Ffix_to_s(in) +fix_to_s(in) VALUE in; { return fix2str(in, 10); } static VALUE -Ffix_plus(x, y) +fix_plus(x, y) VALUE x; struct RFloat *y; { @@ -554,7 +571,7 @@ Ffix_plus(x, y) r = INT2FIX(c); if (FIX2INT(r) != c) { - r = Fbig_plus(int2big(a), int2big(b)); + r = big_plus(int2big(a), int2big(b)); } return r; } @@ -566,7 +583,7 @@ Ffix_plus(x, y) } static VALUE -Ffix_minus(x, y) +fix_minus(x, y) VALUE x; struct RFloat *y; { @@ -582,7 +599,7 @@ Ffix_minus(x, y) r = INT2FIX(c); if (FIX2INT(r) != c) { - r = Fbig_minus(int2big(a), int2big(b)); + r = big_minus(int2big(a), int2big(b)); } return r; } @@ -594,7 +611,7 @@ Ffix_minus(x, y) } static VALUE -Ffix_mul(x, y) +fix_mul(x, y) VALUE x; struct RFloat *y; { @@ -606,7 +623,7 @@ Ffix_mul(x, y) VALUE r = INT2FIX(c); if (FIX2INT(r) != c) { - r = Fbig_mul(int2big(a), int2big(b)); + r = big_mul(int2big(a), int2big(b)); } return r; } @@ -618,7 +635,7 @@ Ffix_mul(x, y) } static VALUE -Ffix_div(x, y) +fix_div(x, y) VALUE x; struct RFloat *y; { @@ -634,10 +651,10 @@ Ffix_div(x, y) } static VALUE -Ffix_mod(x, y) +fix_mod(x, y) VALUE x, y; { - int mod, i; + int i; if (TYPE(y) == T_FIXNUM) { i = FIX2INT(y); @@ -649,7 +666,7 @@ Ffix_mod(x, y) } static VALUE -Ffix_pow(x, y) +fix_pow(x, y) VALUE x, y; { extern double pow(); @@ -661,7 +678,7 @@ Ffix_pow(x, y) if (b == 0) return INT2FIX(1); a = FIX2INT(x); if (b > 0) { - return Fbig_pow(int2big(a), y); + return big_pow(int2big(a), y); } return float_new(pow((double)a, (double)b)); } @@ -672,7 +689,7 @@ Ffix_pow(x, y) } static VALUE -Ffix_equal(x, y) +fix_equal(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -682,12 +699,12 @@ Ffix_equal(x, y) return Qnil; } else { - return num_coerce_bin(x, y); + return num_equal(x, y); } } static VALUE -Ffix_cmp(x, y) +fix_cmp(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -703,7 +720,7 @@ Ffix_cmp(x, y) } static VALUE -Ffix_gt(x, y) +fix_gt(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -718,7 +735,7 @@ Ffix_gt(x, y) } static VALUE -Ffix_ge(x, y) +fix_ge(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -733,7 +750,7 @@ Ffix_ge(x, y) } static VALUE -Ffix_lt(x, y) +fix_lt(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -748,7 +765,7 @@ Ffix_lt(x, y) } static VALUE -Ffix_le(x, y) +fix_le(x, y) VALUE x, y; { if (FIXNUM_P(y)) { @@ -763,15 +780,7 @@ Ffix_le(x, y) } static VALUE -Ffix_dot2(left, right) - VALUE left, right; -{ - Need_Fixnum(right); - return range_new(left, right); -} - -static VALUE -Ffix_rev(num) +fix_rev(num) VALUE num; { unsigned long val = FIX2UINT(num); @@ -781,46 +790,46 @@ Ffix_rev(num) } static VALUE -Ffix_and(x, y) +fix_and(x, y) VALUE x, y; { long val; if (TYPE(y) == T_BIGNUM) { - return Fbig_and(y, x); + return big_and(y, x); } val = NUM2INT(x) & NUM2INT(y); return int2inum(val); } static VALUE -Ffix_or(x, y) +fix_or(x, y) VALUE x, y; { long val; if (TYPE(y) == T_BIGNUM) { - return Fbig_or(y, x); + return big_or(y, x); } val = NUM2INT(x) | NUM2INT(y); return INT2FIX(val); } static VALUE -Ffix_xor(x, y) +fix_xor(x, y) VALUE x, y; { long val; if (TYPE(y) == T_BIGNUM) { - return Fbig_xor(y, x); + return big_xor(y, x); } val = NUM2INT(x) ^ NUM2INT(y); return INT2FIX(val); } static VALUE -Ffix_lshift(x, y) +fix_lshift(x, y) VALUE x, y; { long val, width; @@ -829,14 +838,14 @@ Ffix_lshift(x, y) width = NUM2INT(y); if (width > (sizeof(VALUE)*CHAR_BIT-1) || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-width) > 0) { - return Fbig_lshift(int2big(val), y); + return big_lshift(int2big(val), y); } val = val << width; return int2inum(val); } static VALUE -Ffix_rshift(x, y) +fix_rshift(x, y) VALUE x, y; { long val; @@ -846,7 +855,7 @@ Ffix_rshift(x, y) } static VALUE -Ffix_aref(fix, idx) +fix_aref(fix, idx) VALUE fix, idx; { unsigned long val = FIX2INT(fix); @@ -860,14 +869,14 @@ Ffix_aref(fix, idx) } static VALUE -Ffix_to_i(num) +fix_to_i(num) VALUE num; { return num; } static VALUE -Ffix_to_f(num) +fix_to_f(num) VALUE num; { double val; @@ -878,25 +887,25 @@ Ffix_to_f(num) } static VALUE -Ffix_class(fix) +fix_type(fix) VALUE fix; { - return C_Fixnum; + return cFixnum; } -static -Ffix_abs(fix) +static VALUE +fix_abs(fix) VALUE fix; { int i = FIX2INT(fix); - if (fix < 0) i = -i; + if (i < 0) i = -i; - return int2inum(fix); + return int2inum(i); } static VALUE -Ffix_id2name(fix) +fix_id2name(fix) VALUE fix; { char *name = rb_id2name(FIX2UINT(fix)); @@ -905,7 +914,7 @@ Ffix_id2name(fix) } static VALUE -Ffix_next(fix) +fix_next(fix) VALUE fix; { int i = FIX2INT(fix) + 1; @@ -913,91 +922,87 @@ Ffix_next(fix) return int2inum(i); } -extern VALUE M_Comparable; -extern Fkrn_inspect(); +extern VALUE mComparable; +void Init_Numeric() { coerce = rb_intern("coerce"); to_i = rb_intern("to_i"); - C_Numeric = rb_define_class("Numeric", C_Object); - - rb_undef_method(CLASS_OF(C_Numeric), "new"); - rb_undef_method(C_Numeric, "clone"); - - rb_include_module(C_Numeric, M_Comparable); - rb_define_method(C_Numeric, "+@", Fnum_uplus, 0); - rb_define_method(C_Numeric, "-@", Fnum_uminus, 0); - rb_define_method(C_Numeric, "..", Fnum_dot2, 1); - rb_define_method(C_Numeric, "divmod", Fnum_divmod, 1); - - rb_define_method(C_Numeric, "next", Fnum_next, 0); - rb_define_method(C_Numeric, "upto", Fnum_upto, 1); - rb_define_method(C_Numeric, "downto", Fnum_downto, 1); - rb_define_method(C_Numeric, "step", Fnum_step, 2); - rb_define_method(C_Numeric, "times", Fnum_dotimes, 0); - rb_define_method(C_Numeric, "is_integer", Fnum_is_int, 0); - rb_define_method(C_Numeric, "chr", Fnum_chr, 0); - rb_define_method(C_Numeric, "_inspect", Fkrn_inspect, 0); - - C_Integer = rb_define_class("Integer", C_Numeric); - rb_define_method(C_Integer, "is_integer", Fint_is_int, 0); - - C_Fixnum = rb_define_class("Fixnum", C_Integer); - - rb_define_method(C_Fixnum, "to_s", Ffix_to_s, 0); - rb_define_method(C_Fixnum, "class", Ffix_class, 0); - - rb_define_method(C_Fixnum, "id2name", Ffix_id2name, 0); - - rb_define_method(C_Fixnum, "-@", Ffix_uminus, 0); - rb_define_method(C_Fixnum, "+", Ffix_plus, 1); - rb_define_method(C_Fixnum, "-", Ffix_minus, 1); - rb_define_method(C_Fixnum, "*", Ffix_mul, 1); - rb_define_method(C_Fixnum, "/", Ffix_div, 1); - rb_define_method(C_Fixnum, "%", Ffix_mod, 1); - rb_define_method(C_Fixnum, "**", Ffix_pow, 1); - - rb_define_method(C_Fixnum, "abs", Ffix_abs, 0); - - rb_define_method(C_Fixnum, "==", Ffix_equal, 1); - rb_define_method(C_Fixnum, "<=>", Ffix_cmp, 1); - rb_define_method(C_Fixnum, ">", Ffix_gt, 1); - rb_define_method(C_Fixnum, ">=", Ffix_ge, 1); - rb_define_method(C_Fixnum, "<", Ffix_lt, 1); - rb_define_method(C_Fixnum, "<=", Ffix_le, 1); - rb_define_method(C_Fixnum, "..", Ffix_dot2, 1); - - rb_define_method(C_Fixnum, "~", Ffix_rev, 0); - rb_define_method(C_Fixnum, "&", Ffix_and, 1); - rb_define_method(C_Fixnum, "|", Ffix_or, 1); - rb_define_method(C_Fixnum, "^", Ffix_xor, 1); - rb_define_method(C_Fixnum, "[]", Ffix_aref, 1); - - rb_define_method(C_Fixnum, "<<", Ffix_lshift, 1); - rb_define_method(C_Fixnum, ">>", Ffix_rshift, 1); - - rb_define_method(C_Fixnum, "to_i", Ffix_to_i, 0); - rb_define_method(C_Fixnum, "to_f", Ffix_to_f, 0); - - rb_define_method(C_Fixnum, "next", Ffix_next, 0); - - C_Float = rb_define_class("Float", C_Numeric); - - rb_define_method(C_Float, "to_s", Fflo_to_s, 0); - rb_define_method(C_Float, "coerce", Fflo_coerce, 1); - rb_define_method(C_Float, "-@", Fflo_uminus, 0); - rb_define_method(C_Float, "+", Fflo_plus, 1); - rb_define_method(C_Float, "-", Fflo_minus, 1); - rb_define_method(C_Float, "*", Fflo_mul, 1); - rb_define_method(C_Float, "/", Fflo_div, 1); - rb_define_method(C_Float, "%", Fflo_mod, 1); - rb_define_method(C_Float, "**", Fflo_pow, 1); - rb_define_method(C_Float, "==", Fflo_eq, 1); - rb_define_method(C_Float, "<=>", Fflo_cmp, 1); - rb_define_method(C_Float, "hash", Fflo_hash, 0); - rb_define_method(C_Float, "to_i", Fflo_to_i, 0); - rb_define_method(C_Float, "to_f", Fflo_to_f, 0); - rb_define_method(C_Float, "abs", Fflo_abs, 0); + cNumeric = rb_define_class("Numeric", cObject); + + rb_undef_method(CLASS_OF(cNumeric), "new"); + + rb_include_module(cNumeric, mComparable); + rb_define_method(cNumeric, "+@", num_uplus, 0); + rb_define_method(cNumeric, "-@", num_uminus, 0); + rb_define_method(cNumeric, "divmod", num_divmod, 1); + + rb_define_method(cNumeric, "next", num_next, 0); + rb_define_method(cNumeric, "upto", num_upto, 1); + rb_define_method(cNumeric, "downto", num_downto, 1); + rb_define_method(cNumeric, "step", num_step, 2); + rb_define_method(cNumeric, "times", num_dotimes, 0); + rb_define_method(cNumeric, "integer?", num_int_p, 0); + rb_define_method(cNumeric, "chr", num_chr, 0); + + cInteger = rb_define_class("Integer", cNumeric); + rb_define_method(cInteger, "integer?", int_int_p, 0); + + cFixnum = rb_define_class("Fixnum", cInteger); + + rb_define_method(cFixnum, "to_s", fix_to_s, 0); + rb_define_method(cFixnum, "type", fix_type, 0); + + rb_define_method(cFixnum, "id2name", fix_id2name, 0); + + rb_define_method(cFixnum, "-@", fix_uminus, 0); + rb_define_method(cFixnum, "+", fix_plus, 1); + rb_define_method(cFixnum, "-", fix_minus, 1); + rb_define_method(cFixnum, "*", fix_mul, 1); + rb_define_method(cFixnum, "/", fix_div, 1); + rb_define_method(cFixnum, "%", fix_mod, 1); + rb_define_method(cFixnum, "**", fix_pow, 1); + + rb_define_method(cFixnum, "abs", fix_abs, 0); + + rb_define_method(cFixnum, "==", fix_equal, 1); + rb_define_method(cFixnum, "<=>", fix_cmp, 1); + rb_define_method(cFixnum, ">", fix_gt, 1); + rb_define_method(cFixnum, ">=", fix_ge, 1); + rb_define_method(cFixnum, "<", fix_lt, 1); + rb_define_method(cFixnum, "<=", fix_le, 1); + + rb_define_method(cFixnum, "~", fix_rev, 0); + rb_define_method(cFixnum, "&", fix_and, 1); + rb_define_method(cFixnum, "|", fix_or, 1); + rb_define_method(cFixnum, "^", fix_xor, 1); + rb_define_method(cFixnum, "[]", fix_aref, 1); + + rb_define_method(cFixnum, "<<", fix_lshift, 1); + rb_define_method(cFixnum, ">>", fix_rshift, 1); + + rb_define_method(cFixnum, "to_i", fix_to_i, 0); + rb_define_method(cFixnum, "to_f", fix_to_f, 0); + + rb_define_method(cFixnum, "next", fix_next, 0); + + cFloat = rb_define_class("Float", cNumeric); + + rb_define_method(cFloat, "to_s", flo_to_s, 0); + rb_define_method(cFloat, "coerce", flo_coerce, 1); + rb_define_method(cFloat, "-@", flo_uminus, 0); + rb_define_method(cFloat, "+", flo_plus, 1); + rb_define_method(cFloat, "-", flo_minus, 1); + rb_define_method(cFloat, "*", flo_mul, 1); + rb_define_method(cFloat, "/", flo_div, 1); + rb_define_method(cFloat, "%", flo_mod, 1); + rb_define_method(cFloat, "**", flo_pow, 1); + rb_define_method(cFloat, "==", flo_eq, 1); + rb_define_method(cFloat, "<=>", flo_cmp, 1); + rb_define_method(cFloat, "hash", flo_hash, 0); + rb_define_method(cFloat, "to_i", flo_to_i, 0); + rb_define_method(cFloat, "to_f", flo_to_f, 0); + rb_define_method(cFloat, "abs", flo_abs, 0); } @@ -11,53 +11,42 @@ ************************************************/ #include "ruby.h" -#include "env.h" #include "st.h" #include <stdio.h> -VALUE C_Kernel; -VALUE C_Object; -VALUE C_Module; -VALUE C_Class; -VALUE C_Nil; -VALUE C_Data; +VALUE cKernel; +VALUE cObject; +VALUE cModule; +VALUE cClass; +VALUE cNil; +VALUE cData; struct st_table *new_idhash(); -VALUE Fsprintf(); +VALUE f_sprintf(); -VALUE obj_responds_to(); VALUE obj_alloc(); -static ID eq, match; - -static ID init_object; +static ID eq; static ID init; -static VALUE -P_true(obj) - VALUE obj; +VALUE +rb_equal(obj1, obj2) + VALUE obj1, obj2; { - return TRUE; + return rb_funcall(obj1, eq, 1, obj2); } static VALUE -P_false(obj) +krn_nil_p(obj) VALUE obj; { return FALSE; } -VALUE -rb_equal(obj1, obj2) - VALUE obj1, obj2; -{ - return rb_funcall(obj1, eq, 1, obj2); -} - static VALUE -Fkrn_equal(obj1, obj2) +krn_equal(obj1, obj2) VALUE obj1, obj2; { if (obj1 == obj2) return TRUE; @@ -65,45 +54,71 @@ Fkrn_equal(obj1, obj2) } static VALUE -Fkrn_to_a(obj) +krn_to_a(obj) VALUE obj; { return ary_new3(1, obj); } static VALUE -Fkrn_id(obj) +krn_id(obj) VALUE obj; { return obj | FIXNUM_FLAG; } static VALUE -Fkrn_class(obj) +krn_type(obj) struct RBasic *obj; { return obj->class; } +static VALUE +krn_clone(obj) + VALUE obj; +{ + VALUE clone; + + if (TYPE(obj) != T_OBJECT) { + Fail("can't clone %s", rb_class2name(CLASS_OF(obj))); + } + + clone = obj_alloc(RBASIC(obj)->class); + if (ROBJECT(obj)->iv_tbl) { + ROBJECT(clone)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl); + } + RBASIC(clone)->class = singleton_class_clone(RBASIC(obj)->class); + + return clone; +} + +static VALUE +krn_dup(obj) + VALUE obj; +{ + return rb_funcall(obj, rb_intern("clone"), 0, 0); +} + VALUE -Fkrn_to_s(obj) +krn_to_s(obj) VALUE obj; { char buf[256]; - sprintf(buf, "#<%s: 0x%x>", rb_class2name(CLASS_OF(obj)), obj); + sprintf(buf, "#<%s:0x%x>", rb_class2name(CLASS_OF(obj)), obj); return str_new2(buf); } VALUE -Fkrn_inspect(obj) +krn_inspect(obj) VALUE obj; { - return rb_funcall(obj, rb_intern("to_s"), 0, Qnil); + return rb_funcall(obj, rb_intern("to_s"), 0, 0); } -static -obj_inspect(id, value, str) +static int +inspect_i(id, value, str) ID id; VALUE value; struct RString *str; @@ -120,31 +135,39 @@ obj_inspect(id, value, str) ivname = rb_id2name(id); str_cat(str, ivname, strlen(ivname)); str_cat(str, "=", 1); - str2 = rb_funcall(value, rb_intern("_inspect"), 0, Qnil); + str2 = rb_funcall(value, rb_intern("inspect"), 0, 0); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); return ST_CONTINUE; } static VALUE -Fobj_inspect(obj) +obj_inspect(obj) struct RObject *obj; { VALUE str; char buf[256]; - if (FIXNUM_P(obj) || !obj->iv_tbl) return Fkrn_to_s(obj); + switch (TYPE(obj)) { + case T_OBJECT: + case T_MODULE: + case T_CLASS: + if (obj->iv_tbl) break; + /* fall through */ + default: + return krn_inspect(obj); + } sprintf(buf, "-<%s: ", rb_class2name(CLASS_OF(obj))); str = str_new2(buf); - st_foreach(obj->iv_tbl, obj_inspect, str); + st_foreach(obj->iv_tbl, inspect_i, str); str_cat(str, ">", 1); return str; } VALUE -obj_is_member_of(obj, c) +obj_is_instance_of(obj, c) VALUE obj, c; { struct RClass *class = (struct RClass*)CLASS_OF(obj); @@ -187,52 +210,42 @@ obj_is_kind_of(obj, c) } static VALUE -Fobj_clone(obj) +obj_initialize(obj) VALUE obj; { - VALUE clone; - - Check_Type(obj, T_OBJECT); - - clone = obj_alloc(RBASIC(obj)->class); - if (ROBJECT(obj)->iv_tbl) { - ROBJECT(clone)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl); - } - RBASIC(clone)->class = single_class_clone(RBASIC(obj)->class); - - return clone; + return Qnil; } static VALUE -Fobj_initialize(obj) - VALUE obj; +obj_s_added(obj, id) + VALUE obj, id; { return Qnil; } static VALUE -Fobj_s_added(obj, id) - VALUE obj, id; +nil_nil_p(obj) + VALUE obj; { - return Qnil; + return TRUE; } static VALUE -Fnil_to_s(obj) +nil_to_s(obj) VALUE obj; { return str_new2("nil"); } static VALUE -Fnil_class(nil) +nil_type(nil) VALUE nil; { - return C_Nil; + return cNil; } static VALUE -Fnil_plus(x, y) +nil_plus(x, y) VALUE x, y; { switch (TYPE(y)) { @@ -250,14 +263,14 @@ Fnil_plus(x, y) } static VALUE -Fmain_to_s(obj) +main_to_s(obj) VALUE obj; { return str_new2("main"); } static VALUE -Ftrue_to_s(obj) +true_to_s(obj) VALUE obj; { return str_new2("t"); @@ -274,7 +287,7 @@ obj_alloc(class) } static VALUE -Fcls_new(argc, argv, class) +mod_new(argc, argv, class) int argc; VALUE *argv; VALUE class; @@ -286,78 +299,65 @@ Fcls_new(argc, argv, class) } static VALUE -Fcls_to_s(class) - VALUE class; +mod_clone(module) + struct RClass *module; { - return str_new2(rb_class2name(class)); + NEWOBJ(clone, struct RClass); + OBJSETUP(clone, CLASS_OF(module), TYPE(module)); + + clone->super = module->super; + clone->m_tbl = st_copy(module->m_tbl); + + return (VALUE)clone; } +char *rb_class2path(); + static VALUE -Fcls_attr(argc, argv, class) - int argc; - VALUE *argv; +mod_to_s(class) VALUE class; { - VALUE name, pub; - - rb_scan_args(argc, argv, "11", &name, &pub); - Check_Type(name, T_STRING); - rb_define_attr(class, RSTRING(name)->ptr, pub); - return Qnil; + return rb_class_path(class); } -void -method_visibility(argc, argv, ex) - int argc; - VALUE *argv; - int ex; +ID +rb_to_id(name) + VALUE name; { - VALUE self = Qself; - int i; - ID id; - - for (i=0; i<argc; i++) { - if (FIXNUM_P(argv[i])) { - id = FIX2INT(argv[i]); - } - else { - Check_Type(argv[i], T_STRING); - id = rb_intern(RSTRING(argv[i])->ptr); - } - rb_export_method(self, id, ex); + if (TYPE(name) == T_STRING) { + return rb_intern(RSTRING(name)->ptr); } + return NUM2INT(name); } static VALUE -Fcls_public(argc, argv) +mod_attr(argc, argv, class) int argc; VALUE *argv; + VALUE class; { - method_visibility(argc, argv, NOEX_PUBLIC); - return Qnil; -} + VALUE name, pub; + ID id; -static VALUE -Fcls_private(argc, argv) - int argc; - VALUE *argv; -{ - method_visibility(argc, argv, NOEX_PRIVATE); + rb_scan_args(argc, argv, "11", &name, &pub); + rb_define_attr(class, rb_to_id(name), pub); return Qnil; } static VALUE -Fcant_clone(obj) - VALUE obj; +mod_public_attr(class, name) + VALUE class, name; { - Fail("can't clone %s", rb_class2name(CLASS_OF(obj))); + rb_define_attr(class, rb_to_id(name), 1); + return Qnil; } static VALUE -Fdata_class(data) - VALUE data; +mod_private_attr(class, name) + VALUE class, name; { - return C_Data; + rb_define_attr(class, rb_to_id(name), 0); + return Qnil; } static @@ -376,21 +376,22 @@ VALUE boot_defclass(name, super) } VALUE TopSelf; -VALUE TRUE = 1; +VALUE TRUE = INT2FIX(1); +void Init_Object() { VALUE metaclass; - C_Kernel = boot_defclass("Kernel", Qnil); - C_Object = boot_defclass("Object", C_Kernel); - C_Module = boot_defclass("Module", C_Object); - C_Class = boot_defclass("Class", C_Module); + cKernel = boot_defclass("Kernel", Qnil); + cObject = boot_defclass("Object", cKernel); + cModule = boot_defclass("Module", cObject); + cClass = boot_defclass("Class", cModule); - metaclass = RBASIC(C_Kernel)->class = single_class_new(C_Class); - metaclass = RBASIC(C_Object)->class = single_class_new(metaclass); - metaclass = RBASIC(C_Module)->class = single_class_new(metaclass); - metaclass = RBASIC(C_Class)->class = single_class_new(metaclass); + metaclass = RBASIC(cKernel)->class = singleton_class_new(cClass); + metaclass = RBASIC(cObject)->class = singleton_class_new(metaclass); + metaclass = RBASIC(cModule)->class = singleton_class_new(metaclass); + metaclass = RBASIC(cClass)->class = singleton_class_new(metaclass); /* * Ruby's Class Hierarchy Chart @@ -423,63 +424,60 @@ Init_Object() */ - rb_define_method(C_Kernel, "is_nil", P_false, 0); - rb_define_method(C_Kernel, "==", Fkrn_equal, 1); - rb_define_alias(C_Kernel, "equal", "=="); - rb_define_method(C_Kernel, "hash", Fkrn_id, 0); - rb_define_method(C_Kernel, "id", Fkrn_id, 0); - rb_define_method(C_Kernel, "class", Fkrn_class, 0); - rb_define_alias(C_Kernel, "=~", "=="); + rb_define_method(cKernel, "nil?", krn_nil_p, 0); + rb_define_method(cKernel, "==", krn_equal, 1); + rb_define_alias(cKernel, "equal?", "=="); + rb_define_alias(cKernel, "=~", "=="); - rb_define_method(C_Kernel, "to_a", Fkrn_to_a, 0); - rb_define_method(C_Kernel, "to_s", Fkrn_to_s, 0); - rb_define_method(C_Kernel, "_inspect", Fkrn_inspect, 0); + rb_define_method(cKernel, "hash", krn_id, 0); + rb_define_method(cKernel, "id", krn_id, 0); + rb_define_method(cKernel, "type", krn_type, 0); - rb_define_private_method(C_Kernel, "sprintf", Fsprintf, -1); - rb_define_alias(C_Kernel, "format", "sprintf"); + rb_define_method(cKernel, "clone", krn_clone, 0); + rb_define_method(cKernel, "dup", krn_dup, 0); - rb_define_private_method(C_Object, "initialize", Fobj_initialize, -1); - rb_define_private_method(C_Object, "single_method_added", Fobj_s_added, 1); + rb_define_method(cKernel, "to_a", krn_to_a, 0); + rb_define_method(cKernel, "to_s", krn_to_s, 0); + rb_define_method(cKernel, "inspect", krn_inspect, 0); - rb_define_method(C_Object, "clone", Fobj_clone, 0); + rb_define_private_method(cKernel, "sprintf", f_sprintf, -1); + rb_define_alias(cKernel, "format", "sprintf"); - rb_define_method(C_Object, "responds_to", obj_responds_to, 1); - rb_define_method(C_Object, "is_member_of", obj_is_member_of, 1); - rb_define_method(C_Object, "is_kind_of", obj_is_kind_of, 1); - rb_define_method(C_Object, "clone", Fobj_clone, 0); + rb_define_private_method(cObject, "initialize", obj_initialize, -1); + rb_define_private_method(cObject, "singleton_method_added", obj_s_added, 1); - rb_define_method(C_Module, "to_s", Fcls_to_s, 0); - rb_define_method(C_Module, "clone", Fcant_clone, 0); - rb_define_private_method(C_Module, "attr", Fcls_attr, -1); + rb_define_method(cObject, "is_instance_of?", obj_is_instance_of, 1); + rb_define_method(cObject, "is_kind_of?", obj_is_kind_of, 1); + rb_define_method(cObject, "inspect", obj_inspect, 0); - rb_define_method(C_Module, "public", Fcls_public, -1); - rb_define_method(C_Module, "private", Fcls_private, -1); + rb_define_method(cModule, "to_s", mod_to_s, 0); + rb_define_method(cModule, "clone", mod_clone, 0); + rb_define_private_method(cModule, "attr", mod_attr, -1); + rb_define_private_method(cModule, "public_attr", mod_public_attr, -1); + rb_define_private_method(cModule, "private_attr", mod_private_attr, -1); - rb_define_method(C_Class, "new", Fcls_new, -1); + rb_define_method(cClass, "new", mod_new, -1); - C_Nil = rb_define_class("Nil", C_Kernel); - rb_define_method(C_Nil, "to_s", Fnil_to_s, 0); - rb_define_method(C_Nil, "clone", P_false, 0); - rb_define_method(C_Nil, "class", Fnil_class, 0); + cNil = rb_define_class("Nil", cKernel); + rb_define_method(cNil, "to_s", nil_to_s, 0); + rb_define_method(cNil, "type", nil_type, 0); - rb_define_method(C_Nil, "is_nil", P_true, 0); + rb_define_method(cNil, "nil?", nil_nil_p, 0); /* default addition */ - rb_define_method(C_Nil, "+", Fnil_plus, 1); + rb_define_method(cNil, "+", nil_plus, 1); - C_Data = rb_define_class("Data", C_Kernel); - rb_define_method(C_Data, "clone", Fcant_clone, 0); - rb_define_method(C_Data, "class", Fdata_class, 0); + cData = rb_define_class("Data", cKernel); eq = rb_intern("=="); - Qself = TopSelf = obj_alloc(C_Object); - rb_define_single_method(TopSelf, "to_s", Fmain_to_s, 0); + Qself = TopSelf = obj_alloc(cObject); + rb_define_singleton_method(TopSelf, "to_s", main_to_s, 0); - TRUE = obj_alloc(C_Object); - rb_define_single_method(TRUE, "to_s", Ftrue_to_s, 0); - rb_define_const(C_Kernel, "TRUE", TRUE); - rb_define_const(C_Kernel, "FALSE", FALSE); + TRUE = obj_alloc(cObject); + rb_define_singleton_method(TRUE, "to_s", true_to_s, 0); + rb_define_const(cKernel, "TRUE", TRUE); + rb_define_const(cKernel, "FALSE", FALSE); init = rb_intern("initialize"); } @@ -39,7 +39,7 @@ #define vtohl(x) (x) #endif -extern VALUE C_String, C_Array; +extern VALUE cString, cArray; double atof(); static char *toofew = "too few arguments"; @@ -48,7 +48,7 @@ int strtoul(); static void encodes(); static VALUE -Fpck_pack(ary, fmt) +pack_pack(ary, fmt) struct RArray *ary; struct RString *fmt; { @@ -70,7 +70,7 @@ Fpck_pack(ary, fmt) items = ary->len; idx = 0; -#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : Fail(toofew)) +#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : (Fail(toofew),0)) while (p < pend) { type = *p++; /* get data type */ @@ -92,7 +92,7 @@ Fpck_pack(ary, fmt) case 'H': case 'h': from = NEXTFROM; if (from == Qnil) { - ptr = Qnil; + ptr = 0; plen = 0; } else { @@ -110,7 +110,7 @@ Fpck_pack(ary, fmt) str_cat(res, ptr, len); else { str_cat(res, ptr, plen); - len == plen; + len = plen; while (len >= 10) { str_cat(res, nul10, 10); len -= 10; @@ -124,7 +124,7 @@ Fpck_pack(ary, fmt) str_cat(res, ptr, len); else { str_cat(res, ptr, plen); - len == plen; + len = plen; while (len >= 10) { str_cat(res, spc10, 10); len -= 10; @@ -479,7 +479,7 @@ encodes(str, s, len) } static VALUE -Fpck_unpack(str, fmt) +pack_unpack(str, fmt) struct RString *str, *fmt; { static char *hexdigits = "0123456789abcdef0123456789ABCDEFx"; @@ -498,7 +498,6 @@ Fpck_unpack(str, fmt) ary = ary_new(); while (p < pend) { - retry: type = *p++; if (*p == '*') { len = send - s; @@ -840,8 +839,9 @@ Fpck_unpack(str, fmt) return ary; } +void Init_pack() { - rb_define_method(C_Array, "pack", Fpck_pack, 1); - rb_define_method(C_String, "unpack", Fpck_unpack, 1); + rb_define_method(cArray, "pack", pack_pack, 1); + rb_define_method(cString, "unpack", pack_unpack, 1); } @@ -24,21 +24,27 @@ # undef const #endif -#include "ident.h" +#define ID_SCOPE_SHIFT 3 +#define ID_SCOPE_MASK 0x07 +#define ID_LOCAL 0x00 +#define ID_INSTANCE 0x01 +#define ID_GLOBAL 0x02 +#define ID_ATTRSET 0x03 +#define ID_CONST 0x04 + #define is_id_nonop(id) ((id)>LAST_TOKEN) #define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL) #define is_global_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL) #define is_instance_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE) #define is_attrset_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET) #define is_const_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_CONST) -#define is_nthref_id(id) (((id)&ID_SCOPE_MASK)==ID_NTHREF) struct op_tbl { ID token; char *name; }; -NODE *eval_tree = Qnil; +NODE *eval_tree = 0; char *sourcefile; /* current source file */ int sourceline; /* current line no. */ @@ -49,16 +55,16 @@ static enum lex_state { EXPR_BEG, /* ignore newline, +/- is a sign. */ EXPR_MID, /* newline significant, +/- is a sign. */ EXPR_END, /* newline significant, +/- is a operator. */ + EXPR_ARG, /* newline significant, +/- is a sign(meybe). */ EXPR_FNAME, /* ignore newline, +/- is a operator. */ } lex_state; static int class_nest = 0; static int in_single = 0; -static ID cur_mid = Qnil; +static ID cur_mid = 0; static int value_expr(); static NODE *cond(); -static NODE *cond2(); static NODE *block_append(); static NODE *list_append(); @@ -66,11 +72,13 @@ static NODE *list_concat(); static NODE *list_copy(); static NODE *expand_op(); static NODE *call_op(); +static int in_defined = 0; static NODE *gettable(); static NODE *asignable(); static NODE *aryset(); static NODE *attrset(); +static void backref_error(); static void local_push(); static void local_pop(); @@ -78,12 +86,17 @@ static int local_cnt(); static int local_id(); static ID *local_tbl(); +static struct RVarmap *dyna_push(); +static void dyna_pop(); +static int dyna_in_block(); + +VALUE dyna_var_ref(); +#define dyna_id(id) dyna_var_ref(id) + #define cref_push() NEW_CREF(0) static void cref_pop(); static NODE *cref_list; -struct global_entry* rb_global_entry(); - static void top_local_init(); static void top_local_setup(); %} @@ -93,6 +106,7 @@ static void top_local_setup(); VALUE val; ID id; int num; + struct RVarmap *vars; } %token CLASS @@ -100,7 +114,7 @@ static void top_local_setup(); DEF UNDEF BEGIN - RESQUE + RESCUE ENSURE END IF @@ -124,24 +138,27 @@ static void top_local_setup(); NIL AND OR + NOT _FILE_ _LINE_ IF_MOD WHILE_MOD ALIAS + DEFINED -%token <id> IDENTIFIER GVAR IVAR CONSTANT NTH_REF +%token <id> IDENTIFIER FID GVAR IVAR CONSTANT %token <val> INTEGER FLOAT STRING XSTRING REGEXP -%token <node> STRING2 XSTRING2 DREGEXP +%token <node> STRING2 XSTRING2 DREGEXP NTH_REF BACK_REF %type <node> singleton %type <val> literal numeric %type <node> compexpr exprs expr arg primary var_ref -%type <node> if_tail opt_else case_body cases resque ensure +%type <node> if_tail opt_else case_body cases rescue ensure %type <node> call_args call_args0 args args2 opt_args -%type <node> f_arglist f_args array assoc_list assocs assoc -%type <node> mlhs mlhs_head mlhs_tail lhs iter_var opt_iter_var -%type <id> superclass variable symbol +%type <node> superclass f_arglist f_args f_optarg f_opt +%type <node> array assoc_list assocs assoc undef_list +%type <node> mlhs mlhs_head mlhs_tail lhs iter_var opt_iter_var backref +%type <id> variable symbol operation %type <id> cname fname op rest_arg %type <num> f_arg %token UPLUS /* unary+ */ @@ -160,7 +177,10 @@ static void top_local_setup(); %token COLON2 /* :: */ %token <id> OP_ASGN /* +=, -= etc. */ %token ASSOC /* => */ -%token LPAREN LBRACK LBRACE +%token LPAREN /* ( */ +%token LBRACK /* [ */ +%token LBRACE /* { */ +%token STAR /* * */ %token SYMBEG /* @@ -168,10 +188,9 @@ static void top_local_setup(); */ %left IF_MOD WHILE_MOD +%right NOT %left OR AND -%left YIELD RETURN FAIL %right '=' OP_ASGN -%right COLON2 %nonassoc DOT2 DOT3 %left OROP %left ANDOP @@ -184,6 +203,7 @@ static void top_local_setup(); %left '*' '/' '%' %right '!' '~' UPLUS UMINUS %right POW +%left COLON2 %token LAST_TOKEN @@ -246,24 +266,29 @@ expr : mlhs '=' args2 value_expr($2); $$ = NEW_YIELD($2); } - | IDENTIFIER call_args0 + | DEFINED {in_defined = 1;} arg + { + in_defined = 0; + $$ = NEW_DEFINED($3); + } + | operation call_args0 { $$ = NEW_FCALL($1, $2); } - | primary '.' IDENTIFIER call_args0 + | primary '.' operation call_args0 { value_expr($1); $$ = NEW_CALL($1, $3, $4); } | SUPER call_args0 { - if (!cur_mid && !in_single) + if (!cur_mid && !in_single && !in_defined) Error("super called outside of method"); $$ = NEW_SUPER($2); } - | UNDEF fname + | UNDEF undef_list { - $$ = NEW_UNDEF($2); + $$ = $2; } | ALIAS fname {lex_state = EXPR_FNAME;} fname { @@ -271,27 +296,41 @@ expr : mlhs '=' args2 } | expr IF_MOD expr { + value_expr($3); $$ = NEW_IF(cond($3), $1, Qnil); } | expr WHILE_MOD expr { - $$ = NEW_WHILE2(cond($3), $1); + value_expr($3); + if (nd_type($1) == NODE_BEGIN) { + $$ = NEW_WHILE2(cond($3), $1); + } + else { + $$ = NEW_WHILE(cond($3), $1); + } } | expr AND expr { + value_expr($1); $$ = NEW_AND(cond($1), cond($3)); } | expr OR expr { + value_expr($1); $$ = NEW_OR(cond($1), cond($3)); } + | NOT expr + { + value_expr($2); + $$ = NEW_NOT(cond($2)); + } | arg mlhs : mlhs_head { $$ = NEW_MASGN(NEW_LIST($1), Qnil); } - | mlhs_head '*' lhs + | mlhs_head STAR lhs { $$ = NEW_MASGN(NEW_LIST($1), $3); } @@ -299,7 +338,7 @@ mlhs : mlhs_head { $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2),Qnil); } - | mlhs_head mlhs_tail comma '*' lhs + | mlhs_head mlhs_tail comma STAR lhs { $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2),$5); } @@ -327,6 +366,11 @@ lhs : variable { $$ = attrset($1, $3, Qnil); } + | backref + { + backref_error($1); + $$ = Qnil; + } cname : IDENTIFIER { @@ -335,6 +379,7 @@ cname : IDENTIFIER | CONSTANT fname : IDENTIFIER + | FID | CONSTANT | op { @@ -342,8 +387,16 @@ fname : IDENTIFIER $$ = $1; } -op : COLON2 { $$ = COLON2; } - | DOT2 { $$ = DOT2; } +undef_list : fname + { + $$ = NEW_UNDEF($1); + } + | undef_list ',' fname + { + $$ = block_append($1, NEW_UNDEF($3)); + } + +op : DOT2 { $$ = DOT2; } | '|' { $$ = '|'; } | '^' { $$ = '^'; } | '&' { $$ = '&'; } @@ -359,6 +412,7 @@ op : COLON2 { $$ = COLON2; } | '+' { $$ = '+'; } | '-' { $$ = '-'; } | '*' { $$ = '*'; } + | STAR { $$ = '*'; } | '/' { $$ = '/'; } | '%' { $$ = '%'; } | POW { $$ = POW; } @@ -381,13 +435,20 @@ arg : variable '=' arg { $$ = attrset($1, $3, $5); } + | backref '=' arg + { + value_expr($3); + backref_error($1); + $$ = Qnil; + } | variable OP_ASGN arg { NODE *val; value_expr($3); if (is_local_id($1)) { - val = NEW_LVAR($1); + if (local_id($1)) val = NEW_LVAR($1); + else val = NEW_DVAR($1); } else if (is_global_id($1)) { val = NEW_GVAR($1); @@ -411,33 +472,22 @@ arg : variable '=' arg { $$ = NEW_OP_ASGN2($1, $4, $5); } + | backref OP_ASGN arg + { + backref_error($1); + $$ = Qnil; + } | arg DOT2 arg { - $$ = call_op($1, DOT2, 1, $3); + $$ = NEW_DOT2($1, $3); } | arg DOT3 arg { - $$ = NEW_DOT3(cond2($1), cond2($3)); + $$ = NEW_DOT3($1, $3); } | arg '+' arg { - $$ = Qnil; - if ($1 && $3 - && (nd_type($3) == NODE_LIT - || nd_type($3) == NODE_STR) - && nd_type($1) == NODE_CALL && $1->nd_mid == '+') { - if ($1->nd_args->nd_head == Qnil) - Bug("bad operand for `+'"); - if (nd_type($1->nd_args->nd_head) == NODE_LIT - || nd_type($1->nd_args->nd_head) == NODE_STR) { - $1->nd_args->nd_head = - expand_op($1->nd_args->nd_head, '+', $3); - $$ = $1; - } - } - if ($$ == Qnil) { - $$ = call_op($1, '+', 1, $3); - } + $$ = call_op($1, '+', 1, $3); } | arg '-' arg { @@ -517,20 +567,12 @@ arg : variable '=' arg } | '!' arg { + value_expr($2); $$ = NEW_NOT(cond($2)); } | '~' arg { - if ($2 - && (nd_type($2) == NODE_STR - || (nd_type($2) == NODE_LIT - && (TYPE($2->nd_lit) == T_REGEXP - || TYPE($2->nd_lit) == T_STRING)))) { - $$ = NEW_CALL($2, '~', Qnil); - } - else { - $$ = call_op($2, '~', 0); - } + $$ = call_op($2, '~', 0); } | arg LSHFT arg { @@ -540,16 +582,18 @@ arg : variable '=' arg { $$ = call_op($1, RSHFT, 1, $3); } - | arg COLON2 arg + | arg COLON2 cname { - $$ = call_op($1, COLON2, 1, $3); + $$ = NEW_COLON2($1, $3); } | arg ANDOP arg { + value_expr($1); $$ = NEW_AND(cond($1), cond($3)); } | arg OROP arg { + value_expr($1); $$ = NEW_OR(cond($1), cond($3)); } | primary @@ -562,10 +606,6 @@ call_args : /* none */ $$ = Qnil; } | call_args0 opt_nl - | '*' arg opt_nl - { - $$ = $2; - } call_args0 : args | assocs @@ -576,10 +616,19 @@ call_args0 : args { $$ = list_append($1, NEW_HASH($3)); } - | args comma '*' arg + | args comma assocs comma STAR arg + { + $$ = list_append($1, NEW_HASH($3)); + $$ = call_op($$, '+', 1, $6); + } + | args comma STAR arg { $$ = call_op($1, '+', 1, $4); } + | STAR arg + { + $$ = $2; + } opt_args : /* none */ { @@ -630,15 +679,16 @@ primary : literal | XSTRING2 | DREGEXP | var_ref + | backref | SUPER '(' call_args rparen { - if (!cur_mid && !in_single) + if (!cur_mid && !in_single && !in_defined) Error("super called outside of method"); $$ = NEW_SUPER($3); } | SUPER { - if (!cur_mid && !in_single) + if (!cur_mid && !in_single && !in_defined) Error("super called outside of method"); $$ = NEW_ZSUPER(); } @@ -710,25 +760,39 @@ primary : literal { $$ = NEW_YIELD(Qnil); } - | primary '{' opt_iter_var compexpr rbrace + | DEFINED '(' {in_defined = 1;} arg ')' + { + in_defined = 0; + $$ = NEW_DEFINED($4); + } + | primary '{' + { + $<vars>$ = dyna_push(); + } + opt_iter_var + compexpr rbrace { if (nd_type($1) == NODE_LVAR - || nd_type($1) == NODE_LVAR2 || nd_type($1) == NODE_CVAR) { $1 = NEW_FCALL($1->nd_vid, Qnil); } - $$ = NEW_ITER($3, $1, $4); + $$ = NEW_ITER($4, $1, $5); + dyna_pop($<vars>3); + } + | FID + { + $$ = NEW_FCALL($1, Qnil); } - | IDENTIFIER '(' call_args rparen + | operation '(' call_args rparen { $$ = NEW_FCALL($1, $3); } - | primary '.' IDENTIFIER '(' call_args rparen + | primary '.' operation '(' call_args rparen { value_expr($1); $$ = NEW_CALL($1, $3, $5); } - | primary '.' IDENTIFIER + | primary '.' operation { value_expr($1); $$ = NEW_CALL($1, $3, Qnil); @@ -738,10 +802,12 @@ primary : literal if_tail END { + value_expr($2); $$ = NEW_IF(cond($2), $4, $5); } | WHILE expr term compexpr END { + value_expr($2); $$ = NEW_WHILE(cond($2), $4); } | CASE compexpr @@ -751,29 +817,20 @@ primary : literal value_expr($2); $$ = NEW_CASE($2, $3); } - | FOR iter_var IN expr term - compexpr - END + | FOR iter_var IN expr term compexpr END { - value_expr($4); + value_expr($2); $$ = NEW_FOR($2, $4, $6); } | BEGIN compexpr - resque + rescue ensure END { - if ($3 == Qnil && $4 == Qnil) { - $$ = $2; - } - else { - $$ = NEW_BEGIN($2, $3, $4); - } + $$ = NEW_BEGIN($2, $3, $4); } - | LPAREN expr - opt_nl - rparen + | LPAREN exprs opt_nl rparen { $$ = $2; } @@ -823,13 +880,14 @@ primary : literal { $$ = NEW_DEFN($2, $4, $5, class_nest?0:1); local_pop(); - cur_mid = Qnil; + cur_mid = 0; } | DEF singleton '.' fname { value_expr($2); in_single++; local_push(); + lex_state = EXPR_END; /* force for args */ } f_arglist compexpr @@ -849,6 +907,7 @@ if_tail : opt_else compexpr if_tail { + value_expr($2); $$ = NEW_IF(cond($2), $4, $5); } @@ -864,7 +923,11 @@ opt_else : /* none */ iter_var : lhs | mlhs -opt_iter_var : '|' /* none */ '|' +opt_iter_var : /* node */ + { + $$ = Qnil; + } + | '|' /* none */ '|' { $$ = Qnil; } @@ -887,11 +950,11 @@ case_body : WHEN args then cases : opt_else | case_body -resque : /* none */ +rescue : /* none */ { $$ = Qnil; } - | RESQUE compexpr + | RESCUE compexpr { if ($2 == Qnil) $$ = (NODE*)1; @@ -926,7 +989,6 @@ variable : IDENTIFIER | IVAR | GVAR | CONSTANT - | NTH_REF | NIL { $$ = NIL; @@ -941,6 +1003,9 @@ var_ref : variable $$ = gettable($1); } +backref : NTH_REF + | BACK_REF + superclass : term { $$ = Qnil; @@ -949,7 +1014,7 @@ superclass : term { lex_state = EXPR_BEG; } - CONSTANT + expr term { $$ = $3; } @@ -958,36 +1023,47 @@ f_arglist : '(' f_args rparen { $$ = $2; } - | term + | f_args term { - $$ = NEW_ARGS(0, -1); + $$ = $1; } f_args : /* no arg */ { - $$ = NEW_ARGS(0, -1); + $$ = NEW_ARGS(0, 0, -1); } | f_arg { - $$ = NEW_ARGS($1, -1); + $$ = NEW_ARGS($1, 0, -1); } | f_arg comma rest_arg { - $$ = NEW_ARGS($1, $3); + $$ = NEW_ARGS($1, 0, $3); } - | rest_arg + | f_arg comma f_optarg { - $$ = NEW_ARGS(Qnil, $1); + $$ = NEW_ARGS($1, $3, -1); } - | f_arg error + | f_arg comma f_optarg comma rest_arg { - lex_state = EXPR_BEG; - $$ = NEW_ARGS($1, -1); + $$ = NEW_ARGS($1, $3, $5); + } + | f_optarg + { + $$ = NEW_ARGS(0, $1, -1); + } + | f_optarg comma rest_arg + { + $$ = NEW_ARGS(0, $1, $3); + } + | rest_arg + { + $$ = NEW_ARGS(0, 0, $1); } | error { lex_state = EXPR_BEG; - $$ = NEW_ARGS(0, -1); + $$ = NEW_ARGS(0, 0, -1); } f_arg : IDENTIFIER @@ -1005,7 +1081,23 @@ f_arg : IDENTIFIER $$ += 1; } -rest_arg : '*' IDENTIFIER +f_opt : IDENTIFIER '=' arg + { + if (!is_local_id($1)) + Error("formal argument must be local variable"); + $$ = asignable($1, $3); + } + +f_optarg : f_opt + { + $$ = NEW_BLOCK($1); + } + | f_optarg comma f_opt + { + $$ = block_append($1, $3); + } + +rest_arg : STAR IDENTIFIER { if (!is_local_id($2)) Error("rest argument must be local variable"); @@ -1025,7 +1117,7 @@ singleton : var_ref $$ = $1; } } - | LPAREN compexpr rparen + | LPAREN expr opt_nl rparen { switch (nd_type($2)) { case NODE_STR: @@ -1048,6 +1140,9 @@ assoc_list : /* none */ $$ = Qnil; } | assocs trailer + { + $$ = $1; + } | args trailer { if ($1->nd_alen%2 != 0) { @@ -1067,6 +1162,9 @@ assoc : arg ASSOC arg $$ = list_append(NEW_LIST($1), $3); } +operation : IDENTIFIER + | FID + opt_term : /* none */ | term @@ -1109,7 +1207,6 @@ VALUE newinteger(); char *strdup(); static NODE *var_extend(); -static void read_escape(); #define LEAVE_BS 1 @@ -1122,7 +1219,7 @@ lex_setsrc(src, ptr, len) char *ptr; int len; { - sourcefile = (char*)strdup(src); + sourcefile = strdup(src); sourceline = 1; lex_p = ptr; @@ -1151,7 +1248,7 @@ do { \ #define tokfix() (tokenbuf[tokidx]='\0') #define tok() tokenbuf #define toklen() tokidx -#define toknow() &toknbuf[tokidx] +#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0) char * newtok() @@ -1179,6 +1276,88 @@ tokadd(c) } static int +read_escape() +{ + int c; + + switch (c = nextc()) { + case '\\': /* Backslash */ + return c; + + case 'n': /* newline */ + return '\n'; + + case 't': /* horizontal tab */ + return '\t'; + + case 'r': /* carriage-return */ + return '\r'; + + case 'f': /* form-feed */ + return '\f'; + + case 'v': /* vertical tab */ + return '\13'; + + case 'a': /* alarm(bell) */ + return '\007'; + + case 'e': /* escape */ + return 033; + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + pushback(); + SCAN_OCT(c); + return c; + + case 'x': /* hex constant */ + SCAN_HEX(c); + return c; + + case 'b': /* backspace */ + return '\b'; + + case 'M': + if ((c = nextc()) != '-') { + Error("Invalid escape character syntax"); + return '\0'; + } + if ((c = nextc()) == '\\') { + return read_escape() | 0x80; + } + else if (c == -1) goto eof; + else { + return ((c & 0xff) | 0x80); + } + + case 'C': + if ((c = nextc()) != '-') { + Error("Invalid escape character syntax"); + return '\0'; + } + case 'c': + case '^': + if ((c = nextc())== '\\') { + c = read_escape(); + } + else if (c == '?') + return 0177; + else if (c == -1) goto eof; + return c & 0x9f; + + eof: + case -1: + Error("Invalid escape character syntax"); + pushback(); + return '\0'; + + default: + return c; + } +} + +static int parse_regx() { register int c; @@ -1188,7 +1367,7 @@ parse_regx() NODE *list = Qnil; newtok(); - while (c = nextc()) { + while ((c = nextc()) != -1) { switch (c) { case '[': in_brack = 1; @@ -1235,7 +1414,8 @@ parse_regx() /* fall through */ default: pushback(); - read_escape(LEAVE_BS); + tokadd('\\'); + tokadd(read_escape()); } continue; @@ -1247,6 +1427,7 @@ parse_regx() casefold = 1; } else { + casefold = 0; pushback(); } @@ -1263,7 +1444,7 @@ parse_regx() return DREGEXP; } else { - yylval.val = regexp_new(tok(), toklen(), casefold); + yylval.val = reg_new(tok(), toklen(), casefold); return REGEXP; } case -1: @@ -1279,6 +1460,8 @@ parse_regx() } tokadd(c); } + Error("unterminated regexp"); + return 0; } static int @@ -1287,7 +1470,6 @@ parse_string(term) { int c; NODE *list = Qnil; - ID id; int strstart; strstart = sourceline; @@ -1320,10 +1502,9 @@ parse_string(term) tokadd(c); } else { - int flags = 0; - if (term != '"') flags = LEAVE_BS; pushback(); - read_escape(flags); + if (term != '"') tokadd('\\'); + tokadd(read_escape()); } continue; } @@ -1369,6 +1550,7 @@ static struct kwtable { "class", CLASS, EXPR_BEG, "continue", CONTINUE, EXPR_END, "def", DEF, EXPR_FNAME, + "defined?", DEFINED, EXPR_END, "else", ELSE, EXPR_BEG, "elsif", ELSIF, EXPR_BEG, "end", END, EXPR_END, @@ -1379,9 +1561,10 @@ static struct kwtable { "in", IN, EXPR_BEG, "module", MODULE, EXPR_BEG, "nil", NIL, EXPR_END, + "not", NOT, EXPR_BEG, "or", OR, EXPR_BEG, "redo", REDO, EXPR_END, - "resque", RESQUE, EXPR_BEG, + "rescue", RESCUE, EXPR_BEG, "retry", RETRY, EXPR_END, "return", RETURN, EXPR_MID, "self", SELF, EXPR_END, @@ -1393,10 +1576,17 @@ static struct kwtable { "yield", YIELD, EXPR_END, }; +static void +arg_ambiguous() +{ + Warning("ambiguous first argument; make sure"); +} + static int yylex() { register int c; + int space_seen = 0; struct kwtable *low = kwtable, *mid, *high = LAST(kwtable); retry: @@ -1410,6 +1600,7 @@ retry: /* white spaces */ case ' ': case '\t': case '\f': case '\r': case '\13': /* '\v' */ + space_seen = 1; goto retry; case '#': /* it's a comment */ @@ -1424,14 +1615,15 @@ retry: /* fall through */ case '\n': sourceline++; - if (lex_state == EXPR_BEG || lex_state == EXPR_FNAME) + if (lex_state == EXPR_BEG + || lex_state == EXPR_FNAME) goto retry; lex_state = EXPR_BEG; return '\n'; case '*': - lex_state = EXPR_BEG; if ((c = nextc()) == '*') { + lex_state = EXPR_BEG; if (nextc() == '=') { yylval.id = POW; return OP_ASGN; @@ -1439,11 +1631,21 @@ retry: pushback(); return POW; } - else if (c == '=') { + if (c == '=') { yylval.id = '*'; + lex_state = EXPR_BEG; return OP_ASGN; } pushback(); + if (lex_state == EXPR_ARG && space_seen && !isspace(c)){ + arg_ambiguous(); + lex_state = EXPR_BEG; + return STAR; + } + if (lex_state == EXPR_BEG) { + return STAR; + } + lex_state = EXPR_BEG; return '*'; case '!': @@ -1558,9 +1760,7 @@ retry: case '?': if ((c = nextc()) == '\\') { - newtok(); - read_escape(0); - c = tok()[0]; + c = read_escape(); } c &= 0xff; yylval.val = INT2FIX(c); @@ -1592,21 +1792,27 @@ retry: return '|'; case '+': + c = nextc(); if (lex_state == EXPR_FNAME) { - if ((c = nextc()) == '@') { + if (c == '@') { return UPLUS; } pushback(); return '+'; } - c = nextc(); + if (lex_state == EXPR_ARG) { + if (!space_seen || c == '=' || isspace(c)) { + arg_ambiguous(); + lex_state = EXPR_END; + } + } if (lex_state != EXPR_END) { pushback(); if (isdigit(c)) { goto start_num; } lex_state = EXPR_BEG; - return UMINUS; + return UPLUS; } lex_state = EXPR_BEG; if (c == '=') { @@ -1617,21 +1823,28 @@ retry: return '+'; case '-': + c = nextc(); if (lex_state == EXPR_FNAME) { - if ((c = nextc()) == '@') { + if (c == '@') { return UMINUS; } pushback(); return '-'; } - c = nextc(); + if (lex_state == EXPR_ARG) { + if (!space_seen || c == '=' || isspace(c)) { + arg_ambiguous(); + lex_state = EXPR_END; + } + } if (lex_state != EXPR_END) { - pushback(); if (isdigit(c)) { + pushback(); c = '-'; goto start_num; } lex_state = EXPR_BEG; + pushback(); return UMINUS; } lex_state = EXPR_BEG; @@ -1774,16 +1987,25 @@ retry: return SYMBEG; case '/': - if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + if (lex_state == EXPR_BEG + || lex_state == EXPR_MID) { return parse_regx(); } + c = nextc(); + if (lex_state == EXPR_ARG) { + if (space_seen && c != '=' && !isspace(c)) { + pushback(); + arg_ambiguous(); + return parse_regx(); + } + } lex_state = EXPR_BEG; - if (nextc() == '=') { + if (c == '=') { yylval.id = '/'; return OP_ASGN; } pushback(); - return c; + return '/'; case '^': lex_state = EXPR_BEG; @@ -1795,6 +2017,9 @@ retry: return c; case ',': + lex_state = EXPR_BEG; + return c; + case ';': lex_state = EXPR_BEG; return c; @@ -1809,15 +2034,23 @@ retry: return c; case '(': - if (lex_state != EXPR_END) + if (lex_state == EXPR_BEG + || lex_state == EXPR_MID) { c = LPAREN; - lex_state = EXPR_BEG; + lex_state = EXPR_BEG; + } + else if (lex_state == EXPR_ARG && space_seen) { + arg_ambiguous(); + c = LPAREN; + lex_state = EXPR_BEG; + } + else { + lex_state = EXPR_BEG; + } return c; case '[': - if (lex_state == EXPR_BEG || lex_state == EXPR_MID) - c = LBRACK; - else if (lex_state == EXPR_FNAME) { + if (lex_state == EXPR_FNAME) { if ((c = nextc()) == ']') { if ((c = nextc()) == '=') { return ASET; @@ -1828,11 +2061,19 @@ retry: pushback(); return '['; } + else if (lex_state == EXPR_BEG + || lex_state == EXPR_MID) { + c = LBRACK; + } + else if (lex_state == EXPR_ARG && space_seen) { + arg_ambiguous(); + c = LBRACK; + } lex_state = EXPR_BEG; return c; case '{': - if (lex_state != EXPR_END) + if (lex_state != EXPR_END && lex_state != EXPR_ARG) c = LBRACE; lex_state = EXPR_BEG; return c; @@ -1841,6 +2082,7 @@ retry: c = nextc(); if (c == '\n') { sourceline++; + space_seen = 1; goto retry; /* skip \\n */ } pushback(); @@ -1860,6 +2102,9 @@ retry: newtok(); c = nextc(); switch (c) { + case '~': /* $~: match-data */ + local_cnt('~'); + /* fall through */ case '*': /* $*: argv */ case '$': /* $$: pid */ case '?': /* $?: last status */ @@ -1870,51 +2115,42 @@ retry: case ',': /* $,: output field separator */ case '.': /* $.: last read line number */ case '_': /* $_: last read line string */ - case '&': /* $&: last match */ - case '`': /* $&: string before last match */ - case '\'': /* $&: string after last match */ - case '+': /* $&: string matches last paren. */ - case '~': /* $~: match-data */ case '=': /* $=: ignorecase */ case ':': /* $:: load path */ case '<': /* $<: reading filename */ case '>': /* $>: default output handle */ - case '"': /* $": already loaded files */ + case '\"': /* $": already loaded files */ tokadd('$'); tokadd(c); tokfix(); yylval.id = rb_intern(tok()); return GVAR; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case '&': /* $&: last match */ + case '`': /* $`: string before last match */ + case '\'': /* $': string after last match */ + case '+': /* $+: string matches last paren. */ + yylval.node = NEW_BACK_REF(c); + return BACK_REF; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': while (isdigit(c)) { tokadd(c); c = nextc(); } pushback(); tokfix(); - { - ID id = atoi(tok()); - id <<= ID_SCOPE_SHIFT; - id |= ID_NTHREF; - yylval.id = id; - return NTH_REF; - } + yylval.node = NEW_NTH_REF(atoi(tok())); + return NTH_REF; - case '0': default: if (!is_identchar(c)) { pushback(); return '$'; } + case '0': tokadd('$'); } break; @@ -1947,7 +2183,12 @@ retry: } c = nextc(); } - pushback(); + if (c == '!' || c == '?') { + tokadd(c); + } + else { + pushback(); + } tokfix(); { @@ -1955,9 +2196,11 @@ retry: switch (tok()[0]) { case '$': + lex_state = EXPR_END; result = GVAR; break; case '@': + lex_state = EXPR_END; result = IVAR; break; default: @@ -1967,7 +2210,8 @@ retry: if (( c = strcmp(mid->name, tok())) == 0) { enum lex_state state = lex_state; lex_state = mid->state; - if (state != EXPR_BEG) { + if (state != EXPR_BEG + && state != EXPR_BEG) { if (mid->id == IF) return IF_MOD; if (mid->id == WHILE) return WHILE_MOD; } @@ -1982,6 +2226,7 @@ retry: } if (lex_state == EXPR_FNAME) { + lex_state = EXPR_END; if ((c = nextc()) == '=') { tokadd(c); } @@ -1989,14 +2234,21 @@ retry: pushback(); } } + else if (lex_state == EXPR_BEG){ + lex_state = EXPR_ARG; + } + else { + lex_state = EXPR_END; + } if (isupper(tok()[0])) { result = CONSTANT; } - else { + else if (toklast() == '!' || toklast() == '?') { + result = FID; + } else { result = IDENTIFIER; } } - lex_state = EXPR_END; yylval.id = rb_intern(tok()); return result; } @@ -2007,16 +2259,16 @@ var_extend(list, term) NODE *list; char term; { - int c, t; + int c, t, brace; VALUE ss; + NODE *node; ID id; c = nextc(); switch (c) { - default: - tokadd('#'); - pushback(); - return list; + case '$': + case '{': + break; case '@': t = nextc(); pushback(); @@ -2025,9 +2277,10 @@ var_extend(list, term) tokadd(c); return list; } - case '$': - case '{': - break; + default: + tokadd('#'); + pushback(); + return list; } ss = str_new(tok(), toklen()); @@ -2038,49 +2291,64 @@ var_extend(list, term) list_append(list, NEW_STR(ss)); } newtok(); - if (c == '{') { - while ((c = nextc()) != '}') { - if (c == -1) { - return (NODE*)-1; - } - if (isspace(c)) { - Error("Invalid variable name in string"); - break; - } - if (c == term) { - Error("Inmature variable name in string"); - pushback(); - return list; - } - tokadd(c); - } + if (c == '{') { + brace = 1; + c = nextc(); } else { + brace = 0; + } + + switch (c) { + case '$': + c = nextc(); + if (c == -1) return (NODE*)-1; switch (c) { - case '$': - tokadd(c); + case '&': + case '`': + case '\'': + case '+': + node = NEW_BACK_REF(c); c = nextc(); - if (c == -1) return (NODE*)-1; - if (!is_identchar(c)) { + goto append_node; + + case '~': + local_cnt('~'); + id = '~'; + goto id_node; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + while (isdigit(c)) { tokadd(c); - goto fetch_id; + c = nextc(); } - /* through */ - case '@': - tokadd(c); - c = nextc(); - break; + tokfix(); + node = NEW_NTH_REF(atoi(tok())); + goto append_node; } - while (is_identchar(c)) { + + tokadd('$'); + if (!is_identchar(c)) { tokadd(c); - if (ismbchar(c)) { - c = nextc(); - tokadd(c); - } + goto fetch_id; + } + /* through */ + case '@': + tokadd(c); + c = nextc(); + break; + } + while (is_identchar(c)) { + tokadd(c); + if (ismbchar(c)) { c = nextc(); + tokadd(c); } - pushback(); + c = nextc(); } + fetch_id: tokfix(); if (strcmp("__LINE__", tok()) == 0) @@ -2089,90 +2357,21 @@ var_extend(list, term) id = _FILE_; else id = rb_intern(tok()); - list_append(list, gettable(id)); + id_node: + node = gettable(id); + + append_node: + if (brace) { + if (c != '}') + Error("Invalid variable name in string"); + } + else pushback(); + + list_append(list, node); newtok(); return list; } -static void -read_escape(flag) - int flag; -{ - char c; - - switch (c = nextc()) { - case '\\': /* Backslash */ - tokadd('\\'); - break; - - case 'n': /* newline */ - tokadd('\n'); - break; - - case 't': /* horizontal tab */ - tokadd('\t'); - break; - - case 'r': /* carriage-return */ - tokadd('\r'); - break; - - case 'f': /* form-feed */ - tokadd('\f'); - break; - - case 'v': /* vertical tab */ - tokadd('\13'); - break; - - case 'a': /* alarm(bell) */ - tokadd('\007'); - break; - - case 'e': /* escape */ - tokadd(033); - break; - - case 'c': - if ((c = nextc()) == '?') - tokadd(0177); - else { - if (islower(c)) - c = toupper(c); - c = c - '@'; - tokadd(c); - } - break; - - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - { /* octal constant */ - pushback(); - SCAN_OCT(c); - tokadd(c); - } - break; - - case 'x': /* hex constant */ - { - SCAN_HEX(c); - tokadd(c); - } - break; - - case 'b': /* backspace */ - tokadd('\b'); - return; - - default: - if (flag & LEAVE_BS) { - tokadd('\\'); - } - case '#': - tokadd(c); - break; - } -} NODE* newnode(type, a0, a1, a2) @@ -2282,72 +2481,6 @@ list_concat(head, tail) return head; } -static NODE* -list_copy(list) - NODE *list; -{ - NODE *tmp; - - if (list == Qnil) return Qnil; - - tmp = Qnil; - while(list) { - tmp = list_append(tmp, list->nd_head); - list = list->nd_next; - } - return tmp; -} - -struct call_arg { - ID id; - VALUE recv; - int narg; - VALUE arg; -}; - -static VALUE -call_lit(arg) - struct call_arg *arg; -{ - return rb_funcall(arg->recv, arg->id, arg->narg, arg->arg); -} - -static VALUE -except_lit() -{ - extern VALUE errstr; - - Error("%s", RSTRING(errstr)->ptr); - return Qnil; -} - -static NODE * -expand_op(recv, id, arg) - NODE *recv, *arg; - ID id; -{ - struct call_arg arg_data; - VALUE val; - NODE *result; - - arg_data.recv = recv->nd_lit; - arg_data.id = id; - arg_data.narg = arg?1:0; - arg_data.arg = arg->nd_lit; - - val = rb_resque(call_lit, &arg_data, except_lit, Qnil); - if (TYPE(val) == T_STRING) { - result = NEW_STR(val); - } - else { - result = NEW_LIT(val); - } - - return result; -} - -#define NODE_IS_CONST(n) (nd_type(n) == NODE_LIT || nd_type(n) == NODE_STR) - static NODE * call_op(recv, id, narg, arg1) NODE *recv; @@ -2360,9 +2493,6 @@ call_op(recv, id, narg, arg1) value_expr(arg1); } - if (NODE_IS_CONST(recv) && (narg == 0 || NODE_IS_CONST(arg1))) { - return expand_op(recv, id, (narg == 1)?arg1:Qnil); - } return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):Qnil); } @@ -2385,10 +2515,10 @@ gettable(id) return NEW_STR(s); } else if (is_local_id(id)) { - if (local_id(id)) - return NEW_LVAR(id); - else - return NEW_LVAR2(id); + if (local_id(id)) return NEW_LVAR(id); + if (dyna_id(id)) return NEW_DVAR(id); + /* method call without arguments */ + return NEW_FCALL(id, Qnil); } else if (is_global_id(id)) { return NEW_GVAR(id); @@ -2399,9 +2529,8 @@ gettable(id) else if (is_const_id(id)) { return NEW_CVAR(id); } - else if (is_nthref_id(id)) { - return NEW_NTH_REF(id>>ID_SCOPE_SHIFT); - } + Bug("invalid id for gettable"); + return Qnil; } static NODE* @@ -2409,6 +2538,7 @@ asignable(id, val) ID id; NODE *val; { + extern VALUE dyna_var_asgn(); NODE *lhs = Qnil; if (id == SELF) { @@ -2421,7 +2551,12 @@ asignable(id, val) Error("Can't asign to special identifier"); } else if (is_local_id(id)) { - lhs = NEW_LASGN(id, val); + if (local_id(id) || !dyna_in_block()) + lhs = NEW_LASGN(id, val); + else{ + dyna_var_asgn(id, TRUE); + lhs = NEW_DASGN(id, val); + } } else if (is_global_id(id)) { lhs = NEW_GASGN(id, val); @@ -2434,9 +2569,6 @@ asignable(id, val) Error("class constant asigned in method body"); lhs = NEW_CASGN(id, val); } - else if (is_nthref_id(id)) { - Error("Can't set variable $%d", id>>ID_SCOPE_SHIFT); - } else { Bug("bad id for variable"); } @@ -2452,6 +2584,15 @@ aryset(recv, idx, val) return NEW_CALL(recv, ASET, list_append(idx, val)); } +ID +id_attrset(id) + ID id; +{ + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + return id; +} + static NODE * attrset(recv, id, val) NODE *recv, *val; @@ -2460,12 +2601,26 @@ attrset(recv, id, val) value_expr(recv); value_expr(val); - id &= ~ID_SCOPE_MASK; + id &= ~ID_SCOPE_MASK; id |= ID_ATTRSET; return NEW_CALL(recv, id, NEW_LIST(val)); } +static void +backref_error(node) + NODE *node; +{ + switch (nd_type(node)) { + case NODE_NTH_REF: + Error("Can't set variable $%d", node->nd_nth); + break; + case NODE_BACK_REF: + Error("Can't set variable $%c", node->nd_nth); + break; + } +} + static int value_expr(node) NODE *node; @@ -2503,6 +2658,8 @@ value_expr(node) } } +static NODE *cond2(); + static NODE* cond0(node) NODE *node; @@ -2515,6 +2672,12 @@ cond0(node) else if (type == NODE_LIT && TYPE(node->nd_lit) == T_REGEXP) { return call_op(node,MATCH,1,NEW_GVAR(rb_intern("$_"))); } + else if (type == NODE_DOT2 || type == NODE_DOT3) { + node->nd_beg = cond2(node->nd_beg); + node->nd_end = cond2(node->nd_end); + if (type == NODE_DOT2) nd_set_type(node,NODE_FLIP2); + else if (type == NODE_DOT3) nd_set_type(node, NODE_FLIP3); + } return node; } @@ -2524,10 +2687,10 @@ cond(node) { enum node_type type = nd_type(node); - value_expr(node); switch (type) { case NODE_MASGN: case NODE_LASGN: + case NODE_DASGN: case NODE_GASGN: case NODE_IASGN: case NODE_CASGN: @@ -2561,6 +2724,7 @@ st_table *new_idhash(); static struct local_vars { ID *tbl; int cnt; + int dlev; struct local_vars *prev; } *lvtbl; @@ -2572,7 +2736,8 @@ local_push() local = ALLOC(struct local_vars); local->prev = lvtbl; local->cnt = 0; - local->tbl = Qnil; + local->tbl = 0; + local->dlev = 0; lvtbl = local; } @@ -2600,11 +2765,11 @@ local_cnt(id) if (id == 0) return lvtbl->cnt; - for (cnt=0, max=lvtbl->cnt; cnt<max ;cnt++) { - if (lvtbl->tbl[cnt+1] == id) return cnt; + for (cnt=1, max=lvtbl->cnt+1; cnt<max ;cnt++) { + if (lvtbl->tbl[cnt] == id) return cnt-1; } - if (lvtbl->tbl == Qnil) { + if (lvtbl->tbl == 0) { lvtbl->tbl = ALLOC_N(ID, 2); lvtbl->tbl[0] = 0; } @@ -2622,7 +2787,7 @@ local_id(id) { int i, max; - if (lvtbl == Qnil) return FALSE; + if (lvtbl == 0) return FALSE; for (i=1, max=lvtbl->cnt+1; i<max; i++) { if (lvtbl->tbl[i] == id) return TRUE; } @@ -2632,7 +2797,7 @@ local_id(id) static void top_local_init() { - if (lvtbl == Qnil) { + if (lvtbl == 0) { local_push(); } else if (the_scope->local_tbl) { @@ -2643,12 +2808,13 @@ top_local_init() } if (lvtbl->cnt > 0) { lvtbl->tbl = ALLOC_N(ID, lvtbl->cnt+1); - MEMCPY(lvtbl->tbl, the_scope->local_tbl, ID, lvtbl->cnt); + MEMCPY(lvtbl->tbl, the_scope->local_tbl, ID, lvtbl->cnt+1); } else { - lvtbl->tbl = Qnil; + lvtbl->tbl = 0; } NEW_CREF0(); /* initialize constant c-ref */ + lvtbl->dlev = (the_dyna_vars?1:0); } static void @@ -2661,14 +2827,7 @@ top_local_setup() i = lvtbl->tbl[0]; if (i < len) { - if (the_scope->flags & SCOPE_MALLOCED) { - VALUE *vars = the_scope->local_vars; - - REALLOC_N(the_scope->local_vars, VALUE, len); - MEMZERO(the_scope->local_vars+i, VALUE, len-i); - free(the_scope->local_tbl); - } - else { + if (the_scope->flag == SCOPE_ALLOCA) { VALUE *vars = the_scope->local_vars; the_scope->local_vars = ALLOC_N(VALUE, len); if (vars) { @@ -2678,10 +2837,15 @@ top_local_setup() else { MEMZERO(the_scope->local_vars, VALUE, len); } + the_scope->flag = SCOPE_MALLOC; + } + else { + REALLOC_N(the_scope->local_vars, VALUE, len); + MEMZERO(the_scope->local_vars+i, VALUE, len-i); + free(the_scope->local_tbl); } lvtbl->tbl[0] = len; the_scope->local_tbl = lvtbl->tbl; - the_scope->flags |= SCOPE_MALLOCED; } else if (lvtbl->tbl) { free(lvtbl->tbl); @@ -2690,6 +2854,27 @@ top_local_setup() cref_list = Qnil; } +static struct RVarmap* +dyna_push() +{ + lvtbl->dlev++; + return the_dyna_vars; +} + +static void +dyna_pop(vars) + struct RVarmap* vars; +{ + lvtbl->dlev--; + the_dyna_vars = vars; +} + +static int +dyna_in_block() +{ + return (lvtbl->dlev > 0); +} + static void cref_pop() { @@ -2721,7 +2906,7 @@ yywhole_loop(chop, split) if (chop) { eval_tree = block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")), - rb_intern("chop"), Qnil), eval_tree); + rb_intern("chop!"), Qnil), eval_tree); } eval_tree = NEW_WHILE(NEW_FCALL(rb_intern("gets"),0),eval_tree); } @@ -2808,14 +2993,14 @@ rb_intern(name) /* operator */ int i; - id = Qnil; + id = 0; for (i=0; rb_op_tbl[i].token; i++) { if (strcmp(rb_op_tbl[i].name, name) == 0) { id = rb_op_tbl[i].token; break; } } - if (id == Qnil) Bug("Unknown operator `%s'", name); + if (id == 0) Bug("Unknown operator `%s'", name); break; } @@ -2844,7 +3029,7 @@ rb_intern(name) static char *find_ok; -static +static int id_find(name, id1, id2) char *name; ID id1, id2; @@ -2860,7 +3045,7 @@ char * rb_id2name(id) ID id; { - find_ok = Qnil; + find_ok = 0; if (id < LAST_TOKEN) { int i = 0; @@ -2897,7 +3082,7 @@ const_check(id, val, class) VALUE val; struct RClass *class; { - if (is_const_id(id) && rb_const_bound(class, id)) { + if (is_const_id(id) && rb_const_defined(class, id)) { Warning("constant redefined for %s", rb_class2name(class)); return ST_STOP; } @@ -11,11 +11,20 @@ ************************************************/ #include "ruby.h" +#include "sig.h" #include <stdio.h> #include <errno.h> #include <ctype.h> #include <signal.h> -#include <sys/time.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif + #include <sys/resource.h> #ifdef HAVE_VFORK_H #include <vfork.h> @@ -31,9 +40,17 @@ get_pid() static VALUE get_ppid() { +#ifdef NT + return INT2FIX(0); +#else return INT2FIX(getppid()); +#endif } +#ifdef NT +#define HAVE_WAITPID +#endif + static VALUE status; #if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4) @@ -96,7 +113,7 @@ static wait_each(key, value) #endif static VALUE -Fwait(obj) +f_wait() { int pid, state; @@ -118,12 +135,12 @@ Fwait(obj) } static VALUE -Fwaitpid(obj, vpid, vflags) +f_waitpid(obj, vpid, vflags) VALUE obj, vpid, vflags; { int pid, flags; - if (vflags == Qnil) flags = Qnil; + if (vflags == Qnil) flags = 0; else flags = FIX2UINT(vflags); if ((pid = rb_waitpid(FIX2UINT(vpid), flags)) < 0) @@ -133,6 +150,7 @@ Fwaitpid(obj, vpid, vflags) char *strtok(); +int rb_proc_exec(str) char *str; { @@ -161,7 +179,7 @@ rb_proc_exec(str) } static VALUE -Fexec(obj, str) +f_exec(obj, str) VALUE obj; struct RString *str; { @@ -171,18 +189,22 @@ Fexec(obj, str) } static VALUE -Ffork(obj) +f_fork(obj) VALUE obj; { int pid; switch (pid = fork()) { case 0: - return INT2FIX(0); + if (iterator_p()) { + rb_yield(Qnil); + _exit(0); + } + return Qnil; case -1: rb_sys_fail("fork(2)"); - break; + return Qnil; default: return INT2FIX(pid); @@ -190,7 +212,7 @@ Ffork(obj) } static VALUE -F_exit(obj, status) +f_exit_bang(obj, status) VALUE obj, status; { int code = -1; @@ -223,11 +245,21 @@ rb_syswait(pid) } static VALUE -Fsystem(obj, str) +f_system(obj, str) VALUE obj; struct RString *str; { - int pid, w; +#ifdef NT + int state; + + Check_Type(str, T_STRING); + state = do_spawn(str->ptr); + status = INT2FIX(state); + + if (state == 0) return TRUE; + return FALSE; +#else + int pid; Check_Type(str, T_STRING); @@ -255,10 +287,13 @@ Fsystem(obj, str) rb_syswait(pid); } - return status; + if (status == INT2FIX(0)) return TRUE; + return FALSE; +#endif } -Fsleep(argc, argv) +VALUE +f_sleep(argc, argv) int argc; VALUE *argv; { @@ -269,7 +304,9 @@ Fsleep(argc, argv) sleep((32767<<16)+32767); } else if (argc == 1) { + TRAP_BEG; sleep(NUM2INT(argv[0])); + TRAP_END; } else { Fail("wrong # of arguments"); @@ -280,8 +317,9 @@ Fsleep(argc, argv) return int2inum(end); } +#ifndef NT static VALUE -Fproc_getpgrp(argc, argv, obj) +proc_getpgrp(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -302,7 +340,7 @@ Fproc_getpgrp(argc, argv, obj) } static VALUE -Fproc_setpgrp(obj, pid, pgrp) +proc_setpgrp(obj, pid, pgrp) VALUE obj, pid, pgrp; { int ipid, ipgrp; @@ -316,7 +354,7 @@ Fproc_setpgrp(obj, pid, pgrp) } static VALUE -Fproc_getpriority(obj, which, who) +proc_getpriority(obj, which, who) VALUE obj, which, who; { #ifdef HAVE_GETPRIORITY @@ -334,7 +372,7 @@ Fproc_getpriority(obj, which, who) } static VALUE -Fproc_setpriority(obj, which, who, prio) +proc_setpriority(obj, which, who, prio) VALUE obj, which, who, prio; { #ifdef HAVE_GETPRIORITY @@ -351,9 +389,10 @@ Fproc_setpriority(obj, which, who, prio) Fail("The setpriority() function is unimplemented on this machine"); #endif } +#endif static VALUE -Fproc_getuid(obj) +proc_getuid(obj) VALUE obj; { int uid = getuid(); @@ -361,7 +400,7 @@ Fproc_getuid(obj) } static VALUE -Fproc_setuid(obj, id) +proc_setuid(obj, id) VALUE obj, id; { int uid; @@ -385,7 +424,7 @@ Fproc_setuid(obj, id) } static VALUE -Fproc_getgid(obj) +proc_getgid(obj) VALUE obj; { int gid = getgid(); @@ -393,7 +432,7 @@ Fproc_getgid(obj) } static VALUE -Fproc_setgid(obj, id) +proc_setgid(obj, id) VALUE obj, id; { int gid; @@ -417,7 +456,7 @@ Fproc_setgid(obj, id) } static VALUE -Fproc_geteuid(obj) +proc_geteuid(obj) VALUE obj; { int euid = geteuid(); @@ -425,7 +464,7 @@ Fproc_geteuid(obj) } static VALUE -Fproc_seteuid(obj, euid) +proc_seteuid(obj, euid) VALUE obj, euid; { #ifdef HAVE_SETEUID @@ -445,7 +484,7 @@ Fproc_seteuid(obj, euid) } static VALUE -Fproc_getegid(obj) +proc_getegid(obj) VALUE obj; { int egid = getegid(); @@ -453,7 +492,7 @@ Fproc_getegid(obj) } static VALUE -Fproc_setegid(obj, egid) +proc_setegid(obj, egid) VALUE obj, egid; { #ifdef HAVE_SETEGID @@ -472,53 +511,58 @@ Fproc_setegid(obj, egid) return egid; } -VALUE rb_readonly_hook(); -VALUE M_Process; +VALUE mProcess; -extern VALUE Fkill(); +extern VALUE f_kill(); +void Init_process() { - extern VALUE C_Kernel; - - rb_define_variable("$$", Qnil, get_pid, Qnil, 0); - rb_define_variable("$?", &status, Qnil, rb_readonly_hook, 0); - rb_define_private_method(C_Kernel, "exec", Fexec, 1); - rb_define_private_method(C_Kernel, "fork", Ffork, 0); - rb_define_private_method(C_Kernel, "_exit", Ffork, 1); - rb_define_private_method(C_Kernel, "wait", Fwait, 0); - rb_define_private_method(C_Kernel, "waitpid", Fwaitpid, 2); - rb_define_private_method(C_Kernel, "system", Fsystem, 1); - rb_define_private_method(C_Kernel, "sleep", Fsleep, -1); - - M_Process = rb_define_module("Process"); - rb_extend_object(M_Process, M_Process); - - rb_define_single_method(M_Process, "fork", Ffork, 0); - rb_define_single_method(M_Process, "_exit", Ffork, 1); - rb_define_single_method(M_Process, "wait", Fwait, 0); - rb_define_single_method(M_Process, "waitpid", Fwaitpid, 2); - rb_define_single_method(M_Process, "kill", Fkill, -1); - - rb_define_method(M_Process, "pid", get_pid, 0); - rb_define_method(M_Process, "ppid", get_ppid, 0); - - rb_define_method(M_Process, "getpgrp", Fproc_getpgrp, -1); - rb_define_method(M_Process, "setpgrp", Fproc_setpgrp, 2); - - rb_define_method(M_Process, "getpriority", Fproc_getpriority, 2); - rb_define_method(M_Process, "setpriority", Fproc_setpriority, 3); - - rb_define_const(M_Process, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS)); - rb_define_const(M_Process, "PRIO_PGRP", INT2FIX(PRIO_PGRP)); - rb_define_const(M_Process, "PRIO_USER", INT2FIX(PRIO_USER)); - - rb_define_method(M_Process, "uid", Fproc_getuid, 0); - rb_define_method(M_Process, "uid=", Fproc_setuid, 1); - rb_define_method(M_Process, "gid", Fproc_getgid, 0); - rb_define_method(M_Process, "gid=", Fproc_setgid, 1); - rb_define_method(M_Process, "euid", Fproc_geteuid, 0); - rb_define_method(M_Process, "euid=", Fproc_seteuid, 1); - rb_define_method(M_Process, "egid", Fproc_getegid, 0); - rb_define_method(M_Process, "egid=", Fproc_setegid, 1); + extern VALUE cKernel; + + rb_define_virtual_variable("$$", get_pid, Qnil); + rb_define_readonly_variable("$?", &status); +#ifndef NT + rb_define_private_method(cKernel, "exec", f_exec, 1); + rb_define_private_method(cKernel, "fork", f_fork, 0); + rb_define_private_method(cKernel, "exit!", f_exit_bang, 1); + rb_define_private_method(cKernel, "wait", f_wait, 0); + rb_define_private_method(cKernel, "waitpid", f_waitpid, 2); +#endif + rb_define_private_method(cKernel, "system", f_system, 1); + rb_define_private_method(cKernel, "sleep", f_sleep, -1); + + mProcess = rb_define_module("Process"); + +#ifndef NT + rb_define_singleton_method(mProcess, "fork", f_fork, 0); + rb_define_singleton_method(mProcess, "exit!", f_exit_bang, 1); + rb_define_singleton_method(mProcess, "wait", f_wait, 0); + rb_define_singleton_method(mProcess, "waitpid", f_waitpid, 2); + rb_define_singleton_method(mProcess, "kill", f_kill, -1); +#endif + + rb_define_module_function(mProcess, "pid", get_pid, 0); + rb_define_module_function(mProcess, "ppid", get_ppid, 0); + +#ifndef NT + rb_define_module_function(mProcess, "getpgrp", proc_getpgrp, -1); + rb_define_module_function(mProcess, "setpgrp", proc_setpgrp, 2); + + rb_define_module_function(mProcess, "getpriority", proc_getpriority, 2); + rb_define_module_function(mProcess, "setpriority", proc_setpriority, 3); + + rb_define_const(mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS)); + rb_define_const(mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP)); + rb_define_const(mProcess, "PRIO_USER", INT2FIX(PRIO_USER)); + + rb_define_module_function(mProcess, "uid", proc_getuid, 0); + rb_define_module_function(mProcess, "uid=", proc_setuid, 1); + rb_define_module_function(mProcess, "gid", proc_getgid, 0); + rb_define_module_function(mProcess, "gid=", proc_setgid, 1); + rb_define_module_function(mProcess, "euid", proc_geteuid, 0); + rb_define_module_function(mProcess, "euid=", proc_seteuid, 1); + rb_define_module_function(mProcess, "egid", proc_getegid, 0); + rb_define_module_function(mProcess, "egid=", proc_setegid, 1); +#endif } @@ -16,7 +16,7 @@ static int first = 1; static char state[256]; static VALUE -Fsrand(argc, argv, obj) +f_srand(argc, argv, obj) int argc; VALUE *argv; VALUE obj; @@ -54,7 +54,7 @@ Fsrand(argc, argv, obj) } static VALUE -Frand(obj, max) +f_rand(obj, max) VALUE obj, max; { int val; @@ -73,10 +73,11 @@ Frand(obj, max) return int2inum(val); } +void Init_Random() { - extern VALUE C_Kernel; + extern VALUE cKernel; - rb_define_private_method(C_Kernel, "srand", Fsrand, -1); - rb_define_private_method(C_Kernel, "rand", Frand, 1); + rb_define_private_method(cKernel, "srand", f_srand, -1); + rb_define_private_method(cKernel, "rand", f_rand, 1); } @@ -12,18 +12,21 @@ #include "ruby.h" -VALUE M_Comparable; -VALUE C_Range; +VALUE mComparable; +static VALUE cRange; -static ID next; +static ID upto; static VALUE -Srng_new(class, start, end) +range_s_new(class, start, end) VALUE class, start, end; { VALUE obj; - if (!obj_is_kind_of(start, M_Comparable) || TYPE(start) != TYPE(end)) { + if (!(FIXNUM_P(start) && FIXNUM_P(end)) + && (TYPE(start) != TYPE(end) + || CLASS_OF(start) != CLASS_OF(end) + || !rb_responds_to(start, upto))) { Fail("bad value for range"); } @@ -39,11 +42,11 @@ VALUE range_new(start, end) VALUE start, end; { - return Srng_new(C_Range, start, end); + return range_s_new(cRange, start, end); } static VALUE -Frng_match(rng, obj) +range_match(rng, obj) VALUE rng, obj; { VALUE beg, end; @@ -71,13 +74,15 @@ struct upto_data { VALUE end; }; -static rng_upto(data) +static VALUE +range_upto(data) struct upto_data *data; { - return rb_funcall(data->beg, rb_intern("upto"), 1, data->end); + return rb_funcall(data->beg, upto, 1, data->end); } -static rng_upto_yield(v) +static VALUE +range_upto_yield(v) VALUE v; { rb_yield(v); @@ -85,19 +90,19 @@ static rng_upto_yield(v) } static VALUE -Frng_each(obj) +range_each(obj) VALUE obj; { - VALUE b, e, current; + VALUE b, e; b = rb_iv_get(obj, "start"); e = rb_iv_get(obj, "end"); if (FIXNUM_P(b)) { /* fixnum is a special case(for performance) */ - Fnum_upto(b, e); + num_upto(b, e); } else if (TYPE(b) == T_STRING) { - Fstr_upto(b, e); + str_upto(b, e); } else { struct upto_data data; @@ -105,14 +110,14 @@ Frng_each(obj) data.beg = b; data.end = e; - rb_iterate(rng_upto, &data, rng_upto_yield, Qnil); + rb_iterate(range_upto, &data, range_upto_yield, Qnil); } return Qnil; } static VALUE -Frng_start(obj) +range_start(obj) VALUE obj; { VALUE b; @@ -122,7 +127,7 @@ Frng_start(obj) } static VALUE -Frng_end(obj) +range_end(obj) VALUE obj; { VALUE e; @@ -132,7 +137,7 @@ Frng_end(obj) } static VALUE -Frng_to_s(obj) +range_to_s(obj) VALUE obj; { VALUE args[4]; @@ -140,21 +145,36 @@ Frng_to_s(obj) args[0] = str_new2("%d..%d"); args[1] = rb_iv_get(obj, "start"); args[2] = rb_iv_get(obj, "end"); - return Fsprintf(3, args); + return f_sprintf(3, args); +} + +VALUE +range_beg_end(range, begp, endp) + VALUE range; + int *begp, *endp; +{ + int beg, end; + + if (!obj_is_kind_of(range, cRange)) return FALSE; + + beg = rb_iv_get(range, "start"); *begp = NUM2INT(beg); + end = rb_iv_get(range, "end"); *endp = NUM2INT(end); + return TRUE; } -extern VALUE M_Enumerable; +extern VALUE mEnumerable; +void Init_Range() { - C_Range = rb_define_class("Range", C_Object); - rb_include_module(C_Range, M_Enumerable); - rb_define_single_method(C_Range, "new", Srng_new, 2); - rb_define_method(C_Range, "=~", Frng_match, 1); - rb_define_method(C_Range, "each", Frng_each, 0); - rb_define_method(C_Range, "start", Frng_start, 0); - rb_define_method(C_Range, "end", Frng_end, 0); - rb_define_method(C_Range, "to_s", Frng_to_s, 0); - - next = rb_intern("next"); + cRange = rb_define_class("Range", cObject); + rb_include_module(cRange, mEnumerable); + rb_define_singleton_method(cRange, "new", range_s_new, 2); + rb_define_method(cRange, "=~", range_match, 1); + rb_define_method(cRange, "each", range_each, 0); + rb_define_method(cRange, "start", range_start, 0); + rb_define_method(cRange, "end", range_end, 0); + rb_define_method(cRange, "to_s", range_to_s, 0); + + upto = rb_intern("upto"); } @@ -13,6 +13,9 @@ #include "ruby.h" #include "re.h" +#define BEG(no) regs->beg[no] +#define END(no) regs->end[no] + #if 'a' == 97 /* it's ascii */ static char casetable[] = { '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', @@ -77,20 +80,37 @@ str_cicmp(str1, str2) p1 = str1->ptr; p2 = str2->ptr; for (i = 0; i < len; i++, p1++, p2++) { - if (casetable[*p1] != casetable[*p2]) - return casetable[*p1] - casetable[*p2]; + if (casetable[(int)*p1] != casetable[(int)*p2]) + return casetable[(int)*p1] - casetable[(int)*p2]; } return str1->len - str2->len; } +#define REG_IGNORECASE FL_USER0 + +#define KCODE_NONE 0 +#define KCODE_EUC FL_USER1 +#define KCODE_SJIS FL_USER2 +#define KCODE_MASK (KCODE_EUC|KCODE_SJIS) + +static int reg_kcode = +#ifdef EUC + KCODE_EUC; +#else +# ifdef SJIS + KCODE_SJIS; +# else + KCODE_NONE; +# endif +#endif + static Regexp* make_regexp(s, len) -char *s; -int len; + char *s; + int len; { Regexp *rp; char *err; - register int c; /* Handle escaped characters first. */ @@ -111,20 +131,36 @@ int len; return rp; } -struct match last_match; +static VALUE +match_alloc() +{ + NEWOBJ(match, struct RMatch); + OBJSETUP(match, cData, T_MATCH); + + match->ptr = 0; + match->len = 0; + match->regs = ALLOC(struct re_registers); + MEMZERO(match->regs, struct re_registers, 1); + + return (VALUE)match; +} + VALUE ignorecase; int -research(reg, str, start) +reg_search(reg, str, start, regs) struct RRegexp *reg; struct RString *str; int start; + struct re_registers *regs; { int result; int casefold = ignorecase; + VALUE match = 0; + struct re_registers *regs0 = 0; /* case-flag set for the object */ - if (FL_TEST(reg, FL_USER1)) { + if (FL_TEST(reg, REG_IGNORECASE)) { casefold = TRUE; } if (casefold) { @@ -139,142 +175,113 @@ research(reg, str, start) } if (start > str->len) return -1; - result = re_search(reg->ptr, str->ptr, str->len, - start, str->len - start, &last_match.regs); - if (result >= 0) { - last_match.len = str->len; - if (last_match.ptr == Qnil) { - last_match.ptr = ALLOC_N(char, str->len+1); - } - else { - REALLOC_N(last_match.ptr, char, str->len+1); + if (regs == (struct re_registers *)-1) { + regs = 0; + } + else if (match = backref_get()) { + if (match == 1) { + match = match_alloc(); + backref_set(match); } - memcpy(last_match.ptr, str->ptr, last_match.len); - last_match.ptr[last_match.len] = '\0'; + regs0 = RMATCH(match)->regs; + } + + if (regs && !match) regs0 = regs; + + if ((RBASIC(reg)->flags & KCODE_MASK) != reg_kcode) { + char *err; + + if ((err = re_compile_pattern(reg->str, reg->len, reg->ptr)) != NULL) + Fail("%s: /%s/", err, reg->str); + RBASIC(reg)->flags = RBASIC(reg)->flags & ~KCODE_MASK; + RBASIC(reg)->flags |= reg_kcode; + } + + result = re_search(reg->ptr, str->ptr, str->len, + start, str->len - start, regs0); + + if (match && result >= 0) { + RMATCH(match)->len = str->len; + REALLOC_N(RMATCH(match)->ptr, char, str->len+1); + memcpy(RMATCH(match)->ptr, str->ptr, str->len); + RMATCH(match)->ptr[str->len] = '\0'; } + if (regs && regs0 && regs0 != regs) re_copy_registers(regs, regs0); return result; } VALUE -re_nth_match(nth) +reg_nth_defined(nth, match) int nth; + struct RMatch *match; +{ + if (!match) return FALSE; + if (nth >= match->regs->num_regs) { + return FALSE; + } + if (match->BEG(nth) == -1) return FALSE; + return TRUE; +} + +VALUE +reg_nth_match(nth, match) + int nth; + struct RMatch *match; { int start, end, len; - if (nth >= last_match.regs.num_regs) { + if (!match) return Qnil; + if (nth >= match->regs->num_regs) { return Qnil; } - start = BEG(nth); + start = match->BEG(nth); if (start == -1) return Qnil; - end = END(nth); + end = match->END(nth); len = end - start; - return str_new(last_match.ptr + start, len); + return str_new(match->ptr + start, len); } VALUE -re_last_match(id) - ID id; +reg_last_match(match) + struct RMatch *match; { - return re_nth_match(0); + return reg_nth_match(0, match); } -static VALUE -re_match_pre() +VALUE +reg_match_pre(match) + struct RMatch *match; { - struct match *match; - - if (BEG(0) == -1) return Qnil; - return str_new(last_match.ptr, BEG(0)); + if (!match) return Qnil; + if (match->BEG(0) == -1) return Qnil; + return str_new(match->ptr, match->BEG(0)); } -static VALUE -re_match_post() +VALUE +reg_match_post(match) + struct RMatch *match; { - struct match *match; - - if (BEG(0) == -1) return Qnil; - return str_new(last_match.ptr+END(0), - last_match.len-END(0)); + if (!match) return Qnil; + if (match->BEG(0) == -1) return Qnil; + return str_new(match->ptr+match->END(0), + match->len-match->END(0)); } -static VALUE -re_match_last() +VALUE +reg_match_last(match) + struct RMatch *match; { - int i, len; + int i; - if (BEG(0) == -1) return Qnil; + if (!match) return Qnil; + if (match->BEG(0) == -1) return Qnil; - for (i=last_match.regs.num_regs-1; BEG(i) == -1 && i > 0; i--) { - } + for (i=match->regs->num_regs-1; match->BEG(i) == -1 && i > 0; i--) + ; if (i == 0) return Qnil; - return re_nth_match(i); -} - -static VALUE -get_match_data(id, nth) - ID id; - int nth; -{ - return re_nth_match(nth); -} - -static void -free_match(data) - struct match *data; -{ - free(data->ptr); - if (data->regs.allocated > 0) { - free(data->regs.beg); - free(data->regs.end); - } -} - -static VALUE -get_match() -{ - struct match *data; - int beg, i; - - data = ALLOC(struct match); - - data->len = last_match.len; - data->ptr = ALLOC_N(char, last_match.len+1); - memcpy(data->ptr, last_match.ptr, data->len+1); - data->regs.allocated = 0; - re_copy_registers(&data->regs, &last_match.regs); - - return data_new(data, free_match, Qnil); -} - -static VALUE -set_match(val) - struct RArray *val; -{ - struct match *match; - - Check_Type(val, T_DATA); - match = (struct match*)DATA_PTR(val); - last_match.len = match->len; - if (last_match.len == 0) { - if (last_match.ptr) { - free(last_match.ptr); - last_match.ptr = Qnil; - } - } - else { - if (last_match.ptr == Qnil) { - last_match.ptr = ALLOC_N(char, match->len+1); - } - else { - REALLOC_N(last_match.ptr, char, match->len+1); - } - } - memcpy(last_match.ptr, match->ptr, last_match.len+1); - last_match.regs = match->regs; - - return (VALUE)val; + return reg_nth_match(i, match); } void @@ -293,10 +300,10 @@ const char *s; Fail(s); } -VALUE C_Regexp; +VALUE cRegexp; static VALUE -regexp_new_1(class, s, len, ci) +reg_new_1(class, s, len, ci) VALUE class; char *s; int len, ci; @@ -310,41 +317,44 @@ regexp_new_1(class, s, len, ci) re->str[len] = '\0'; re->len = len; - if (ci) FL_SET(re, FL_USER1); + FL_SET(re, reg_kcode); + if (ci) FL_SET(re, REG_IGNORECASE); + return (VALUE)re; } VALUE -regexp_new(s, len, ci) +reg_new(s, len, ci) char *s; int len, ci; { - return regexp_new_1(C_Regexp, s, len, ci); + return reg_new_1(cRegexp, s, len, ci); } -static VALUE str_cache, reg_cache; +static VALUE reg_cache, ign_cache; VALUE -re_regcomp(str) +reg_regcomp(str) struct RString *str; { - if (str_cache && RSTRING(str_cache)->len == str->len && - memcmp(RSTRING(str_cache)->ptr, str->ptr, str->len)) + if (reg_cache && RREGEXP(reg_cache)->len == str->len + && ign_cache == ignorecase + && memcmp(RREGEXP(reg_cache)->str, str->ptr, str->len) == 0) return reg_cache; - str_cache = (VALUE)str; - return reg_cache = regexp_new(str->ptr, str->len, ignorecase); + ign_cache = ignorecase; + return reg_cache = reg_new(str->ptr, str->len, ignorecase); } VALUE -Freg_match(re, str) +reg_match(re, str) struct RRegexp *re; struct RString *str; { int start; - Check_Type(str, T_STRING); - start = research(re, str, 0); + if (TYPE(str) != T_STRING) return FALSE; + start = reg_search(re, str, 0, 0); if (start < 0) { return Qnil; } @@ -352,7 +362,7 @@ Freg_match(re, str) } VALUE -Freg_match2(re) +reg_match2(re) struct RRegexp *re; { extern VALUE rb_lastline; @@ -361,7 +371,7 @@ Freg_match2(re) if (TYPE(rb_lastline) != T_STRING) Fail("$_ is not a string"); - start = research(re, rb_lastline, 0); + start = reg_search(re, rb_lastline, 0, 0); if (start == -1) { return Qnil; } @@ -369,7 +379,7 @@ Freg_match2(re) } static VALUE -Sreg_new(argc, argv, self) +reg_s_new(argc, argv, self) int argc; VALUE *argv; VALUE self; @@ -387,10 +397,10 @@ Sreg_new(argc, argv, self) src = argv[0]; switch (TYPE(src)) { case T_STRING: - reg = regexp_new_1(self, RREGEXP(src)->ptr, RREGEXP(src)->len, ci); + reg = reg_new_1(self, RREGEXP(src)->ptr, RREGEXP(src)->len, ci); case T_REGEXP: - reg = regexp_new_1(self, RREGEXP(src)->str, RREGEXP(src)->len, ci); + reg = reg_new_1(self, RREGEXP(src)->str, RREGEXP(src)->len, ci); default: Check_Type(src, T_STRING); @@ -400,7 +410,7 @@ Sreg_new(argc, argv, self) } static VALUE -Sreg_quote(re, str) +reg_s_quote(re, str) VALUE re; struct RString *str; { @@ -430,20 +440,22 @@ Sreg_quote(re, str) } static VALUE -Freg_clone(re) +reg_clone(re) struct RRegexp *re; { - int ci = FL_TEST(re, FL_USER1); - return regexp_new_1(CLASS_OF(re), re->str, re->len, ci); + int ci = FL_TEST(re, REG_IGNORECASE); + return reg_new_1(CLASS_OF(re), re->str, re->len, ci); } VALUE -re_regsub(str) +reg_regsub(str, src, regs) struct RString *str; + struct RString *src; + struct re_registers *regs; { VALUE val = Qnil; char *p, *s, *e, c; - int no, len; + int no; p = s = str->ptr; e = s + str->len; @@ -471,10 +483,10 @@ re_regsub(str) if (no < 0) { /* Ordinary character. */ if (c == '\\' && (*s == '\\' || *s == '&')) - p = ++s; + p = s++; } else { if (BEG(no) == -1) continue; - str_cat(val, last_match.ptr+BEG(no), END(no)-BEG(no)); + str_cat(val, src->ptr+BEG(no), END(no)-BEG(no)); } } @@ -489,12 +501,26 @@ re_regsub(str) } static VALUE -kcode() +reg_to_s(re) + struct RRegexp *re; { - switch (re_syntax_options & RE_MBCTYPE_MASK) { - case RE_MBCTYPE_SJIS: + VALUE str = str_new2("/"); + + str_cat(str, re->str, re->len); + str_cat(str, "/", 1); + if (FL_TEST(re, REG_IGNORECASE)) { + str_cat(str, "i", 1); + } + return str; +} + +static VALUE +kcode_getter() +{ + switch (reg_kcode) { + case KCODE_SJIS: return str_new2("SJIS"); - case RE_MBCTYPE_EUC: + case KCODE_EUC: return str_new2("EUC"); default: return str_new2("NONE"); @@ -502,19 +528,21 @@ kcode() } void -rb_set_kanjicode(code) +rb_set_kcode(code) char *code; { - if (code == Qnil) goto set_no_conversion; + if (code == 0) goto set_no_conversion; switch (code[0]) { case 'E': case 'e': + reg_kcode = KCODE_EUC; re_syntax_options &= ~RE_MBCTYPE_MASK; re_syntax_options |= RE_MBCTYPE_EUC; break; case 'S': case 's': + reg_kcode = KCODE_SJIS; re_syntax_options &= ~RE_MBCTYPE_MASK; re_syntax_options |= RE_MBCTYPE_SJIS; break; @@ -522,25 +550,57 @@ rb_set_kanjicode(code) case 'N': case 'n': set_no_conversion: + reg_kcode = KCODE_NONE; re_syntax_options &= ~RE_MBCTYPE_MASK; break; } re_set_syntax(re_syntax_options); } -void -rb_setup_kcode() +static VALUE +kcode_setter(val) + struct RString *val; { - rb_define_const(C_Object, "KCODE", kcode()); + Check_Type(val, T_STRING); + rb_set_kcode(val->ptr); + return (VALUE)val; +} + +static VALUE +match_getter() +{ + VALUE match = backref_get(); + + if (match && match != 1) { + NEWOBJ(m, struct RMatch); + OBJSETUP(m, cData, T_MATCH); + + m->len = RMATCH(match)->len; + if (RMATCH(match)->ptr) { + m->ptr = ALLOC_N(char, m->len+1); + memcpy(m->ptr, RMATCH(match)->ptr, m->len); + m->ptr[m->len] = '\0'; + } + else { + m->ptr = 0; + } + m->regs = ALLOC(struct re_registers); + re_copy_registers(m->regs, RMATCH(match)->regs); + return (VALUE)m; + } + return Qnil; } -VALUE rb_readonly_hook(); +static void +match_setter(val) +{ + Check_Type(val, T_MATCH); + backref_set(val); +} void Init_Regexp() { - int i; - re_set_syntax(RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_AWK_CLASS_HACK | RE_INTERVALS @@ -551,23 +611,20 @@ Init_Regexp() #endif ); - rb_define_variable("$~", Qnil, get_match, set_match, 0); - - rb_define_variable("$&", Qnil, re_last_match, Qnil, 0); - rb_define_variable("$`", Qnil, re_match_pre, Qnil, 0); - rb_define_variable("$'", Qnil, re_match_post, Qnil, 0); - rb_define_variable("$+", Qnil, re_match_last, Qnil, 0); + rb_define_virtual_variable("$~", match_getter, match_setter); - rb_define_variable("$=", &ignorecase, Qnil, Qnil, 0); + rb_define_variable("$=", &ignorecase, 0); + rb_define_virtual_variable("$KCODE", kcode_getter, kcode_setter); - C_Regexp = rb_define_class("Regexp", C_Object); - rb_define_single_method(C_Regexp, "new", Sreg_new, -1); - rb_define_single_method(C_Regexp, "compile", Sreg_new, -1); - rb_define_single_method(C_Regexp, "quote", Sreg_quote, 1); + cRegexp = rb_define_class("Regexp", cObject); + rb_define_singleton_method(cRegexp, "new", reg_s_new, -1); + rb_define_singleton_method(cRegexp, "compile", reg_s_new, -1); + rb_define_singleton_method(cRegexp, "quote", reg_s_quote, 1); - rb_define_method(C_Regexp, "=~", Freg_match, 1); - rb_define_method(C_Regexp, "~", Freg_match2, 0); + rb_define_method(cRegexp, "clone", reg_clone, 0); + rb_define_method(cRegexp, "=~", reg_match, 1); + rb_define_method(cRegexp, "~", reg_match2, 0); + rb_define_method(cRegexp, "to_s", reg_to_s, 0); - rb_global_variable(&str_cache); rb_global_variable(®_cache); } @@ -21,16 +21,14 @@ typedef struct re_pattern_buffer Regexp; -struct match { +struct RMatch { + struct RBasic basic; UINT len; char *ptr; - struct re_registers regs; + struct re_registers *regs; }; -extern struct match last_match; - -#define BEG(no) last_match.regs.beg[no] -#define END(no) last_match.regs.end[no] +#define RMATCH(obj) (R_CAST(RMatch)(obj)) VALUE re_regcomp(); VALUE re_regsub(); @@ -1743,7 +1743,6 @@ re_search(pbufp, string, size, startpos, range, regs) { register char *fastmap = pbufp->fastmap; register unsigned char *translate = (unsigned char *) pbufp->translate; - int endpos = startpos + range; int val; /* Check for out-of-range starting position. */ @@ -1811,9 +1810,9 @@ re_search(pbufp, string, size, startpos, range, regs) return -2; #ifndef NO_ALLOCA -#ifdef C_ALLOCA +#ifdef cALLOCA alloca(0); -#endif /* C_ALLOCA */ +#endif /* cALLOCA */ #endif /* NO_ALLOCA */ advance: @@ -1968,10 +1967,6 @@ struct register_info } \ } -/* Test if at very beginning or at very end of the virtual concatenation - of string1 and string2. If there is only one string, we've put it in - string2. */ - #define AT_STRINGS_BEG (d == string) #define AT_STRINGS_END (d == dend) @@ -2667,6 +2662,7 @@ re_copy_registers(regs1, regs2) { int i; + if (regs1 == regs2) return; if (regs1->allocated == 0) { regs1->beg = TMALLOC(regs2->num_regs, int); regs1->end = TMALLOC(regs2->num_regs, int); @@ -2680,3 +2676,11 @@ re_copy_registers(regs1, regs2) regs1->end[i] = regs2->end[i]; } } + +void +re_free_registers(regs) + struct re_registers *regs; +{ + if (regs->beg) free(regs->beg); + if (regs->end) free(regs->end); +} @@ -275,6 +275,7 @@ extern int re_search (); extern int re_match (); extern long re_set_syntax(); extern void re_copy_registers (); +extern void re_free_registers (); #endif /* __STDC__ */ diff --git a/ruby.1 b/ruby.1 deleted file mode 100644 index b8c6162554..0000000000 --- a/ruby.1 +++ /dev/null @@ -1,248 +0,0 @@ -.\"ruby.1 - -*- Nroff -*- -.\" $Author: matz $ -.\" $Date: 1994/11/22 01:22:40 $ -.\" created at: Tue Apr 12 01:45:04 JST 1994 -.TH RUBY 1 "\*(RP" -.UC -.SH NAME -ruby \- オブジェクト指向スクリプト言語 -.SH SYNOPSIS -.B ruby -[ -.B options -] filename args -.SH DESCRIPTION -.B Ruby -は, 手軽なオブジェクト指向プログラミングを実現するための種々 -の機能を持つオブジェクト指向スクリプト言語である.その設計の -基本原則は, 以下の通りである. - -.IP 機能性 -オブジェクト指向プログラミングとスクリプトプログラミングのた -めに必要な機能を十分に備える. 特にテキスト処理関係の機能を豊 -富に持つ。また, 純粋なオブジェクト指向言語でありながら, 必要 -であれば手続き型プログラミングも可能である. -.IP 拡張性 -必要に応じて容易に機能を拡張できる. クラスを自由に追加できる -ことは勿論, Cプログラムのリンクによってインタプリタにあらゆ -る機能を追加できる. さらにプラットフォームによっては, 動的に -オブジェクトコードをリンクする機能も提供する. -.IP 一貫性 -少数の原則が全体に適用されるような一貫性のある言語仕様を持つ. -これによって「パズルの楽しさ」は減少したかも知れない. ただし, -一貫性のため使いやすさを犠牲にすることはない. -.PP -.B Ruby -はshやperlを知っている人にとっての常識にできる限り従ったので, -それらの言語に精通している人にとっては習得が(多分)容易だろう. -.SH OPTIONS -.B ruby -インタプリタは以下の引数を受け付ける. -.TP 5 -.B \-0数字 -入力レコードセパレータ(`$/')の8進数で指定する. - -数字を指定しない場合はヌルキャラクタがセパレータになる。数の -後に他のスイッチがあってもよい。 - -\-00で, パラグラフモード, \-0777で(そのコードを持つ文字は存 -在しないので)全ファイルを一度に読み込むモードに設定できる. -.TP 5 -.B \-a -`\-n'や`\-p'とともに用いて, オートスプリットモードをONにする. -オートスプリットモードでは各ループの先頭で, -.nf -.ne 2 - - $F = $_.split - -.fi -が実行される. `\-n'か`\-p'オプションが同時に指定されない限り, -このオプションは意味を持たない. -.TP 5 -.B \-c -スクリプトの内部形式へのコンパイルのみを行い, 実行しない. コ -ンパイル終了後, 文法エラーが無ければ, "Syntax OK"と出力する. -.TP 5 -.B \-K " c" -.B ruby -の処理する漢字コードを指定する. -.B ruby -は指定した文字列 `E'または`e'の場合は文字列やアクセスするファ -イルがEUCで記述されていると仮定する. 同様に`S'または`s'の場 -合はSJISとして処理する. `N'は漢字を処理しない. デフォルトは -EUC. -.nf -.ne 2 - - ruby -CE -e 'print "テスト"' - ruby -Cs -e 'print "テスト"' - ruby -Cn -e 'print "テスト"' - -.fi -このオプションは将来文字コードの自動判別機能が追加された場合 -等には変更される. -.TP 5 -.B \-d, \-\-debug -デバッグモードをonにする. このフラグがセットされるとシステム -変数$DEBUGがセットされる. -.TP 5 -.B \-e " script" -コマンドラインからスクリプトを指定する. \-eオプションを付け -た時には引数からスクリプトファイル名を取らない. -.TP 5 -.B \-F" 文字列" -入力フィールドセパレータ(`$;')の値を文字列にセットする. awk -の同名のオプションと同じ働きをする. -.TP 5 -.B \-i" extension" -引数で指定されたファイルの内容を置き換える(in-place edit)こ -とを指定する. 元のファイルは拡張子をつけた形で保存される. -.nf -例: -.ne 2 - - % echo matz > /tmp/junk - % cat /tmp/junk - matz - % ruby -p -i.bak -e '$_.upcase' /tmp/junk - % cat /tmp/junk - MATZ - % cat /tmp/junk.bak - matz - -.fi -.TP 5 -.B \-I" directory" -ファイルをロードするパスを指定(追加)する. 指定されたディレク -トリは -.B ruby -の配列変数$LOAD_PATHに追加される. -.TP 5 -.B \-l -`$\\'を`$/'と同じ値に設定し, print()での出力時に改行を付加す -る. また, \-nまたは\-pとともに用いられると, 入力された各行の -最後の文字をchopする. -.TP 5 -.B \-n -このフラグがセットされるとプログラム全体が -.nf -.ne 2 - - while gets() - \.\.\. - end - -.fi -で囲まれているように動作する. -.TP 5 -.B \-p -`\-n'フラグと同じだが, 各ループの最後に変数`$_'の値を出力する. -.nf -例: -.ne 2 - - % echo matz | ruby \-p \-e '$_\.tr "a-z", "A-Z"' - MATZ - -.fi -.TP 5 -.B \-s -スクリプト名に続く, \-で始まる引数を解釈して, 同名の大域変数 -に値を設定する. `\-\-'なる引数以降は解釈を行なわない. 該当す -る引数は$ARGVから取り除かれる. -.nf -例: -.ne 2 - - #! /usr/local/bin/ruby \-s - # \-xyzオプションが与えられると"true"を表示する. - print "true\n" if $xyz - -.fi -.TP 5 -.B \-S -スクリプト名が`/'で始まっていない場合, 環境変数`PATH'の値を -使ってスクリプトを探す. これは、#! をサポートしていないマシ -ンで、#! による実行をエミュレートするために、以下のようにし -て使うことができる: - - #! /usr/local/bin/ruby - # This line makes the next one a comment in ruby \ - eval "exec /usr/local/bin/ruby -S $0 $*" - -システムは最初の行を無視し,スクリプトを`/bin/sh'に渡す. -`/bin/sh'はrubyスクリプトをシェルスクリプトとして実行しよう -とする.シェルは2行目だけをコメントであると解釈し,3行目を通 -常のシェルコマンドとして実行し,rubyインタプリタを起動する. - -システムによっては`$0'は必ずしもフルパスを含まないので,`-S' -を用いてrubyに必要に応じてスクリプトを探すように指示する. -rubyがスクリプトを見つけると、これらの行の解析を始めるが, -rubyは2行目の行末にあるバックスラッシュにより,2行目のコメン -トが3行目まで継続するとみなして,3行目を無視する. - -ファイル名に含まれるスペースなどを正しく扱うには,`$*'よりも -`${1+"$@"}'のほうがよいだろうが,cshが解釈する場合には動作し -ない. - -.TP 5 -.B \-v, \-\-verbose -冗長モード. 起動時にバージョン番号の表示を行い, システム変数 -$VERBOSEをセットする. この変数がセットされている時, いくつか -のメソッドは実行時に冗長なメッセージを出力する. \-v オプショ -ンが指定されてq, オプション以外の引数がない時にはバージョン -を表示した後, 実行を終了する(標準入力からのスクリプトを待た -ない). -.TP 5 -.B \-\-version -.B ruby -のバージョンを表示する. -.nf -表示例: -.ne 2 - - ruby - version 0.56 (94/11/19) - -.fi -.TP 5 -.B \-x"directory" -メッセージ中のスクリプトを取り出して実行する. #!で始まり, -"ruby"という文字列を含む行までを読み飛ばす. スクリプトの終り -はEOF(ファイルの終り), ^D(コントロールD), ^Z(コントロールZ) -または予約語``__END__''で指定する. - -ディレクトリ名を指定すると,スクリプト実行前に指定されたディ -レクトリに移る. -.TP 5 -.B \-y, \-\-yydebug -コンパイラデバッグモード. コンパイル時の構文解析の過程を表示 -する. この表示は非常に冗長なので, コンパイラそのものをデバッ -グする人以外は表示させない方が良いと思う. - -.SH BUGS -.PP -遅い. 単純な処理の場合perlやawkなどの2,3倍の実行時間がかかる. -これらの言語と異なり, その提供する機能のほとんどがメソッド呼 -び出しを介することが原因だが, 他の言語でも関数呼び出しが多く -なるような処理ではメソッドキャッシュの分だけ -.B ruby -が有利になるし, データ構造が複雑になれば, オブジェクト指向の -メリットが活かせるので, まあ許せるかも知れない. -.PP -perlより記述量が多い. これは -.B ruby -が一貫性を追求した結果である. だが, その結果, -.B ruby -スクリプトはperlより読みやすいはずで, 若干の記述量を犠牲に理 -解しやすさと可読性を得ていると思って欲しい. -.PP -ドキュメントが不十分. 必要な情報を得るためにはソースを読んで -欲しい. -.PP -テストが不十分. バグにつき当たったら, できれば自分で直して, -こっそり私に教えて欲しい. 無理ならば, せめてバグが再現する条 -件を明確にしてレポートして欲しい. - -.SH AUTHOR -松本 行弘 (matz@caelum.co.jp) @@ -31,7 +31,7 @@ int debug = 0; int verbose = 0; static int sflag = FALSE; -char *inplace = Qnil; +char *inplace = 0; char *strdup(); extern int yydebug; @@ -39,10 +39,6 @@ extern int nerrs; int xflag = FALSE; -#ifdef USE_DL -char *rb_dln_argv0; -#endif - static void load_stdin(); static void load_file(); @@ -52,6 +48,37 @@ static int do_split = FALSE; static char *script; +#ifndef RUBY_LIB +#define RUBY_LIB ".:/usr/local/lib/ruby" +#endif + +#define RUBY_LIB_SEP ':' + +extern VALUE rb_load_path; +VALUE Frequire(); + +static void +addpath(path) + char *path; +{ + char *p, *s; + + if (path == 0) return; + + p = s = path; + while (*p) { + while (*p == RUBY_LIB_SEP) p++; + if (s = strchr(p, RUBY_LIB_SEP)) { + ary_push(rb_load_path, str_new(p, (int)(s-p))); + p = s + 1; + } + else { + ary_push(rb_load_path, str_new2(p)); + break; + } + } +} + static void proc_options(argcp, argvp) int *argcp; @@ -62,7 +89,6 @@ proc_options(argcp, argvp) int script_given, do_search; char *s; - extern VALUE rb_load_path; extern VALUE RS, ORS, FS; if (argc == 0) return; @@ -81,7 +107,7 @@ proc_options(argcp, argvp) do_split = TRUE; s++; goto reswitch; - + case 'p': do_print = TRUE; /* through */ @@ -121,7 +147,7 @@ proc_options(argcp, argvp) ORS = RS; s++; goto reswitch; - + case 'S': do_search = TRUE; s++; @@ -140,6 +166,16 @@ proc_options(argcp, argvp) yyparse(); break; + case 'r': + if (*++s) { + f_require(Qnil, str_new2(s)); + } + else if (argv[1]) { + f_require(Qnil, str_new2(argv[1])); + argc--,argv++; + } + break; + case 'i': inplace = strdup(s+1); break; @@ -152,18 +188,34 @@ proc_options(argcp, argvp) } break; + case 'X': + s++; + if (!*s) { + s = argv[1]; + argc--,argv++; + } + if (*s && chdir(s) < 0) { + Fatal("Can't chdir to %s", s); + } + break; + case 'F': FS = str_new2(s+1); break; case 'K': s++; - rb_set_kanjicode(s); + rb_set_kcode(s); s++; goto reswitch; case 'I': - ary_unshift(rb_load_path, str_new2(s+1)); + if (*++s) + ary_push(rb_load_path, str_new2(s)); + else if (argv[1]) { + ary_push(rb_load_path, str_new2(argv[1])); + argc--,argv++; + } break; case '0': @@ -185,9 +237,6 @@ proc_options(argcp, argvp) } goto reswitch; - case 'u': - case 'U': - case '-': if (!s[1]) { argc--,argv++; @@ -218,7 +267,7 @@ proc_options(argcp, argvp) } switch_end: - if (*argvp[0] == Qnil) return; + if (*argvp[0] == 0) return; if (version) { show_version(); @@ -228,8 +277,6 @@ proc_options(argcp, argvp) show_copyright(); } - rb_setup_kcode(); - if (script_given == 0) { if (argc == 0) { /* no more args */ if (verbose) exit(0); @@ -263,10 +310,10 @@ proc_options(argcp, argvp) argv[0][0] = '$'; if (s = strchr(argv[0], '=')) { *s++ = '\0'; - rb_gvar_set2((*argvp)[0], str_new2(s)); + rb_gvar_set2(argv[0], str_new2(s)); } else { - rb_gvar_set2((*argvp)[0], TRUE); + rb_gvar_set2(argv[0], TRUE); } } *argcp = argc; *argvp = argv; @@ -281,7 +328,7 @@ readin(fd, fname, script) int script; { struct stat st; - char *ptr, *p, *pend, *s; + char *ptr, *p, *pend; if (fstat(fd, &st) < 0) rb_sys_fail(fname); if (!S_ISREG(st.st_mode)) @@ -351,7 +398,6 @@ load_file(fname, script) int script; { int fd; - char *ptr; if (fname[0] == '\0') { load_stdin(); @@ -391,7 +437,7 @@ load_stdin() readin(fd, "-"); } -static VALUE Progname; +VALUE Progname; VALUE Argv; static int origargc; @@ -454,13 +500,13 @@ ruby_options(argc, argv, envp) origargc = argc; origargv = argv; origenvp = envp; - rb_define_variable("$@", &errat, Qnil, Qnil, 0); + rb_define_variable("$@", &errat); errat = str_new2(argv[0]); - rb_define_variable("$VERBOSE", &verbose, Qnil, Qnil, 0); - rb_define_variable("$DEBUG", &debug, Qnil, Qnil, 0); + rb_define_variable("$VERBOSE", &verbose); + rb_define_variable("$DEBUG", &debug); -#ifdef USE_DL - rb_dln_argv0 = argv[0]; +#if defined(USE_DLN_A_OUT) + dln_argv0 = argv[0]; #endif proc_options(&argc, &argv); @@ -475,11 +521,14 @@ ruby_options(argc, argv, envp) yywhole_loop(do_line, do_split); } - rb_define_variable("$0", &Progname, Qnil, set_arg0, 0); + rb_define_hooked_variable("$0", &Progname, 0, set_arg0); ruby_script(script); - rb_define_variable("$ARGV", &Argv, Qnil, Qnil, 0); - rb_define_variable("$*", &Argv, Qnil, Qnil, 0); + addpath(getenv("RUBYLIB")); + addpath(RUBY_LIB); + + rb_define_readonly_variable("$ARGV", &Argv); + rb_define_readonly_variable("$*", &Argv); Argv = ary_new2(argc); for (i=0; i < argc; i++) { ary_push(Argv, str_new2(argv[i])); @@ -13,16 +13,19 @@ #ifndef RUBY_H #define RUBY_H -#include "config.h" +#ifndef NT +# include "config.h" +#endif + #include "defines.h" #ifndef __STDC__ -#define volatile -#ifdef __GNUC__ -#define const __const__ -#else -#define const -#endif +# define volatile +# ifdef __GNUC__ +# define const __const__ +# else +# define const +# endif #endif #if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) @@ -39,9 +42,11 @@ typedef unsigned short USHORT; # include <limits.h> #else # ifndef LONG_MAX -# if !defined(LONG_MAX) || !defined(CHAR_BIT) -# include <limits.h> -# endif +# ifdef HAVE_LIMITS_H +# include <limits.h> +# else +# define LONG_MAX 2147483647 /* assuming 32bit(2's compliment) LONG */ +# endif # endif # ifndef LONG_MIN # if (0 != ~0) @@ -50,17 +55,17 @@ typedef unsigned short USHORT; # define LONG_MIN (-LONG_MAX) # endif # endif +# ifndef CHAR_BIT +# define CHAR_BIT 8 +# endif #endif -#ifndef CHAR_BIT -# define CHAR_BIT 8 -#endif - -# define FIXNUM_MAX (LONG_MAX>>1) -# define FIXNUM_MIN RSHIFT((long)LONG_MIN,1) +#define FIXNUM_MAX (LONG_MAX>>1) +#define FIXNUM_MIN RSHIFT((long)LONG_MIN,1) #define FIXNUM_FLAG 0x01 #define INT2FIX(i) (VALUE)(((int)(i))<<1 | FIXNUM_FLAG) +VALUE int2inum(); #if (-1==(((-1)<<1)&FIXNUM_FLAG)>>1) # define RSHIFT(x,y) ((x)>>y) @@ -69,25 +74,24 @@ typedef unsigned short USHORT; #endif #define FIX2INT(x) RSHIFT((int)x,1) -#define FIX2UINT(f) ((unsigned int)(f)>>1) +#define FIX2UINT(f) ((UINT)(f)>>1) #define FIXNUM_P(f) (((int)(f))&FIXNUM_FLAG) #define POSFIXABLE(f) ((f) <= FIXNUM_MAX) #define NEGFIXABLE(f) ((f) >= FIXNUM_MIN) #define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) -#define POINTER(p) (p) #define NIL_P(p) ((p) == Qnil) #undef TRUE extern VALUE TRUE; #define FALSE Qnil -extern VALUE C_Object; -extern VALUE C_Nil; -extern VALUE C_Fixnum; -extern VALUE C_Data; +extern VALUE cObject; +extern VALUE cNil; +extern VALUE cFixnum; +extern VALUE cData; -#define CLASS_OF(obj) (FIXNUM_P(obj)?C_Fixnum: NIL_P(obj)?C_Nil:\ +#define CLASS_OF(obj) (FIXNUM_P(obj)?cFixnum: NIL_P(obj)?cNil:\ RBASIC(obj)->class) #define T_NIL 0x00 @@ -103,9 +107,11 @@ extern VALUE C_Data; #define T_HASH 0x0a #define T_STRUCT 0x0b #define T_BIGNUM 0x0c -#define T_ASSOC 0x0f + #define T_DATA 0x10 +#define T_MATCH 0x11 +#define T_VARMAP 0xfd #define T_SCOPE 0xfe #define T_NODE 0xff @@ -122,10 +128,10 @@ int num2int(); #define NEWOBJ(obj,type) type *obj = (type*)newobj() #define OBJSETUP(obj,c,t) {\ RBASIC(obj)->class = (c);\ - RBASIC(obj)->flags |= (t);\ + RBASIC(obj)->flags = (t);\ } #define CLONESETUP(obj1,obj2) \ - OBJSETUP(obj1,RBASIC(obj2)->class,RBASIC(obj2)->flags&T_MASK); + OBJSETUP(obj1,RBASIC(obj2)->class,RBASIC(obj2)->flags); struct RBasic { UINT flags; @@ -178,18 +184,18 @@ struct RData { struct RBasic basic; void (*dmark)(); void (*dfree)(); - VALUE *data; + void *data; }; #define DATA_PTR(dta) (RDATA(dta)->data) VALUE data_new(); -VALUE rb_ivar_get_1(); -VALUE rb_ivar_set_1(); +VALUE rb_ivar_get(); +VALUE rb_ivar_set(); #define Get_Data_Struct(obj, iv, type, sval) {\ VALUE _data_;\ - _data_ = rb_ivar_get_1(obj, iv);\ + _data_ = rb_ivar_get(obj, iv);\ Check_Type(_data_, T_DATA);\ sval = (type*)DATA_PTR(_data_);\ } @@ -197,19 +203,15 @@ VALUE rb_ivar_set_1(); #define Make_Data_Struct(obj, iv, type, mark, free, sval) {\ VALUE _new_;\ sval = ALLOC(type);\ - _new_ = data_new(sval,free,mark);\ + _new_ = data_new(sval,mark,free);\ memset(sval, 0, sizeof(type));\ - rb_ivar_set_1(obj, iv, _new_);\ + rb_ivar_set(obj, iv, _new_);\ } struct RStruct { struct RBasic basic; UINT len; - struct kv_pair { - ID key; - VALUE value; - } *tbl; - char *name; + VALUE *ptr; }; struct RBignum { @@ -219,14 +221,6 @@ struct RBignum { USHORT *digits; }; -struct RAssoc { - struct RBasic basic; - VALUE car, cdr; -}; - -#define CAR(c) (RASSOC(c)->car) -#define CDR(c) (RASSOC(c)->cdr) - #define R_CAST(st) (struct st*) #define RBASIC(obj) (R_CAST(RBasic)(obj)) #define ROBJECT(obj) (R_CAST(RObject)(obj)) @@ -239,7 +233,6 @@ struct RAssoc { #define RDATA(obj) (R_CAST(RData)(obj)) #define RSTRUCT(obj) (R_CAST(RStruct)(obj)) #define RBIGNUM(obj) (R_CAST(RBignum)(obj)) -#define RASSOC(obj) (R_CAST(RAssoc)(obj)) #define FL_SINGLE (1<<8) #define FL_MARK (1<<9) @@ -271,38 +264,75 @@ extern VALUE Qself; #define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n)) #define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n)) +#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n)) -#ifdef SAFE_SIGHANDLE -extern int trap_immediate; -# define TRAP_BEG (trap_immediate=1) -# define TRAP_END (trap_immediate=0) -#else -# define TRAP_BEG -# define TRAP_END -#endif +void *xmalloc(); +void *xcalloc(); +void *xrealloc(); VALUE rb_define_class(); VALUE rb_define_module(); +void rb_include_module(); +void rb_extend_object(); void rb_define_variable(); void rb_define_const(); void rb_define_method(); -void rb_define_single_method(); +void rb_define_singleton_method(); void rb_undef_method(); void rb_define_alias(); void rb_define_attr(); ID rb_intern(); char *rb_id2name(); +ID rb_to_id(); + +char *rb_class2name(); +VALUE rb_method_boundp(); VALUE rb_eval_string(); VALUE rb_funcall(); VALUE rb_funcall2(); int rb_scan_args(); +VALUE rb_iv_get(); +VALUE rb_iv_set(); +void rb_const_set(); + VALUE rb_yield(); +VALUE iterator_p(); + +VALUE rb_equal(); extern int verbose, debug; +#ifdef __GNUC__ +typedef void voidfn (); +volatile voidfn Fail; +volatile voidfn Fatal; +volatile voidfn Bug; +volatile voidfn WrongType; +volatile voidfn rb_sys_fail; +volatile voidfn rb_break; +volatile voidfn rb_exit; +volatile voidfn rb_fail; +#else +void Fail(); +void Fatal(); +void Bug(); +void WrongType(); +void rb_sys_fail(); +void rb_break(); +void rb_exit(); +void rb_fail(); +#endif + +void Warning(); + +#if defined(EXTLIB) && defined(USE_DLN_A_OUT) +/* hook for external modules */ +static char *libs_to_be_linked[] = { EXTLIB, 0 }; +#endif + #endif diff --git a/ruby.texi b/ruby.texi new file mode 100644 index 0000000000..50ffb63a16 --- /dev/null +++ b/ruby.texi @@ -0,0 +1,5044 @@ +\input texinfo @c -*-texinfo-*- created at: Tue Jun 20 16:58:39 JST 1995 +@setfilename ruby.info +@settitle Ruby Reference Manual + +@titlepage +@title Ruby Reference Manual +@subtitle The Object-Oriented Scripting Language +@author Yukihiro Matsumoto +@author matz@@caelum.co.jp +@end titlepage + +@node Top, はじめに, (dir), (dir) + +Ruby Reference Manual + +@menu +* はじめに:: +* コマンドラインオプション:: +* rubyの文法:: +* 組み込み関数:: +* 組み込み変数と定数:: +* 組み込みクラスとモジュール:: +* C言語とのインタフェース:: +* 謝辞:: +* 文法:: +* Variables Index:: +* Concept Index:: +* Function Index:: +@end menu + +@node はじめに, コマンドラインオプション, Top, Top +@comment node-name, next, previous, up +@chapter はじめに + +Rubyは, 手軽なオブジェクト指向プログラミングを実現するための種々の機能 +を持つオブジェクト指向スクリプト言語である.本格的なオブジェクト指向言 +語であるSmalltalk, EiffelやC++などでは大げさに思われるような領域でのオ +ブジェクト指向プログラミングを支援することを目的とする.その設計の基本 +原則は, 以下の通りである. + +@itemize @bullet +@item + +機能性 + +単純で例外の少ない文法で,オブジェクト指向プログラミングとスクリプトプ +ログラミングのために必要な機能を十分に備える. + +@item + +拡張性 + +必要に応じて容易に機能を拡張できる.クラスを自由に追加できることは勿論, +Cプログラムのリンクによってインタプリタにあらゆる機能を追加できる. さ +らにプラットフォームによっては, 動的にオブジェクトコードをリンクする機 +能も提供する. + +@item + +一貫性 + +少数の原則が全体に適用されるような一貫性のある言語仕様を持つ.これに +よって「パズルの楽しさ」は減少したかも知れない.ただし,一貫性のため使 +いやすさを犠牲にすることはない. +@end itemize + +Rubyは「手軽」ではあるが本格的なオブジェクト指向機能を持つので,perl, +tcl, pythonなどスクリプト言語にオブジェクト指向機能を追加したような処 +理系よりも自然にオブジェクト指向できる.更にガーベージコレクタや例外処 +理機能はより快適なプログラミングを支援する. + +Rubyはテキスト処理関係の機能を豊富に(perlと同じくらい)持ち,OSを直接操 +作するような処理も記述できる.また, 純粋なオブジェクト指向言語でありな +がら, 必要であれば手続き型プログラミングも可能である. + +Rubyはshやperlを知っている人にとっての常識にできる限り従ったので,それ +らの言語に精通している人にとっては習得が(多分)容易だろう.プログラマが +Rubyのオブジェクト指向機能について学べば,より強力なこともできるように +なるだろう. + +@node コマンドラインオプション, rubyの文法, はじめに, Top +@comment node-name, next, previous, up +@chapter コマンドラインオプション + +rubyインタプリタは以下の引数を受け付ける. + +@table @samp + +@item -0数字 + +入力レコードセパレータ(@code{$/})の8進数で指定する. + +数字を指定しない場合はヌルキャラクタがセパレータになる。数の後に他のス +イッチがあってもよい。 + +-00で, パラグラフモード, -0777で(そのコードを持つ文字は存在しないので) +全ファイルを一度に読み込むモードに設定できる. + +@item -a +@cindex{オートスプリットモード} + +@code{-n}や@code{-p}とともに用いて, オートスプリットモードをONにする. +オートスプリットモードでは各ループの先頭で, + +@example +$F = $_.split +@end example + +が実行される.@code{-n}か@code{-p}オプションが同時に指定されない限り, +このオプションは意味を持たない. + +@item -c + +スクリプトの内部形式へのコンパイルのみを行い, 実行しない.コンパイル終 +了後, 文法エラーが無ければ, @samp{"Syntax OK"}と出力する. + +@item -K c + +rubyの処理する漢字コードを指定する. rubyは指定された文字が @code{E}ま +たは@code{e}の場合は文字列やアクセスするファイルの内容のコードがEUCで +あると仮定する.同様に@code{S}または@code{s}の場合はSJISとして処理する. +@code{N}は漢字を処理しない.デフォルトはEUC. + +@example +ruby -CE -e 'print "テスト"' +ruby -Cs -e 'print "テスト"' +ruby -Cn -e 'print "テスト"' +@end example + +このオプションは将来文字コードの自動判別機能が追加された場合等には変更 +される. + +@item -d +@itemx --debug + +デバッグモードをonにする.このフラグがセットされるとシステム変数 +@code{$DEBUG}がセットされる. + +@item -e @var{script} + +コマンドラインからスクリプトを指定する.-eオプションを付けた時には引数 +からスクリプトファイル名を取らない. + +@item -F @var{文字列} + +入力フィールドセパレータ(@code{$;})の値を文字列にセットする.awkの同名 +のオプションと同じ働きをする. + +@item -i @var{extension} + +引数で指定されたファイルの内容を置き換える(in-place edit)ことを指定す +る.元のファイルは拡張子をつけた形で保存される. + +例: + +@example +% echo matz > /tmp/junk +% cat /tmp/junk +matz +% ruby -p -i.bak -e '$_.upcase' /tmp/junk +% cat /tmp/junk +MATZ +% cat /tmp/junk.bak +matz +@end example + +拡張子がなければ,バックアップはされず,変更されたファイルだけが残る. + +@item -I @var{directory} + +ファイルをロードするパスを指定(追加)する.指定されたディレクトリはruby +の配列変数@code{$:}に追加される. + +@item -l + +@code{$\}を@code{$/}と同じ値に設定し, @code{print}での出力時に改行を付 +加する.また, @samp{-n}または@samp{-p}とともに用いられると, 入力された +各行の最後の文字を@code{chop!}する. + +@item -n + +このフラグがセットされるとプログラム全体が + +@example +while gets + @dots{} +end +@end example + +で囲まれているように動作する. +@item -p + +@code{-n}フラグと同じだが, 各ループの最後に変数@code{$_}の値を出力する. + +例: + +@example +% echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"' +MATZ +@end example + +@item -r ファイル名 + +スクリプト実行前にファイル名で指定されるファイルを@code{require}する. +@samp{-n}オプション,@samp{-p}オプションとともに使う時に特に有効である. + +@xref{組み込み関数} + +@item -s + +スクリプト名に続く, @code{-}で始まる引数を解釈して, 同名の大域変数に値 +を設定する.@code{--}なる引数以降は解釈を行なわない.該当する引数は +@code{$ARGV}から取り除かれる. + +例: +@example +#! /usr/local/bin/ruby -s +# -xyzオプションが与えられると"true"を表示する. +print "true\n" if $xyz +@end example + +@item -S + +スクリプト名が@code{/}で始まっていない場合, 環境変数@code{PATH}の値を +使ってスクリプトを探す. これは、@samp{#!} をサポートしていないマシン +で、@samp{#!} による実行をエミュレートするために、以下のようにして使う +ことができる: + +例: +@example +#! /usr/local/bin/ruby +# This line makes the next one a comment in ruby \ + exec /usr/local/bin/ruby -S $0 $* +@end example + +システムは最初の行を無視し,スクリプトを@code{/bin/sh}に渡す. +@code{/bin/sh}はrubyスクリプトをシェルスクリプトとして実行しようとする. +シェルは2行目だけをコメントであると解釈し,3行目を通常のシェルコマンド +として実行し,rubyインタプリタを起動する. + +システムによっては@code{$0}は必ずしもフルパスを含まないので,@code{-S} +を用いてrubyに必要に応じてスクリプトを探すように指示する.rubyがスクリ +プトを見つけると、これらの行の解析を始めるが,rubyは2行目の行末にある +バックスラッシュにより,2行目のコメントが3行目まで継続するとみなして, +3行目を無視する. + +ファイル名に含まれるスペースなどを正しく扱うには,@code{$*}よりも +@code{$@{1+"$@@"@}}のほうがよいだろうが,cshが解釈する場合には動作しな +い.@cindex{OSが#!を解釈しない場合の対策} + +@item -v +@itemx --verbose + +冗長モード.起動時にバージョン番号の表示を行い, システム変数 +@code{$VERBOSE}をセットする.この変数がセットされている時, いくつかの +メソッドは実行時に冗長なメッセージを出力する.@samp{-v}オプションが指 +定されて, それ以外の引数がない時にはバージョンを表示した後, 実行を終了 +する(標準入力からのスクリプトを待たない). + +@item --version + +rubyのバージョンを表示する. + +表示例: + +@example +ruby - version 0.87 (95/09/23) +@end example + +@item -x[directory] + +メッセージ中のスクリプトを取り出して実行する.スクリプトを読み込む時に, +@code{#!}で始まり, @code{ruby}という文字列を含む行までを読み飛ばす.ス +クリプトの終りは@samp{EOF}(ファイルの終り), @samp{^D}(コントロールD), +@samp{^Z}(コントロールZ)または予約語@code{__END__}で指定する. + +ディレクトリ名を指定すると,スクリプト実行前に指定されたディレクトリに +移る. + +@item -X directory + +スクリプト実行前に指定されたディレクトリに移る. + +@item -y +@itemx --yydebug + +コンパイラデバッグモード.スクリプトを内部表現にコンパイルする時の構文 +解析の過程を表示する.この表示は非常に冗長なので, コンパイラそのものを +デバッグする人以外は表示させない方が良いと思う. +@end table + +@node rubyの文法, 組み込み関数, コマンドラインオプション, Top +@comment node-name, next, previous, up +@chapter rubyの文法 + +@menu +* Lexical structure:: +* プログラム:: +* 式:: +@end menu + +@node Lexical structure, プログラム, rubyの文法, rubyの文法 +@comment node-name, next, previous, up +@section Lexical structure + +現在のrubyの実装はキャラクタセットとしてASCIIを用いる.rubyは大文字と +小文字を区別する.識別子の途中でなければ任意のところに空白文字をおくこ +とが出来る.空白文字はスペース(space),タブ(tab),垂直タブ(vertical +tab), CR(carriage return),改頁(form feed)である.改行(newline)は明ら +かに式が継続する場合には空白文字として,それ以外では文の区切りとして解 +釈される. + +識別子は英文字(@samp{"_"}を含む)から始まり,英数字が続いたものである. +rubyの識別子の長さに制限はない.現在の実装は識別子としてマルチバイトコー +ド(EUC,SJIS)も通すが勧められない. + +識別子の例 + +@example +foobar +ruby_is_simple +@end example + +@menu +* コメント:: +* 予約語:: +* 区切り文字:: +@end menu + +@node コメント, 予約語, Lexical structure, Lexical structure +@comment node-name, next, previous, up +@subsection コメント + +例 + +@example +# this is a comment line +@end example + +スクリプト言語の習慣にならい,文字列中や文字表現(@code{?#})以外の +@code{#}から行末まではコメントと見なす.コメント行末のバックスラッシュ +は次の行へのコメントの継続を意味する. + +@node 予約語, 区切り文字, コメント, Lexical structure +@comment node-name, next, previous, up +@subsection 予約語 + +予約語は以下の通りである + +@display +alias def for redo undef +and defined? if rescue when +begin else in retry while +break elsif module return yield +case end nil self __END__ +class ensure not super __FILE__ +continue fail or then __LINE__ +@end display + +予約語はクラス名,メソッド名,変数名などに用いることはできない.ただし, +@samp{$}, @samp{@@}が先頭についたものは予約語と見なされないので,グロー +バル変数,インスタンス変数については問題ない. + +@node 区切り文字, , 予約語, Lexical structure +@comment node-name, next, previous, up +@subsection 区切り文字 + +文字列などのリテラルの内部以外の場所の空白文字(タブとスペース)および改 +行(@samp{\n})が区切り記号となる.更に改行は + +@example +a + +b +@end example + +のように行が式の途中で終り,次の行に続くことが明白な(最後の非空白文字 +が演算子あるいは@code{,}である)場合を除き,式の区切りとして認識される. + +@node プログラム, 式, Lexical structure, rubyの文法 +@comment node-name, next, previous, up +@section プログラム + +例 + +@example +print "hello world!\n" +@end example + +プログラムは式を並べたものである.式と式の間はセミコロン(@code{;})また +は改行で区切られる. + +@node 式, , プログラム, rubyの文法 +@comment node-name, next, previous, up +@section 式 + +例 + +@example +TRUE +(1+2)*3 +foo() +if test then ok else ng end +@end example + +Rubyでは@code{nil}が偽,それ以外が真と評価される.CやPerlなどとは異な +り,0や@code{""}(空文字列)は偽とは評価されないので気をつけること. + +式は括弧によってグルーピングすることができる. + +@menu +* 文字列式:: +* コマンド出力:: +* 正規表現式:: +* 変数展開:: +* 数値リテラル:: +* 変数と定数:: +* グローバル変数:: +* インスタンス変数:: +* クラス定数:: +* ローカル変数:: +* 疑似変数:: +* 配列式:: +* 連想配列式:: +* メソッド呼出式:: +* SUPER:: +* 代入:: +* 演算子式:: +* 制御構造:: +* クラス定義:: +* モジュール定義:: +* メソッド定義:: +* 特異メソッド定義:: +* ALIAS:: +* UNDEF:: +* DEFINED?:: +@end menu + +@node 文字列式, コマンド出力, 式, 式 +@comment node-name, next, previous, up +@subsection 文字列式 +@cindex 文字列式 + +例 + +@example +"this is a string expression\n" +'文字列式' +@end example + +ダブルクォート(@code{"})で括られた文字列の中はバックスラッシュに続く文 +字が以下のように解釈される. + +バックスラッシュ記法 + +@table @samp +@item \t +タブ(0x09) +@item \n +改行文字(0x0a) +@item \r +復帰文字(0x0d) +@item \f +改ページ文字(0x0c) +@item \b +バックスペース(0x08) +@item \a +ベル(0x07) +@item \e +エスケープ(0x1b) +@item \nnn +8進数表記(nは0-7) +@item \xnn +16進数表記(nは0-9,a-f) +@item \cx +コントロール文字(xはASCII文字) +@item \x +文字xそのもの +@end table + +また,@code{#}による変数展開も行われる. + +@xref{変数展開} + +一方,シングルクォート(@code{'})で括られた文字列は,@code{\\}(バックス +ラッシュそのもの)と@code{\'}(シングルクォート)を除いて,文字列の中身の +解釈を行わない. + +文字列式は毎回新しい文字列オブジェクトを生成するので,文字列の内容を変 +更しても,もともとの文字列は変わらない. + +@node コマンド出力, 正規表現式, 文字列式, 式 +@comment node-name, next, previous, up +@subsection コマンド出力 +@cindex コマンド出力 + +例 + +@example +`date` +@end example + +Rubyではshのようにコマンドの実行結果を文字列リテラルのように使うことが +できる.@code{``}で囲まれた文字列は,ダブルクォートで囲まれた文字列と +同様にバックスラッシュ記法の解釈と変数展開が行なわれた後,コマンドとし +て実行され,その実行結果が文字列として与えられる.コマンドは評価される +たびに実行される. + +@node 正規表現式, 変数展開, コマンド出力, 式 +@comment node-name, next, previous, up +@subsection 正規表現式 +@cindex 正規表現式 + +例 + +@example +/^ruby the OOPL/ +/ruby/i +@end example + +@code{/}で囲まれた文字列は正規表現を表す.終りの@code{/}の後ろに文字 +@code{i}が与えられた場合には,その正規表現はマッチ時に大文字小文字の区 +別をしない. + +@table @code +@item ^ +行頭 +@item $ +行末 +@item . +任意の1文字 +@item \w +英数字.[0-9A-Za-z_]と同じ +@item \W +非英数字 +@item \s +空白文字.[ \t\n\r\f]と同じ +@item \S +非空白文字 +@item \d +数字.[0-9] と同じ +@item \D +非数字 +@item \b +語境界文字(文字クラス外) +@item \B +非語境界文字 +@item \b +後退(0x08)(文字クラス内) +@item [ ] +文字クラス指定 +@item * +直前の表現の0回以上の繰り返し +@item + +直前の表現の1回以上の繰り返し +@item {m,n} +m回からn回の繰り返し +@item ? +0または1回 +@item | +選択 +@item ( ) +正規表現をまとめる +@end table + +その他に文字列と同じバックスラッシュ記法や変数展開も有効である. + +@node 変数展開, 数値リテラル, 正規表現式, 式 +@comment node-name, next, previous, up +@subsection 変数展開 +@cindex 変数展開 + +例 + +@example +"my name is #@{$ruby@}" +@end example + +ダブルクォート(@code{"})で囲まれた文字列式,コマンド文字列,正規表現, +およびワイルドカード式の中では@code{#{変数名}}という形式で変数の内容を +展開することができる.変数が変数記号(@code{$},@code{@@})で始まる場合に +は@code{#変数名}という形式でも展開できる.文字@code{#}に続く文字が +@code{@{},@code{$},@code{@@}でなければ,そのまま文字@code{#}として解釈 +される. + +@node 数値リテラル, 変数と定数, 変数展開, 式 +@comment node-name, next, previous, up +@subsection 数値リテラル + +@table @samp +@item 123 +整数 +@item -123 +整数(符合つき数) +@item 1_234 +整数(10進数は@code{_}を含むことができる) +@item 123.45 +浮動小数点数 +@item 1.2e-3 +浮動小数点数 +@item 0xffff +16進整数 +@item 0377 +8進整数 +@item ?a +文字@code{a}のコード(97) +@item ?\C-a +コントロールaのコード(1) +@item ?\M-a +メタaのコード(225) +@item ?\M-\C-a +メタ-コントロールaのコード(129) +@item :シンボル +識別子/変数名/演算子と一対一対応する整数.sendなどでメソッドを指定する +時などに使う. +@end table + +?表現では全てのバックスラッシュ記法が有効である. + +@node 変数と定数, 配列式, 数値リテラル, 式 +@comment node-name, next, previous, up +@subsection 変数と定数 + +Rubyの変数はスコープ(有効範囲)と寿命(有効期限)によって4種類に分類され, +その種類は変数名の最初の一文字で決定される.通常の変数の2文字目以降は +英数時または@code{_}であるが,システム変数の一部は「@code{$}+1文字の記 +号」という変数がある.変数名の長さに関して特別な制限はない. + +@menu +* グローバル変数:: +* インスタンス変数:: +* クラス定数:: +* ローカル変数:: +* 疑似変数:: +@end menu + +@node グローバル変数, インスタンス変数, 変数と定数, 変数と定数 +@comment node-name, next, previous, up +@subsection グローバル変数 + +例 + +@example +$foobar +$/ +@end example + +@code{$}で始まる変数のスコープはグローバルであり,プログラムのどこから +でも参照できる.その寿命はプログラムの寿命と等しい.グローバル変数には +宣言は必要ない.初期化されていないグローバル変数を参照した時の値は +@code{nil}である. + +@node インスタンス変数, クラス定数, グローバル変数, 変数と定数 +@comment node-name, next, previous, up +@subsection インスタンス変数 + +例 + +@example +@@foobar +@end example + +@code{@@}で始まる変数はインスタンス変数であり,そのクラスまたはサブク +ラスのメソッドから参照できる.スコープはメソッド内であり,その寿命はオ +ブジェクトの寿命に等しい.インスタンス変数にも宣言は必要ない.初期化さ +れていないインスタンス変数を参照した時の値は@code{nil}である. + +@node クラス定数, ローカル変数, インスタンス変数, 変数と定数 +@comment node-name, next, previous, up +@subsection クラス定数 + +例 + +@example +FOOBAR +@end example + +大文字で始まる識別子は定数へのアクセスであり,最初に定義されたクラスと +全てのサブクラスのスコープ内で参照できる.定数の定義は代入か,定数を定 +義しているモジュールをインクルードすることによって行なわれる.定数への +代入はトップレベル,すなわちメソッドが定義できるレベルでのみ可能である. +定数はクラス間で値が共有され,一度代入すると値を変更することができない +(代入は例外を発生させる).クラス定数の寿命はクラスの寿命と等しい.初期 +化されていないクラス定数を参照した時の値は@code{nil}である. + +クラス定義は自動的に定数を定義するので,クラス名は定数である. + +あるクラスまたはモジュールに属する定数を外部から参照するためには +@code{::}演算子を用いる. + +例 +@example +Foo::Bar +@end example + +@code{::}演算子を用いた代入はできない. + +@node ローカル変数, 疑似変数, クラス定数, 変数と定数 +@comment node-name, next, previous, up +@subsection ローカル変数 + +例 + +@example +foobar +@end example + +小文字または@code{_}で始まる識別子はローカル変数またはメソッド呼出しで +ある.ローカル変数スコープにおける小文字で始まる識別子への最初の代入は +そのスコープに属するローカル変数の宣言になる.宣言されていない識別子の +参照は引数の無いメソッド呼び出しとみなされる. + +ローカル変数のスコープは,その変数が宣言されたイテレータブロック,メソッ +ド定義,またはクラス/モジュール定義ブロックの終りまでである.寿命もそ +のブロックの終りまで(トップレベルのローカル変数はプログラムの終了まで) +であるが,例外としてイテレータブロックが手続きオブジェクト化された場合 +は,そのオブジェクトが消滅するまで存在する.同じスコープを参照する手続 +きオブジェクト間ではローカル変数は共有される. + +@node 疑似変数, 変数と定数, ローカル変数, 変数と定数 +@comment node-name, next, previous, up +@subsection 疑似変数 + +通常の変数以外に疑似変数と呼ばれる特殊な変数が4つある. + +@table @code +@item self +現在のメソッドの実行主体 +@item nil +Nilクラスの唯一のインスタンス(偽を表す) +@item __FILE__ +スクリプトのファイル名(文字列) +@item __LINE__ +現在の行番号(整数) +@end table + +これらの疑似変数は代入によってその値を変更することはできない.これらの +変数への代入は例外を発生させる. + +@node 配列式, 連想配列式, 変数と定数, 式 +@comment node-name, next, previous, up +@subsection 配列式 + +例 + +@example +[1, 2, 3] +@end example + +配列はArrayクラスのインスタンスである.配列を生成する式は以下の形式で +ある. + +@example +@code{[} 式,@dots{}@code{]} +@end example + +それぞれの式を評価した結果を含む配列を返す.要素数が0の空配列を生成す +るためには空の配列式 + +@example +@code{[} @code{]} +@end example + +を用いる. + +@node 連想配列式, メソッド呼出式, 配列式, 式 +@comment node-name, next, previous, up +@subsection 連想配列式 + +例 + +@example +@{1=>2, 2=>4, 3=>6@} +@end example + +連想配列とは任意のオブジェクトをキー(添字)として持つ配列である.Rubyの +連想配列はHash(連想配列)クラスのインスタンスである.詳細はクラス +@code{Hash}の項を参照されたい.@xref{Hash} + +連想配列を生成する連想配列式は以下の形式である. + +@example +@code{@{} 式 @code{=>} 式@dots{}@code{@}} +@end example + +それぞれの式を評価した結果をキーと値とする連想配列オブジェクトを返す. +要素数が0の連想配列を生成するためには空の連想配列式 + +@example +@code{@{} @code{@}} +@end example + +を用いる.要素が1つ以上ある場合,曖昧でなければ@code{@{}, @code{@}}は +省略できる. + +@node メソッド呼出式, SUPER, 連想配列式, 式 +@comment node-name, next, previous, up +@subsection メソッド呼出式 + +例 + +@example +foo.bar() +foo.bar +bar() +print "hello world\n" +print +@end example + +オブジェクトにメッセージを送る基本的な構文がメッセージ式であり,その基 +本形式は以下の通りである. + +@display +式1 `.' メソッド名 [`(' 引数@dots{}[`*' 引数] `)'] +@end display + +式1を評価して得られるオブジェクトの,識別子で指定されるメソッドを呼び +出す. + +メソッド名には通常の識別子の他,識別子に@code{?}または@code{!}の続いた +ものが許される.慣習として,述語(真偽値を返すメソッド)には@code{?}を, +同名のメソッドに比べてより破壊的な作用をもつメソッド(例:@code{tr}と +@code{tr!})には@code{!}をつける. + +メッセージ式で,レシーバが@code{self}の場合,レシーバを省略して通常の +プログラミング言語における関数のような形式でメソッドを呼び出すことがで +きる. + +@display +メソッド名 `(' 引数@dots{}[`*' 引数]`)' +@end display + +メソッド呼び出しの引数の周りの括弧を省略できるが,第一引数となる式が以 +下の文字または予約語で始まる場合は,解釈に曖昧性が生じる. + +@example +(, [, @{, /, +, -, if, while, * +@end example + +rubyは第1引数のように見える部分を,人間にとって自然だと思われるように +演算子の前後の空白を見ながら,若干複雑なルールで解釈する.予想通りの結 +果が得られなかったり,どのように評価されるか分からない場合は省略せずに +括弧をつける事. + +例 + +@example +foo bar+baz # メソッド呼び出しfoo(bar+baz) +foo(1+2)*5 # メソッド呼び出し(foo(1+2)) * 5 +foo (1+2)*5 # メソッド呼び出しfoo((1+2) * 5) +foo 1 # メソッド呼び出しfoo(1) +foo -1 # メソッド呼び出しfoo(-1) +foo - 1 # ローカル変数foo - 1 +@end example + +メソッド呼び出しでは引数が1つもない時にも括弧を省略できる.ただし,レ +シーバを指定しないメソッド呼び出しの場合はローカル変数の参照と解釈され +うる. + +メソッド名としては任意の識別子を用いることができる.最初の文字は大文字 +でも小文字でも構わない.変数名とは識別子の名前空間が違うので重複しても +構わない. + +クラスModuleで定義されているメソッド(@code{public},@code{private})でメ +ソッドの呼び出し方を制御することが出来る.@code{private}で指定された制 +限されたメソッドは関数形式でしか呼び出すことが出来ない. + +@node SUPER, 代入 ,メソッド呼出式, 連想配列式, 式 +@comment node-name, next, previous, up +@subsection SUPER + +例 + +@example +super +super(1,2,3) +@end example + +メッセージ式の特殊なケースとしてスーパークラスのメソッドの呼び出しがあ +る.この形式はメソッドを再定義した時にスーパークラスの定義を利用するた +めに使う. + +@display +super +@end display + + +現在のメソッドに与えられた引数のままスーパクラスの同名のメソッドを呼び +出す.引数として与えられた変数の値を変更しても,渡されるのは元の引数の +値である. + +@display +super`(' 引数@dots{}`)' +@end display + +引数とともにスーパークラスの同名のメソッドを呼び出す.一番最後の引数が +@code{*}に続く場合は通常のメソッド呼び出しと同様に展開して渡される. + +@node 代入, 演算子式, SUPER, 式 +@comment node-name, next, previous, up +@subsection 代入 + +例 + +@example +foo = bar +foo[0] = bar +foo.bar = baz +@end example + +代入式は変数などに値を設定するために用いられる.代入式は演算子形式をとっ +ているが,メソッドではないので再定義することはできない.左辺になること +が出来るのは以下の3種類の式である. + +変数(`$'識別子 | `@@'識別子 | 識別子) + +@display +変数 `=' 式 +@end display + +変数への代入は右辺の式を評価して得られた値を左辺で指定された変数に代入 +する. + +配列参照(式[式@dots{}]) + +@display +式1`[' 式2@dots{}`]' `=' 式n +@end display + +配列参照式への代入は,式1を評価して得られるオブジェクトに,式2から式n +までを引数として,@code{[]=} というメソッドを呼び出す. + +属性参照(式`.'識別子) + +@display +式1 `.' 識別子 `=' 式2 +@end display + +属性参照(引数なしのメソッド呼び出し)への代入は,式1を評価して得られる +オブジェクト(レシーバが省略された場合は@code{self})に対して, +@code{識別子=}というメソッドを,式2を引数として呼び出す. + +@menu +* 自己代入:: +* 多重代入:: +@end menu + +@node 自己代入, 多重代入, 代入, 代入 +@comment node-name, next, previous, up +@subsubsection 自己代入 + +例 + +@example +foo += 12 +@end example + +式の値そのものに演算を加えるために自己代入形式がある. + +@display +式1 op= 式2 # 式1は代入可能でなければならない. +@end display + +この形式は内部的に@code{式1 = 式1 op 式2}と同様に評価される.ただし, +式1は1回しか評価されないので,式1に副作用がある場合は, +@code{式1 = 式1 op 式2}とは動作が異なる結果となる. +opとして使える演算子は + +@display + +, -, *, /, %, **, &, |, ^, <<, >> +@end display + +の11種類である.演算子と@code{=}の間にスペースを空けてはいけない. + +@node 多重代入, , 自己代入, 代入 +@comment node-name, next, previous, up +@subsubsection 多重代入 + +例 + +@example +foo, bar, baz = 1, 2, 3 +foo, = list() +foo, *rest = list2() +@end example + +同時に複数の変数に代入を行なうことができる.その形式は以下の通りである. + +@display + 左辺 `,' [左辺 `,'@dots{}] [`*' 左辺]= 式 [, 式@dots{}] +@end display + +左辺には代入式の3種類の式が来る.右辺の式が一つしかない場合は,その値 +を配列として(必要ならば@code{to_a}メソッドで配列に変換して),要素をそ +れぞれ左辺に代入する.それ以外の場合には,それぞれの式の値が左辺に代入 +される.左辺の数と右辺の要素の数が合わない時には足りない変数には +@code{nil}が代入され,余った要素は無視される.多重代入の最後の要素の前 +に@code{*}がある場合,残りの全て引数が配列として代入される. + +例 + +@example +foo, bar = [1, 2] # foo = 1; bar = 2 +foo, bar = 1, 2 # foo = 1; bar = 2 +foo, bar = 1 # foo = 1; bar = nil + +foo, bar, baz = 1, 2 # foo = 1; bar = 2; baz = nil +foo, bar = 1, 2, 3 # foo = 1; bar = 2 +foo,*bar = 1, 2, 3 # foo = 1; bar = [2, 3] +@end example + +多重代入の値は(配列に変換された)右辺である. + +@node 演算子式, 制御構造, 代入, 式 +@comment node-name, next, previous, up +@subsection 演算子式 + +例 + +@example +1+2*3/4 +@end example + +プログラミングの利便のために一部のメソッド呼び出しと制御構造は演算子形 +式をとる.Rubyには以下にあげる演算子がある.上のものほど結合順位が強く, +同じ列の演算子の結合順位は同じである. +@cindex{結合規則} + +@display + 強 :: + [](配列参照), []=(配列代入) + ** + -(unary) +(unary) ! ~ + * / % + + - + << >> + & + | ^ + > >= < <= + <=> == != =~ !~ + && + || + .. ... + =(代入) 自己代入(+=, -=@dots{}) + and or + not + 弱 if修飾子 while修飾子 +@end display + +ほとんどの演算式にはメソッド呼び出しとして解釈される(クラス毎に再定義 +できる)が,一部再定義できない特殊なものがある.再定義できない特殊演算 +子は + +@display + =, .., ..., !, not, &&, and, |, or, if修飾子, while修飾子 +@end display + +の各演算子と,これらとの組み合わせになる !=, !~ および自己代入演算子で +ある. + +上であげた特殊演算子以外の演算子形式は以下のようなメソッド呼び出しと見 +なされる. + +単項演算子(+, -, ~) + +@display +式1. 演算子 () +@end display + +配列(連想配列を含む)の要素の参照(式1 `[' 式2@dots{}`]') + +@display +式1. `[]' (式2@dots{}) +@end display + +配列要素の代入( 式1 `[' 式2@dots{}`]' `=' 式n) + +@display +式1. `[]=' (式2@dots{}, 式n) +@end display + +それ以外の2項演算子(式1 演算子 式2) + +@display +式1. 演算子 (式2) +@end display + +これはあくまでもそういう形式のメソッド呼び出しとして解釈されるというだ +けで,rubyプログラムでこういう記述が許されるというわけではない. + +@node 制御構造, クラス定義, 演算子式, 式 +@comment node-name, next, previous, up +@subsection 制御構造 + +Rubyでは(Cなどとは異なり)制御構造は式であり,何らかの値を持つ.この点で +lispなどに似ているといえる.RubyはC言語やPerlから引き継いだ制御構造を持 +つが,特徴的な制御構造としてイテレータを持つ.イテレータは繰り返しを始め +とする制御をユーザが定義する事が出来るものである. +@xref{イテレータ(繰り返し子)} + +@menu +* IF:: +* IF修飾子:: +* CASE:: +* AND:: +* OR:: +* 範囲指定式:: +* NOT:: +* WHILE:: +* WHILE修飾子:: +* イテレータ(繰り返し子):: +* FOR:: +* YIELD:: +* FAIL:: +* BEGIN:: +* RETRY:: +* RETURN:: +* BREAK:: +* CONTINUE:: +* REDO:: +@end menu + +@node IF, IF修飾子, 制御構造, 制御構造 +@comment node-name, next, previous, up +@subsubsection IF + +例 + +@example +if age >= 12 then print "adult fee\n" else print "child fee\n" end +gender = if foo.gender == "male" then "male" else "female" end +@end example + +構文 + +@display +if 式1 [then] + 式@dots{} +[elsif 式2 [then] + 式@dots{}]@dots{} +[else + 式@dots{}] +end +@end display + +条件判断式.Rubyの@code{if}式は@code{else if}でも@code{elif}でもなく +@code{elsif}で@code{if}の連続を行なうことに注意すること.条件が成立し +て実行した式の値を返す.実行しなかった場合の値は@code{nil}. + +@code{if}の条件判断部の式では文字列と正規表現リテラルは式 +@example +$_=~ リテラル +@end example +の省略であるとみなされる. + +@node IF修飾子, CASE, IF, 制御構造 +@comment node-name, next, previous, up +@subsubsection IF修飾子 + +例 + +@example +print "debug\n" if $debug +@end example + +構文 + +@display +式 if 式 +@end display + +条件修飾子(@code{if})の式は先行する式に先だって評価される.動作も対応 +する@code{if}式と同様である.@code{if}修飾子のついた式の値は条件が成立 +した場合には式の値,不成立の場合には@code{nil}である. + +@node CASE, AND, IF修飾子, 制御構造 +@comment node-name, next, previous, up +@subsubsection CASE + +例 + +@example +case $age +when 0 .. 2 + "baby" +when 3 .. 6 + "little child" +when 7 .. 12 + "child" +when 12 .. 18 + "youth" +else + "adult" +end +@end example + +構文 + +@display +case 式0 +[when 式1 [, 式2]@dots{}[then] + 式@dots{}]@dots{} +[else + 式@dots{}] +end +@end display + +条件分岐,Cの@code{switch}よりもPascalの@code{case}に似ている. +@code{break}で脱出することも後ろの式に継続することもないので注意. + +条件の一致は@code{式n =~ 式0}で行なわれる.つまり, + +@example +case expr0 +when expr1, expr2 + stmt1 +when expr3, expr4 + stmt2 +else + stmt3 +end +@end example + +は以下の@code{if}式とほぼ等価である. + +@example +_tmp = expr0 +if expr1 =~ _tmp || expr2 =~ _tmp + stmt1 +elsif expr3 =~ _tmp || expr4 =~ _tmp + stmt2 +else + stmt3 +end +@end example + +@node AND, OR, CASE, 制御構造 +@comment node-name, next, previous, up +@subsubsection AND + +例 + +@example +test && set +test and set +@end example + +構文 + +@display +式1 `&&' 式2 +式1 `and' 式2 +@end display + +式1を評価し,その値が真(@code{nil}以外)であれば,式2を評価する. +@code{and}は優先順位が低い別名である. + +@code{and}の両辺の式では文字列と正規表現リテラルは式 +@code{$_ =~ リテラル} の省略であるとみなされる. + +@node OR, 範囲指定式, AND, 制御構造 +@comment node-name, next, previous, up +@subsubsection OR + +例 + +@example +demo || die +demo or die +@end example + +構文 + +@display +式1 `||' 式2 +式1 'or 式2 +@end display + +式1を評価し,その値が偽であれば,式2を評価する.@code{or}は優先順位が +低い別名である. + +@code{or}の両辺の式では文字列と正規表現リテラルは式 +@code{$_ =~ リテラル}の省略 +であるとみなされる. + +@node 範囲指定式, NOT, OR, 制御構造 +@comment node-name, next, previous, up +@subsubsection 範囲指定式 + +例 + +@example +1 .. 20 +/first/ ... /second/ +@end example + +構文 + +@display +式1 `..' 式2 +式1 `...' 式2 +@end display + +条件式以外の場所では式1から式2までの範囲オブジェクトを返す. + +条件式として範囲指定式が用いられた場合は,式1が真になるまでは偽を返し, +その後は式2が真を返すまでは真を返す.式2が真になれば状態は偽に戻る. +@code{..}は式1が真になった時に式2を評価し(awkのように),@code{...}は次 +の評価まで式2を評価しない(sedのように). + +条件式で範囲指定式の両辺となる式では,文字列と正規表現リテラルは式 +@code{$_ =~ リテラル}の省略,整数定数は@code{$. == 定数}の省略と解釈さ +れる. + +@node NOT, WHILE, 範囲指定式, 制御構造 +@comment node-name, next, previous, up +@subsubsection NOT + +例 + +@example +! me +not me +i != you +@end example + +構文 + +@display +`!' 式 +not 式 +@end display + +式が真であれば偽,偽であれば真を返す. + +@code{!}式では文字列と正規表現リテラルは式@code{$_ =~ リテラル}の省略 +であるとみなされる. + +@display +式1 `!=' 式2 +@end display + +@code{!(式1 == 式2)}の省略形 + +@display +式1 `!~' 式2 +@end display + +@code{!(式1 ~= 式2)}の省略形 + +@node WHILE, WHILE修飾子, NOT, 制御構造 +@comment node-name, next, previous, up +@subsubsection WHILE + +例 + +@example +while sunshine() + work() +end +@end example + +構文 + +@display +while 式 + @dots{} +end +@end display + +式を評価した値が真の間,本体を繰り返し実行する.@code{while}式の値は +@code{nil}である. + +whileの条件判断部の式では文字列と正規表現リテラルは式 +@code{$_ =~ リテラル} の省略であるとみなされる. + +@node WHILE修飾子, イテレータ(繰り返し子), WHILE, 制御構造 +@comment node-name, next, previous, up +@subsubsection WHILE修飾子 + +例 + +@example +sleep while idle +@end example + +構文 + +@display +単純式 while 式 +@end display + +左の式を評価した値が真の間,右の単純式を繰り返し実行する.右の単純式が +@code{begin}式である場合はまず@code{begin}式を評価してから条件式を評価 +する(最低一度はbegin式を実行する).@code{while}修飾子のついた式の値は +@code{nil}である. + +@node イテレータ(繰り返し子), FOR, WHILE修飾子, 制御構造 +@comment node-name, next, previous, up +@subsubsection イテレータ(繰り返し子) + +例 + +@example +[1,2,3].each@{|i| print i*2, "\n"@} +@end example + +イテレータとは制御構造(特にループ)の抽象化のために用いられるメソッドの +一種である.コードの断片(ブロックと呼ばれる)を指定してイテレータを呼び +出すと,イテレータは適当な値をセットしてブロックを評価する(おそらくは +複数回).イテレータからのブロックの呼び出しは@code{yield}式を用いる(後 +述). + +イテレータの呼び出しは以下の構文で行なわれる. + +@display +式 `@{' [`|' 左辺式@dots{}`|'] 式@dots{}`@}' +@end display + +「式」をブロックとして設定し,「式」のメソッドをイテレータとして評価す +る.「式」のトップレベルのメソッドだけがイテレータとして呼び出され, +レシーバを表す式や,引数の式はイテレータとしては呼び出されない.「式」 +が複数の式を含む時,各々がイテレータとして順に呼ばれる. + +イテレータ内で@code{yield}式が実行されると,そこで指定された値が左辺式 +で指定された変数に代入され,ブロックが実行される.ブロックの実行が終了 +するとその値は @code{yield}式の値として返される.あるメソッドがイテレー +タとして呼び出されたかどうかはメソッド@code{iterator?}の戻り値で知るこ +とができる.中には@code{Enumerable:grep}メソッドのようにイテレータとし +て呼ばれた時と普通のメソッドとして呼ばれた時とで動作が異なるメソッドも +ある. + +@node FOR, YIELD, イテレータ(繰り返し子), 制御構造 +@comment node-name, next, previous, up +@subsubsection FOR + +例 + +@example +for i in [1, 2, 3] + print i*2, "\n" +end +@end example + +オブジェクトの各要素に対して操作を行なうための形式も提供されている.形 +式は以下の通り. + +@display +for 左辺式@dots{} in 式 + 式 +end +@end display + +式の各要素に対し式を実行する.これは以下の式とほぼ等価である.「ほぼ」 +というのは,イテレータブロックは新しいローカル変数の有効範囲を導入する +のに対し,@code{for}文はローカル変数のスコープに影響を及ぼさない点が異 +なるからである + +@display +(式).each `@{' `|' 左辺式@dots{}`|' 式 `@}' +@end display + +よって式の値のオブジェクトがメソッド@code{each}を持たない場合, +@code{for}を実行すると例外が発生する. + +@node YIELD, FAIL, FOR, 制御構造 +@comment node-name, next, previous, up +@subsubsection YIELD + +例 + +@example +yield data +@end example + +構文 + +@display +yield `(' [式 [`,' 式@dots{}]]) +@end display + +イテレータの中でブロックの呼び出しを行なう.@code{yield}を実行したメソッ +ドがイテレータとして呼び出されていない時には例外が発生する. +@code{yield} の値はブロックの戻り値である. + +@code{yield}の引数の括弧は曖昧でない限り省略できる. + +@node FAIL, BEGIN, YIELD, 制御構造 +@comment node-name, next, previous, up +@subsubsection FAIL + +例 + +@example +fail +fail "you lose" +@end example + +構文 + +@display +fail `(' [メッセージ] `)' +@end display + +例外を発生させる.メッセージが与えられた場合には発生したソースファイル +名,行番号をシステム変数@code{$@@}に,メッセージを@code{$!}にセットする. + +@code{fail}の引数の括弧は省略できる. + +@node BEGIN, RETRY, FAIL, 制御構造 +@comment node-name, next, previous, up +@subsubsection BEGIN + +例 + +@example +begin + do_something() +rescue + recover() +ensure + must_to_do() +end +@end example + +複数の式をまとめるためと例外所理のために@code{begin}式がある. +@code{begin}式の形式は以下の通りである. + +@display +begin + 式@dots{} +[rescue + 式@dots{}] +[ensure + 式@dots{}] +end +@end display + +@code{begin}式の値は一番最後に評価された式の値である.@code{begin}式の +処理中に発生した時に例外は@code{rescue}節で捕獲することが出来る.この +場合の値@code{begin}式の値はは@code{rescue}部で最後に評価した式の値で +ある.更に@code{ensure}節が存在する時は@code{begin}式を終了する前に必 +ず(正常終了時だけでなく,例外, @code{return}, @code{break}, +@code{continue}, @code{redo}などによる脱出でも)@code{ensure}節の式を評 +価する. + +@node RETRY, RETURN, BEGIN, 制御構造 +@comment node-name, next, previous, up +@subsubsection RETRY + +例 + +@example +retry +@end example + +構文 + +@display +retry +@end display + +再実行.@code{begin}式の@code{rescue}節で使われた場合,@code{begin}式 +を始めからもう一度実行する.例外処理を行なってから再試行するのに使う. + +@example +begin + 何らかの処理(例外が発生する) +rescue + 例外処理 + retry # 例外に対応して再実行 +end +@end example + +イテレータ,イテレータブロックまたはfor文の中で使われた場合には,その +イテレータの評価自体を最初から実行する.イテレータの引数も再評価される. + +@example +for i in 1..5 + retry if some_condition # i == 1 からやり直し +end +@end example + +@example +# ユーザ定義のuntil loop +def until(cond) + yield + retry if not cond +end +@end example + +@code{rescue}節やイテレータ以外でretryが用いられた場合例外が発生する. + +@node RETURN, BREAK, RETRY, 制御構造 +@comment node-name, next, previous, up +@subsubsection RETURN + +例 + +@example +return +return 12 +return 1,2,3 +@end example + +構文 + +@display +return [式[`,' 式@dots{}]] +@end display + +式の値を戻り値としてメソッドの実行を終了する.式が2つ以上与えられた時 +には,それらを要素とする配列をメソッドの戻り値とする.式が一つもない場 +合には @code{nil} が戻り値となる. + +@node BREAK, CONTINUE, RETURN, 制御構造 +@comment node-name, next, previous, up +@subsubsection BREAK + +例 + +@example +break +@end example + +構文 + +@display +break +@end display + +@code{break} はループを脱出する.Cと違い,@code{break}はもっとも内側の +ループを脱出する作用だけを持ち,@code{case} を抜ける作用は持たない. + +@node CONTINUE, REDO, BREAK, 制御構造 +@comment node-name, next, previous, up +@subsubsection CONTINUE + +例 + +@example +continue +@end example + +構文 + +@display +continue +@end display + +@code{continue}はもっとも内側のループの次の繰り返しを始める. + +@node REDO, 制御構造, CONTINUE, 制御構造 +@comment node-name, next, previous, up +@subsubsection REDO + +例 + +@example +redo +@end example + +構文 + +@display +redo +@end display + +@findex redo +@code{redo}はループ条件のチェックを行なわず,現在の繰り返しをやり直す. + +@node クラス定義, モジュール定義, 制御構造, 式 +@comment node-name, next, previous, up +@subsection クラス定義 +@cindex クラスを定義する + +例 + +@example +class Foo:Super + def test + : + end + : +end +@end example + +構文 + +@display +class クラス名 [`:' スーパークラス名 ] + 定義実体 +end +@end display + +@findex class +クラス名は大文字で始まる識別子である. + +@node モジュール定義, メソッド定義, クラス定義, 式 +@comment node-name, next, previous, up +@subsection モジュール定義 +@cindex モジュールを定義する + +例 + +@example +module Foo + def test + : + end + : +end +@end example + +構文 + +@display +module クラス名 + 定義実体 +end +@end display + +@findex module +モジュール名は大文字で始まる識別子である. + +@node メソッド定義, 特異メソッド定義, モジュール定義, 式 +@comment node-name, next, previous, up +@subsection メソッド定義 +@cindex メソッドを定義する + +例 + +@example +def fact(n) + if n == 1 then + 1 + else + n * fact(n-1) + end +end +@end example + +構文 + +@display +def メソッド名 [`(' [引数 [= デフォルト]]@dots{}[`,' `*' 引数 ]`)'] + 定義実体 +end +@end display +@findex def + +引数にデフォルト式が与えられた場合,メソッド呼び出し時に引数が与えられ +なかった場合にはデフォルト式を評価した結果で初期化される(デフォルト式 +の評価は呼び出し時に行われる).一番最後の引数が@code{*}に続く(単一の) +式である場合,その式を評価した結果(配列でなければ変換される)を展開して, +引数として追加する. + +通常メソッド定義はネストできないので,メソッド定義式中ではメソッド定義 +式を再び呼び出せない. + +メソッド名は識別子または文字列である.演算子の再定義をする時には文字列 +で指定する.仮引数並びの最後に@code{*}がある場合,仮引数より多く与えら +れた実引数は,最後の引数に配列として与えられる(足りない時にはエラー). + +メソッドには呼び出し制限を加えることができ,制限を加えられたメソッドは, +関数形式でしか呼び出せない(privateメソッド). + +新規にメソッドを定義する場合,クラス定義式の外にあるdef式はデフォルト +ではprivateメソッドを定義し,クラス定義式の中にあるdef式はpublicメソッ +ドを定義する.スーパークラスのメソッドを再定義する場合には定義されるメ +ソッドの可視性はスーパークラスのメソッドのものを受け継ぐ. + +メソッドの可視性を変更する場合には@code{Module}クラスで定義されている +@code{public}, @code{private}の各メソッドを用いる. + +@node 特異メソッド定義, ALIAS, メソッド定義, 式 +@comment node-name, next, previous, up +@subsection 特異メソッド定義 + +例 + +@example +def foo.test() + print "this is foo\n" +end +@end example + +構文 + +@display +def 式 `.' メソッド名 [`(' [引数 [= デフォルト]]@dots{}[`,' `*' 引数 ]`)'] + 定義実体 +end +@end display + +特異メソッドとはある特定のオブジェクトに固有のメソッドである. + +この形式は式の値であるオブジェクトに特異メソッドを定義する.式の値は +(ビルトインクラスでない)通常オブジェクトか,クラスまたはモジュールであ +る必要がある.通常メソッド定義とは異なり,特異メソッドはメソッド本体内 +でもネストして定義することができる. + +特異メソッドは通常は継承しないが,例外としてクラスの特異メソッドはその +サブクラスにも継承される.言い替えればクラスの特異メソッドは他のオブジェ +クト指向システムにおけるクラスメソッドの働きをする. + +注意: インクルードしたモジュールの特異メソッドは継承しない. + +@node ALIAS, UNDEF, 特異メソッド, 式 +@comment node-name, next, previous, up +@subsection ALIAS +@cindex メソッドに別名をつける + +例 + +@example +alias foo bar +@end example + +構文 + +@display +alias メソッド名1 メソッド名2 +@end display + +@findex alias +@code{alias}文でメソッドに別名をつけることができる.別名を付けられたメ +ソッドは,その時点でのメソッド定義を引き継ぎ,元のメソッドが再定義され +ても,再定義前の古いメソッドが呼び出されたのと全く同じ働きをする. + +@node UNDEF, DEFINED?, ALIAS, 式 +@comment node-name, next, previous, up +@subsection UNDEF +@cindex メソッドの定義を取り消す + +例 + +@example +undef bar +@end example + +構文 + +@display +undef メソッド名 +@end display + +@findex undef +メソッドの定義を取り消すためにはundefを用いる. + +defによる別名定義と@code{undef}による定義取り消しによってクラスのイン +タフェースをスーパークラスと独立に変更することができる.ただし,メソッ +ドがselfにメッセージを送っている場合もあるので,よく注意しないと既存の +メソッドが動作しなくなる可能性がある. + +@node DEFINED?, , UNDEF, 式 +@comment node-name, next, previous, up +@subsection DEFINED? +@cindex メソッドが定義されているかどうか +@cindex 変数が定義されているかどうか +@cindex 定数が定義されているかどうか + +例 + +@example +defined? print +defined? File.print +defined?(foobar) +defined?($foobar) +defined?(@@foobar) +defined?(Foobar) +@end example + +構文 + +@display +defined? 式 +@end display + +@findex defined? +式がメソッド呼び出しの場合,そのメソッドが定義されている時に真を返す. +式が変数や定数の参照である場合は,それらの変数や定数が定義されている時 +に真を返す.それ以外の式の場合は式を評価して,例外が発生しなければ真を +返す. + +@node 組み込み関数, 組み込み変数と定数, rubyの文法, Top +@comment node-name, next, previous, up +@chapter 組み込み関数 + +Rubyには厳密な意味では関数はないが@code{Kernel}クラスの関数メソッドは +(全ての通常クラスから関数形式で呼び出せるので),関数的に用いられる.関 +数的に用いられるメソッドを以下にあげる.これらのメソッドを再定義する際 +には互換性を考えて行なうべきである. + +@ftable @code +@item autoload(@var{module}, @var{file}) + +@var{module}に最初にアクセスした時に@var{file}を@code{require}するよう +に設定する.@var{module}は文字列またはシンボルで指定する. + +@item caller([@var{level}]) + +@var{level}段上の呼出し元の情報を@code{$@@}の形式で得る.トップレベル +では@code{nil}を返す.callerの戻り値を@code{$@@}に代入することで例外の +発生位置を設定できる.また,以下のようなコードで呼出し関係のバックトレー +スを表示できる. + +@example +n = 0 +while c = caller(n) + print c, "\n" +end +@end example + +@item eof +@itemx eof? + +コマンドラインからの入力が@code{EOF}に到達している場合,真を返す. + +@item eval(@var{expr}) + +@var{expr}として与えられた文字列をrubyプログラムとして解釈,実行する. + +@item exec(@var{command}) + +プログラムの実行を終了する.@var{status}として整数が与えられた場合,そ +の値をrubyコマンドの終了ステータスとする.デフォルトは0. + +@item exit!(@var{status}) + +プログラムの実行を終了する.整数@var{status}を終了ステータスとする. +@code{exit}とは違って,例外処理などは一切行なわない.@code{fork}の後, +子プロセスを終了させる時などに用いる. + +@item fork + +@samp{fork}システムコールを実行し,子プロセスを生成する.詳細は +@samp{fork(2)}を参照のこと.親プロセス側では子プロセスのプロセスidを返 +し,子プロセス側では@code{nil}を返す.何らかの原因で子プロセスの生成に +失敗した時には例外が発生する.イテレータとして呼ばれた時は,生成した子 +プロセスで与えられたブロックを評価し,ブロックの評価が終了した時点で子 +プロセスは正常終了する. + +@item format(@var{format}@dots{}) + +フォーマットとして与えられた文字列をC言語の@samp{sprintf}と同じように +解釈し,引数を展開した文字列を返す.メソッド@code{sprintf}の別名. + +Rubyにおける@samp{format}指定子の拡張については@code{sprintf}の項を参 +照のこと. + +@item getc + +標準入力から一文字取り出す.戻り値は読み込んだ文字の文字コード(ASCII) +を表す@code{Fixnum}である. + +@item gets + +引数として与えられたファイル(なければ標準入力)で構成される仮想 +的なファイル(システム変数@code{$<}でアクセスできる)から一行読み込ん +で,読み込みに成功した時にはその文字列を返す.ファイルの終りに +到達した時には@code{nil}を返す.行の区切りはシステム変数@code{$/}によって +変更できる.読み込んだ文字列はシステム変数@code{$_}にもセットされる. + +@item gsub(@var{pattern}[, @var{replace}]) +@itemx gsub!(@var{pattern}[, @var{replace}]) + +システム変数@code{$_}の指す文字列内で @var{pattern}にマッチする部分を +全て@var{replace}に置き換える.@code{String}クラスの@code{gsub}メソッ +ドの解説を参照のこと.引数@var{replace}が省略された時にはイテレータと +して動作し,ブロックを評価した結果で置換する.@code{gsub}メソッドは +@code{$_}の値をコピーして,コピーの方を更新し,@code{$_}に代入する. + +@code{gsub!}は@code{$_}の指している文字列そのものを書き換える. + +@item iterator? + +メソッドがイテレータとして呼び出された時には真,そうでない時に偽を返す +述語. + +@item kill(@var{signal}, @var{pid}@dots{}) + +@var{pid}で指定されたプロセスにシグナルを送る.@var{signal}はシグナル +番号か名前で指定する.負の値を持つシグナル(あるいはシグナル名の前に +@code{-})を与えるとプロセスではなくプロセスグループにシグナルを送る. + +@item load(@var{file}) + +@var{file}をロードする.@var{file}をロードするパスはシステム変数 +@code{$:}で決定される. + +@item loop + +無限ループするイテレータ.(中断されない限り)永久にイテレータブロックを +評価し続ける. + +@item open(@var{file}[, @var{mode}]) + +@var{file}をオープンして,@code{File}オブジェクトを返す.ファイル名は +オープンするファイルを示す.ファイル名が@code{|}で始まる時には続く文字 +列をコマンドとして起動し,パイプラインを生成する. + +コマンド名が@samp{"-"}である時,@code{open}はrubyの子プロセスを生成し, +その子プロセスとのパイプを返す. + +@var{mode}はファイルのアクセスモードを指定する.これは以下のうちのいず +れかの文字列である. + +@table @samp +@item r +読み込み専用.@code{open}するファイルはあらかじめ存在している必要があ +る. + +@item r+ +読み書き両用.@code{open}するファイルはあらかじめ存在している必要があ +る. + +@item w +書き込み専用.ファイルが存在していた場合,長さを0にする.存在していな +ければ新たにファイルを作成する. + +@item w+ +読み書き両用.読み込みが行なえること以外は@samp{"w"}と同じ働きをする. + +@item a +追加書き込み専用.ファイルはあらかじめ存在している必要がある.書き込み +はファイルの最後に追加される. + +@item a+ +読み書き両用.ファイルが存在していなければ新たに作成する.アクセス位置 +はファイルの最後に初期化される. +@end table + +モードが省略された場合のデフォルトは@samp{"r"}である. + +@item print(@var{arg}1@dots{}) + +引数を順に出力する.引数が与えられない時には@code{$_}の値を出力する. +文字列以外のオブジェクトが引数として与えられた場合には,当該オブジェク +トの@code{to_s}メソッドによって文字列に変換してから出力される.システ +ム変数@code{$;}(出力フィールドセパレータ)に@code{nil}でない値がセット +されている時には,各引数の間にその文字列を出力する.システム変数 +@code{$\}(出力フィールドセパレータ)に@code{nil}でない値がセットされている時 +には,最後にそれを出力する. + +@item printf([@var{port}, ]@var{format}, @var{arg}@dots{}) + +C言語のprintfと同じように@var{format}に従い引数を文字列に変換し,出力 +する.第1引数がIOのサブクラスのインスタンスであった場合はそのオブジェ +クトに対して出力を行なう.デフォルトは@code{$stdout}に出力する. + +Rubyにおけるformat指定子の拡張についてはsprintfの項を参照のこと. + +@item proc +@itemx lambda + +与えられたイテレータブロックを手続きオブジェクト(クラス@code{Proc}のイ +ンスタンス)として返す. + +@item rand(@var{max}) + +0から@var{max}を越えない範囲の整数の乱数を発生する.戻り値は +@code{Fixnum}. + +@item require(@var{feature}) + +@var{feature}で指定されるfileをロードする.@var{feature}はロードするファ +イルを指定する文字列で,拡張子@code{.rb}が指定されている時はrubyスクリ +プト,拡張子@code{.o}が指定されている時は,バイナリモジュールをロード +する.ただし,いくつかのアーキテクチャではバイナリモジュールのロードは +提供されない.バイナリモジュールの実際のファイルの拡張子はアーキテクチャ +毎に異なるが,@var{feature}名の拡張子はいつも@code{.o}を用いる. + +拡張子が指定されない場合は,まず@code{.rb},次に@code{.o}を補って,ファ +イルを検索する. + +requireは実際にロードした時には @code{TRUE},既にロードされている時に +は@code{FALSE}を返す.またロードした@var{feature}の名前を(拡張子も含め +て),変数@code{$"}に追加する. + +@item select(@var{reads}[, @var{writes}[, @var{execpts}[, @var{timeout}]]]) + +@samp{select(2)}を実行する.@var{reads}/@var{writes}/@var{execpts}には +IO(またはそのサブクラス)のインスタンスの配列を与える.@var{timeout}は +Fixnum/Float/Timeのいずれかで指定する.戻り値は@var{timeout}が成立した +場合には@code{nil},そうでないときは3要素の配列を返し,その各要素が入 +力/出力/例外待ちのオブジェクトの配列である(指定した配列のサブセット. +待ちオブジェクトの配列を指定しなかった場合は@code{nil}).システムコー +ル実行中に割込みが起こった場合には各配列は空になる. + +@item sleep([@var{sec}]) + +@var{sec}秒だけプログラムの実行を停止する.@var{sec}が省略された場合, +プロセスに@code{SIGALRM}が送られない限り,永久にスリープする.実際にス +リープした秒数を返す. + +@item sprintf(@var{format}@dots{}) + +@var{format}文字列をC言語の@samp{sprintf}と同じように解釈し,引数を展 +開した文字列を返す.メソッド@code{format}の別名. + +@var{format}指定子はC言語の@samp{sprintf}()が受け付けるもの(ただし, +Rubyには unsignedがないので,%uは除く)に加えて, %b, %B, %O, %Xを使うこ +とができる.%bは数値の2進表示,%B, %O, %Xはそれぞれ2進,8進,16進数の +表示を行なうが,負の数の処理の際に2の補数表現ではなく,その絶対値表記 +の先頭に@code{-}をつけたものを表示する. + +@item srand([@var{seed}]) + +乱数の@var{seed}を設定し,古い初期値を返す.初期値が省略された時には +@samp{time(3)}の返す値をデフォルトとする. + +@item sub(@var{pattern}[, @var{replace}]) +@itemx sub!(@var{pattern}[, @var{replace}]) + +システム変数@code{$_}の指す文字列で最初に@var{pattern}にマッチする部分 +を@var{replace}に置き換える.引数@var{replace} が省略された時にはイテ +レータとして動作し,ブロックを評価した結果で置換する.subメソッドは +@code{$_}の値をコピーして,コピーの方を更新し,@code{$_}に代入する.そ +の他の詳細に関しては@code{String}クラスの@code{sub}メソッドの解説を参 +照のこと. + +@code{sub!}は@code{$_}の指している文字列そのものを書き換える. + +@item syscall(@var{num}, @var{arg}@dots{}) + +@var{num}で指定された番号のシステムコールを実行する.第2引数以降をシス +テムコールの引数として渡す.引数は文字列または整数でなければならない. + +@item system(@var{command}) + +@var{command}を実行し,成功した時(サブプロセスがstatus 0で終了した時) +には真を,失敗した時には偽を返す.終了ステータスは変数@code{$?} で参 +照できる. + +@item test(@var{cmd}, @var{file} [, @var{file}]) + +ファイルテストを行う.@var{cmd}は以下に示す文字リテラルである.ファイ +ル名として@code{"&"}を指定すると,直前のファイルへの@samp{stat(2)}の結 +果を再利用する. + + +1つの引数を取るもの + +@display +?r ファイルを実効 uid で読むことができる +?w ファイルに実効 uid で書くことができる +?x ファイルを実効 uid で実行することができる +?o ファイルの所有者が実効 uid である + +?R ファイルを実 uid で読むことができる +?W ファイルに実 uid で書くことができる +?X ファイルを実 uid で実行することができる +?O ファイルの所有者が実 uid である + +?e ファイルが存在する + +?z ファイルサイズが 0 である +?s ファイルサイズが 0 でない(ファイルサイズを返す) + +?f ファイルはプレーンファイルである +?d ファイルはディレクトリである +?l ファイルはシンボリックリンクである +?p ファイルは名前つきパイプ(FIFO)である +?S ファイルはソケットである +?b ファイルはブロック特殊ファイルである +?c ファイルはキャラクター特殊ファイルである + +?u ファイルに setuid ビットがセットされている +?g ファイルに setgid ビットがセットされている +?k ファイルに sticky ビットがセットされている + +?M スクリプトの実行を開始した時点でのファイルの古さ +?A スクリプトの実行を開始した時点でのファイルのアクセス時間 +?C スクリプトの実行を開始した時点でのファイルの inode 変更時間 +@end display + +2つの引数を取るもの + +@display +?= ファイル1とファイル2のタイムスタンプが等しい +?> ファイル1の方がファイル2より更新時間が新しい +?< ファイル1の方がファイル2より更新時間が古い +?- ファイル1がファイル2にハードリンクされている +@end display + +@item trace_var(@var{var}, @var{command}) + +@var{var}で指定された大域変数の値が変更された時に評価される +@var{command}を指定する.@var{command}は文字列,またはブロックで指定す +る.traceを解除するためには@code{untrace_var}を用いる. + +@item trap(@var{signal}, @var{command}) +@itemx trap(@var{signal}) @{@dots{}@} + +@var{signal}の割り込みがかかった時に@var{command}を実行する. +@var{signal}はシグナル名かシグナルの番号.@var{command}は文字列,また +はブロックで指定する.commandとして@samp{"SIG_IGN"}または +@samp{"IGNORE"}を指定した時にはそのシグナルを無視する(可能ならば). +@samp{"SIG_DFL"}または@samp{"DEFAULT"}を指定した時はデフォルトの動作を +行なう.@samp{"EXIT"}を指定した時はシグナルを受け取ると(終了処理を行っ +た後),exit status 1で終了する. + +@item untrace_var(@var{var}) + +@var{var}に対する全てのtraceを解除する.traceとして指定されているオブ +ジェクトを配列にいれて返す. + +@item wait + +子プロセスが終了するのを待ち,終了した子プロセスのpidを返す.子プロセ +スが一つもなければ@code{nil}を返す. + +@item waitpid(@var{pid}, @var{flags}) + +@var{pid}で指定される特定の子プロセスの終了を待ち,そのプロセスが終了 +した時に真を返す.子プロセスが存在しないか,ノンブロッキングモードで子 +プロセスがまだ終了していない時には@code{nil}を返す.@samp{waitpid(2)}か +@samp{wait4(2)}の実装されていないマシンでは@var{flags}はいつも@code{nil}また +は0でなければならない. +@end ftable + +@node 組み込み変数と定数, 組み込みクラスとモジュール, 組み込み関数, Top +@comment node-name, next, previous, up +@chapter 組み込み変数と定数 +@cindex{組込み変数} + +@table @samp + +@item $! +エラーメッセージ.failで設定する. + +@item $@@ +エラーが発生した時点のファイル名と行番号が +@example +"ファイル:行番号[:メソッド名(あれば)]" +@end display +の形式で格納される. + +@item $& +最後に成功したパターンマッチ + +@item $` +最後のパターンマッチでマッチした文字列の前の文字列 + +@item $' +最後のパターンマッチでマッチした文字列の後に続く文字列 + +@item $+ +最後の検索パターンでマッチした最後の括弧 + +@item $1@dots{}$9 +最後に成功したパターンマッチでn番目の括弧にマッチした値が格納される. +該当する括弧がなければ@code{nil}が入っている. + +@item $~ +最後のマッチに関する情報.これをセットすると@code{$&}や +@samp{$1@dots{}$9}の値が変化する. + +@item $= +この変数の値が@code{nil}でない時,パターンマッチや文字列の比較でアルファベッ +トの大文字小文字を区別しない.デフォルトは@code{nil}(区別する). + +@item $/ +入力レコードセパレータ.ファイルや文字列に対して@code{each}を行なう時 +の分割文字を指定する.$/に空文字列(@code{""})を指定すると段落単位で入 +力を行ない,@code{nil}を指定すると全体を一度に読み込む.@code{$/}には +正規表現は使えない.デフォルトは@samp{"\n"}. + +@item $\ +出力レコードセパレータ.この変数に文字列を指定すると@code{write}や +@code{print}の度に最後にこの文字列を付加して出力する.デフォルトは +@code{nil} (なにも追加しない). + +@item $, +@code{Array:join}のデフォルトの区切り文字列.@code{print}の各引数の間 +に出力される文字列. + +@item $; +@code{String:split}のデフォルトの区切り文字. + +@item $. +最後に読んだ入力ファイルの行番号. + +@item $< +引数(なければ標準入力)で構成される仮想ファイル.つまり@code{gets}は +@code{$<.gets}と同じ意味である.@code{$<.file}で現在読み込み中のファイ +ルオブジェクトが,@code{$<.filename}でそのファイル名が得られる.(覚え +方: @code{<}はシェルの入力元指定) + +@item $> +@code{print}や@code{printf}のデフォルトの出力先.初期値は +@code{$stdout}.@samp{-i}オプションを指定した場合には読み込み元と同じ +名前のファイル.(覚え方: @code{>}はシェルの出力先指定) + +@item $_ +最後に@code{gets}などで読み込んだ文字列. + +@item $0 +rubyスクリプトの名前.この変数に代入すると@samp{ps(1)}の出力が変化する. + +@item $* +rubyスクリプトに与えられた引数.ruby自身に対する引数は取り除かれている. + +@item $$ +現在実行中のrubyプロセスのpid. + +@item $? +最後に実行した子プロセスのstatus. + +@item $: +ファイルをロードする時に検索するディレクトリへのパスを含む配列.起動時 +にはデフォルト値(コンパイル時に指定する)に加えて,環境変数 +@var{RUBYLIB}の値とruby起動時の@samp{-I}オプションで指定された値が追加 +される.(覚え方: コロンは環境変数@var{PATH}の区切り文字である) + +@item $" +@code{require}でロードされたファイル名を含む配列.@code{require}で同じ +ファイルを2回ロードしないために用いられる.(覚え方: prevent files to +be doubly quoted(loaded)) + +@item $ARGF +@code{$<}の別名. + +@item $ARGV +@code{$*}の別名. + +@item $DEBUG +@code{-d}フラグの状態(真偽値). + +@item $FILENAME +仮想ファイル@code{$<}で現在読み込み中の(メソッドgetsが今読んでいる)ファ +イル名.@code{$<.filename}と同じ. + +@item $KCODE +現在処理対象としている漢字コードを表す文字列.@samp{"EUC"}, +@samp{"SJIS"}または@samp{"NONE"}.この変数の値を変更すると正規表現のマッ +チの直前に正規表現の再コンパイルが行われる. + +@item $LOAD_PATH +@code{$:}の別名. + +@item $stdin +標準入力 + +@item $stdout +標準出力 + +@item $stderr +標準エラー出力 + +@item $VERBOSE +@code{-v}フラグの状態(真偽値) + +@item TRUE +@itemx FALSE +それぞれ真偽値を表す(@code{TRUE}の値はt,@code{FALSE}の値は@code{nil}). +条件判断は@code{nil}を偽,それ以外の全ての値を真として判断するため, +@code{TRUE}の値は代表的な真の値という以上の意味を持たない.よって,あ +るメソッドの返値が真であるということと,それが@code{TRUE}を返すという +ことは厳密には同じではない(述語的に用いられるメソッドは大抵真の値とし +て@code{TRUE}を返すようにはなっているが).つまり + +@example +if some.method() then @dots{} else @dots{} end +@end example + +と + +@example +if some.method() == TRUE then @dots{} else @dots{} end +@end example + +は完全には同義ではない.@code{FALSE}に関しては,このような問題は生じ +ない. + +@item STDIN +標準入力($stdinの初期値) +@item STDOUT +標準出力($stdoutの初期値) +@item STDERR +標準エラー出力($stderrの初期値) + +@item ENV +環境変数にアクセスする連想配列.文字列をキーとして与えると対応する環境 +変数の値が得られる.環境変数が存在しない場合は@code{nil}が返る. + +@item VERSION +rubyのバージョンを示す文字列 +@end table + +@node 組み込みクラスとモジュール, C言語とのインタフェース, 組み込み変数と定数, Top +@comment node-name, next, previous, up +@chapter 組み込みクラスとモジュール + +@menu +クラス +* Array:: +* Bignum:: +* Class:: +* Dir:: +* File:: +* Fixnum:: +* Float:: +* Hash:: +* Integer:: +* IO:: +* Kernel:: +* Module:: +* Nil:: +* Numeric:: +* Object:: +* Proc:: +* Range:: +* Regexp:: +* String:: +* Struct:: +* Time:: + +モジュール + +* Comparable:: +* Enumerable:: +* Etc:: +* FileTest:: +* GC:: +* Math:: +* Process:: +@end menu + +@node Array, Bignum, 組み込みクラスとモジュール, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Array + +数字を添字とした配列のクラスである.生成は一般的には配列式``[@dots{}]''で +行なわれる. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code +@item self[@var{nth}] +@itemx self[@var{start}..@var{end}] +@itemx self[@var{start}, @var{length}] + +配列の要素にアクセスする.最初の形式では配列の@var{nth}番目の要素を返 +し,2番目の形式では@var{start}番目の要素から@var{end}番目の要素を含む +部分配列を返す.3番目の形式では@var{start}番目から@var{length}個の要素 +を含む部分配列を返す. + +@item self[@var{nth}] = @var{val} +@itemx self[@var{start}..@var{end}] = @var{val} +@itemx self[@var{start}, @var{length}] = @var{val} + +配列の要素を変更する.最初の形式では配列の@var{nth}番目の要素を +@var{val}に変更する.2番目の形式は@var{start}番目の要素から@var{end}番 +目の要素までを@var{val}に変更する.3番目の形式では@var{start}番目から +@var{length}個の要素を@var{val}に変更する. + +2番目,3番目の形式では@var{val}は配列でなければならない. + +例 + +@example +ary = [1, 2, 3, 4, 5] +ary[0..2] = [0, 0] # 配列の内容は [0, 0, 4, 5] +ary[1, 0] = [7] # 配列の内容は [0, 7, 0, 6, 5] +@end example + +@item self + @var{other} + +配列の連結.@code{self}と@var{other}の両方の配列の内容を繋げた新しい配 +列を返す. + +@item self * @var{times} + +配列の繰り返し. + +@item self - @var{other} + +集合の差演算.@code{self}から@var{other}の要素を取り除いた内容の新しい +配列を返す.重複する要素は1度だけ現れる. + +@item self * @var{other} + +集合の積演算.両方の配列に含まれる要素からなる新しい配列を返す. +重複する要素は1度だけ現れる. + +@item self | @var{other} + +集合の和演算.両方の配列にいずれかに含まれる要素を全て含む新し +い配列を返す.重複する要素は1度だけ現れる. + +@item self << @var{obj} + +objを配列の末尾に追加する.@code{self}を返すので@code{C++}的に連鎖でき +る. + +@item assoc(@var{key}) + +連想リスト(2要素の配列を要素とする配列)を検索し,第1要素が@var{key}と +等しい (@code{==}で比較する)配列を返す. + +@item clear + +配列の大きさを0にする. + +@item delete(@var{val}) + +@var{val}と一致する要素を削除する. + +@item delete_if @{@dots{}@} + +要素を削除するイテレータ.ブロックを評価した値が真の時,対応する要素を +配列から削除する. + +@item each @{@dots{}@} + +配列の各要素を順に与えるイテレータ. + +@item fill(@var{val}) +@itemx fill(@var{val}, @var{start}[, @var{length}]) +@itemx fill(@var{val}, @var{start}..@var{end}) + +配列(の指定された部分)の要素の値を@var{val}に設定する.2番めの形式で +@var{length}が省略された時は配列の終りまでの長さをとる.指定された部分 +配列が元の配列の範囲を越える時は自動的に拡張される. + +@item index(@var{val}) + +@var{val}と等しい最初の要素のインデックスを返す.該当する要素が存在し +ない場合は@code{nil}を返す. + +@item indexes(@var{ary}) +@itemx indexes(@var{index_}1,@dots{}, @var{index_n}) + +1番目の形式では整数の配列を引数として受けて,その要素をインデックスと +する要素を含む配列を返す.2番目の形式では各引数の値をインデックスとす +る要素を含む配列を返す. + +@item join([@var{sep}]) + +配列の要素を連結した文字列を返す.各要素は文字列に変換され,間に +@var{sep}を挟んで連結される.@var{sep}が省略された時にはシステム変数 +@code{$,}の値が用いられる. + +@item length +@itemx size + +配列の長さ(要素数)を返す. + +@item pack(@var{template}) + +配列の内容を@var{template}文字列にしたがって,1つの文字列にパックする. +パックした文字列を返す.テンプレートは型指定文字列とその長さ(省略時は +1)を並べたものである.長さとして@code{*}が指定された時は「残りのデータ +全て」の長さを表す. + +型指定文字は以下のものがある. + +@display +a ASCII文字列(null文字を詰める) +A ASCII文字列(スペースを詰める) +b ビットストリング(下位ビットから上位ビット) +B ビットストリング(上位ビットから下位ビット) +h 16進文字列(下位ニブルが先) +H 16進文字列(上位ニブルが先) +c char +C unsigned char +s sort +S unsigned sort +i int +I unsigned int +l long +L unsigned int +n ネットワークバイトオーダーのshort +N ネットワークバイトオーダーのlong +f 単精度浮動小数点数(機種依存) +d 倍精度浮動小数点数(機種依存) +x ナルバイト +X 1バイト後退 +@@ 絶対位置への移動 +@end display + +@item pop + +配列の末尾の要素を取り除いて,それを返す. + +@item push(@var{obj}) + +@var{obj}を配列の末尾に追加する. + +@item rassoc(@var{value}) + +連想リスト(2要素の配列を要素とする配列)を検索し,第2要素が@var{value} +と等しい(@code{==}で比較する)配列を返す. + +@item shift + +配列の先頭の要素を取り除いて,それを返す. + +@item sort +@itemx sort @{|@var{a}, @var{b}|@dots{}@} + +配列の内容をソートする.イテレータとして呼び出された場合はブロックを評 +価した値で要素の大小を決定する.大きい時に正,等しい時に0,小さき時に +負.通常のメソッドとして呼び出された場合は各要素を@code{<=>}で比較する. + +@item to_a + +自分自身を返す.対称性のために用意されているメソッドであまり面白くない. + +@item unshift(@var{obj}) + +@var{obj}を配列の先頭に追加する. +@end ftable + +Single Methods: + +@ftable @code +@item Array[@var{item}@dots{}] + +引数を要素とする配列を生成する. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Bignum, Class, Array, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Bignum + +無限多倍長整数のクラス.演算の結果がこの@code{Fixnum}の範囲内である場 +合には自動的にクラスは@code{Fixnum}に変換される.一般的にrubyプログラ +ムでは@code{Fixnum}と@code{Bignum}の変換は暗黙のうちに行われるので,意 +識する必要は無い.@code{Float}との混合に関しては,@code{Bignum}より +@code{Float}の方がgenericityが高いのにも関わらず,@code{Bignum}の方が, +大きな値を表現できるので,変換時に桁落ちが生じる可能性がある. + +SuperClass: Integer + +Methods: + +@ftable @code +@item self + @var{other} +@itemx self - @var{other} +@itemx self * @var{other} +@itemx self / @var{other} +@itemx self % @var{other} +@itemx self ** @var{other} + +算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. + +@item ~ self +@itemx self | @var{other} +@itemx self & @var{other} +@itemx self ^ @var{other} + +ビット演算.それぞれビット反転,論理和,論理積,排他的論理和を返す. + +@item self << @var{bits} +@itemx self >> @var{bits} + +シフト演算.それぞれ@var{bits}ビットだけ左右にビットシフトを行なう. + +@item divmod(@var{other}) + +商と剰余からなる配列を返す. +@end ftable + +@xref{Integer} + +@node Class, Comparable, Bignum, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Class + +クラスのクラス.より厳密に説明するとクラスは特異メソッドを継承するため +に,それぞれメタクラスと呼ばれる名前のないクラスをクラスとして持ち, +@code{Class}はそのメタクラスのクラスである(分かったかな?).が,この解 +説が理解できなくても,rubyを使うことに何の支障もない.クラスには特異メ +ソッドを定義できる事と,スーパークラスで定義された特異メソッドはそのサ +ブクラスでも有効である事を知れば十分である. + +SuperClass: Module + +Private Methods: + +@ftable @code +@item attr(@var{name}[, @var{public}]) + +そのクラスのインスタンスに対して@var{name}で指定される属性を定義する. +詳しくは@code{Module}の@code{attr}メソッドの項を参照のこと. +@end ftable + +Methods: + +@ftable @code +@item new(@dots{}) + +クラスのインスタンスを生成する.多くの場合このメソッドはサブクラスの特 +異メソッドによってオーバーライドされ,クラスによって引数が異なる. +@end ftable + +@xref{Module} + +@node Comparable, Dir, Class, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Comparable + +比較演算を許すクラスのための@code{Mixin}.このモジュールをインクルード +することによって,@code{<=>}演算子を定義するだけで他の演算子はその定義 +を利用して派生できる. + +Methods: + +@ftable @code +@item self == @var{other} + +@code{self}が@var{other}と等しい時真を返す. + +@item self > other + +@code{self}が@var{other}より大きい時真を返す. + +@item self >= @var{other} + +@code{self}が@var{other}より大きいか等しい時真を返す. + +@item self < @var{other} + +@code{self}が@var{other}より小さい時真を返す. + +@item self <= @var{other} + +@code{self}が@var{other}より小さいか等しい時真を返す. + +@item between?(min, max) + +@code{self}が@var{min}と@var{max}の範囲内にある時真を返す. +@end ftable + +@node Dir, Enumerable, Comparable, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Dir + +ディレクトリ内の要素を順に返すディレクトリストリーム操作のためのクラス. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code + +@item close + +ディレクトリストリームをクローズする.以後の操作は例外を発生させる. + +@item each @{|@var{item}|@dots{}@} + +ディレクトリ内の各要素を順に与えるイテレータ. + +@item getwd +@itemx pwd + +カレントディレクトリを返す. + +@item rewind + +ディレクトリストリームを先頭にリセットする. + +@item seek(@var{pos}) + +ディレクトリストリームの位置を@var{pos}に設定する. + +@item tell + +ディレクトリストリームの現在の位置を返す. + +Single Methods: + +@item self[@var{pat}] +@itemx glob(@var{pat}) + +文字列@var{pat}を@samp{sh}形式のワイルドカードとして展開した結果を文字 +列の配列として返す.書式は以下の通りである. + +@ftable @samp +@item * +任意の文字列(空文字列を含む)と一致 +@item ? +任意の1文字と一致 +@item [ ] +[]内のいずれか1文字と一致 +@item {@dots{}} +{}内の(コンマで区切られた)いずれかの文字列と一致 +@end ftable + +@item chdir(@var{path}) + +カレントディレクトリを@var{path}に変更する. + +@item chroot(@var{path}) + +プロセスのルートディレクトリを変更する,同名のシステムコールと同じ働き +をする.この操作は実効uidがスーパユーザである時だけに制限されている. +ルートディレクトリを元に戻す(ルートディレクトリを上方に変更する)方法は +提供されていない. + +@item mkdir(@var{path}[, @var{mode}]) + +@var{mode}で指定されたモードを持つディレクトリ@var{path}を作成する.モー +ドは@code{umask}によって修正される.@var{mode}のデフォルト値は0777. + +@item open(@var{path}) + +@var{path}に対するディレクトリストリームをオープンする. + +@item rmdir(@var{path}) + +@var{path}で指定されたディレクトリを削除する.ディレクトリは空である必 +要がある. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Enumerable, File, Dir, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Enumerable + +要素に対する繰り返しを行なうクラスのための@code{Mixin}.このモジュール +をインクルードするためには,メソッド@code{each}を定義する必要がある. + +Methods: + +@ftable @code + +@item collect @{|@var{item}|@dots{}@} + +各要素に対してブロックを評価した結果を全て含む配列を返す + +@item find @{|@var{item}|@dots{}@} + +要素に対してブロックを評価した値が真になった最初の要素を返す. + +@item find_all @{|@var{item}|@dots{}@} + +各要素に対してブロックを評価した値が真であった要素を全て含む配列を返す. + +@item grep(pattern) +@itemx grep(pattern) @{|@var{item}|@dots{}@} + +@code{要素 =~ @var{pattern}}が成立する全ての要素を含む配列を返す.イテ +レータとして用いられた時は上記の条件の成立した要素に対してブロックを実 +行する. + +@item member?(@var{val}) + +@var{val}と@code{==}の関係にある要素を持つ時,真を返す. + +@item index(@var{val}) + +@var{val}と@code{==}の関係にあるオブジェクトが何番目に現れたかを返す. +一番最初の要素が0になる.要素が存在しない時には@code{nil}を返す.順序 +のないクラスに対してはあまり意味がない. + +@item length + +要素の数を返す. + +@item min + +最小の要素を返す.全ての要素がお互いに@code{<=>}メソッドで比較できるこ +とを仮定している. + +@item max + +最大の要素を返す.各要素が@code{<=>}メソッドで比較できることを仮定して +いる. + +@item reverse + +全ての要素を逆順に並べた配列を返す. + +@item sort +@itemx sort @{|@var{a}, @var{b}|@dots{}@} + +全ての要素をソートした配列を返す. +@end ftable + +@node File, FileTest, Enumerable, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section File + +ファイルアクセスのためのクラス.メソッド@code{open}で生成される.また, +このクラスの特異メソッドとして@code{test}のファイルテスト演算子相当の +メソッドが定義されている(@code{FileTest}モジュールのメソッド郡). + +SuperClass: IO + +Methods: + +@ftable @code + +@item atime + +ファイルの最終アクセス時刻を返す. + +@item ctime + +ファイルの最終ステータス変更時刻を返す. + +@item chmod(@var{mode}) + +ファイルのパーミッションを変更する(cf @samp{chmod(2)}). + +@item chown(@var{owner}, @var{group}) + +ファイルの所有者とグループを変更する(cf @samp{chown(2)}).@code{nil}か +@code{-1}を指定することによって所有者やグループを現在のまま変えないで +おくことができる. + +@item eof +@itemx eof? + +ファイルの終端に到達した時に真を返す. + +@item lstat + +ファイルに関する@code{Stat}構造体を返す.@code{lstat}はファイルがシン +ボリックリンクであればリンクそのものに関する@code{Stat}構造体を返す. +構造体の内容については@code{stat}を参照のこと. + +@item mtime + +ファイルの最終修正時刻を返す. + +@item rewind + +ファイルのファイルポインタの位置を先頭に移動する. + +@item path + +ファイルのパス名を返す. + +@item seek(@var{offset}, @var{ptrname}) + +ファイルのファイルポインタの位置を@var{offset}に移動する. +@var{ptrname}は0,1,2のいずれかであって,それぞれファイルの先頭,現在 +位置,ファイルの終端からの相対を示す. + +@item stat + +ファイルに関する@code{Stat}構造体を返す(@xref{Struct}). + +@display +struct stat + dev # ファイルの存在するデバイス + ino # ファイルのi-node番号 + mode # モード + nlink # ハードリンクの数 + uid # 所有者のユーザID + gid # 所有者のグループID + rdev # デバイスのID(スペシャルファイルのみ) + size # ファイルサイズ(byte数) + blksize # ファイルシステムにおいて適切なブロックサイズ + blocks # ブロック数 + atime # 最終アクセス時間 + mtime # 最終更新時間 + ctime # 最終状態変更時間 +end +@end display + +詳細な説明は@samp{stat(2)}を参照のこと.システム上で定義されている +@code{Stat}構造体に該当するメンバがない場合は0が設定されている. + +@item tell + +ファイルの現在のファイルポインタの位置を返す. + +@item truncate(@var{length}) + +ファイルを切り捨てて最大@var{length}バイトにする.ファイルは +@code{write}モードでオープンされていなければならない. + +Single Methods: + +@item atime(@var{filename}) + +@var{filename}の最終アクセス時刻を返す. + +@item basename(@var{filename}[, @var{suffix}]) + +@var{filename}の最後の要素を返す.@var{suffix}が与えられた場合は,その +拡張子も取り除く. + +@example +basename("ruby/ruby.c") + @result{} "ruby.c" +basename("ruby/ruby.c", ".c") + @result{} "ruby" +@end example + +@item ctime(@var{filename}) + +@var{filename}の最終ステータス変更時刻を返す. + +@item chmod(@var{mode}, @var{path}, @var{file}@dots{}) + +ファイルのパーミッションを変更する(cf @samp{chmod(2)}).変更したファイ +ル数を返す. + +@item chown(@var{owner}, @var{group}, @var{file}@dots{}) + +ファイルの所有者とグループを変更する(cf @samp{chown(2)}).@code{nil}か +@code{-1}を指定することによって所有者やグループを現在のまま変えないで +おくことができる.変更したファイル数を返す. + +@item dirname(@var{fname}) + +ファイル名の最後の要素以外を返す. + +@item expand_path(@var{path}) + +ファイル名を絶対パスに展開する.@samp{~}はホームディレクトリに展開され +る. + +@example +expand_file_name("..") + @result{} "/home/matz/work" +expand_file_name("~") + @result{} "/home/matz" +expand_file_name("~matz") + @result{} "/home/matz" +@end example + +@item link(@var{old}, @var{new}) + +@var{old}へのハードリンク@var{new}を生成する.@samp{link(2)}と同じ制限 +がある. + +@item mtime(@var{filename}) + +@var{filename}の最終修正時刻を返す. + +@item readlink(@var{path}) + +シンボリックリンク@var{path}の内容を文字列として返す. + +@item rename(@var{from}, @var{to}) + +ファイル名@var{from}を@var{to}に変更する.@samp{rename(2)}参照.既に +@var{to}という名前のファイルが存在する時にはまずそのファイルが削除され +る. + +@item stat(@var{filename}) + +@var{filename}のファイルの@code{Stat}構造体を返す. + +@item symlink(@var{old}, @var{new}) + +@var{old}へのシンボリックリンク@var{new}を生成する. + +@item truncate(@var{path}, @var{length}) + +@var{path}で指定されたファイルを切り捨てて最大@var{length}バイトにする. + +@item type(@var{filename}) + +@var{filename}のファイルのタイプを表す文字列を返す.文字列は +@code{"file"},@code{"directory"},@code{"characterSpecial"}, +@code{"blockSpecial"},@code{"fifo"},@code{"link"},@code{"socket"}の +うちのいずれか一つである. + +@item unlink(@var{file}@dots{}) + +ファイルを削除する.ディレクトリの削除には@code{Dir.rmdir}を使うこと. + +@item utime(@var{atime}, @var{mtime}, @var{file}@dots{}) + +ファイルのアクセス時刻を@var{atime}に,修正時刻を@var{mtime}に設定する. +@var{atime},@var{mtime}は数または@code{Time}クラスのインスタンスでな +ければならない. +@end ftable + +これ以外に@code{FileTest}モジュールのメソッドも特異メソッドとして持つ. + +@xref{IO} + +@node FileTest, Fixnum, File, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section FileTest + +ファイルテスト用メソッドを集めたモジュール.インクルードして用いること +もできる.このモジュールのメソッドにファイル名として@code{"&"}を指定す +ると,直前のファイルへの@samp{stat(2)}の結果を再利用する. + +Methods: +Single Methods: + +@ftable @code +@item blockdev?(@var{filename}) + +@var{filename}のファイルがブロックスペシャルファイルである時,真を返す. + +@item chardev?(@var{filename}) + +@var{filename}のファイルがキャラクタスペシャルファイルである時,真を返 +す. + +@item executable?(@var{filename}) + +@var{filename}のファイルが実行可能の時,真を返す. + +@item executable_real?(@var{filename}) + +@var{filename}のファイルが実uid/gidで実行可能の時,真を返す. + +@item exists?(@var{filename}) + +@var{filename}のファイルが存在する時,真を返す. + +@item grpowned?(@var{filename}) + +@var{filename}のファイルのgidが実効グループのgidと同じ時,真を返す. + +@item directory?(@var{filename}) + +@var{filename}がディレクトリの時,真を返す. + +@item file?(@var{filename}) + +@var{filename}のファイルが通常ファイルの時,真を返す. + +@item link?(@var{filename}) + +@var{filename}のファイルがシンボリックリンクである時,真を返す. + +@item pipe?(@var{filename}) + +@var{filename}のファイルが名前つきパイプ(@code{FIFO})である時,真を返 +す. + +@item socket?(@var{filename}) + +@var{filename}のファイルがソケットである時,真を返す. + +@item owned?(@var{filename}) + +@var{filename}のファイルを実効ユーザが所有している時,真を返す. + +@item readable?(@var{filename}) + +@var{filename}のファイルを読みとり可能の時,真を返す. + +@item readable_real?(@var{filename}) + +@var{filename}のファイルを実uid/gidで読みとり可能の時,真を返す. + +@item setuid?(@var{filename}) + +@var{filename}のファイルのsetuidビットがセットされている時,真を返す. + +@item setgid?(@var{filename}) + +@var{filename}のファイルのsetgidビットがセットされている時,真を返す. + +@item size(@var{filename}) + +@var{filename}のファイルが存在する時,ファイルの大きさを返す.存在しな +い時は@code{nil}を返す. + +@item sticky?(@var{filename}) + +@var{filename}のファイルのstickyビットがセットされている時,真を返す. + +@item symlink?(@var{filename}) + +@var{filename}がシンボリックリンクである時,真を返す. + +@item writable?(@var{filename}) + +@var{filename}のファイルが実uid/gidで書き込み可能の時,真を返す. + +@item writable_real?(@var{filename}) + +@var{filename}のファイルが書き込み可能の時,真を返す. + +@item zero?(@var{filename}) + +@var{filename}のファイルが存在し,大きさが0である時,真を返す. +@end ftable + +@node Fixnum, Float, FileTest, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Fixnum + +31bit(マシンのlongの長さ-1 bit)整数のクラス.builtin classである.この +クラスはpointer内の即値であるためcall by valueで呼び出される点が特徴的 +である(他のクラスはcall by reference).演算の結果が31bitを越える場合に +は自動的に@code{Bignum}(無限多倍長整数)に拡張される. + +イテレータ@code{upto},@code{downto},@code{step}は繰り返しのために用 +いられ,一般に@code{Range}クラスを用いるより高速である. + +SuperClass: Integer + +Methods: + +@ftable @code +@item self + @var{other} +@itemx self - @var{other} +@itemx self * @var{other} +@itemx self / @var{other} +@itemx self % @var{other} +@itemx self ** @var{other} + +算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. + +@item ~ self +@itemx self | @var{other} +@itemx self & @var{other} +@itemx self ^ @var{other} + +ビット演算.それぞれビット反転,論理和,論理積,排他的論理和を返す. + +@item self << @var{bits} +@itemx self >> @var{bits} + +シフト演算.それぞれ@var{bits}ビットだけ左右にビットシフトを行なう. + +@item downto(@var{min}) @{@dots{}@} + +イテレータ.@code{self}から@var{min}まで下向きに繰り返す. + +@item id2name + +整数値をIDだとみなして,相当する文字列を返す.相当する文字列が存在しな +い場合は@code{nil}を返す. + +@item step(@var{max}, @var{step}) @{@dots{}@} + +イテレータ.@code{self}から@var{max}まで@var{step}ずつ変化しながら,繰 +り返す. + +@item to_f + +@code{self}を@code{Float}に変換したものを返す. + +@item to_i + +@code{self}をそのまま返す. + +@item upto(@var{max}) @{@dots{}@} + +イテレータ.@code{self}から@var{max}まで繰り返す. +@end ftable + +@xref{Integer} + +@node Float, GC, Fixnum, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Float + +浮動小数点数のクラス. + +SuperClass: Numeric + +Methods: + +@ftable @code +@item self + @var{other} +@itemx self - @var{other} +@itemx self * @var{other} +@itemx self / @var{other} +@itemx self % @var{other} +@itemx self ** @var{other} + +算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. + +@item self == @var{other} +@itemx self > @var{other} + +比較演算. + +@item coerce(@var{num}) + +@var{num}を@code{Float}に変換する.ただし現時点で@code{Float}が理解で +きる他の数は@code{Fixnum}と@code{Bignum}だけである. + +@item to_f + +@code{self}をそのまま返す. + +@item to_i + +@code{self}を整数に変換した結果を返す. +@end ftable + +Single Methods: + +@ftable @code +@item new(@var{float}) + +@var{float}と同じ値を持つ新しい@code{Float}オブジェクトを返す. +@end ftable + +@xref{Numeric} + +@node GC, Hash, Float, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section GC + +Ruby組み込みのgarbage collectorの制御を行なうためのモジュール.このモ +ジュールのメソッドをを用いることによって,一時的にGCを止めたり,GCの起 +きるタイミングを制御したりできる. + +Methods: + +@ftable @code +@item garbage_collect + +GCを開始する.@code{GC.start}と同義. +@end ftable + +Single Methods: + +@ftable @code +@item disable + +GCを禁止する. + +@item enable + +GCを許可する. + +@item start + +GCを開始する. +@end ftable + +@node Hash, Integer, GC, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Hash + +連想配列あるいはハッシュ表.任意のオブジェクトを添字とできる配列のクラ +スである.連想配列オブジェクトの生成は一般的には連想配列式 + +@display +{a=>b,@dots{}} +@end display + +で行なわれる. + +キーとして与えたオブジェクトの内容が変化し,メソッド@code{hash}の返す +値が変わると@code{Hash}は正常に動作しない(値が取り出せなくなる).内容 +によって@code{hash}の値が変化するクラス(たとえば@code{Array}, +@code{Hash}など)のインスタンスはキーに向かない.ただし,内容が +@code{hash}の値に影響するオブジェクトのうち,文字列だけは特別に扱われ +る.文字列をキーとして与えると,文字列をコピーし,コピーを更新不可に設 +定した上で,キーとして使用する.よって,元の文字列を更新してもキーの文 +字列は変化しない.@code{each}, @code{each_key}, @code{keys}などのメソッ +ドがキーとして文字列を返す時,その文字列は更新できない(例外が発生する). + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code +@item self [@var{key}] + +@var{key}をキーとする値を返す. + +@item self [@var{key}]= @var{value} + +@var{key}をキーとして,@var{value}を格納する.@var{value}として +@code{nil}を指定するとその@var{key}に対する項目の削除となる.つまり, +@code{Hash}は値として@code{nil}を持つことはできない. + +@item clear + +連想配列を空にする. + +@item delete(@var{key}) + +@var{key}をキーとする組を削除する. + +@item delete_if @{|@var{item}|@dots{}@} + +要素を削除するイテレータ.@code{[key,value]}という配列を与えて,ブロッ +クを評価した値が真の時,該当する項目を削除する. + +@item each @{|@var{key}, @var{value}|@dots{}@} +@itemx each_pair @{|@var{key}, @var{value}|@dots{}@} + +@code{[key,value]}なる2要素の配列を与えるイテレータ. + +@item each_key @{|@var{key}|@dots{}@} + +全てのkeyに対して繰り返すイテレータ. + +@item each_value @{|@var{value}|@dots{}@} + +全てのvalueに対して繰り返すイテレータ. + +@item has_key?(@var{key}) + +@var{key}をキーとする組が連想配列中に存在する時,真を返す + +@item has_value?(@var{value}) + +@var{value}を値とする組が連想配列中に存在する時,真を返す + +@item indexes(@var{ary}) +@itemx indexes(@var{key_}1,@dots{}, @var{key_n}) + +1番目の形式では配列を引数として受けて,その要素をキーとする要素を含む +配列を返す.2番目の形式では各引数の値をキーとする要素を含む配列を返す. + +@item keys + +連想配列中に存在するキー全てを含む配列を返す. +@item length +@itemx size + +連想配列中の要素の数を返す. + +@item shift + +連想配列中の要素を一つ取り出し(削除して),@code{[key,value]}なる2要素 +の配列を返す. + +@item to_a + +連想配列中の@code{key-value}2要素の配列を要素とする配列を返す. + +@item values + +連想配列中に存在する値全てを含む配列を返す. +@end ftable + +Single Methods: + +@ftable @code +@item Hash[@var{key}, @var{value}@dots{}] + +奇数番目の引数を@var{key},偶数番目の引数を@var{value}とする連想配列を +生成する. + +@item new + +新しい(空の)連想配列オブジェクトを返す. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Integer, IO, Hash, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Integer + +整数クラス.実際はその大きさによって@code{Fixnum}と@code{Bignum}いう二 +つのサブクラスで実現されている.@code{Integer}はそれらのスーパークラス +となる抽象クラスである.Rubyではほとんどの場合,@code{Fixnum}と +@code{Bignum}の区別は必要なく,相互の変換は自動的に行なわれる.整数を +ビット列だとみなす場合には,無限の長さをもつビット列と考えて構わない. + +SuperClass: Numeric + +Methods: + +@ftable @code +@item self[@var{idx}] + +整数の@var{idx}ビット目がセットされていれば1,セットされていなければ0 +を返す. + +@item chr + +その数をコードとする文字だけを含む1文字の文字列を返す.一般に長さ1以上 +の文字列について,次の関係が常に成立する. + +@example +str[0].chr == str[0,1] +@end example + +整数が文字の範囲内(0@dots{}255)になければ例外が発生する. + +@item integer? + +いつも真を返す. +@end ftable + +@xref{Numeric} + +@node IO, Kernel, Integer, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section IO + +入出力のための基本クラス. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code +@item self << @var{object} + +@var{object}を出力する.@var{object}が文字列でない時にはメソッド +@code{to_s}を用いて文字列に変換する.@code{self}を戻り値とするので, +@code{C++}のような@code{<<}の連鎖を使える. + +例 + +@example +$stdout << 1 << " is a " << Fixnum << "\n" +@end example + +@item close + +入出力ポートをクローズする.以後のこのオブジェクトに対する入出力操作は +エラーになる. + +@item closed? + +ポートがクローズされている時,真を返す. + +@item each @{|@var{line}|@dots{}@} +@item each_line @{|@var{line}|@dots{}@} + +一行ずつ読み込んでくるためのイテレータ.行の区切りはシステム変数 +@code{$/}によって変更できる.読み込んだ文字列はシステム変数@code{$_}に +もセットされる. + +@itemx each_byte @{|@var{ch}|@dots{}@} + +一文字ずつ読み込んでくるためのイテレータ.文字は文字コードを表す +@code{Fixnum}である. + +@item fileno +@itemx to_i + +@code{IO}オブジェクトが使っているファイルディスクリプタ(@code{Fixnum}) +を返す. + +@item flush + +バッファをフラッシュする. + +@item getc + +一行読み込んで,読み込みに成功した時にはその文字列を返す.ファイルの終 +りに到達した時には@code{nil}を返す.カーネルメソッド@code{getc}は +@code{$stdin.getc}と同じ意味である. + +@item gets + +一行読み込んで,読み込みに成功した時にはその文字列を返す.ファイルの終 +りに到達した時には@code{nil}を返す. + +@item isatty +@itemx tty? + +入出力ポートがttyである時,真を返す. + +@item print(@var{arg}@dots{}) + +引数を順に出力する.出力先が@code{$>}でなく,レシーバである以外は +@code{Kernel}クラスの@code{print}メソッドと同じ動作をする. + +@item printf(@var{format}, @var{arg}@dots{}) + +@code{C}言語の@code{printf()}と同じ@var{format}に従い引数を文字列に変 +換し,レシーバに出力する. + +@item puts(@var{obj}) + +@var{obj}を出力する.@code{self << obj}と同じ意味である. + +@item read([@var{length}]) + +@var{length}バイト読み込んで,その文字列を返す.@var{length}が省略され +た時には,@code{EOF}までの全てのデータを読み込む. + +@item readlines + +ファイルを全て読み込んで各行を要素としてもつ配列を返す. + +@item sync + +現在の出力同期モードを真偽値で返す.同期モードが真の時は出力関数の呼出 +毎にバッファがフラッシュされる. + +@item sync= @var{newstate} + +出力同期モードを設定する. + +@item sysread(@var{length}) + +@samp{stdio}を経由せずに@samp{read(2)}を用いて入力を行なう.入力された +データを含む文字列を返す.ファイルの終りに到達した時には@code{nil}を返 +す.@samp{read(2)}の性質により必ず@var{length}バイトの文字列が読み込ま +れるわけではない.@code{gets}や@code{getc}など@samp{stdio}を経由するメ +ソッドと混用することはバッファリングの不整合などで思わぬ動作をすること +がある. + +@item syswrite(@var{str}) + +@samp{stdio}を経由せずに,@samp{write(2)}を用いて出力を行なう.このメ +ソッドはバッファリングなど@samp{stdio}がしてくれることは一切行なわない. +@code{syswrite}は実際に書き込んだバイト数を返す.@code{print}や +@code{printf}と@code{syswrite}を混用するのは推奨できない. + +@item write(@var{str}) + +@var{str}を出力する.出力したバイト数を返す. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Kernel, Math, IO, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Kernel + +全てのクラスの基底クラス.Ruby組み込みの全ての関数メソッドはこのクラス +で定義されている.関数メソッドについては「関数」の項目を参照のこと. + +SuperClass: なし + +Methods: + +@ftable @code +@item self == @var{other} +@itemx equal?(@var{other}) + +オブジェクトの一致判定.レシーバと引数の引数が一致する時,真を返す. +@code{Kernel}クラスの定義では双方のオブジェクトが同一の時真を返す. +@code{==}メソッドは各オブジェクトの性質に応じて再定義する必要がある. +@code{==}メソッドを再定義した時には,@code{hash}メソッドもそれに合わせ +て再定義する必要がある. + +equal?メソッドは@code{==}メソッドの別名で,@code{==}を再定義した後でも +オブジェクトの同一性判定を行なうために用いられる.よって@code{equal?} +メソッドはサブクラスで再定義するべきではない. + +@item self =~ @var{other} + +マッチ.デフォルトの動作は@code{==}と同じである.@code{=~}は +@code{case}文での比較にも用いられる. + +@item hash + +オブジェクトのハッシュ値(@code{Fixnum})を返す.@code{Hash}クラスでキー +となるオブジェクトを格納するのに用いられている.@code{A == B}が成立する +時は必ず@code{A.hash == B.hash}が成立する必要があるので,@code{==}を再 +定義した時には必ずこちらもそれに合わせて再定義すること. + +@item id + +各オブジェクトに対して一意の@code{Fixnum}を返す.が,@code{Fixnum}は自 +分自身を返すので,@code{id}が一致しても同じオブジェクトであることは保 +証されない.つまり,@code{obj1.id == obj2.id}が成立しても,どちらかが +@code{Fixnum}であれば,@code{obj1}と@code{obj2}が同じであるとは限らな +い.ただし,両方が@code{Fixnum}でないことが保証できれば,2つのオブジェ +クトが同一であることは確実である. + +@item inspect + +オブジェクトを人間が読める形式の文字列に変換する. + +@item nil? + +オブジェクトが@code{nil}であるかどうか.@code{Kernel}クラスの定義では +真を返す.@code{Nil}クラスで偽を返すよう再定義されている. + + +@item type + +オブジェクトの動的な型(クラス)を返す. + +@example +obj.is_kind_of?(obj.type) +@end example + +は常に成立する. + +@item send(@var{symbol}[, @var{args}@dots{}]) + +@var{symbol}で指定されるメソッドを@var{args}とともに呼び出す. + +@end ftable + +@node Math, Module, Kernel, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Math + +浮動小数点演算をサポートするクラス.Mathモジュールは同じ定義のメソッド +と特異メソッドとの両方が定義されているので,特異メソッドを呼び出して使 +う使い方と,クラスにインクルードして使う使い方との両方ができる. + +例 + +@example +pi = Math.atan2(1, 1) * 4; +include Math +pi2 = atan2(1, 1) +@end example + +Methods: +Single Methods: + +@ftable @code +@item atan2(@var{x}, @var{y}) + +π〜-πの範囲で@var{x}/@var{y}のアークタンジェントを返す. + +@item cos(@var{x}) +@itemx sin(@var{x}) +@itemx tan(@var{x}) + +ラジアンで表された@var{x}の三角関数の値を返す. + +@item exp(@var{x}) + +@var{x}の指数関数の値を返す. + +@item log(@var{x}) + +@var{x}の自然対数を返す. + +@item log10(@var{x}) + +@var{x}の常用対数を返す. + +@item sqrt(@var{x}) + +@var{x}の平方根を返す.@var{x}の値が負である時には例外が発生する. + +@item cbrt(@var{x}) + +@var{x}の立方根を返す. +@end ftable + +@node Module, Nil, Math, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Module + +モジュールのクラス. + +SuperClass: Object + +Private Methods: + +@ftable @code +@item attr(@var{name}[, @var{public}]) + +そのモジュールをインクルードしたクラスのインスタンスに対して@var{name} +で指定される属性を付加し,属性に対するアクセスメソッドを定義する. +@code{attr("attr")}はクラス定義に以下に示すコードを追加するのとほぼ同 +義である. + +@example +def attr; @@attr; end +@end example + +省略可能な第2引数@var{public}が与えられて,かつその値が@code{nil}でな +い時にはその属性には属性設定メソッドも用意され,外部から代入可能になる. + +@code{attr("attr", TRUE)}はクラス定義に以下のコードを追加するのとほぼ +同義である. + +@example +def attr; @@attr; end +def attr=(val); @@attr = val; end +@end example + +属性を構成するメソッドを再定義することによって,アクセス時の動作を変更 +できる.例えば + +@example +attr("test", TRUE) +def test=(val) + print("test was ", @@test, "\n") + print("and now is ", @@test = val, "\n") +end +@end example + +のように設定時に属性の値を表示するようなことが可能である.@var{attr}は +アクセスメソッドがすでに定義されている場合は,デフォルトのアクセスメソッ +ドを定義しない. +@end ftable + +Methods: + +@ftable @code +@item include(@var{module}@dots{}) + +引数で指定したモジュールをインクルードして,メソッド,定数を追加する. +クラス,モジュールに別のモジュールをインクルードすることによって,限定 +された多重継承(@code{Mixin})を実現できる. + +@item module_function(@var{name}@dots{}) + +@var{name}で指定されたメソッドを@samp{module function}に指定する. +@samp{Module function}とはモジュールの特異メソッドであり,かつそのモジュー +ルをインクルードしたクラスのprivateメソッドにもなるようなメソッドの事 +である.例えば,Mathモジュールの関数群は@samp{module function}である. + +@item private(@var{name}@dots{}) + +@var{name}で指定されたメソッドを関数形式でだけ呼び出し可能にする.すで +にprivateメソッドである場合には何もしない. + +@item public(@var{name}@dots{}) + +@var{name}で指定されたメソッドを通常形式で呼び出し可能にする.すでに +publicメソッドである場合には何もしない. + +@example +def foo() 1 end +foo + @result{} 1 +self.foo + @result{} 1 + +def bar() 2 end +private :bar +bar + @result{} 2 +self.bar + @error{} method `bar' not available for "main"(Object) + +Module Baz + def baz() 3 end + module_function :baz +end +Baz.baz + @result{} 3 +include Baz +baz + @result{} 3 +self.baz + @error{} method `baz' not available for "main"(Object) +@end example + +@item to_s + +モジュールの文字列表現であるモジュール名を返す. +@end ftable + +@xref{Object} + +@node Nil, Numeric, Module, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Nil + +偽を表すオブジェクト@code{nil}のクラス.偽変数(の値)@code{nil}は +@code{Nil}クラスの唯一のインスタンスである. + +SuperClass: Kernel + +Methods: + +@ftable @code +@item self + @var{other} + +@var{other}が整数,浮動小数点数,文字列,配列である時,@var{other}を返 +す. + +@item nil? + +常に真を返す. +@end ftable + +@xref{Kernel} + +@node Numeric, Object, Nil, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Numeric + +数一般の性質を表す抽象クラス. + +SuperClass: Object + +Included Modules: Comparable + +Methods: + +@ftable @code +@item + self + +オブジェクト@code{self}そのものを返す + +@item - self + +@code{0 - self}の値を返す.サブクラスでより効率的に再定義されることが +期待される. + +@item abs + +絶対値を返す. + +@item divmod(@var{other}) + +商と剰余の2要素の配列を返す. + +@item next + +次の数を返す.次の数とはその数を越える最小の整数である. +@end ftable + +@xref{Object} +@xref{Comparable} + +@node Object, Proc, Numeric, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Object + +全ての通常クラスのスーパクラス.通常クラスのインスタンスの一般的な振舞 +いを定義している.このクラスのサブクラスでないクラスは@code{Kernel}と +@code{Nil}だけである. + +SuperClass: Kernel + +Methods: + +@ftable @code +@item extened(module@dots{}) + +引数で指定したモジュールを@code{self}にインクルードする.モジュールで +定義されているメソッドが特異メソッドとして追加される. + +@item initialize(@dots{}) + +@code{Class:new}からオブジェクトの生成時に自動的に呼び出される.デフォ +ルトの定義は何もしない.サブクラスで必要に応じて再定義されることが期待 +されている.@code{Class:new}に与えられた引数がそのまま渡される. + +@item is_instance_of?(@var{class}) + +オブジェクト@code{self}がクラス@var{class}のインスタンスである時,真を +返す.@code{obj.is_instance_of?(c)}が成立する時,いつも +@code{obj.is_kind_of?(c)}も成立する. + +@item is_kind_of?(@var{class}) + +オブジェクト@code{self}がクラス@var{class}かそのサブクラスのインスタン +スである時,真を返す. + +@item clone +@item dup + +オブジェクトの複製を作る.インスタンスが即値であるFixnumクラス以外のク +ラスの場合,@code{obj.equal?(obj.clone)}は偽であるが,多くの場合 +@code{obj == obj.clone}は真である. + +Stringクラス以外では(特に再定義しない限り)dupはcloneの別名である. + +@item to_s + +オブジェクトの文字列表現を返す.このメソッドは内部的にprintやformatメ +ソッドで用いられている. + +@item to_a + +オブジェクトを配列に変換する.@code{Kernel}クラスで定義されているデフォ +ルトは,そのオブジェクト自身を含む1要素の配列を返す. +@end ftable + +@xref{Kernel} + +@node Proc, Process, Object, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Proc + +イテレータに渡されたイテレータブロックを手続きとしてオブジェクト化した +もの.実行するコードだけでなくコンテキスト(ローカル変数)なども保存する. +ブロックオブジェクトは,@code{call}メソッドによって,生成されたのと同 +じ環境で評価することができる.ただし,大域脱出(@code{return}, +@code{break}, @code{continue}, @code{redo}, @code{retry})の環境は保存 +されないので,ブロックオブジェクトからの大域脱出の実行は例外を発生させ +ることになる. + +SuperClass: Object + +Methods: + +@ftable @code +@item call(@var{arg}[,@dots{}]) + +ブロックを実行する. +@end ftable + +Single Methods: + +@ftable @code +@item new + +新しいブロックを生成する.@code{yield}を実行できる場所でこのメソッドが +呼ばれると,その時点で実行されるべきコードをコンテキストとともに包み込 +んだオブジェクト(@code{Proc})を生成する. +@end ftable + +@xref{Object} + +@node Process, Range, Proc, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Process + +プロセスに関する操作を行なうためのモジュール.@code{Math}モジュールと +同様に全てのメソッドは特異メソッドとしても通常のメソッドとしても使える. +@code{Process}はプロセスオブジェクトのクラスではなくて,プロセス操作の +メソッドをまとめたものであることに注意すること. + +Methods: +Single Methods: + +@ftable @code +@item egid + +プロセスの現在の実効GIDを返す. + +@item egid= @var{gid} + +プロセスの現在の実効GIDを@var{gid}にセットする. + +@item euid + +プロセスの現在の実効UIDを返す. + +@item euid= @var{uid} + +プロセスの現在の実効UIDを@var{uid}にセットする. + +@item getpgrp([@var{pid}]) + +@var{pid}で指定されたプロセスが現在所属しているプロセスグループのidを +返す.@var{pid}を省略した時と@var{pid}に0を与えた時は現在実行している +プロセスを対象にする. + +@item getpriority(@var{which}, @var{who}) + +@var{which}と@var{who}で指定されるプロセス,プロセスグループ,ユーザの +現在の優先順位を返す.詳細は@samp{getpriority(2)}を参照.Processモジュー +ルではwhichとして指定できる定数@var{PRIO_PROCESS},@var{PRIO_PGRP}, +@var{PRIO_USER}が定義されている. + +@item gid + +プロセスの現在の実GIDを返す. + +@item gid= @var{gid} + +プロセスの現在の実GIDをgidにセットする. + +@item pid + +プロセスのプロセスIDを返す.これはシステム変数@code{$$}の値と同じであ +る. + +@item ppid + +親プロセスのプロセスのプロセスIDを返す.UNIXでは直接の親プロセスが終了 +した場合,親プロセスのpidは1(initのpid)になる. + +@item setpgrp(@var{pid}, @var{pgrp}) + +@var{pid}で指定されたプロセスのプロセスグループを@var{pgrp}にする. +@var{pid}に0を与えると現在実行中のプロセスを対象にする. + +@item setpriority(@var{which}, @var{who}, @var{prio}) + +@var{which}と@var{who}で指定されるプロセス,プロセスグループ,ユーザの +現在の優先順位を@var{prio}に設定する.詳細は@samp{setpriority(2)}を参 +照のこと. + +@item uid + +プロセスの現在の実UIDを返す. + +@item uid= @var{uid} + +プロセスの現在の実UIDを@var{uid}にセットする. +@end ftable + +@node Range, Regexp, Process, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Range + +範囲オブジェクトのクラス.範囲オブジェクトは@code{..}演算子によって生 +成され,一般的には以下のような使い方をする + +@example +for i in 1..5 + @dots{} +end +@end example + +しかし,この場合は以下の方が速い. + +@example +1.upto(5) { + @dots{} +} +@end example + +範囲オブジェクトを生成する@code{..}演算子の両辺は@code{Comparable}を含 +むクラスのインスタンスであれば何でも構わない.範囲は始点と終点を含むこ +とに注意すること. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code +@item self =~ @var{other} + +@code{self}が@var{other}と同じクラスに対する範囲オブジェクトで,その範 +囲内に@var{other}がある時(@code{start <= @var{other} <= end}),真を返 +す.これは@code{case}式で範囲指定する時に便利である.例えば + +@example +case i +when 1, 3..5 + @dots{} +end case +@end example + +のようなコードを書くことができる. + +@item each + +範囲内に存在するオブジェクトを与えるイテレータ.主に@code{for}式のため +に用いられる. + +@item end + +範囲の終点を返す + +@item start + +範囲の始点を返す. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Regexp, String, Range, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Regexp + +正規表現のクラス.正規表現のリテラルは@code{/@dots{}/}という形式で表す +が,動的に生成するためには + +@example +Regexp.new(文字列) +@end example + +とする.ただし,Stringクラスの@code{=~}を始めとして多くのメソッドは正 +規表現の替わりに文字列が与えられた時には内部的に正規表現を生成するので, +生成コストを節約したいと思う時や,正規表現の大文字小文字の区別を明示的 +に指定したい時など以外は明示的に生成したいと思うことは少ないはずだ. + +SuperClass: Object + +Methods: + +@ftable @code +@item self =~ @var{string} + +正規表現が文字列にマッチした場合,マッチした位置を返す.マッチしない場 +合は@code{nil}を返す. + +@item ~ self + +@code{$_ =~ self}と同義. +@end ftable + +Single Methods: + +@ftable @code +@item compile(@var{string}[, @var{casefold}]) +@itemx new(@var{string}[, @var{casefold}]) + +文字列を正規表現に変換したオブジェクトを返す.省略可能な第2引数が与え +られ,その値が@code{nil}でない時には,生成された正規表現オブジェクトは +システム変数@code{$=}の値に関わらず,マッチする時に大文字小文字の違い +を無視する. + +@item quote(@var{str}) + +文字列の中の正規表現で意味を持つ文字をエスケープする.新しい文字列を返 +す. +@end ftable + +@xref{Object} + +@node String, Struct, Regexp, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section String + +文字列クラス.Rubyの文字列はヌルターミネートではないので,バイナリデー +タも扱える.従ってどちらかというと単なる文字列というよりバイト列である. +その思想に基づいて,正規表現に関するメソッド以外は2byte系の文字を意識 +していない.これは作者の手抜きではなく意図的にそうしているのである(信 +じてくれ). + +Stringクラスのメソッドのうち@code{!}で終るものはレシーバを書き換える. +同じ名前で@code{!}の無いものはレシーバのコピーを作ってから,そちらを書 +き換える.@code{!}のあるものの方が高速だが,予期せぬ結果を招きやすいの +で,無いものの方が安全である. + +@example +f = "string" +print f, sub("str", "ski"), f + @result{} string, skiing, string +print f, sub!("str", "ski"), f + @result{} skiing, skiing, skiing +@end example + +SuperClass: Object + +Included Modules: Comparable, Enumerable + +Methods: + +@ftable @code +@item self + @var{other} + +文字列の連結.連結された文字列を返す. + +@item self * @var{times} + +文字列の繰り返し.例えば@code{x" * 4 == "xxxx"}である. + +@item self == @var{other} +@item self > @var{other} + +文字列の比較.システム変数@code{$=}が@code{nil}でない時には大文字小文 +字を区別せずに比較を行なう. + +@item self =~ @var{other} + +文字列のマッチ.@var{other}は正規表現か文字列.@var{other}が文字列の場 +合には動的に正規表現に変換される.マッチした場合はマッチした位置,しな +かった場合は@code{nil}が返る. + +@item ~ self + +@code{$_ =~ self}と同義. + +@item self[@var{nth}] +@item self[@var{beg}..@var{end}] +@item self[@var{beg}, @var{len}] + +内容の取り出し.1番目の形式では@var{nth}バイト目のデータをFixnumとして +返す.2番目の形式では@var{beg}バイト目から@var{end}バイト目までの部分 +文字列を返す(両端を含む).3番目の形式では@var{beg}バイト目から +@var{len}バイト分の部分文字列を返す. + +@item self[@var{nth}] = @var{val} +@item self[@var{beg}..@var{end}] = @var{val} +@item self[@var{beg}, @var{len}] = @var{val} + +内容の更新.1番目の形式では@var{nth}バイト目のデータを@var{val}(整数) +に変更する.2番目の形式は@var{beg}バイト目から@var{end}バイト目までの +部分文字列を@var{val}として与えられた文字列で置き換える.3番目の形式は +@var{beg}バイト目から@var{len}バイト分の部分文字列を@var{val}として与 +えられた文字列で置き換える. + +@item capitalize +@itemx capitalize! + +文字列中の最初の文字を(それがアルファベットであれば),大文字に変換し, +残る文字列中のアルファベットを小文字に置き換える. + +@item chop +@itemx chop! + +文字列の最後のバイトを切り落とす.元の文字列を変更することに注意するこ +と.@code{chop!}は元の文字列を更新する. + +@item crypt(@var{salt}) + +@samp{crypt(3)}を用いて暗号化した文字列を返す.@var{salt}は2バイト以上 +の長さの任意の文字列である. + +@item delete(@var{str}) +@itemx delete!(@var{str}) + +文字列のうち,@var{str}に含まれる文字を削除する.文字列の指定は +@code{tr}と同様であり,@code{a-b}で@code{a}から@code{b}までの範囲を, +先頭の@code{^}で文字列の否定(含まれてないものを指定)を意味する. + +@item dup + +@code{self}と同じ内容を持つ文字列を生成する.@code{clone}は +@code{freeze}状態もコピーするが,@code{dup}は内容だけが等しい文字列を +生成する. + +@item downcase +@itemx downcase! + +文字列中のアルファベットを全て小文字に置き換えた文字列を返す. +@code{tr("A-Z", "a-z")}より少し速い. + +@item each @{|@var{char}|@dots{}@} +@itemx each_byte @{|@var{char}|@dots{}@} + +文字列のそれぞれのバイトについて繰り返すイテレータ. + +@item each_line @{|@var{line}|@dots{}@} + +文字列から1行ずつ読み込んでくるイテレータ. + +@item freeze + +文字列を更新不可にする.一度更新不可に設定された文字列の内容を変更しよ +うとすると例外が発生する. + +@item gsub(@var{pattern}, @var{replace}) +@itemx gsub(@var{pattern}) @{@dots{}@} +@itemx gsub!(@var{pattern}, @var{replace}) +@itemx gsub!(@var{pattern}) @{@dots{}@} + +文字列中で@var{pattern}にマッチする部分を全て@var{replace}に置き換える. +置換文字列@var{replace}中の@samp{&}と@samp{\0}はマッチした文字列に, +@samp{\1@dots{}\9}はn番目の括弧の内容に置き換えられる.引数 +@var{replace}が省略された時にはイテレータとして動作し,ブロックを評価 +した結果で置換する. + +@code{gsub}は置換された文字列を返す(置換が行なわれなかった場合は元の文 +字列を返す).@code{gsub!}は置換が行なわれた時には対象となる文字列を, +行なわれなかった時には@code{nil}を返す. + +@item hex + +文字列を16進数を表す文字列と解釈して,整数に変換する. + +@item index(@var{substr}[, @var{pos}]) + +@var{substr}が最初に出現する位置を返す.@var{pos}を与えるとその位置か +ら検索を開始する.見つからない時には@code{nil}を返す. + +@item intern + +文字列に一意に対応する整数を返す.文字列はナル文字を含んではならない. + +@item length +@itemx size + +文字列の長さ(バイト数)を返す. + +@item ljust(@var{width}) +@itemx rjust(@var{width}) +@itemx center(@var{width}) + +文字列をそれぞれ,右詰め,左詰め,真中寄せした幅@var{width}の文字列を +返す.文字列長が@var{width}より長い場合は元の文字列を返し,切り詰めな +い. + +@item next + +@code{self}の「次の」文字列を返す.次の文字列とは数字は数字として,英 +文字は英文字として増加し,桁上がりの処理が行なわれたものである. + +@example +"aa".next @result{} "ab" +"99".next @result{} "100" +"a9".next @result{} "b0" +@end example + +@item oct + +文字列を8進数を表す文字列と解釈して,整数に変換する.8進数の定義は +@code{/[0-7]+/}であり,文字列の先頭からこのパターンにマッチする部分を +整数に変換する.この定義に全く当てはまらない文字列に対しては0を返す. +perlとは違って文字列が0xから始まっているからといって 16進数だと見なし +てくれたりはしない.それらは先頭の0が8進数と認識され,0を返す. + +@item reverse +@itemx reverse! + +文字列の各バイトを逆順に並べた文字列を返す.文字列が2バイトで構成され +る文字を含んでいてもお構いなしにバイト単位で反転する.@code{split}は2 +バイト文字を理解するので,2バイト文字を含む文字列を文字単位に反転する +には + +@example +"全角文字列".split(//).reverse.join("") +@end example + +とすればよい. + +@item rindex(@var{substr}[, @var{pos}]) + +文字列@var{substr}が最後に出現する位置を返す.@var{pos}を与えるとその +位置で検索を終了する.見つからない時には@code{nil}を返す.@code{index} +との相違点は + +@itemize +@item +文字列の末尾から検索する. +@item +substrとして正規表現を受け付けない. +@end itemize + +の2点である. + +@item split([@var{sep}[, @var{limit}]]) + +文字列を@var{sep}で指定されたパターンによって,フィールドに分割する. +@var{sep}が省略された時のデフォルトはシステム変数@code{$;}の値が用いら +れる.@var{limit}が指定された時には最大@var{limit}個のフィールドに分割 +する.s@code{plit}は分割された文字列を含む配列を返す.@var{sep}で指定 +されたパターンが空文字列とマッチする場合は文字列が1文字ずつに分割され +る. + +@item squeeze([@var{str}]) +@itemx squeeze!([@var{str}]) + +文字列のうち,@var{str}に含まれる文字が連続していた場合,一文字に圧縮 +する.@var{str}が省略された場合,すべての文字を対象とする.文字列の指 +定はtrと同様であり,@code{a-b}で@code{a}から@code{b}までの範囲を,先頭 +の@code{^}で文字列の否定(含まれてないものを指定)を意味する. + +@item strip +@itemx strip! + +文字列の前後の空白を取り除く. + +@item sub(@var{pattern}, @var{replace}) +@itemx sub(@var{pattern}) @{@dots{}@} +@itemx sub!(@var{pattern}, @var{replace}) +@itemx sub!(@var{pattern}) @{@dots{}@} + +文字列の@var{pattern}にマッチする最初の部分を@var{replace}に置き換える. +置換文字列@var{replace}中の@samp{&}と@samp{\0}はマッチした文字列に, +@samp{\1@dots{}\9}は n番目の括弧の内容に置き換えられる.引数 +@var{replace}のない形式の時にはイテレータとして動作し,ブロックを評価 +した結果で置換する. + +@code{sub}は置換された文字列を返す(置換が行なわれなかった場合は元の文 +字列を返す).@code{sub!}は置換が行なわれた時には対象となる文字列を,行 +なわれなかった時には@code{nil}を返す. + +@item sum([@var{bits}]) + +文字列の@var{bits}ビットのチェックサムを得る.省略値は16である.rubyで +は以下のコードでSystem Vの@code{sum}プログラムと同じ値を得られる. + +@example +while gets() + sum += $_.sum +end +sum %= 65536 +@end example + +@item swapcase +@itemx swapcase! + +文字列中のアルファベットのうち大文字を小文字に,小文字を大文字に置き換 +える. + +@item to_f + +文字列をFloatに変換する. + +@item to_i + +文字列を10進数を表す文字列と解釈して,整数に変換する. + +@item tr(@var{search}, @var{replace}) +@itemx tr!(@var{search}, @var{replace}) + +文字列の中に@var{search}文字列に含まれる文字が存在すれば, +@var{replace}文字列の対応する文字で置き換える.@var{replace}文字列が省 +略された場合は空文字列が与えられたと見なす.@var{replace}文字列が +@var{search}文字列よりも短い時は@var{replace}文字列の最後の文字が繰り +返されていると見なす.@var{search}文字列の方が短い時には対応する文字の +ない@var{replace}部は単に無視される(BSDの@samp{tr}の動作). + +@var{search}文字列,@var{replace}文字列中に@code{a-b}という形式が現れ +た場合,その@code{a}から@code{b}までの範囲の文字をASCIIの昇順で指定し +たことになる.また,@var{search}文字列の最初の文字が@code{^}である場合, +続く文字列に*含まれない*文字列が置換の対象になる. + +@samp{tr(1)}の機能のうち,文字を削除する機能,連続する文字を圧縮する機 +能は別のメソッドに分割されている.それらの機能については@code{delete}, +@code{squeeze}を参照のこと. + +簡便のため,@code{str.tr(src,repl).squeeze(repl)}に相当するメソッド +@code{tr_s(src,repl)}が提供されている. + +@item unpack(@var{template}) + +文字列を@var{template}文字列にしたがってアンパックし,それらの要素を含 +む配列を返す.@var{template}文字列はArrayクラスのpackメソッドとほぼ同 +様である. + +@display +a ASCII文字列(後続するnull文字やスペースを残す) +A ASCII文字列(後続するnull文字やスペースを削除) +b ビットストリング(下位ビットから上位ビット) +B ビットストリング(上位ビットから下位ビット) +h 16進文字列(下位ニブルが先) +H 16進文字列(上位ニブルが先) +c char +C unsigned char +s sort +S unsigned sort +i int +I unsigned int +l long +L unsigned int +n ネットワークバイトオーダーのshort +N ネットワークバイトオーダーのlong +f 単精度浮動小数点数(機種依存) +d 倍精度浮動小数点数(機種依存) +x 1バイト読み飛ばす +X 1バイト後退 +@@ 絶対位置への移動 +@end display + +rubyの@code{unpack}はperlと違ってチェックサムの計算機能がないことに注 +意すること. + + +@item upcase +@itemx upcase! + +文字列中のアルファベットを全て大文字に置き換えた文字列を返す. +@code{tr("a-z", "A-Z")}より少し速い. + +@item upto(@var{end}) @{@dots{}@} + +@code{self}から始まって,@var{end}まで「次の」文字列を順に与えるイテレー +タ.次の文字列とは@code{str.next}で与えられる文字列である. + +このメソッドは@code{Range:each}で用いられているので,以下のような処理 +が可能である. + +@example +for i in "a" .. "ba" + print(i, "\n"); +end +@end example + +これは@samp{a, b, c,@dots{}aa,@dots{}az, ba}までを各行に出力する. +@end ftable + +Single Methods: + +@ftable @code +@item new(@var{string}) + +@var{string}と同じ内容を持つ新しい文字列を返す. +@end ftable + +@xref{Object} +@xref{Enumerable} +@xref{Comparable} + +@node Struct, Time, String, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Struct + +構造体クラス.このクラスのサブクラスは複数のデータをまとめる時に用いら +れる(例: @code{Time:times}).データをまとめる時には配列クラスが用いら +れることもあるが(例: @code{select}),構造体を使うべき時は以下のような +場合である.@code{Struct:new}は@code{Struct}のサブクラスを新たに生成し +て,それを返す.構造体はそのサブクラスのインスタンスとなる. + +@enumerate +@item +要素の数が固定 + +要素の数が変動するものは構造体を使うのには向かない. + +@item +要素の数が多い + +人間が一度に容易に扱える概念の数は7つまでであるという仮説がある.この +仮説に従えば,要素が4つ以上あるデータの場合は配列を用いた場合,要素数 +の2倍(つまりオフセットとその意味の総和)が7を越える.よって,そのような +場合には構造体を使った方が理解しやすいと思われる. + +@item +同時に大量に生成されない + +構造体は配列よりも若干生成コストが高いので,速度が問題になる場合 (例え +ば同時に大量に生成される場合など)は構造体の使用が適切でない可能性があ +る. +@end enumerate + +各構造体にはメンバ名と同名の引数のないメソッドが定義される. + +本ドキュメント内で,構造体を表現するためには以下の形式を使う. + +@display +struct 構造体名 + メンバ@dots{} +end +@end display + +しかし,プログラム中でこの形式で構造体を生成するわけではない. + +SuperClass: Object + +Included Modules: Enumerable + +Methods: + +@ftable @code +@item self[@var{idx}] + +@var{idx}が数の時は@var{idx}番目の要素を返す. + +@item values +@itemx to_a + +構造体のメンバの値を要素に持つ配列を返す.例えば以下のコードで自分の +passwdエントリを出力することができる. + +@example +print(Etc.getpwuid().values.join(":"), "\n") +@end example + +この出力は@samp{grep "$USER" /etc/passwd}の出力と余分なフィールドがい +くつか(システムによって異なる)がある以外は同じである. +@end ftable + +Single Methods: + +@ftable @code +@item new(@var{name}, @var{member}@dots{}) + +@var{name}という名前を持つ構造体のクラスを生成する.@var{member_value} +は構造体のメンバを表す文字列である.生成された構造体クラスにはメンバで +指定された名前のメソッドが定義されていて,そのメソッドによってメンバの +内容を得ることができる. +@end ftable + +Single Methods for subclasses: + +@ftable @code +@item new(@var{value}@dots{}) + +構造体クラスのインスタンスを生成する.@var{value}は構造体のメンバの値 +である.メンバの数が構造体クラスで定義された数と異なる時には例外が発生 +する. +@end ftable + +@xref{Object} +@xref{Enumerable} + +@node Time, , Struct, 組み込みクラスとモジュール +@comment node-name, next, previous, up +@section Time + +時間を表すクラス.大小比較などができる.@code{Time.now}で現在の時間を +得ることができる.またファイルのタイムスタンプを得るメソッドの戻り値も +このクラスのインスタンスである. + +SuperClass: Object + +Included Modules: Comparable + +Methods: + +@ftable @code +@item self <=> @var{other} + +@var{other}はTimeのインスタンスか整数.整数が与えられた場合には +@samp{1970年1月 1日 00:00:00 GMT}からの秒数であるとして時刻との比較を +行なう. + +@item asctime +@itemx ctime +@itemx to_s + +時刻を@samp{date(1)}形式の文字列に変換する. + +@item gmtime + +タイムゾーンの修正を行なわないGMTでの時刻を得る.このメソッドを受けとっ +たTimeクラスのインスタンスは,以後の時刻変換をGMTで行なう. +@code{gmtime}は自分自身を返す. + +ロンドンの時刻を表示するには@code{print(Time.now.gmtime, "\n")}とすれ +ばよい. + +@item localtime + +タイムゾーンの修正を行なった時刻を得る(デフォルト).@code{localtime}は +自分自身を返す. + +@item to_i +@itemx tv_sec + +@samp{1970年 1月 1日 00:00:00 GMT}から時刻までの秒数を整数で返す.時刻 +のsecondの部分でもある. + +@item sec +@itemx min +@itemx hour +@itemx mday +@itemx year +@itemx wday +@itemx yday +@itemx zone +@itemx isdst + +内部的に保持している@code{tm}構造体の内容を返す.@code{zone}以外は整数 +を返す.@code{zone}はタイムゾーンを表す文字列を返す.(cf +@samp{localtime(3)}) + +@item strftime(@var{format}) + +時刻を@var{format}文字列に従って文字列に変換した結果を返す. +@var{format}文字列として指定できるものは 以下の通りである. + +@display +%A 曜日の名称(Sunday, Monday@dots{}) +%a 曜日の省略名(Sun, Mon@dots{}) +%B 月の名称(January, February@dots{}) +%b 月の省略名(Jan, Feb@dots{}) +%c 時刻表現(cf @samp{ctime(3)}) +%d 十進数での日(01-31) +%H 24時間制の時(00-23) +%I 12時間制の時(01-12) +%j 年中の通算日(001-366) +%M 分(00-59) +%m 月を表す数字(01-12) +%p 午前または午後(AM,PM) +%S 秒(00-61) +%U 週を表す数字.最初の日曜日が第1週の + 始まり(00-53) +%W 週を表す数字.最初の月曜日が第1週の + 始まり(00-53) +%w 曜日を表す数字.日曜日が0(0-6) +%X 時刻(例: 15:01:06) +%x 日付(例: Fri Jan 14 1994) +%Y 西暦を表す数字 +%y 西暦の下2桁(00-99) +%Z タイムゾーン +%% %自身 +@end display + +@item usec +@itemx tv_usec + +時刻のmicro secondの部分を返す. +@end ftable + +Single Methods: + +@ftable @code +@item now + +現在の時刻を表す@code{Time}クラスのインスタンスを生成する. + +@item at(@var{time}) + +@var{time}と同じ時刻を表す@code{Time}クラスのインスタンスを生成する. +@var{time}は@code{Time}クラスのインスタンスかあるいは数(整数/浮動小数 +点数)であり,数の場合は@samp{1970年 1月 1日 00:00:00 GMT}からの秒数で +あるとして時刻を計算する. + +@item times + +現在のプロセスとその子プロセスが消費したユーザ/システムCPUタイムの積算 +を構造体として返す(@xref{Struct}). + +@display +struct tms + utime # プロセスのユーザ時間 + stime # プロセスのシステム時間 + cutime # 子プロセスのユーザ時間 + cstime # 子プロセスのシステム時間 +end +@end display + +時間の単位は秒であり,浮動小数点数で与えられる.詳細は@samp{times(3)} +を参照のこと. +@end ftable + +@xref{Object} +@xref{Comparable} + +@node C言語とのインタフェース, 謝辞, 組み込みクラスとモジュール, Top +@comment node-name, next, previous, up +@chapter C言語とのインタフェース + +rubyはC言語とのインターフェースを提供し,C言語からのクラス,モジュール +の定義,C言語で記述したメソッドの定義,rubyのメソッドの呼び出し,イテ +レータの呼び出し,例外処理などを行なうことが出来る.また,OSが許せば実 +行時にCで書かれたモジュールをロードすることも出来る. + +具体的なインタフェースに関しては,別ドキュメント(添付ファイル C-IF)を +参照のこと. + +@node 謝辞, 文法, C言語とのインタフェース, Top +@comment node-name, next, previous, up +@chapter 謝辞 + +Rubyの言語仕様は数多くの言語の影響を受けている.以下にあげるのはその主 +な言語である. + + C, Perl, CLU, Sather, CLOS, Eiffel, Icon, tcl, AWK, bourne shell, + Smalltalk, Emacs Lisp. + +またrubyの言語仕様を決定するために協力して下さった方々を以下にあげる. + + 石塚圭樹,大庭康生,伊藤純一郎,中村@NEC.関根@日本DEC, + たなか@赤坂.富士通(敬称略). + +@node 文法, Variables Index, 謝辞, Top +@comment node-name, next, previous, up +@chapter 文法 + +以下は疑似BNFで記述したrubyの文法である.より正確な記述はparse.yを参照 +されたい. + +@example +PROGRAM : COMPEXPR + +COMPEXPR : EXPR (TERM EXPR)* [TERM] + +EXPR : MLHS `=' ARGS + | return ARGS + | fail ARGS + | yield ARGS + | defined? ARG + | identifier CALL_ARGS0 + | PRIMARY `.' identifier CALL_ARGS0 + | super CALL_ARGS + | undef FNAME + | alias FNAME FNAME + | include identifier (`,' identifier)* + | EXPR if EXPR + | EXPR while EXPR + | EXPR and EXPR + | EXPR or EXPR + | ASSOCS + | ARG + +ARG : LHS `=' ARG + | LHS OP_ASGN ARG + | ARG `..' ARG + | ARG `...' ARG + | ARG `+' ARG + | ARG `-' ARG + | ARG `*' ARG + | ARG `/' ARG + | ARG `%' ARG + | ARG `**' ARG + | `+' ARG + | `-' ARG + | ARG `|' ARG + | ARG `^' ARG + | ARG `&' ARG + | ARG `<=>' ARG + | ARG `>' ARG + | ARG `>=' ARG + | ARG `<' ARG + | ARG `<=' ARG + | ARG `==' ARG + | ARG `!=' ARG + | ARG `=~' ARG + | ARG `!~' ARG + | `!' ARG + | `~' ARG + | ARG `<<' ARG + | ARG `>>' ARG + | ARG `&&' ARG + | ARG `||' ARG + | ARG `::' identifier + | PRIMARY + +PRIMARY : `(' COMPEXPR `)' + | LITERAL + | VARIABLE + | super `(' [CALL_ARGS] `)' + | super + | PRIMARY `[' [ARGS] `]' + | `[' [ARGS [`,']] `]' + | `@{' [ (ARGS|ASSOCS) [`,'] ] `@}' + | redo + | break + | continue + | retry + | return + | fail [`(' [ARGS] `)'] + | yield [`(' [ARGS] `)'] + | defined? `(' ARG `)' + | PRIMARY `@{' [`|' [ITER_VAR] `|'] COMPEXPR `@}' + | OPERATION `(' [CALL_ARGS] `)' + | PRIMARY `.' OPERATION `(' [CALL_ARGS] `)' + | PRIMARY `.' OPERATION + | if EXPR THEN + COMPEXPR + (elsif EXPR THEN COMPEXPR)* + [else COMPEXPR] + end + | while EXPR TERM COMPEXPR end + | case COMPEXPR + (when ARGS THEN)+ + [else COMPEXPR] + end + | for ITER_VAR in EXPR TERM + COMPEXPR + end + | begin + COMPEXPR + [rescue COMPEXPR] + [ensure COMPEXPR] + end + | class identifier `:' identifier + COMPEXPR + end + | module identifier + COMPEXPR + end + | def FNAME ARGLIST + COMPEXPR + end + | def SINGLETON `.' FNAME ARGLIST + COMPEXPR + end + +THEN : TERM + | then + | TERM then + +ITER_VAR : LHS + | MLHS + +MLHS : LHS `,' [LHS (`,' LHS)*] [`*' LHS] + +LHS : VARIABLE + | PRIMARY `[' [ARGS] `]' + | PRIMARY `.' identifier + +CALL_ARGS : ARGS + | ASSOCS + | ARGS [`,' ASSOCS] [`,' `*' ARG] + | `*' ARG + +ARGS : ARG (`,' ARG)* + +ARGLIST : `('[identifier(`,'identifier)*][`*'identifier]`)' + | TERM + +SINGLETON : VARIABLE + | `(' EXPR `)' + +ASSOCS : ASSOC (`,' ASSOC)* + +ASSOC : ARG `=>' ARG + +VARIABLE : VARNAME + | nil + | self + | `__FILE__' + | `__LINE__' + +LITERAL : numeric + | SYMBOL + | STRING + | REGEXP + +TERM : `;' + | `\n' + +@end example + +ここより下は字句解析部で認識される. + +@example + +SYMBOL : `:'FNAME + | `:'VARNAME + +FNAME : identifier | `..' | `|' | `^' | `&' + | `<=>' | `==' | `=~' | `>' | `>=' | `<' | `<=' + | `<<' | `>>' | `+' | `-' | `*' | `/' | `%' | `**' + | `~' | `+@@' | `-@@' | `[]' | `[]=' + +OPERATION : identifier + | identifier'!' + | identifier'?' + +VARNAME : GLOBAL + | `@@'identifier + | identifier + +GLOBAL : `$'identifier + | `$'any_char + +STRING : `"' any_char* `"' + | `'' any_char* `'' + | ``' any_char* ``' + +REGEXP : `/' any_char* `/'[i] + +@end example + +@node Variables Index, Concept Index, Function Index, Top +@comment node-name, next, previous, up +@unnumbered Variable Index + +@printindex vr + +@node Concept Index, Function Index , Variables Index, Top +@comment node-name, next, previous, up +@unnumbered Concept Index + +@printindex cp + +@node Function Index, Top , Concept Index, Top +@comment node-name, next, previous, up +@unnumbered Function Index + +@printindex fn + +@summarycontents +@contents +@bye + +Local variables: +fill-column: 70 +end: diff --git a/sample/MANIFEST b/sample/MANIFEST deleted file mode 100644 index 93c971b114..0000000000 --- a/sample/MANIFEST +++ /dev/null @@ -1,63 +0,0 @@ -MANIFEST -aset.rb -attr.rb -biorhythm.rb -blk.rb -case.rb -cat.rb -cat2.rb -cbreak.rb -clnt.rb -clone.rb -const.rb -dbm.rb -dir.rb -evaldef.rb -export.rb -exyacc.rb -fib.awk -fib.pl -fib.rb -fib.scm -freq.rb -from.rb -fullpath.pl -fullpath.rb -gctest.rb -gctest2.rb -getopts.rb -getopts.test -hash.rb -io.rb -less.rb -list.rb -list2.rb -list3.rb -math.rb -mpart.rb -occur.pl -occur.rb -occur2.rb -opt_s.rb -opt_x.test -parsearg.rb -rcs.awk -rcs.dat -rcs.rb -ruby-mode.el -samp.rb -sieve.rb -split.rb -struct.rb -svr.rb -system.rb -t1.rb -t2.rb -test.rb -time.rb -trap.pl -trap.rb -trojan.pl -trojan.rb -tt.rb -uumerge.rb diff --git a/sample/aset.rb b/sample/aset.rb deleted file mode 100644 index 414c13ba70..0000000000 --- a/sample/aset.rb +++ /dev/null @@ -1,7 +0,0 @@ -# array set example -# output: -# 07045 - -ary = [0, 0, 4, 5] -ary[1, 0] = [7] -print ary, "\n" diff --git a/sample/attr.rb b/sample/attr.rb deleted file mode 100644 index 1d329ea06a..0000000000 --- a/sample/attr.rb +++ /dev/null @@ -1,14 +0,0 @@ -# attribute access example -# output: -# 10 -# #<Foo: @test=10> - -class Foo - attr "test", TRUE -end - -foo = Foo.new -foo.test = 10 -print foo.test, "\n" -foo._inspect.print -print "\n" diff --git a/sample/blk.rb b/sample/blk.rb deleted file mode 100644 index e11cc026ea..0000000000 --- a/sample/blk.rb +++ /dev/null @@ -1,9 +0,0 @@ -def foo() - $block = Block.new -end - -foo(){|i| print "i = ", i, "\n"} -$block.call(2) - -foo(){|i| print "i*2 = ", i*2, "\n"} -$block.call(2) diff --git a/sample/case.rb b/sample/case.rb deleted file mode 100644 index e844cddfd6..0000000000 --- a/sample/case.rb +++ /dev/null @@ -1,14 +0,0 @@ -# case statement example -# output: -# 3..5 - -case "t" -when /1/ - print 1, "\n" -when /t/ - print 3..5, "\n" -when /./ - print 2, "\n" -else - print "else\n" -end diff --git a/sample/cat.rb b/sample/cat.rb deleted file mode 100644 index a3243d308d..0000000000 --- a/sample/cat.rb +++ /dev/null @@ -1,5 +0,0 @@ -# cat -n & `...' operator test -while gets() - if $. == 1 ... ~ /^\*/; print("--") end - printf("%5d: %s", $., $_) -end diff --git a/sample/cat2.rb b/sample/cat2.rb deleted file mode 100644 index bbc1ebb0ff..0000000000 --- a/sample/cat2.rb +++ /dev/null @@ -1,5 +0,0 @@ -# cat -n & `...' operator test -while gets() - if 1 ... /^\*/; print("--") end - printf("%5d: %s", $., $_) -end diff --git a/sample/clnt.rb b/sample/clnt.rb index d2c71ec563..c8c4b2db9f 100644 --- a/sample/clnt.rb +++ b/sample/clnt.rb @@ -1,5 +1,8 @@ # socket example - client side # usage: ruby clnt.rb [host] port + +require "socket" + host=(if $ARGV.length == 2; $ARGV.shift; else "localhost"; end) print("Trying ", host, " ...") STDOUT.flush diff --git a/sample/clone.rb b/sample/clone.rb deleted file mode 100644 index e7d6b00a31..0000000000 --- a/sample/clone.rb +++ /dev/null @@ -1,18 +0,0 @@ -# object cloning & single method test -# output: -# test2 -# test -# test -# clone.rb:18: undefined method `test2' for "#<Object: 0xbfca4>"(Object) -foo = Object.new -def foo.test - print("test\n") -end -bar = foo.clone -def bar.test2 - print("test2\n") -end -bar.test2 -bar.test -foo.test -foo.test2 diff --git a/sample/const.rb b/sample/const.rb deleted file mode 100644 index 50780407b8..0000000000 --- a/sample/const.rb +++ /dev/null @@ -1,24 +0,0 @@ -# constant access test -# output: -# 1234 -# 1268 -TEST1 = 1 -TEST2 = 2 - -module Const - TEST3 = 3 - TEST4 = 4 -end - -module Const2 - TEST3 = 6 - TEST4 = 8 -end - -include Const - -print(TEST1,TEST2,TEST3,TEST4,"\n") - -include Const2 - -print(TEST1,TEST2,TEST3,TEST4,"\n") diff --git a/sample/dbm.rb b/sample/dbm.rb index a2e0659bf5..c77cc2065b 100644 --- a/sample/dbm.rb +++ b/sample/dbm.rb @@ -1,4 +1,6 @@ # ruby dbm acess +require "dbm" + d = DBM.open("test") keys = d.keys if keys.length > 0 then diff --git a/sample/exyacc.rb b/sample/exyacc.rb index cd1170feb4..dafcb037cc 100644 --- a/sample/exyacc.rb +++ b/sample/exyacc.rb @@ -8,15 +8,15 @@ while gets() sbeg = $_.index("\n%%") + 1 send = $_.rindex("\n%%") + 1 $_ = $_[sbeg, send-sbeg] - sub(/.*\n/, "") - gsub(/'{'/, "'\001'") - gsub(/'}'/, "'\002'") - gsub('\*/', "\003\003") - gsub("/\\*[^\003]*\003\003", '') - while gsub(/{[^}{]*}/, ''); end - gsub(/'\001'/, "'{'") - gsub(/'\002'/, "'}'") - while gsub(/^[ \t]*\n(\s)/, '\1'); end - gsub(/([:|])[ \t\n]+(\w)/, '\1 \2') + sub!(/.*\n/, "") + gsub!(/'{'/, "'\001'") + gsub!(/'}'/, "'\002'") + gsub!('\*/', "\003\003") + gsub!("/\\*[^\003]*\003\003", '') + while gsub!(/{[^}{]*}/, ''); end + gsub!(/'\001'/, "'{'") + gsub!(/'\002'/, "'}'") + while gsub!(/^[ \t]*\n(\s)/, '\1'); end + gsub!(/([:|])[ \t\n]+(\w)/, '\1 \2') print $_ end diff --git a/sample/from.rb b/sample/from.rb index f21b1d10f5..2f5fcebe12 100755 --- a/sample/from.rb +++ b/sample/from.rb @@ -1,97 +1,10 @@ #! /usr/local/bin/ruby -module ParseDate - MONTHS = { - 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, - 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, - 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12 } - MONTHPAT = MONTHS.keys.join('|') - DAYPAT = 'mon|tue|wed|thu|fri|sat|sun' - - def parsedate(date) - if date.sub(/(#{DAYPAT})/i, ' ') - dayofweek = $1 - end - if date.sub(/\s+(\d+:\d+(:\d+)?)/, ' ') - time = $1 - end - if date =~ /19(\d\d)/ - year = $1 - end - if date.sub(/\s*(\d+)\s+(#{MONTHPAT})\S*\s+/i, ' ') - dayofmonth = $1 - monthname = $2 - elsif date.sub(/\s*(#{MONTHPAT})\S*\s+(\d+)\s+/i, ' ') - monthname = $1 - dayofmonth = $2 - elsif date.sub(/\s*(#{MONTHPAT})\S*\s+(\d+)\D+/i, ' ') - monthname = $1 - dayofmonth = $2 - elsif date.sub(/\s*(\d\d?)\/(\d\d?)/, ' ') - month = $1 - dayofmonth = $2 - end - if monthname - month = MONTHS[monthname.tolower] - end - if ! year && date =~ /\d\d/ - year = $& - end - return year, month, dayofmonth - end - -end +require "parsedate" +require "base64" include ParseDate -def decode64(str) - e = -1; - c = "," - for line in str.split("\n") - line.tr 'A-Za-z0-9+/', "\000-\377" - line.each_byte { |ch| - e+=1 - if e==0 - c = ch << 2 - elsif e==1 - c |= ch >>4 - string += [c].pack('c') - c = ch << 4 - elsif e == 2 - c |= ch >> 2 - string += [c].pack('c'); - c = ch << 6 - elsif e==3 - c |= ch - string += [c].pack('c') - e = -1; - end - } - end - return string; -end - -def j2e(str) - while str =~ /\033\$B([^\033]*)\033\(B/ - s = $1 - pre, post = $`, $' - s.gsub(/./) { |ch| - (ch[0]|0x80).chr - } - str = pre + s + post - end - str -end - -def decode_b(str) - while str =~ /=\?ISO-2022-JP\?B\?(.*)=\?=/ - pre, post = $`, $' - s = decode64($1) - str = pre + s + post - end - j2e(str) -end - if $ARGV[0] == '-w' wait = TRUE $ARGV.shift @@ -100,7 +13,7 @@ end class Mail def Mail.new(f) - if !f.is_kind_of(IO) + if !f.is_kind_of?(IO) f = open(f, "r") me = super f.close @@ -114,9 +27,9 @@ class Mail @header = {} @body = [] while f.gets() - $_.chop + $_.chop! continue if /^From / # skip From-line - break if /^[ \t]*$/ # end of header + break if /^$/ # end of header if /^(\S+):\s*(.*)/ @header[attr = $1.capitalize] = $2 elsif attr @@ -149,17 +62,18 @@ $outcount = 0; def fromout(date, from, subj) return if !date y = m = d = 0 + esc = "\033\(B" y, m, d = parsedate(date) if date from = "sombody@somewhere" if ! from subj = "(nil)" if ! subj from = decode_b(from) subj = decode_b(subj) - printf "%-02d/%02d/%02d [%-28.28s] %-40.40s\n", y, m, d, from, subj + printf "%-02d/%02d/%02d [%-28.28s%s] %-40.40s%s\n",y,m,d,from,esc,subj,esc $outcount += 1 end for file in $ARGV - continue if !File.exists(file) + continue if !File.exists?(file) f = open(file, "r") while !f.eof mail = Mail.new(f) diff --git a/sample/gctest.rb b/sample/gctest.rb deleted file mode 100644 index 6810b95481..0000000000 --- a/sample/gctest.rb +++ /dev/null @@ -1,67 +0,0 @@ -# GC stress test -def cons(car, cdr) - [car, cdr] -end - -def car(x) - if x == nil ; nil else x[0] end -end - -def cdr(x) - if x == nil ; nil else x[1] end -end - -def reverse1(x, y) - if x == nil ; y else reverse1(cdr(x), cons(car(x), y)) end -end - -def reverse(x) - reverse1(x, nil) -end - -def ints(low, up) - if low > up - nil - else - cons(low, ints(low+1, up)) - end -end - -def print_int_list(x) - if x == nil - print("NIL\n") - else - print(car(x)) - if nil != cdr(x) - print(", ") - print_int_list(cdr(x)) - else - print("\n") - end - end -end - -print("start\n") - -a = ints(1, 100) -print_int_list(a) -b = ints(1, 50) -print_int_list(b) -print_int_list(reverse(a)) -print_int_list(reverse(b)) -for i in 1 .. 100 - b = reverse(reverse(b)) -# print(i, ": ") -# print_int_list(b) -end -print("a: ") -print_int_list(a) -print("b: ") -print_int_list(b) -print("reverse(a): ") -print_int_list(reverse(a)) -print("reverse(b): ") -print_int_list(reverse(b)) -a = b = nil -print("finish\n") -GC.start() diff --git a/sample/gctest2.rb b/sample/gctest2.rb deleted file mode 100644 index 851d14f217..0000000000 --- a/sample/gctest2.rb +++ /dev/null @@ -1,71 +0,0 @@ -# GC stress test -def cons(car, cdr) - car::cdr -end - -def car(x) - x.car -end - -def cdr(x) - x.cdr -end - -def reverse1(x, y) - if x == nil then - y - else - reverse1(cdr(x), cons(car(x), y)) - end -end - -def reverse(x) - reverse1(x, nil) -end - -def ints(low, up) - if low > up - nil - else - cons(low, ints(low+1, up)) - end -end - -def print_int_list(x) - if x == nil - print("NIL\n") - else - print(car(x)) - if cdr(x) - print(", ") - print_int_list(cdr(x)) - else - print("\n") - end - end -end - -print("start\n") - -a = ints(1, 100) -print_int_list(a) -b = ints(1, 50) -print_int_list(b) -print_int_list(reverse(a)) -print_int_list(reverse(b)) -for i in 1 .. 100 - b = reverse(reverse(b)) -# print(i, ": ") -# print_int_list(b) -end -print("a: ") -print_int_list(a) -print("b: ") -print_int_list(b) -print("reverse(a): ") -print_int_list(reverse(a)) -print("reverse(b): ") -print_int_list(reverse(b)) -a = b = nil -print("finish\n") -GC.start() diff --git a/sample/hash.rb b/sample/hash.rb deleted file mode 100644 index 85f719e7eb..0000000000 --- a/sample/hash.rb +++ /dev/null @@ -1,11 +0,0 @@ -# hash value -# output: -# 78651 -# 78651 -# 78651 -# -45637 - -print(+-1.0.hash,"\n") -print(-1.0.hash,"\n") -print((-1.0).hash,"\n") -print(-(1.0.hash),"\n") diff --git a/sample/list3.rb b/sample/list3.rb index 2c1beb6fa2..1d756fdff0 100644 --- a/sample/list3.rb +++ b/sample/list3.rb @@ -1,5 +1,5 @@ # Linked list example -- short version -# using _inspect +# using inspect class Point def initialize(x, y) @@ -14,5 +14,5 @@ end list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)] list2 = [20, Point.new(4, 5), list1] -print("list1: ", list1._inspect, "\n") -print("list2: ", list2._inspect, "\n") +print("list1: ", list1.inspect, "\n") +print("list2: ", list2.inspect, "\n") diff --git a/sample/marshal.rb b/sample/marshal.rb new file mode 100644 index 0000000000..3d399ffe68 --- /dev/null +++ b/sample/marshal.rb @@ -0,0 +1,13 @@ +require "marshal" +include Marshal +a = 25.6; +pt = Struct.new('point', :x,:y); +x = pt.new(10, 10) +y = pt.new(20, 20) +rt = Struct.new('rectangle', :origin,:corner); +z = rt.new(x, y) +c = Object.new +s = [a, x, z, c, c, "fff"]; +print s.inspect; +d = dumps(s); +print load(d).inspect diff --git a/sample/math.rb b/sample/math.rb deleted file mode 100644 index c0b5225080..0000000000 --- a/sample/math.rb +++ /dev/null @@ -1,4 +0,0 @@ -# math example -include Math -sqrt(4) -print(Math.sqrt(257), "\n") diff --git a/sample/mkproto.rb b/sample/mkproto.rb new file mode 100644 index 0000000000..1d9c9faccb --- /dev/null +++ b/sample/mkproto.rb @@ -0,0 +1,27 @@ +$/ = nil +while gets() + if /^((void|VALUE|int|char *\*|ID|struct [\w_]+ *\*|st_table *\*) *)?\n([\w\d_]+)\(.*\)\n\s*((.+;\n)*){/ + $_ = $' + printf "%s %s(", $2, $3 + args = [] + for arg in $4.split(/;\n\s*/) + arg.gsub! ' +', ' ' + if arg =~ /,/ + if arg =~ /(([^*]+) *\** *[\w\d_]+),/ + type = $2.strip! + args.push $1.strip! + arg = $' + else + type = "" + end + while arg.sub!(/(\** *[\w\d_]+)(,|$)/, "") + args.push type + " " + $1.strip! + end + else + args.push arg.strip! + end + end + printf "%s);\n", args.join(', ') + redo + end +end diff --git a/sample/occur2.rb b/sample/occur2.rb index 8cd5acbe5e..c450c30b0f 100644 --- a/sample/occur2.rb +++ b/sample/occur2.rb @@ -5,7 +5,7 @@ while gets() for word in $_.split(/\W+/) begin freq[word] = freq[word] + 1 - resque + rescue freq[word] = 1 end end diff --git a/sample/opt_s.rb b/sample/opt_s.rb deleted file mode 100644 index 56ff0eea15..0000000000 --- a/sample/opt_s.rb +++ /dev/null @@ -1,12 +0,0 @@ -#! ./ruby -s -# test for option `-s' - -if ($xyz) - print("xyz = TRUE\n") -end -if ($zzz) - print("zzz = ", $zzz, "\n") -end -if ($ARGV.length > 0) - print($ARGV.join(", "), "\n") -end diff --git a/sample/opt_x.test b/sample/opt_x.test deleted file mode 100644 index 47a67f6cfa..0000000000 --- a/sample/opt_x.test +++ /dev/null @@ -1,10 +0,0 @@ -test for option `-x' - -this is a forwarding header -this is a header too. - -from here script starts -#! ./ruby -v -print("tt\n") -__END__ -this is a trailer diff --git a/sample/rcs.rb b/sample/rcs.rb index faa4606788..13476267b2 100644 --- a/sample/rcs.rb +++ b/sample/rcs.rb @@ -16,7 +16,7 @@ while gets() s = ""; while xr < hdw x = xr * (1 + y) - y * w / 2 - i = (x / (1 + h) + sw /2) + i = (x / (1 + h) + sw / 2) if (1 < i && i < $_.length); c = $_[i, 1].to_i else diff --git a/sample/ruby-mode.el b/sample/ruby-mode.el index bcbbdc35f3..b555994fea 100644 --- a/sample/ruby-mode.el +++ b/sample/ruby-mode.el @@ -12,21 +12,24 @@ ) (defconst ruby-block-mid-re - "else\\|elsif\\|when\\|resque\\|ensure" + "then\\|else\\|elsif\\|when\\|rescue\\|ensure" ) (defconst ruby-block-end-re "end") (defconst ruby-delimiter - (concat "[$/<(){}#\"'`]\\|\\[\\|\\]\\|\\b\\(" - ruby-block-beg-re "\\|" ruby-block-end-re "\\)\\b") + (concat "[?$/(){}#\"'`]\\|\\[\\|\\]\\|\\<\\(" + ruby-block-beg-re "\\|" ruby-block-end-re "\\)\\>") ) (defconst ruby-negative (concat "^[ \t]*\\(\\b\\(" ruby-block-mid-re "\\)\\|\\(" - ruby-block-end-re "\\)\\b\\|\\}\\|\\]\\)") + ruby-block-end-re "\\)\\>\\|\\}\\|\\]\\)") ) +(defconst ruby-operator-chars "[,.+*/%-&|^~=<>:]") +(defconst ruby-symbol-chars "[a-zA-Z0-9_]") + (defvar ruby-mode-abbrev-table nil "Abbrev table in use in ruby-mode buffers.") @@ -53,17 +56,16 @@ (setq ruby-mode-syntax-table (make-syntax-table)) (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table) (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table) -;;(modify-syntax-entry ?\n ">" ruby-mode-syntax-table) -;;(modify-syntax-entry ?\f ">" ruby-mode-syntax-table) (modify-syntax-entry ?# "<" ruby-mode-syntax-table) - (modify-syntax-entry ?$ "/" ruby-mode-syntax-table) + (modify-syntax-entry ?\n ">" ruby-mode-syntax-table) (modify-syntax-entry ?\\ "'" ruby-mode-syntax-table) - (modify-syntax-entry ?_ "w" ruby-mode-syntax-table) + (modify-syntax-entry ?$ "/" ruby-mode-syntax-table) + (modify-syntax-entry ?? "/" ruby-mode-syntax-table) + (modify-syntax-entry ?_ "_" ruby-mode-syntax-table) (modify-syntax-entry ?< "." ruby-mode-syntax-table) (modify-syntax-entry ?> "." ruby-mode-syntax-table) (modify-syntax-entry ?& "." ruby-mode-syntax-table) (modify-syntax-entry ?| "." ruby-mode-syntax-table) - (modify-syntax-entry ?$ "." ruby-mode-syntax-table) (modify-syntax-entry ?% "." ruby-mode-syntax-table) (modify-syntax-entry ?= "." ruby-mode-syntax-table) (modify-syntax-entry ?/ "." ruby-mode-syntax-table) @@ -152,6 +154,19 @@ The variable ruby-indent-level controls the amount of indentation. (indent-to x) (if (> p 0) (forward-char p))))) +(defun ruby-expr-beg () + (save-excursion + (skip-chars-backward " \t") + (or (bolp) (forward-char -1)) + (or (looking-at ruby-operator-chars) + (looking-at "[\\[({]") + (bolp) + (and (looking-at ruby-symbol-chars) + (forward-word -1) + (or + (looking-at ruby-block-beg-re) + (looking-at ruby-block-mid-re)))))) + (defun ruby-parse-region (start end) (let ((indent-point end) (indent 0) @@ -163,113 +178,97 @@ The variable ruby-indent-level controls the amount of indentation. (if start (goto-char start) (ruby-beginning-of-defun)) - (while (and (> indent-point (point)) - (re-search-forward ruby-delimiter indent-point t)) - (let ((w (buffer-substring (match-beginning 0) (match-end 0))) - (pnt (match-beginning 0))) - (cond - ((or (string= "\"" w) ;skip string - (string= "'" w) - (string= "`" w)) - (cond - ((string= w (char-to-string (char-after (point)))) - (forward-char 1)) - ((re-search-forward (format "[^\\]%s" w) indent-point t) + (save-restriction + (narrow-to-region (point) end) + (while (and (> indent-point (point)) + (re-search-forward ruby-delimiter indent-point t)) + (let ((pnt (point)) w) + (goto-char (match-beginning 0)) + (cond + + ((or (looking-at "\"") ;skip string + (looking-at "'") + (looking-at "`")) + (setq w (char-after (point))) + (cond + ((and (not (eobp)) + (equal w (char-after (point))) + (re-search-forward (format "[^\\]%c" w) indent-point t)) nil) - (t - (goto-char indent-point) - (setq in-string t)))) - ((or (string= "/" w) - (string= "<" w)) - (if (string= "<" w) (setq w ">")) - (let (c) - (save-excursion - (goto-char pnt) - (skip-chars-backward " \t") - (setq c (char-after (1- (point)))) - (if c - (setq c (char-syntax c)))) + (t + (goto-char indent-point) + (setq in-string t)))) + ((looking-at "/") + (if (and (ruby-expr-beg) + (goto-char pnt) + (looking-at "\\([^/\n]\\|\\\\/\\)*") + (eq ?/ (char-after (match-end 0)))) + (goto-char (1+ (match-end 0))) + (goto-char indent-point) + (setq in-string t))) + ((looking-at "\\?") ;skip ?char (cond - ((or (eq c ?.) - (and (eq c ?w) - (save-excursion - (forward-word -1) - (or - (looking-at ruby-block-beg-re) - (looking-at ruby-block-mid-re))))) - (if (search-forward w indent-point t) - nil - (goto-char indent-point) - (setq in-string t)))))) - ((string= "$" w) ;skip $char - (forward-char 1)) - ((string= "#" w) ;skip comment - (forward-line 1)) - ((string= "(" w) ;skip to matching paren - (let ((orig depth)) - (setq nest (cons (point) nest)) - (setq depth (1+ depth)) - (while (and (/= depth orig) - (re-search-forward "[()]" indent-point t)) - (cond - ((= (char-after (match-beginning 0)) ?\( ) - (setq nest (cons (point) nest)) - (setq depth (1+ depth))) - (t - (setq nest (cdr nest)) - (setq depth (1- depth))))) - (if (> depth orig) (setq in-paren ?\()))) - ((string= "[" w) ;skip to matching paren - (let ((orig depth)) - (setq nest (cons (point) nest)) - (setq depth (1+ depth)) - (while (and (/= depth orig) - (re-search-forward "\\[\\|\\]" indent-point t)) - (cond - ((= (char-after (match-beginning 0)) ?\[ ) - (setq nest (cons (point) nest)) - (setq depth (1+ depth))) - (t - (setq nest (cdr nest)) - (setq depth (1- depth))))) - (if (> depth orig) (setq in-paren ?\[)))) - ((string= "{" w) ;skip to matching paren - (let ((orig depth)) - (setq nest (cons (point) nest)) + ((ruby-expr-beg) + (looking-at "?\\(\\\\C-\\|\\\\M-\\)*.") + (goto-char (match-end 0))) + (t + (goto-char pnt)))) + ((looking-at "\\$") ;skip $char + (goto-char pnt) + (forward-char 1)) + ((looking-at "#") ;skip comment + (forward-line 1) + (goto-char pnt)) + ((looking-at "[\\[({]") + (setq nest (cons (cons (char-after (point)) pnt) nest)) (setq depth (1+ depth)) - (while (and (/= depth orig) - (re-search-forward "[{}]" indent-point t)) - (cond - ((= (char-after (match-beginning 0)) ?{ ) - (setq nest (cons (point) nest)) - (setq depth (1+ depth))) - (t - (setq nest (cdr nest)) - (setq depth (1- depth))))) - (if (> depth orig) (setq in-paren ?{)))) - ((string-match ruby-block-end-re w) - (setq nest (cdr nest)) - (setq depth (1- depth))) - ((string-match ruby-block-beg-re w) - (let (c) - (save-excursion - (goto-char pnt) - (skip-chars-backward " \t") - (setq c (char-after (1- (point))))) - (if (or (null c) (= c ?\n) (= c ?\;)) - (progn - (setq nest (cons (point) nest)) - (setq depth (1+ depth)))))) - (t - (error (format "bad string %s" w))))))) - (list in-string in-paren (car nest) depth))) + (goto-char pnt)) + ((looking-at "[])}]") + (setq nest (cdr nest)) + (setq depth (1- depth)) + (goto-char pnt)) + ((looking-at ruby-block-end-re) + (if (and (not (bolp)) + (progn + (forward-char -1) + (eq ?_ (char-after (point)))) + (progn + (goto-char pnt) + (eq ?_ (char-after (point))))) + nil + (setq nest (cdr nest)) + (setq depth (1- depth))) + (goto-char pnt)) + ((looking-at ruby-block-beg-re) + (and + (or (bolp) + (progn + (forward-char -1) + (not (eq ?_ (char-after (point)))))) + (save-excursion + (goto-char pnt) + (not (eq ?_ (char-after (point))))) + (skip-chars-backward " \t") + (or (bolp) + (save-excursion + (forward-char -1) + (looking-at ruby-operator-chars))) + (progn + (setq nest (cons (cons nil pnt) nest)) + (setq depth (1+ depth)))) + (goto-char pnt)) + (t + (error (format "bad string %s" + (buffer-substring (point) pnt) + ))))))) + (list in-string (car nest) depth)))) (defun ruby-calculate-indent (&optional parse-start) (save-excursion (beginning-of-line) (let ((indent-point (point)) (case-fold-search nil) - state eol + state bol eol (indent 0)) (if parse-start (goto-char parse-start) @@ -281,25 +280,74 @@ The variable ruby-indent-level controls the amount of indentation. (setq indent nil)) ; do nothing ((nth 1 state) ; in paren - (goto-char (nth 2 state)) + (goto-char (cdr (nth 1 state))) (setq indent - (if (and (eq (nth 1 state) ?\( ) (not (looking-at "$"))) + (if (and (eq (car (nth 1 state)) ?\( ) + (not (looking-at "(\\s *$"))) (current-column) (+ (current-indentation) ruby-indent-level)))) - ((> (nth 3 state) 0) ; in nest - (goto-char (nth 2 state)) + ((> (nth 2 state) 0) ; in nest + (goto-char (cdr (nth 1 state))) (forward-word -1) ; skip back a keyword (setq indent (+ (current-column) ruby-indent-level))) (t ; toplevel (setq indent 0))) - (goto-char indent-point) - (end-of-line) - (setq eol (point)) - (beginning-of-line) - (if (re-search-forward ruby-negative eol t) + + (cond + (indent + (goto-char indent-point) + (end-of-line) + (setq eol (point)) + (beginning-of-line) + (cond + ((re-search-forward ruby-negative eol t) (setq indent (- indent ruby-indent-level))) + ;;operator terminated lines + ((and + (save-excursion + (beginning-of-line) + (not (bobp))) + (or (null (car (nth 1 state))) ;not in parens + (and (eq (car (nth 1 state)) ?\{) + (save-excursion ;except non-block braces + (goto-char (cdr (nth 1 state))) + (or (bobp) (forward-char -1)) + (not (ruby-expr-beg)))))) + (beginning-of-line) + (skip-chars-backward " \t\n") + (beginning-of-line) ; goto beginning of non-empty line + (setq bol (point)) + (end-of-line) + (setq eol (point)) + (and (search-backward "#" bol t) ; check for comment line + (not (eq ?? (char-after (1- (point))))) + (not (nth 0 (ruby-parse-region parse-start (point)))) + (setq eol (point))) + (goto-char eol) + (skip-chars-backward " \t") + (or (bobp) (forward-char -1)) + (and (looking-at ruby-operator-chars) +;; (or (not (eq ?/ (char-after (point)))) +;; (progn +;; (not (nth 0 (ruby-parse-region parse-start (point)))))) + (or (not (eq ?/ (char-after (point)))) + (null (nth 0 (ruby-parse-region parse-start (point))))) + (save-excursion + (goto-char parse-start) + (sit-for 1)) + (not (eq (char-after (1- (point))) ?$)) + (or (not (eq ?| (char-after (point)))) + (save-excursion + (or (eolp) (forward-char -1)) + (and (search-backward "|" bol t) + (skip-chars-backward " \t\n") + (and (not (eolp)) + (progn + (forward-char -1) + (not (looking-at "\\{"))))))) + (setq indent (+ indent ruby-indent-level))))))) indent))) (defun ruby-electric-brace (arg) @@ -329,7 +377,7 @@ An end of a defun is found by moving forward from the beginning of one." (interactive "*") (save-excursion (delete-region (point) (progn (skip-chars-backward " \t") (point)))) - (insert ?\n) + (newline) (save-excursion (forward-line -1) (indent-according-to-mode)) @@ -348,3 +396,26 @@ An end of a defun is found by moving forward from the beginning of one." (goto-char beg) (while (re-search-forward "^\\([ \t]*\\)#" end t) (replace-match "\\1" nil nil)))) + +(if (featurep 'hilit19) + (hilit-set-mode-patterns + 'ruby-mode + '(("\\s #.*$" nil comment) + ("^#.*$" nil comment) + ("\\$\\(.\\|\\sw+\\)" nil type) + ("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string) + ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string) + ("^/\\([^/\n]\\|\\\\/\\)*/" nil string) + ("[^a-zA-Z_]\\s *\\(/\\([^/\n]\\|\\\\/\\)*/\\)" 1 string) + ("\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|rescue\\|then\\|when\\|while\\)\\s *\\(/\\([^/\n]\\|\\\\/\\)*/\\)" 2 string) + ("^\\s *require.*$" nil include) + ("^\\s *load.*$" nil include) + ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl) + ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun) + ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|rescue\\|then\\|when\\|while\\)\\>[^_]" 1 defun) + ("[^_]\\<\\(and\\|break\\|continue\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\)\\>[^_]" 1 keyword) + ("[^_]\\<\\(self\\|nil\\|TRUE\\|FALSE\\|__LINE__\\|__FILE__\\)\\>[^_]" 1 define) + ("$.[a-zA-Z_0-9]*" nil struct) + ("@[a-zA-Z_0-9]+" nil struct) + ("[^_]\\<[A-Z].[a-zA-Z_0-9]*" nil define) + ("^__END__" nil label)))) diff --git a/sample/samp.rb b/sample/samp.rb deleted file mode 100644 index 4052a308c1..0000000000 --- a/sample/samp.rb +++ /dev/null @@ -1,15 +0,0 @@ -# SpXNvg -# g: samp.rb file.. - -P = 0 -while gets() - printf("%3d: %s", $., $_) - while sub(/\w+/, '') - if $& != ""; - P += 1 - end - end - if ($. >= 10); break; end -end -printf("line: %d\n", $.) -printf("word: %d\n", P) diff --git a/sample/sieve.rb b/sample/sieve.rb index 640cc32b08..a953784284 100644 --- a/sample/sieve.rb +++ b/sample/sieve.rb @@ -12,7 +12,7 @@ for i in 2 .. max print ", " print i sieve.push(i) - resque + rescue end end print "\n" diff --git a/sample/split.rb b/sample/split.rb deleted file mode 100644 index 2b6f3921ce..0000000000 --- a/sample/split.rb +++ /dev/null @@ -1,13 +0,0 @@ -# split test -print("1 byte string", "\n") -print("1 byte string".reverse, "\n") - -print("全角文字列", "\n") -print("全角文字列".reverse, "\n") - -print("1 byte string", "\n") -print("1 byte string".split(//).reverse.join(":"), "\n") -print("全角文字列", "\n") -print("全角文字列".split(//).reverse.join(":"), "\n") -print("全角と1byteの混在", "\n") -print("全角と1byteの混在".split(//).reverse.join(":"), "\n") diff --git a/sample/struct.rb b/sample/struct.rb deleted file mode 100644 index 322764d02f..0000000000 --- a/sample/struct.rb +++ /dev/null @@ -1,8 +0,0 @@ -#output: -# struct test -# 1 - -foo = Struct.new("test", "a1"::1, "a2"::2) -print(foo, "\n") -bar = foo.clone -print(bar.a1, "\n") diff --git a/sample/svr.rb b/sample/svr.rb index 23b2bf71f6..460c16bedf 100644 --- a/sample/svr.rb +++ b/sample/svr.rb @@ -1,6 +1,8 @@ # socket example - server side # usage: ruby svr.rb +require "socket" + gs = TCPserver.open(0) addr = gs.addr addr.shift diff --git a/sample/system.rb b/sample/system.rb deleted file mode 100644 index 02f3782b39..0000000000 --- a/sample/system.rb +++ /dev/null @@ -1,2 +0,0 @@ -# command string -print(`echo foobar`) diff --git a/sample/t1.rb b/sample/t1.rb deleted file mode 100644 index 701a1cd389..0000000000 --- a/sample/t1.rb +++ /dev/null @@ -1,20 +0,0 @@ -def test(a1, *a2) - while 1 - case gets() - when nil - break - when /^-$/ - print("-\n") - return - when /^-help/ - print("-help\n") - break - end - end - print(a1, a2, "\n") -end - -print($ARGV, "\n") -print("in: ") -test(1) -print("end\n") diff --git a/sample/t2.rb b/sample/t2.rb deleted file mode 100644 index 2e3741e8f1..0000000000 --- a/sample/t2.rb +++ /dev/null @@ -1,23 +0,0 @@ -#print("in Print\n") -def t2() end - -def println(*args) - for a in args - t2() - print(a) - end - print("\n") -end - -def tt - for i in 1..10 - println("i:", i); - yield(i); - end -end - -test = tt{|i| - if i == 3; break end - println("ttt: ", i); -} -#exit() diff --git a/sample/test.rb b/sample/test.rb index 9c422cc94a..7f26433181 100644 --- a/sample/test.rb +++ b/sample/test.rb @@ -1,5 +1,1040 @@ -index = 1 -for argument in $ARGV - printf("%d:%s\n", index, argument) - index = index + 1 +#! /usr/local/bin/ruby + +$testnum=0 + +def check(what) + printf "%s\n", what + $what = what + $testnum = 0 end + +def ok + $testnum+=1 + printf "ok %d\n", $testnum +end + +def notok + $testnum+=1 + printf "not ok %s %d\n", $what, $testnum + $failed = TRUE +end + +# make sure conditional operators work + +check "condition" + +$x = '0'; + +$x == $x && ok +$x != $x && notok +$x == $x || notok +$x != $x || ok + +# first test to see if we can run the tests. + +check "if"; + +$x = 'test'; +if $x == $x then ok else notok end +if $x != $x then notok else ok end + +check "case" + +case 5 +when 1, 2, 3, 4, 6, 7, 8 + notok +when 5 + ok +end + +case 5 +when 5 + ok +when 1..10 + notok +end + +case 5 +when 5 + ok +else + notok +end + +case "foobar" +when /^f.*r$/ + ok +else + notok +end + +check "while"; + +tmp = open("while_tmp", "w") +tmp.print "tvi925\n"; +tmp.print "tvi920\n"; +tmp.print "vt100\n"; +tmp.print "Amiga\n"; +tmp.print "paper\n"; +tmp.close + +# test break + +tmp = open("while_tmp", "r") + +while tmp.gets() + break if /vt100/ +end + +if !tmp.eof && /vt100/ then + ok +else + notok +end +tmp.close + +# test continue +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + continue if /vt100/; + $bad = 1 if /vt100/; +end +if !tmp.eof || /vt100/ || $bad + notok +else + ok +end +tmp.close + +# test redo +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + if gsub!('vt100', 'VT100') + gsub!('VT100', 'Vt100') + redo; + end + $bad = 1 if /vt100/; + $bad = 1 if /VT100/; +end +if !tmp.eof || $bad + notok +else + ok +end +tmp.close + +# test interval +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + break if not 1..2 + if /vt100/ || /Amiga/ || /paper/ + $bad = TRUE + notok + break + end +end +ok if not $bad +tmp.close + +File.unlink "while_tmp" or `/bin/rm -f "while_tmp"` + +# exception handling +check "exception"; + +begin + fail "this must be handled" + notok +rescue + ok +end + +$bad = TRUE +begin + fail "this must be handled no.2" +rescue + if $bad + $bad = FALSE + retry + notok + end +end +ok + +$bad = TRUE +$string = "this must be handled no.3" +begin + fail $string +rescue +ensure + $bad = FALSE + ok +end +notok if $bad || $! != $string + +# exception in rescue clause +begin + begin + fail "this must be handled no.4" + rescue + fail "exception in rescue clause" + end + notok +rescue + ok +end + +check "array" +$x = [0, 1, 2, 3, 4, 5] +if $x[2] == 2 + ok +else + notok +end + +if $x[1..3] == [1, 2, 3] + ok +else + notok +end + +if $x[1,3] == [1, 2, 3] + ok +else + notok +end + +if [1, 2] + [3, 4] == [1, 2, 3, 4] + ok +else + notok +end + +$x[0, 2] = 10 +if $x[0] == 10 && $x[1] == 2 + ok +else + notok +end + +$x[0, 0] = -1 +if $x[0] == -1 && $x[1] == 10 + ok +else + notok +end + +$x[-1, 1] = 20 +if $x[-1] == 20 && $x.pop == 20 + ok +else + notok +end + +$x = ["it", "came", "to", "pass", "that", "..."] +$x = $x.sort.join(" ") +if $x == "... came it pass that to" + ok +else + notok +end + +# split test +if "1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1" + ok +else + notok +end + +$x = [1] +if ($x * 5).join(":") == '1:1:1:1:1' then ok else notok end +if ($x * 1).join(":") == '1' then ok else notok end +if ($x * 0).join(":") == '' then ok else notok end + +check "hash" +$x = {1=>2, 2=>4, 3=>6} +$y = {1, 2, 2, 4, 3, 6} + +if $x[1] == 2 + ok +else + notok +end + +begin + for k,v in $y + fail if k*2 != v + end + ok +rescue + notok +end + +if $x.length == 3 + ok +else + notok +end + +if $x.has_key?(1) + ok +else + notok +end + +if $x.has_value?(4) + ok +else + notok +end + +if $x.indexes(2,3) == [4,6] + ok +else + notok +end + +$z = $y.keys.join(":") +if $z == "1:2:3" + ok +else + notok +end + +$z = $y.values.join(":") +if $z == "2:4:6" + ok +else + notok +end + +if $x == $y + ok +else + notok +end + +$y.shift +if $y.length == 2 + ok +else + notok +end + +check "iterator" + +if iterator? then notok else ok end + +def ttt + if iterator? then ok else notok end +end +ttt{} + +# yield at top level +begin + yield + notok +rescue + ok +end + +$x = [1, 2, 3, 4] +$y = [] + +# iterator over array +for i in $x + $y.push i +end +if $x == $y + ok +else + notok +end + +# nested iterator +def tt + 1.upto(10) {|i| + yield i + } +end + +tt{|i| break if i == 5} +if i == 5 + ok +else + notok +end + +# iterator break/redo/continue/retry +done = TRUE +loop{ + break + done = FALSE + notok +} +ok if done + +done = TRUE +$bad = FALSE +loop { + break if not done + done = FALSE + continue + $bad = TRUE +} +if $bad + notok +else + ok +end + +done = TRUE +$bad = FALSE +loop { + break if not done + done = FALSE + redo + $bad = TRUE +} +if $bad + notok +else + ok +end + +$x = [] +for i in 1 .. 7 + $x.push(i) +end +if $x.size == 7 + ok +else + notok +end +# $x == [1, 2, 3, 4, 5, 6, 7] +$done = FALSE +$x = [] +for i in 1 .. 7 # see how retry works in iterator loop + if i == 4 and not $done + $done = TRUE + retry + end + $x.push(i) +end +# $x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7] +if $x.size == 10 + ok +else + notok +end + +check "bignum" +def fact(n) + return 1 if n == 0 + return n*fact(n-1) +end +if fact(40) == 815915283247897734345611269596115894272000000000 + ok +else + notok +end +if fact(40) == 815915283247897734345611269596115894272000000001 + notok +else + ok +end + +check "string & char" + +if "abcd" == "abcd" + ok +else + notok +end + +if "abcd" =~ "abcd" + ok +else + notok +end + +$foo = "abc" +if "#$foo = abc" == "abc = abc" + ok +else + notok +end + +if "#{$foo} = abc" == "abc = abc" + ok +else + notok +end + +foo = "abc" +if "#{foo} = abc" == "abc = abc" + ok +else + notok +end + +if '-' * 5 == '-----' then ok else notok end +if '-' * 1 == '-' then ok else notok end +if '-' * 0 == '' then ok else notok end + +foo = '-' +if foo * 5 == '-----' then ok else notok end +if foo * 1 == '-' then ok else notok end +if foo * 0 == '' then ok else notok end + +# character constants(assumes ASCII) +if "a"[0] == ?a + ok +else + notok +end + +if ?a == ?a + ok +else + notok +end + +if ?\C-a == 1 + ok +else + notok +end + +if ?\M-a == 225 + ok +else + notok +end + +if ?\M-\C-a == 129 + ok +else + notok +end + +$x = "abcdef" +$y = [ ?a, ?b, ?c, ?d, ?e, ?f ] +$bad = FALSE +$x.each_byte {|i| + if i != $y.shift + $bad = TRUE + break + end +} +if not $bad + ok +else + notok +end + +check "asignment" +a = nil +if a == nil + ok +else + notok +end + +a, b = 1, 2 +if a == 1 and b == 2 then + ok +else + notok +end + +a, *b = 1, 2, 3 +if a == 1 and b == [2, 3] then + ok +else + notok +end + +check "call" +def aaa(a, b=100, *rest) + res = [a, b] + res += rest if rest + return res +end + +begin + aaa() + notok +rescue + ok +end + +begin + aaa + notok +rescue + ok +end + +begin + if aaa(1) == [1, 100] + ok + else + fail + end +rescue + notok +end + +begin + if aaa(1, 2) == [1, 2] + ok + else + fail + end +rescue + notok +end + +begin + if aaa(1, 2, 3, 4) == [1, 2, 3, 4] + ok + else + fail + end +rescue + notok +end + +begin + if aaa(1, *[2, 3, 4]) == [1, 2, 3, 4] + ok + else + fail + end +rescue + notok +end + +check "proc" +$proc = proc{|i| i} +if $proc.call(2) == 2 + ok +else + notok +end + +$proc = proc{|i| i*2} +if $proc.call(2) == 4 + ok +else + notok +end + +proc{ + iii=5 # dynamic local variable + $proc = proc{ |i| + iii = i + } + $proc2 = proc { + $x = iii # dynamic variables shared by procs + } + if defined?(iii) # dynamic variables' scope + ok + else + notok + end +}.call +if defined?(iii) # out of scope + notok +else + ok +end +$x=0 +$proc.call(5) +$proc2.call +if $x == 5 + ok +else + notok +end + +check "signal" +begin + kill "SIGINT", $$ + sleep 1 + notok +rescue + ok +end + +$x = 0 +trap "SIGINT", proc{|sig| $x = sig;fail} +begin + kill "SIGINT", $$ + sleep 1 + notok +rescue + if $x == 2 + ok + else + notok + end +end + +$x = FALSE +trap "SIGINT", "$x = TRUE;fail" +begin + kill "SIGINT", $$ + sleep 1 + notok +rescue + if $x + ok + else + notok + end +end + +check "eval" +$bad=FALSE +eval 'while FALSE; $bad = TRUE; print "foo\n" end +if not $bad then ok else notok end' + +$foo = 'ok' +begin + eval $foo +rescue + notok +end + +check "system" +if `echo foobar` == "foobar\n" + ok +else + notok +end + +if `./ruby -e 'print "foobar"'` == 'foobar' + ok +else + notok +end + +tmp = open("script_tmp", "w") +tmp.print "print $zzz\n"; +tmp.close + +if `./ruby -s script_tmp -zzz` == 't' + ok +else + notok +end + +if `./ruby -s script_tmp -zzz=555` == '555' + ok +else + notok +end + +tmp = open("script_tmp", "w") +tmp.print "#! /usr/local/bin/ruby -s\n"; +tmp.print "print $zzz\n"; +tmp.close + +if `./ruby script_tmp -zzz=678` == '678' + ok +else + notok +end + +tmp = open("script_tmp", "w") +tmp.print "this is a leading junk\n"; +tmp.print "#! /usr/local/bin/ruby -s\n"; +tmp.print "print $zzz\n"; +tmp.print "__END__\n"; +tmp.print "this is a trailing junk\n"; +tmp.close + +if `./ruby -x script_tmp` == 'nil' + ok +else + notok +end + +if `./ruby -x script_tmp -zzz=555` == '555' + ok +else + notok +end + +tmp = open("script_tmp", "w") +for i in 1..5 + tmp.print i, "\n" +end +tmp.close + +`./ruby -i.bak -pe 'sub(/^[0-9]+$/){$&.to_i * 5}' script_tmp` +done = TRUE +tmp = open("script_tmp", "r") +while tmp.gets + if $_.to_i % 5 != 0 + done = FALSE + notok + break + end +end +ok if done + +File.unlink "script_tmp" or `/bin/rm -f "script_tmp"` +File.unlink "script_tmp.bak" or `/bin/rm -f "script_tmp.bak"` + +check "const" +TEST1 = 1 +TEST2 = 2 + +module Const + TEST3 = 3 + TEST4 = 4 +end + +module Const2 + TEST3 = 6 + TEST4 = 8 +end + +include Const + +if [TEST1,TEST2,TEST3,TEST4] == [1,2,3,4] + ok +else + notok +end + +include Const2 + +if [TEST1,TEST2,TEST3,TEST4] == [1,2,6,8] + ok +else + notok +end + +check "clone" +foo = Object.new +def foo.test + "test" +end +bar = foo.clone +def bar.test2 + "test2" +end + +if bar.test2 == "test2" + ok +else + notok +end + +if bar.test == "test" + ok +else + notok +end + +if foo.test == "test" + ok +else + notok +end + +begin + foo.test2 + notok +rescue + ok +end + +check "pack" + +$format = "c2x5CCxsdila6"; +# Need the expression in here to force ary[5] to be numeric. This avoids +# test2 failing because ary2 goes str->numeric->str and ary doesn't. +ary = [1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,"abcdef"] +$x = ary.pack($format) +ary2 = $x.unpack($format) + +if ary.length == ary2.length then ok else notok end + +if ary.join(':') == ary2.join(':') then ok else notok end + +if $x =~ /def/ then ok else notok end + +check "math" +if Math.sqrt(4) == 2 + ok +else + notok +end + +include Math +if sqrt(4) == 2 + ok +else + notok +end + +check "struct" +struct_test = Struct.new("Test", :foo, :bar) +if struct_test == Struct::Test + ok +else + notok +end +test = struct_test.new(1, 2) +if test.foo == 1 && test.bar == 2 + ok +else + notok +end +if test[0] == 1 && test[1] == 2 + ok +else + notok +end +a, b = test +if a == 1 && b == 2 + ok +else + notok +end +test[0] = 22 +if test.foo == 22 + ok +else + notok +end +test.bar = 47 +if test.bar == 47 + ok +else + notok +end + +check "variable" +if $$.is_instance_of? Fixnum + ok +else + notok +end + +begin + $$ = 5 + notok +rescue + ok +end + +foobar = "foobar" +$_ = foobar +if $_ == foobar + ok +else + notok +end + +check "trace" +$x = 1234 +$y = 0 +trace_var :$x, proc{$y = $x} +$x = 40414 +if $y == $x + ok +else + notok +end + +untrace_var :$x +$x = 19660208 +if $y != $x + ok +else + notok +end + +trace_var :$x, proc{$x *= 2} +$x = 5 +if $x == 10 + ok +else + notok +end +untrace_var :$x + +check "defined?" +if defined? $x + ok +else + notok +end + +foo=5 +if defined? foo + ok +else + notok +end + +if defined? Array + ok +else + notok +end + +if defined? Object.new + ok +else + notok +end + +if defined? 1 == 2 + ok +else + notok +end + +if defined? fail + ok +else + notok +end + +def defined_test + return defined?(yield) +end + +if defined_test + notok +else + ok +end + +if defined_test{} + ok +else + notok +end + +check "gc" +begin + 1.upto(10000) { + tmp = [0,1,2,3,4,5,6,7,8,9] + } + tmp = nil + ok +rescue + notok +end + +print "end of test\n" if not $failed diff --git a/sample/tkbiff.rb b/sample/tkbiff.rb new file mode 100644 index 0000000000..9b406010cb --- /dev/null +++ b/sample/tkbiff.rb @@ -0,0 +1,121 @@ +#! /usr/local/bin/ruby + +if $ARGV.length == 0 + if ENV['MAIL'] + $spool = ENV['MAIL'] + else + $spool = '/usr/spool/mail/' + ENV['USER'] + end +else + $spool = $ARGV[0] +end + +exit if fork + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.is_kind_of?(IO) + f = open(f, "r") + me = super + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + continue if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +require "tkscrollbox" + +$top = TkRoot.new +$top.withdraw +$list = TkScrollbox.new($top) { + relief 'raised' + width 80 + height 8 + setgrid 'yes' + pack +} +TkButton.new($top) { + text 'Dismiss' + command proc {$top.withdraw} + pack('fill'=>'both','expand'=>'yes') +} +$top.bind "Control-c", proc{exit} +$top.bind "Control-q", proc{exit} +$top.bind "space", proc{exit} + +$spool_size = 0 +def check + size = File.size($spool) + if size and size != $spool_size + pop_up if size > 0 + end + Tk.after 5000, proc{check} +end + +def pop_up + outcount = 0; + $spool_size = File.size($spool) + $list.delete 0, 'end' + f = open($spool, "r") + while !f.eof + mail = Mail.new(f) + date, from, subj = mail.header['Date'], mail.header['From'], mail.header['Subject'] + continue if !date + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" if ! from + subj = "(nil)" if ! subj + from = decode_b(from) + subj = decode_b(subj) + $list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + outcount += 1 + end + f.close + if outcount == 0 + $list.insert 'end', "You have no mail." + end + $top.deiconify + Tk.after 2000, proc{$top.withdraw} +end + +check +Tk.mainloop diff --git a/sample/tkbrowse.rb b/sample/tkbrowse.rb new file mode 100644 index 0000000000..dbaa132d1f --- /dev/null +++ b/sample/tkbrowse.rb @@ -0,0 +1,69 @@ +#!/usr/local/bin/ruby +# +# This script generates a directory browser, which lists the working +# directory and allows you to open files or subdirectories by +# double-clicking. + +# Create a scrollbar on the right side of the main window and a listbox +# on the left side. + +require "tkscrollbox" + +list = TkScrollbox.new { + relief 'raised' + width 20 + height 20 + setgrid 'yes' + pack +} + +# The procedure below is invoked to open a browser on a given file; if the +# file is a directory then another instance of this program is invoked; if +# the file is a regular file then the Mx editor is invoked to display +# the file. + +def browse (dir, file) + if dir != "." + file="#{dir}/#{file}" + if File.isdirectory? file + system "browse #{file} &" + else + if File.isfile? file + if ENV['EDITOR'] + system format("%s %s&", ENV['EDITOR'], file) + else + sysmte "xedit #{file}&" + end + else + STDERR.print "\"#{file}\" isn't a directory or regular file" + end + end + end +end + +# Fill the listbox with a list of all the files in the directory (run +# the "ls" command to get that information). + +if $ARGV.length>0 + dir = $ARGV[0] +else + dir="." +end +list.insert 'end', *`ls #{dir}`.split + +# Set up bindings for the browser. + +list.focus +list.bind "Control-q", proc{exit} +list.bind "Control-c", proc{exit} +list.bind "Control-p", proc{ + print "selection <", TkSelection.get, ">\n" +} + +list.bind "Double-Button-1", proc{ + for i in TkSelection.get.split + print "clicked ", i, "\n" + browse dir, i + end +} +Tk.mainloop diff --git a/sample/tkdialog.rb b/sample/tkdialog.rb new file mode 100644 index 0000000000..e83e16d0a8 --- /dev/null +++ b/sample/tkdialog.rb @@ -0,0 +1,62 @@ +#! /usr/local/bin/ruby +require "tk" + +root = TkFrame.new +top = TkFrame.new(root) { + relief 'raised' + border 1 +} +msg = TkMessage.new(top) { + text "File main.c hasn't been saved to disk since \ +it was last modified. What should I do?" + justify 'center' + aspect 200 + font '-Adobe-helvetica-medium-r-normal--*-240*' + pack('padx'=>5, 'pady'=>5, 'expand'=>'yes') +} +top.pack('fill'=>'both') +root.pack + +bot = TkFrame.new(root) { + relief 'raised' + border 1 +} + +TkFrame.new(bot) { |left| + relief 'sunken' + border 1 + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10, 'pady'=> 10) + TkButton.new(left) { + text "Save File" + command "quit 'save'" + pack('expand'=>'yes','padx'=>6,'pady'=> 6) + top.bind "Enter", proc{state 'active'} + msg.bind "Enter", proc{state 'active'} + bot.bind "Enter", proc{state 'active'} + top.bind "Leave", proc{state 'normal'} + msg.bind "Leave", proc{state 'normal'} + bot.bind "Leave", proc{state 'normal'} + Tk.root.bind "ButtonRelease-1", proc{quit 'save'} + Tk.root.bind "Return", proc{quit 'save'} + } +} +TkButton.new(bot) { + text "Quit Anyway" + command "quit 'quit'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +TkButton.new(bot) { + text "Return To Editor" + command "quit 'return'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +bot.pack +root.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes') + +def quit(button) + print "aaa\n" + print "You pressed the \"#{button}\" button; bye-bye!\n" + exit +end + +Tk.mainloop diff --git a/sample/tkfrom.rb b/sample/tkfrom.rb new file mode 100644 index 0000000000..4a0d8c2b5d --- /dev/null +++ b/sample/tkfrom.rb @@ -0,0 +1,115 @@ +#! /usr/local/bin/ruby + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.is_kind_of?(IO) + f = open(f, "r") + me = super + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + continue if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +$ARGV[0] = '/usr/spool/mail/' + ENV['USER'] if $ARGV.length == 0 + +require "tk" +list = scroll = nil +TkFrame.new{|f| + list = TkListbox.new(f) { + yscroll proc{|idx| + scroll.set *idx + } + relief 'raised' +# geometry "80x5" + width 80 + height 5 + setgrid 'yes' + pack('side'=>'left','fill'=>'both','expand'=>'yes') + } + scroll = TkScrollbar.new(f) { + command proc{|idx| + list.yview *idx + } + pack('side'=>'right','fill'=>'y') + } + pack +} +root = Tk.root +TkButton.new(root) { + text 'Dismiss' + command proc {exit} + pack('fill'=>'both','expand'=>'yes') +} +root.bind "Control-c", proc{exit} +root.bind "Control-q", proc{exit} +root.bind "space", proc{exit} + +$outcount = 0; +for file in $ARGV + continue if !File.exists?(file) + f = open(file, "r") + while !f.eof + mail = Mail.new(f) + date, from, subj = mail.header['Date'], mail.header['From'], mail.header['Subject'] + continue if !date + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" if ! from + subj = "(nil)" if ! subj + from = decode_b(from) + subj = decode_b(subj) + list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + $outcount += 1 + end + f.close +end + +limit = 10000 +if $outcount == 0 + list.insert 'end', "You have no mail." + limit = 2000 +end +Tk.after limit, proc{ + exit +} +Tk.mainloop diff --git a/sample/tkhello.rb b/sample/tkhello.rb new file mode 100644 index 0000000000..1ff1403e71 --- /dev/null +++ b/sample/tkhello.rb @@ -0,0 +1,13 @@ +require "tk" + +TkButton.new { + text 'hello' + command proc{print "hello\n"} + pack('fill'=>'x') +} +TkButton.new { + text 'quit' + command 'exit' + pack('fill'=>'x') +} +Tk.mainloop diff --git a/sample/tkline.rb b/sample/tkline.rb new file mode 100644 index 0000000000..843893b5d9 --- /dev/null +++ b/sample/tkline.rb @@ -0,0 +1,29 @@ +require "tkclass" + +$c = Canvas.new +$c.pack +$start_x = start_y = 0 + +def do_press(x, y) + $start_x = x + $start_y = y + $current_line = Line.new($c, x, y, x, y, 'fill' => 'gray') +end +def do_motion(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + end +end + +def do_release(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + $current_line.fill 'black' + $current_line = nil + end +end + +$c.bind("1", proc{|e| do_press e.x,e.y}) +$c.bind("B1-Motion", proc{|e| do_motion e.x,e.y}) +$c.bind("ButtonRelease-1", proc{|e| do_release e.x,e.y}) +Tk.mainloop diff --git a/sample/tktimer.rb b/sample/tktimer.rb new file mode 100644 index 0000000000..b8b3617646 --- /dev/null +++ b/sample/tktimer.rb @@ -0,0 +1,49 @@ +#!/usr/local/bin/ruby +# This script generates a counter with start and stop buttons. + +require "tk" +$label = TkLabel.new { + text '0.00' + relief 'raised' + width 10 + pack('side'=>'bottom', 'fill'=>'both') +} + +TkButton.new { + text 'Start' + command proc { + if $stopped + $stopped = FALSE + tick + end + } + pack('side'=>'left','fill'=>'both','expand'=>'yes') +} +TkButton.new { + text 'Stop' + command proc{ + $stopped = TRUE + } + pack('side'=>'right','fill'=>'both','expand'=>'yes') +} + +$seconds=0 +$hundredths=0 +$stopped=TRUE + +def tick + if $stopped then return end + Tk.after 50, proc{tick} + $hundredths+=5 + if $hundredths >= 100 + $hundredths=0 + $seconds+=1 + end + $label.text format("%d.%02d", $seconds, $hundredths) +end + +root = Tk.root +root.bind "Control-c", proc{root.destroy} +root.bind "Control-q", proc{root.destroy} +Tk.root.focus +Tk.mainloop diff --git a/sample/trap.pl b/sample/trap.pl deleted file mode 100644 index ce022d4062..0000000000 --- a/sample/trap.pl +++ /dev/null @@ -1,6 +0,0 @@ -$SIG{'INT'} = 'test'; - -while (<>) { - print; -} -sub test { print "C-c handled\n"; } diff --git a/sample/trap.rb b/sample/trap.rb deleted file mode 100644 index e552a0fddc..0000000000 --- a/sample/trap.rb +++ /dev/null @@ -1,3 +0,0 @@ -trap('print("C-c handled\n")', 'INT', 'HUP') -print("---\n") -while gets(); print($_) end diff --git a/sample/tt.rb b/sample/tt.rb deleted file mode 100644 index f4960feaea..0000000000 --- a/sample/tt.rb +++ /dev/null @@ -1,100 +0,0 @@ -module Print - print("in Print\n") - def println(*args) - for a in args - print(a) - end - print("\n") - end - - def println2(*args) - print(*args) - print("\n") - end -end - -module Print2 - def println(*args) - print("pr2: "); - super - end -end - -module Print3 - include Print2 - def println(*args) - print("pr3: "); - super - end -end - -include Print, Print2, Print3 - -println2("in TopLevel") - -print("a: ", $OPT_test, "\n") -printf("%10.5g: %*s -> 0x%x\n", 123345, -10, Print, Print.id); - -println("a+ matches aaa at ", "bccAAaaa" =~ /a+/) -ttt = "this is a 漢字 漢字" -if offset = (ttt =~ /this ([^ ]*) (.*)/) - println("0 = ", $&); - println("1 = ", $1); - println("2 = ", $2); -end - -class Fib : Object - print("in Fib:Object\n") - - def Fib.test(*args) - println("in Fib.test") - - if args; println(*args) end - args = args.grep(/^c/) - super(*args) - end - - def init - println("in Fib.init"); - end - - def fib(n) - a =0; b = 1 - - while b <= n - c = a; a = b; b = c+b - end - return b - end -end - -def Object.test(*args) - println("in Object.test") - if args; println(*args) end -end - -Fib.test("abc", "def", "aaa", "ccc") -println("1:", 0x3fffffffa) -println("2:", 0x3ffffffa) -#println("3:", 0x40000000+0x40000000) - -fib = Fib.new - -fib.init -print(Fib, ":") - -#for i in 1 .. 100 -# fib.fib(90000) -#end - -println(fib.fib(9000)) - -def tt - for i in 1..10 - println("i:", i); - yield(i); - end -end - -test = tt() {|i|break if i == 2} -println([1,2,3,4].join(":")) @@ -0,0 +1,28 @@ +/************************************************ + + sig.h - + + $Author$ + $Date$ + created at: Wed Aug 16 01:15:38 JST 1995 + +************************************************/ +#ifndef SIG_H +#define SIG_H + +#ifdef SAFE_SIGHANDLE +extern int trap_immediate; +# define TRAP_BEG (trap_immediate=1) +# define TRAP_END (trap_immediate=0) +#else +# define TRAP_BEG +# define TRAP_END +#endif + +typedef RETSIGTYPE(*SIGHANDLE)(); +SIGHANDLE sig_beg(); +void sig_end(); + +extern int trap_pending; + +#endif @@ -9,6 +9,7 @@ ************************************************/ #include "ruby.h" +#include "sig.h" #include <signal.h> #include <stdio.h> @@ -162,7 +163,7 @@ signm2signo(nm) } VALUE -Fkill(argc, argv) +f_kill(argc, argv) int argc; VALUE *argv; { @@ -251,7 +252,7 @@ static RETSIGTYPE sighandle(sig) int sig; { - if (sig >= NSIG || trap_list[sig] == Qnil) + if (sig >= NSIG ||(sig != SIGINT && trap_list[sig] == Qnil)) Fail("trap_handler: Bad signal %d", sig); #ifndef HAVE_BSD_SIGNALS @@ -260,14 +261,16 @@ sighandle(sig) #ifdef SAFE_SIGHANDLE if (trap_immediate) { - rb_trap_eval(trap_list[sig]); + if (sig == SIGINT && !trap_list[sig]) Fail("Interrupt"); + rb_trap_eval(trap_list[sig], sig); } else { trap_pending++; trap_pending_list[sig]++; } #else - rb_trap_eval(trap_list[sig]); + if (sig == SIGINT && !trap_list[sig]) Fail("Interrupt"); + rb_trap_eval(trap_list[sig], sig); #endif } @@ -275,119 +278,184 @@ void rb_trap_exit() { if (trap_list[0]) - rb_trap_eval(trap_list[0]); + rb_trap_eval(trap_list[0], 0); } #ifdef SAFE_SIGHANDLE +void rb_trap_exec() { int i; - trap_pending = 0; for (i=0; i<NSIG; i++) { if (trap_pending_list[i]) { trap_pending_list[i] = 0; - rb_trap_eval(trap_list[i]); + if (i == SIGINT && trap_list[SIGINT] == 0) + Fail("Interrupt"); + rb_trap_eval(trap_list[i], i); } } + trap_pending = 0; } #endif -static VALUE -Ftrap(argc, argv) - int argc; - VALUE *argv; -{ - RETSIGTYPE (*func)(); - VALUE command; - int i, sig; -#ifdef HAVE_SIGPROCMASK +struct trap_arg { +#ifndef NT +# ifdef HAVE_SIGPROCMASK sigset_t mask; -#else +# else int mask; +# endif #endif + VALUE sig, cmd; +}; - if (argc < 2) - Fail("wrong # of arguments -- kill(cmd, sig...)"); +static RETSIGTYPE +sigexit() +{ + rb_exit(1); +} - /* disable interrupt */ -#ifdef HAVE_SIGPROCMASK - sigfillset(&mask); - sigprocmask(SIG_BLOCK, &mask, &mask); -#else - mask = sigblock(~0); -#endif +static VALUE +trap(arg) + struct trap_arg *arg; +{ + RETSIGTYPE (*func)(); + VALUE command; + int i, sig; func = sighandle; - - if (argv[0] == Qnil) { + command = arg->cmd; + if (command == Qnil) { func = SIG_IGN; - command = Qnil; } - else { - Check_Type(argv[0], T_STRING); - command = argv[0]; - if (RSTRING(argv[0])->len == 0) { + else if (TYPE(command) == T_STRING) { + if (RSTRING(command)->len == 0) { func = SIG_IGN; } - else if (RSTRING(argv[0])->len == 7) { - if (strncmp(RSTRING(argv[0])->ptr, "SIG_IGN", 7) == 0) { + else if (RSTRING(command)->len == 7) { + if (strncmp(RSTRING(command)->ptr, "SIG_IGN", 7) == 0) { func = SIG_IGN; } - else if (strncmp(RSTRING(argv[0])->ptr, "SIG_DFL", 7) == 0) { + else if (strncmp(RSTRING(command)->ptr, "SIG_DFL", 7) == 0) { func = SIG_DFL; } - else if (strncmp(RSTRING(argv[0])->ptr, "DEFAULT", 7) == 0) { + else if (strncmp(RSTRING(command)->ptr, "DEFAULT", 7) == 0) { func = SIG_DFL; } } - else if (RSTRING(argv[0])->len == 6) { - if (strncmp(RSTRING(argv[0])->ptr, "IGNORE", 6) == 0) { + else if (RSTRING(command)->len == 6) { + if (strncmp(RSTRING(command)->ptr, "IGNORE", 6) == 0) { func = SIG_IGN; } } + else if (RSTRING(command)->len == 4) { + if (strncmp(RSTRING(command)->ptr, "EXIT", 4) == 0) { + func = sigexit; + } + } } - if (func == SIG_IGN || func == SIG_DFL) + if (func == SIG_IGN || func == SIG_DFL) { command = Qnil; + } - for (i=1; i<argc; i++) { - if (TYPE(argv[i]) == T_STRING) { - char *s = RSTRING(argv[i])->ptr; + if (TYPE(arg->sig) == T_STRING) { + char *s = RSTRING(arg->sig)->ptr; - if (strncmp("SIG", s, 3) == 0) - s += 3; - sig = signm2signo(s); - if (sig == 0 && strcmp(s, "EXIT") != 0) - Fail("Invalid signal SIG%s", s); - } - else { - sig = NUM2INT(argv[i]); - } - if (sig < 0 || sig > NSIG) - Fail("Invalid signal no %d", sig); + if (strncmp("SIG", s, 3) == 0) + s += 3; + sig = signm2signo(s); + if (sig == 0 && strcmp(s, "EXIT") != 0) + Fail("Invalid signal SIG%s", s); + } + else { + sig = NUM2INT(arg->sig); + } + if (sig < 0 || sig > NSIG) { + Fail("Invalid signal no %d", sig); + } + signal(sig, func); + trap_list[sig] = command; + /* enable at least specified signal. */ +#ifdef HAVE_SIGPROCMASK + sigdelset(&arg->mask, sig); +#else + arg->mask &= ~sigmask(sig); +#endif + return Qnil; +} - signal(sig, sighandle); - trap_list[sig] = command; - /* enable at least specified signal. */ +#ifndef NT +static void +trap_ensure(arg) + struct trap_arg *arg; +{ + /* enable interrupt */ #ifdef HAVE_SIGPROCMASK - sigdelset(&mask, sig); + sigprocmask(SIG_SETMASK, &arg->mask, NULL); #else - mask &= ~sigmask(sig); + sigsetmask(arg->mask); #endif +} +#endif + +static VALUE +f_trap(argc, argv) + int argc; + VALUE *argv; +{ + struct trap_arg arg; + + if (argc == 0 || argc > 2) { + Fail("wrong # of arguments -- trap(sig, cmd)/trap(sig){...}"); } + + arg.sig = argv[0]; + if (argc == 1) { + arg.cmd = f_lambda(); + } + else if (argc == 2) { + arg.cmd = argv[1]; + } + +#ifndef NT /* disable interrupt */ -#ifdef HAVE_SIGPROCMASK - sigprocmask(SIG_SETMASK, &mask, NULL); +# ifdef HAVE_SIGPROCMASK + sigfillset(&arg.mask); + sigprocmask(SIG_BLOCK, &arg.mask, &arg.mask); +# else + arg.mask = sigblock(~0); +# endif + + return rb_ensure(trap, &arg, trap_ensure, &arg); #else - sigsetmask(mask); + return trap(&arg); #endif - return Qnil; } +SIGHANDLE +sig_beg() +{ + if (!trap_list[SIGINT]) { + return signal(SIGINT, sighandle); + } + return 0; +} + +void +sig_end(handle) + SIGHANDLE handle; +{ + if (!trap_list[SIGINT]) { + signal(SIGINT, handle); + } +} + +void Init_signal() { - extern VALUE C_Kernel; + extern VALUE cKernel; - rb_define_method(C_Kernel, "kill", Fkill, -1); - rb_define_method(C_Kernel, "trap", Ftrap, -1); + rb_define_method(cKernel, "kill", f_kill, -1); + rb_define_method(cKernel, "trap", f_trap, -1); } @@ -1,3538 +0,0 @@ -.\" spec - -*- Indented-Text -*- created at: Tue May 25 15:18:26 JST 1993 - -* はじめに - -Rubyは「UNIXで手軽にオブジェクト指向プログラミング」をしたいという望み -を実現するために生まれた.そのために必要だと思われた性質は: - - * 文法や機能が単純である. - * オブジェクト指向プログラミングをサポートする機能を - 持つ言語として設計されている. - * ターンアラウンドタイムが短い(インタプリタである). - * OS(UNIX)の機能が簡単に利用できる. - * スクリプト言語として使える. - * 処理系がfreeである. - -などである.オブジェクト指向言語として一般的であるSmalltalkやC++などは -上の条件の一部を満たしてはいるが,特に手軽なプログラミングという点に欠 -けており,以上の条件の全てを満たすものではなかった.一方スクリプト言語 -であるPerlや Tclにオブジェクト指向機能を追加したシステムも存在するが, -これらは「手軽にプログラミング」という性質は満たしてはいても,逆にオブ -ジェクト指向機能に不備や不満があった.よって,これらの条件を満たすため -に設計されたRubyの特徴は: - - * インタプリタである. - * 単純で例外の少ない文法. - * 十分なオブジェクト指向機能を持つ. - * できるだけ書きやすい(演算子形式など). - * ガーベージコレクタがある. - * 例外処理機能がある. - * 十分に強力なクラスが組み込まれている. - * OSをアクセスする機能が提供される. - * 拡張しやすい. - -などである.RubyはshやPerlを知っている人にとっての常識になるたけ従った -ので,それらの言語からの自然な移行が可能であると思われる.プログラマが -Rubyのオブジェクト指向機能について学べば,より強力なこともできるように -なるだろう. - -更にC言語でクラスやメソッドを記述し,追加することでRubyを更に強力にす -ることができる.一部のプラットフォームではRubyは動的にオブジェクトファ -イルをリンクできるし,そうでなくてもRubyを再コンパイルして組み込みクラ -スを追加するのは容易である(Perlなどよりもはるかに容易である). - -* Lexical structure - -現在のrubyの実装はキャラクタセットとしてASCIIを用いる.rubyは大文字と -小文字を区別する.識別子の途中でなければ任意のところに空白文字をおくこ -とが出来る.空白文字はスペース(space),タブ(tab),垂直タブ(vertical -tab), CR(carriage return),改頁(form feed)である.改行(newline)は明ら -かに式が継続する場合には空白文字として,それ以外では文の区切りとして解 -釈される. - -識別子は英文字("_"を含む)から始まり,英数字が続いたものである.rubyの -識別子の長さに制限はない.現在の実装は識別子としてマルチバイトコード -(EUC,SJIS)も通すが勧められない. - -グローバル変数名は"$"に続く識別子または記号1文字,インスタンス変数は -"@"に続く識別子,定数には大文字で始まる識別子,ローカル変数名は小文字 -で始まる識別子である.メソッド名には単なる識別子を用いる(メソッド名は -大文字でも小文字でも始められる). - -** コメント - -スクリプト言語の習慣にならい,文字列中や文字表現(?#)以外の`#'から行末 -まではコメントと見なす. - -** 予約語 - -予約語は以下の通りである - - alias def if retry while - and else in return yield - begin elsif module self __END__ - break end nil super __FILE__ - case ensure or then __LINE__ - class fail redo undef - continue for resque when - -予約語はクラス名,メソッド名,変数名などに用いることはできない. - -** 区切り文字 - -文字列などのリテラルの内部以外の場所の空白文字(タブとスペース)および改 -行(\n)が区切り記号となる.更に改行は - - a + - b - -のように行が式の途中で終り,次の行に続くことが明白な(最後の非空白文字 -が演算子あるいは`,'である)場合を除き,式の区切りとして認識される. - -* プログラム - -例: - - print "hello world!\n" - -プログラムは式を並べたものである.式と式の間はセミコロン(`;')または改 -行で区切られる. - -* 式 - -Rubyではnilが偽,それ以外が真と評価される.CやPerlなどとは異なり,0や -""(空文字列)は偽とは評価されないので気をつけること. - -** 文字列式 - -ダブルクォート(`"')で括られた文字列はバックスラッシュに続く文字が以下 -のように解釈される. - -バックスラッシュ記法 - - \t タブ(0x09) - \n 改行文字(0x0a) - \r 復帰文字(0x0d) - \f 改ページ文字(0x0c) - \b バックスペース(0x08) - \a ベル(0x07) - \e エスケープ(0x1b) - \# 文字`#'そのもの - \nnn 8進数表記(nは0-7) - \xnn 16進数表記(nは0-9,a-f) - \cx コントロール文字(xはASCII文字) - \x 文字xそのもの - -また,`#'による変数展開も行われる.一方,シングルクォート(`'')で括られ -た文字列は,`\\'(バックスラッシュそのもの)と`\''(シングルクォート)を除 -いて,文字列の中身の解釈を行わない. - -文字列式は毎回新しい文字列オブジェクトを生成するので,文字列の内容を変 -更しても,もともとの文字列は変わらない. - -** コマンド出力 - -Rubyではshのようにコマンドの実行結果を文字列リテラルのように使うことが -できる.``で囲まれた文字列は,ダブルクォートと同様にバックスラッシュ記 -法の解釈と変数展開が行なわれた後,コマンドとして実行され,その実行結果 -が文字列として与えられる.コマンドは評価されるたびに実行される. - -** 正規表現式 - -`/'で囲まれた文字列は正規表現を表す.終りの`/'の後ろに文字`i'が与えら -れた場合には,その正規表現はマッチ時に大文字小文字の区別をしない. - - ^ 行頭 - $ 行末 - . 任意の1文字 - \w 英数字.[0-9A-Za-z_]と同じ - \W 非英数字 - \s 空白文字.[ \t\n\r\f]と同じ - \S 非空白文字 - \d 数字.[0-9] と同じ - \D 非数字 - \b 語境界文字(文字クラス外) - \B 非語境界文字 - \b 後退(0x08)(文字クラス内) - [ ] 文字クラス指定 - * 直前の表現の0回以上の繰り返し - + 直前の表現の1回以上の繰り返し - {m,n} m回からn回の繰り返し - ? 0または1回 - | 選択 - ( ) 正規表現をまとめる - -その他に文字列と同じバックスラッシュ記法や変数展開も有効である. - -** 変数展開 - -ダブルクォート(`"')で囲まれた文字列式,コマンド文字列,正規表現,およ -びワイルドカード式の中では`#{変数名}'という形式で変数の内容を展開する -ことができる.変数が変数記号(`$',`@')で始まる場合には`#変数名'という形 -式でも展開できる.文字`#'に続く文字が `{',`$',`@'でなければ,そのまま -文字`#'として解釈される. - -** 数値リテラル - - 123 整数 - -123 整数(符合つき数) - 1_234 整数(10進数は`_'を含むことができる) - 123.45 浮動小数点数 - 1.2e-3 浮動小数点数 - 0xffff 16進整数 - 0377 8進整数 - ?a 文字`a'のコード(97) - ?\C-a コントロールaのコード(1) - ?\M-a メタaのコード(225) - ?\M-\C-a メタ-コントロールaのコード(129) - - :シンボル 識別子/変数名/演算子と一対一対応する整数.applyなど - でメソッドを指定する時などに使う. - -?表現では全てのバックスラッシュ記法が有効である. - -** 変数と定数 - -Rubyの変数はスコープ(有効範囲)と寿命(有効期限)によって4種類に分類され, -その種類は変数名の最初の一文字で決定される.通常の変数の2文字目以降は -英数時または`_'であるが,システム変数の一部は「`$'+1文字の記号」という -変数がある.変数名の長さに関して特別な制限はない. - -変数のスコープに関わらず,初期化されていない変数を参照した時の値はnil -である.いずれの種類の変数も宣言は必要ない. - -*** グローバル変数 - -例: - - $foobar - $/ - -`$'で始まる変数のスコープはグローバルであり,プログラムのどこからでも -参照できる.その寿命はプログラムの寿命と等しい. - -*** インスタンス変数 - -例: - - @foobar - -`@'で始まる変数はインスタンス変数であり,そのクラスまたはサブクラスの -メソッドから参照できる.スコープはメソッド内であり,その寿命はオブジェ -クトの寿命に等しい. - -*** クラス定数 - -例: - - FOOBAR - -大文字で始まる識別子は定数へのアクセスであり,最初に定義されたクラスと -全てのサブクラスのスコープ内で参照できる.定数の定義は代入か,定数を定 -義しているモジュールをインクルードすることによって行なわれる.定数への -代入はトップレベル,すなわちメソッドが定義できるレベルでのみ可能である. -定数はクラス間で値が共有され,一度代入すると値を変更することができない -(代入は例外を発生させる).クラス定数の寿命はクラスの寿命と等しい. - -クラス定義は自動的に定数を定義するので,クラス名は定数である. - -*** ローカル変数 - -例: - - foobar - -小文字または`_'で始まる識別子はローカル変数へのアクセスである.初期化 -されないローカル変数の値はnilである - -ローカル変数のスコープはブロックの範囲内(メソッド内ではメソッドの終り -まで,クラス/モジュール定義内ではその定義の終りまで)である.寿命もその -ブロックの終りまで(トップレベルのローカル変数はプログラムの終了まで)で -ある. - -*** 疑似変数 - -通常の変数以外に疑似変数と呼ばれる特殊な変数が4つある. - - self | 現在のメソッドの実行主体 - nil | Nilクラスの唯一のインスタンス(偽を表す) - __FILE__ | スクリプトのファイル名(文字列) - __LINE__ | 現在の行番号(整数) - -これらの疑似変数は代入によってその値を変更することはできない.これらの -変数への代入は例外を発生させる. - -** 配列式 - -例: - - [1, 2, 3] - -配列はArrayクラスのインスタンスである.配列を生成する式は以下の形式で -ある. - - `[' 式, .. `]' - -それぞれの式を評価した結果を含む配列を返す.要素数が0の空配列を生成す -るためには空の配列式 - - `[' `]' - -を用いる. - -** 連想配列式 - -例: - - {1=>2, 2=>4, 3=>6} - -連想配列とは任意のオブジェクトをキー(添字)として持つ配列である.Ruby -の連想配列はHash(連想配列)クラスのインスタンスである.詳細はクラス -Hashの項を参照されたい.連想配列を生成する連想配列式は以下の形式である. - - `{' 式 `=>' 式.. `}' - -それぞれの式を評価した結果をキーと値とする連想配列オブジェクトを返す. -要素数が0の連想配列を生成するためには空の連想配列式 - - `{' `}' - -を用いる.要素が1つ以上ある場合,曖昧でなければ`{', `}'は省略できる. - -** メソッド呼出式 - -例: - - foo.bar() - foo.bar - bar() - -オブジェクトにメッセージを送る基本的な構文がメッセージ式であり,その基 -本形式は以下の通りである. - - 式1 `.' メソッド名 `(' 引数1.. [`,' `*' 引数n ]`)' - -式1を評価して得られるオブジェクトの,識別子で指定されるメソッドを呼び -出す.一番最後の引数が`*'に続く(単一の)式である場合,その式を評価した -結果(配列でなければ変換される)を展開して,引数として追加する. - -メッセージ式で,レシーバがselfの場合,レシーバを省略して通常のプログラ -ミング言語における関数のような形式でメソッドを呼び出すことができる. - -メソッド呼び出しの引数の周りの括弧を省略できるが,第一引数となる式が以 -下の文字または予約語で始まる場合は,優先順位の関係で予想通りの結果が得 -られない場合がある. - - (, [, {, /, +, -, if, while - -どのように評価されるか曖昧な場合には括弧をつける事. - -例: - foo bar+baz # メソッド呼び出しfoo(bar+baz) - foo (1+2)*5 # メソッド呼び出し(foo(1+2)) * 5 - foo 1 # メソッド呼び出しfoo(1) - foo -1 # ローカル変数foo - 1 - -レシーバを指定したメソッド呼び出しではの場合引数が1つもない時にも括弧 -を省略できる(レシーバを指定しない場合,括弧をつけないとローカル変数の -参照として解釈される). - -メソッド名としては任意の識別子を用いることができる.最初の文字は大文字 -でも小文字でも構わない.変数名とは識別子の名前空間が違うので重複しても -構わない. - -クラスModuleで定義されているメソッド(public,private)でメソッドの呼び出 -し方を制御することが出来る.privateで指定された制限されたメソッドは関 -数形式でしか呼び出すことが出来ない. - -** SUPER - -例: - - super - super(1,2,3) - -メッセージ式の特殊なケースとしてスーパークラスのメソッドの呼び出しがあ -る.この形式はメソッドを再定義した時にスーパークラスの定義を利用するた -めに使う. - - super - -現在のメソッドに与えられた引数のままスーパクラスの同名のメソッドを呼び -出す.引数として与えられた変数の値を変更しても,渡されるのは元の引数の -値である. - - super`(' 引数.. `)' - -引数とともにスーパークラスの同名のメソッドを呼び出す.一番最後の引数が -`*'に続く場合は通常のメソッド呼び出しと同様に展開して渡される. - -** 代入 - -例: - - foo = bar - foo[0] = bar - foo.bar = baz - -代入式は変数などに値を設定するために用いられる.代入式は演算子形式をとっ -ているが,メソッドではないので再定義することはできない.左辺になること -が出来るのは以下の3種類の式である. - -変数(`$'識別子 | `@'識別子 | 識別子) - - 変数 `=' 式 - -変数への代入は右辺の式を評価して得られた値を左辺で指定された変数に代入 -する. - -配列参照(式[式..]) - - 式1`[' 式2.. `]' `=' 式n - -配列参照式への代入は,式1を評価して得られるオブジェクトに,式2から式n -までを引数として,"[]=" というメソッドを呼び出す. - -属性参照(式`.'識別子) - - 式1 `.' 識別子 `=' 式2 - -属性参照(引数なしのメソッド呼び出し)への代入は,式1を評価して得られる -オブジェクト(レシーバが省略された場合は`self')に対して,"識別子="とい -うメソッドを式 2を引数として呼び出す. - -** 自己代入 - -例: - - foo += 12 - -式の値そのものに演算を加えるために自己代入形式がある. - - 式1 op= 式2 # 式1は代入可能でなければならない. - -この形式は内部的に「式1 = 式1 op 式2」と同様に評価される.ただし,式1 -は1回しか評価されないので,式1に副作用がある場合は,「式1 = 式1 op 式2」 -とは動作が異なる結果となる.opとして使える演算子は - - +, -, *, /, %, **, &, |, ^, <<, >> - -の11種類である.演算子と`='の間にスペースを空けてはいけない. - -** 多重代入 - -例: - - foo, bar, baz = 1, 2, 3 - foo, = list() - foo, *rest = list2() - -同時に複数の変数に代入を行なうことができる.その形式は以下の通りである. - - 左辺 `,' [左辺 `,'..] [`*' 左辺]= 式 [, 式..] - -左辺には代入式の3種類の式が来る.右辺の式が一つしかない場合は,その値 -を配列として(必要ならばto_aメソッドで配列に変換して),要素をそれぞれ左 -辺に代入する.それ以外の場合には,それぞれの式の値が左辺に代入される. -左辺の数と右辺の要素の数が合わない時には足りない変数には nilが代入され, -余った要素は無視される.多重代入の最後の要素の前に`*'がある場合,残り -の全て引数が配列として代入される. - - foo, bar = [1, 2] # foo = 1; bar = 2 - foo, bar = 1, 2 # foo = 1; bar = 2 - foo, bar = 1 # foo = 1; bar = nil - - foo, bar, baz = 1, 2 # foo = 1; bar = 2; baz = nil - foo, bar = 1, 2, 3 # foo = 1; bar = 2 - foo,*bar = 1, 2, 3 # foo = 1; bar = [2, 3] - -多重代入の値は(配列に変換された)右辺である. - -** 演算子式 - -例: - - 1+2*3/4 - -プログラミングの利便のために一部のメソッド呼び出しと制御構造は演算子形 -式をとる.Rubyには以下にあげる演算子がある.上のものほど結合順位が強く, -同じ列の演算子の結合順位は同じである. - - 強 [](配列参照), []=(配列代入) - -(unary) +(unary) ! ~ - ** - * / % - + - - << >> - & - | ^ - > >= < <= - <=> == != =~ !~ - && - || - .. ... - :: - =(代入) 自己代入(+=, -=,..) - and - or - 弱 if修飾子 while修飾子 - -ほとんどの演算式にはメソッド呼び出しとして解釈される(クラス毎に再定義 -できる)が,一部再定義できない特殊なものがある.再定義できない特殊演算 -子は - - =(代入), ...(範囲), !(否定), &&(論理積), and, |(論理和), or, - if修飾子, while修飾子 - -の9つの演算子とこれらとの組み合わせになる !=, !~ および自己代入演算子 -である. - -上であげた特殊演算子以外の演算子形式は以下のようなメソッド呼び出しと見 -なされる. - -単項演算子(+, -, ~) - - 式1. 演算子 () - -配列(連想配列を含む)の要素の参照(式1 `[' 式2.. `]') - - 式1. `[]' (式2..) - -配列要素の代入( 式1 `[' 式2.. `]' `=' 式n) - - 式1. `[]=' (式2.., 式n) - -それ以外の2項演算子(式1 演算子 式2) - - 式1. 演算子 (式2) - -これはあくまでもそういう形式のメソッド呼び出しとして解釈されるというだ -けで,rubyプログラムでこういう記述が許されるというわけではない. - -** 括弧によるグルーピング - -例: - - (1+2)*3 - -式は括弧によってグルーピングすることができる. - - `(' 式 `)' - -** IF - - if 式1 [then] - 式.. - [elsif 式2 [then] - 式.. ].. - [else - 式.. ] - end - -条件判断式.Rubyのif式はelse ifでもelifでもなくelsifでifの連続を行なう -ことに注意すること.条件が成立して実行した式の値を返す.実行しなかった -場合の値はnil. - -ifの条件判断部の式では文字列と正規表現リテラルは式「$_=~ リテラル」の -省略であるとみなされる. - -** IF修飾子 - - 式 if 式 - -条件修飾子(if)の式は先行する式に先だって評価される.動作も対応するif式 -と同様である.if修飾子のついた式の値は条件が成立した場合には式の値,不 -成立の場合にはnilである. - -** CASE - - case 式0 - [when 式1 [, 式2].. [then] - 式.. ].. - [else - 式.. ] - end - -条件分岐,CのswitchよりもPascalのcaseに似ている.breakで脱出することも -後ろの式に継続することもないので注意. - -条件の一致は「式n =~ 式0]で行なわれる.つまり, - - case expr0 - when expr1, expr2 - stmt1 - when expr3, expr4 - stmt2 - else - stmt3 - end - -は以下のif式とほぼ等価である. - - _tmp = expr0 - if expr1 =~ _tmp || expr2 =~ _tmp - stmt1 - elsif expr3 =~ _tmp || expr4 =~ _tmp - stmt2 - else - stmt3 - end - -** AND 式 - - 式1 `&&' 式2 - 式1 `and' 式2 - -式1を評価し,その値が真(nil以外)であれば,式2を評価する.`and'は優先順 -位が低い別名である. - -andの両辺の式では文字列と正規表現リテラルは式「$_=~ リテラル」の省略で -あるとみなされる. - -** OR 式 - - 式1 `||' 式2 - 式1 'or 式2 - -式1を評価し,その値が偽であれば,式2を評価する.`or'は優先順位が低い別 -名である. - -orの両辺の式では文字列と正規表現リテラルは式「$_=~ リテラル」の省略で -あるとみなされる. - -** 範囲指定式 - - 式1 `...' 式2 - -式1が真になるまでは偽を返し,その後は式2が真を返すまでは真を返す.式2 -が真になれば状態は偽に戻る. - -`...'の両辺の式では文字列と正規表現リテラルは式「$_=~ リテラル」の省略, -整数定数は「$.==定数」の省略と解釈される. - -** NOT 式 - - `!' 式 - -式が真であれば偽,偽であれば真を返す. - -`!'式では文字列と正規表現リテラルは式「$_=~ リテラル」の省略であるとみ -なされる. - - 式1 `!=' 式2 - -「!(式1 == 式2)」の省略形 - - 式1 `!~' 式2 - -「!(式1 ~= 式2)」の省略形 - -** WHILE - - while 式 - 式.. - end - -式を評価した値が真の間,式を繰り返し実行する.while式の値はnilである. - -whileの条件判断部の式では文字列と正規表現リテラルは式「$_=~ リテラル」 -の省略であるとみなされる. - -** WHILE 修飾子 - - 単純式 while 式 - -繰り返し修飾子(while)はまず先行する式を評価してから条件式を評価するの -で,最低一度は式を実行することになる.while修飾子のついた式の値はnilで -ある. - -** イテレータ(繰り返し子) - -イテレータとは制御構造(特にループ)の抽象化のために用いられるメソッドの -一種である.コードの断片(ブロックと呼ばれる)を指定してイテレータを呼び -出すと,イテレータは適当な値をセットしてブロックを評価する(おそらくは -複数回).イテレータからのブロックの呼び出しはyield式を用いる(後述). - -イテレータの呼び出しは以下の構文で行なわれる. - - 式 `{' 左辺式.. `|' 式.. `}' - -「式」をブロックとして設定し,「式」のメソッドをイテレータとして評価す -る.「式」のトップレベルのメソッドだけがイテレータとして呼び出され, -レシーバを表す式や,引数の式はイテレータとしては呼び出されない.「式」 -が複数の式を含む時,各々がイテレータとして順に呼ばれる. - -イテレータ内でyield式が実行されると,そこで指定された値が左辺式で指定さ -れた変数に代入され,ブロックが実行される.ブロックの実行が終了するとそ -の値は yield式の値として返される.あるメソッドがイテレータとして呼び出 -されたかどうかはメソッドiterator_p()の戻り値で知ることができる.中には -Enumerableモジュールのgrepメソッドのようにイテレータとして呼ばれた時と -普通のメソッドとして呼ばれた時とで動作が異なるメソッドもある. - -** FOR - -オブジェクトの各要素に対して操作を行なうための形式も提供されている.形 -式は以下の通り. - - for 左辺式.. in 式 - 式 - end - -式の各要素に対し式を実行する.これは以下の式と等価である. - - (式).each `{' 左辺式.. `|' 式 `}' - -よって式の値のオブジェクトがメソッドeachを持たない場合,forを実行する -と例外が発生する. - -** YIELD - - yield `(' [式 [`,' 式..]]) - yield - -イテレータの中でブロックの呼び出しを行なう.yieldを実行したメソッドが -イテレータとして呼び出されていない時には例外が発生する.yield の値はブ -ロックの戻り値である. - -yieldの引数の括弧は曖昧でない限り省略できる. - -** FAIL - - fail `(' [メッセージ] `)' - -例外を発生させる.メッセージが与えられた場合には発生したソースファイル -名,行番号をシステム変数`$@'に,メッセージを`$!'にセットする. - -failの引数の括弧は省略できる. - -** BEGIN - -複数の式をまとめるためと例外所理のためにbegin式がある.begin式の形式は -以下の通りである. - - begin - 式.. - [resque - 式.. ] - [ensure - 式..] - end - -begin式の値は一番最後に評価された式の値である.begin式の処理中に発生し -た時に例外はresque節で捕獲することが出来る.この場合の値begin式の値は -はresque部で最後に評価した式の値である.更にensure節が存在する時は -begin式を終了する前に必ず(正常終了時だけでなく,例外, return, break, -continue, redoなどによる脱出でも)ensure節の式を評価する. - -** RETRY - - retry - -begin式のresque節で使い,begin式を始めからもう一度実行する.例外処理を -行なってから再試行するのに使う.resque節以外でretryが用いられた場合例 -外が発生する. - -** RETURN - - return [式[`,' 式..]] - -式の値を戻り値としてメソッドの実行を終了する.式が2つ以上与えられた時 -には,それらを要素とする配列をメソッドの戻り値とする.式が一つもない場 -合には nil が戻り値となる. - -** BREAK - - break - -break はループを脱出する.Cと違い,breakはもっとも内側のループを脱出す -る作用だけを持ち,case を抜ける作用は持たない. - -** CONTINUE - - continue - -continueはもっとも内側のループの次の繰り返しを始める. - -** REDO - - redo - -redoはループ条件のチェックを行なわず,現在の繰り返しをやり直す. - -** クラス定義 - -クラスを定義する構式は以下の通りである. - - class クラス名 [`:' スーパークラス名 ] - 定義実体 - end - -クラス名は大文字で始まる識別子である. - -** モジュール定義 - -モジュールを定義する構式は以下の通りである. - - module クラス名 - 定義実体 - end - -モジュール名は大文字で始まる識別子である. - -** メソッド定義 - -通常(特異メソッドでない)メソッド定義の形式は以下の通りである.通常メソッ -ド定義はネストできないので,メソッド定義式中ではメソッド定義式を再び呼 -び出せない. - - def メソッド名 [`(' 引数 [`,' 引数..][`,' `*'引数 ] `)'] - 定義実体 - end - -メソッド名は識別子または文字列である.演算子の再定義をする時には文字列 -で指定する.仮引数並びの最後に`*'がある場合,仮引数より多く与えられた -実引数は,最後の引数に配列として与えられる(足りない時にはエラー). - - -メソッドには呼び出し制限を加えることができ,制限を加えられたメソッドは, -関数形式でしか呼び出せない(privateメソッド). - -新規にメソッドを定義する場合,クラス定義式の外にあるdef式はデフォルト -ではprivateメソッドを定義し,クラス定義式の中にあるdef式はpublicメソッ -ドを定義する.スーパークラスのメソッドを再定義する場合には定義されるメ -ソッドの可視性はスーパークラスのメソッドのものを受け継ぐ. - -メソッドの可視性を変更する場合にはModuleクラスで定義されているpublic, -privateの各メソッドを用いる. - -** 特異メソッド定義 - -メソッド定義にはもう一つ特異メソッドの定義がある.特異メソッドとはある -特定のオブジェクトに固有のメソッドである.形式は以下の通りである. - - def 式 `.' メソッド名 [`(' 引数 [`,' 引数..][`,' `*'引数 ] `)'] - 定義実体 - end - -この形式は式の値であるオブジェクトに特異メソッドを定義する.式の値は -(ビルトインクラスでない)通常オブジェクトか,クラスまたはモジュールであ -る必要がある.通常メソッド定義とは異なり,特異メソッドはメソッド本体内 -でもネストして定義することができる. - -特異メソッドは通常は継承しないが,例外としてクラスの特異メソッドはその -サブクラスにも継承される.言い替えればクラスの特異メソッドは他のオブジェ -クト指向システムにおけるクラスメソッドの働きをする. - -注意: インクルードしたモジュールの特異メソッドは継承しない. - -** ALIAS - -以下の形式でメソッドに別名をつけることができる. - - alias メソッド名1 メソッド名2 - -別名を付けられたメソッドは,その時点でのメソッド定義を引き継ぎ,元のメ -ソッドが再定義されても,再定義前の古いメソッドが呼び出されたのと全く同 -じ働きをする. - -** UNDEF - -メソッドの定義を取り消すためにはundefを用いる. - - undef メソッド名 - -指定したメソッドの定義を取り消す. - -defによる別名定義とundefによる定義取り消しによってクラスのインタフェー -スをスーパークラスと独立に変更することができる.ただし,メソッドがself - にメッセージを送っている場合もあるので,よく注意しないと既存のメソッ -ドが動作しなくなる可能性がある. - -* 組み込み関数 - -Rubyには厳密な意味では関数はないがKernelクラスの関数メソッドは(全ての -通常クラスから関数形式で呼び出せるので),関数的に用いられる.関数的に -用いられるメソッドを以下にあげる.これらのメソッドを再定義する際には互 -換性を考えて行なうべきである. - - _exit(status) - - プログラムの実行を終了する.整数statusを終了ステータスとする. - exit()とは違って,例外処理などは一切行なわない.fork()の後,子 - プロセスを終了させる時などに用いる. - - eof() - - コマンドラインからの入力がEOFに到達している場合,真を返す. - - eval(expr) - - exprとして与えられた文字列をrubyプログラムとして解釈,実行する. - - exec(command) - - 現在実行しているプロセスを終了して,command文字列で指定される - 別プロセスを起動する. - - exit([status]) - - プログラムの実行を終了する.statusとして整数が与えられた場合, - その値をRubyコマンドの終了ステータスとする.デフォルトは0. - - fork() - - forkシステムコールを実行し,子プロセスを生成する.詳細は - fork(2)を参照のこと.親プロセス側では子プロセスのプロセスidを - 返し,子プロセス側ではnilを返す.何らかの原因で子プロセスの生 - 成に失敗した時には例外が発生する. - - format(format, ...) - - フォーマットとして与えられた文字列をC言語のsprintfと同じように - 解釈し,引数を展開した文字列を返す.メソッドsprintf()の別名. - - Rubyにおけるformat指定子の拡張についてはsprintf()の項を参照の - こと. - - getc() - - 標準入力から一文字取り出す.戻り値は読み込んだ文字の文字コード - (ASCII)を表すFixnumである. - - gets() - - 引数として与えられたファイル(なければ標準入力)で構成される仮想 - 的なファイル(システム変数`$<'でアクセスできる)から一行読み込ん - で,読み込みに成功した時にはその文字列を返す.ファイルの終りに - 到達した時にはnilを返す.行の区切りはシステム変数`$/'によって - 変更できる.読み込んだ文字列はシステム変数`$_'にもセットされる. - - gsub(pattern[, replace]) - - システム変数`$_'の指す文字列内で patternにマッチする部分を全て - replaceに置き換える.Stringクラスのgsubメソッドの解説を参照の - こと.引数replaceが省略された時にはイテレータとして動作し,ブ - ロックを評価した結果で置換する. gsubメソッドは`$_'の値を更新 - する. - - iterator_p() - - メソッドがイテレータとして呼び出された時に真,そうでない時に偽 - を返す述語. - - kill(signal, pid..) - - pidで指定されたプロセスにシグナルを送る.シグナルはシグナル番 - 号か名前で指定する.負の値を持つシグナル(あるいはシグナル名の - 前に`-')を与えるとプロセスではなくプロセスグループにシグナルを - 送る. - - load(file) - - fileをロードする.fileをロードするパスはシステム変数$LOAD_PATH - で決定される. - - open(file[, mode]) - - fileをオープンして,Fileオブジェクトを返す.ファイル名はオープ - ンするファイルを示す.ファイル名が`|'で始まる時には続く文字列 - をコマンドとして起動し,パイプラインを生成する. - - コマンド名が"-"である時,open()はRubyの子プロセスを生成し,そ - の子プロセスとのパイプを返す. - - modeはファイルのアクセスモードを指定する.これは以下のうちのい - ずれかの文字列である. - - r 読み込み専用.openするファイルはあらかじめ存在している - 必要がある. - - r+ 読み書き両用.openするファイルはあらかじめ存在している - 必要がある. - - w 書き込み専用.ファイルが存在していた場合,長さを0にす - る.存在していなければ新たにファイルを作成する. - - w+ 読み書き両用.読み込みが行なえること以外は"w"と同じ働き - をする. - - a 追加書き込み専用.ファイルはあらかじめ存在している必要 - がある.書き込みはファイルの最後に追加される. - - a+ 読み書き両用.ファイルが存在していなければ新たに作成す - る.アクセス位置はファイルの最後に初期化される. - - モードが省略された場合のデフォルトは"r"である. - - print(arg1, ..., argn) - - 引数を順に出力する.引数が与えられない時にはレシーバを出力する. - 文字列以外のオブジェクトが引数として与えられた場合には,当該オ - ブジェクトのto_sメソッドによって文字列に変換してから出力される. - システム変数`$;'(出力フィールドセパレータ)にnilでない値がセッ - トされている時には,各引数の間にその文字列を出力する.システム - 変数`$\'(出力フィールドセパレータ)にnil でない値がセットされて - いる時には,最後にそれを出力する. - - printf([port, ]format, arg1, ..., argn) - - C言語のprintf()と同じformatに従い引数を文字列に変換し,出力す - る.第1引数がIOのサブクラスのインスタンスであった場合はそのオ - ブジェクトに対して出力を行なう.デフォルトは$stdoutに出力する. - - Rubyにおけるformat指定子の拡張についてはsprintf()の項を参照の - こと. - - rand(max) - - 0からmaxを越えない範囲の整数の乱数を発生する.戻り値はFixnum. - - require(file) - - fileをロードする.loadとの動作の違いは拡張子を補ってくれる点, - ロードしたファイルのフルパスを変数`$"'に覚えていて既にロードし - たファイルは再ロードしない点とダイナミックローディングに対応し - ている点(OS による)である.実際にロードした時には TRUE,既にロー - ドされている時にはFALSEを返す. - - select(reads[, writes[, execpts[, timeout]]]) - - select(2)を実行する.reads/writes/execptsにはIO(またはそのサブ - クラス)のインスタンスの配列を与える.timeoutはFixnum / Float - / Timeのいずれかで指定する.戻り値はtimeoutが成立した場合には - nil,そうでないときは3要素の配列を返し,その各要素が入力/出力/ - 例外待ちのオブジェクトを要素として持つ. - - sleep([sec]) - - sec秒だけプログラムの実行を停止する.secが省略された場合,プロ - セスにSIGALRMが送られない限り,永久にスリープする.実際にスリー - プした秒数を返す. - - sprintf(format, ...) - - format文字列をC言語のsprintfと同じように解釈し,引数を展開した - 文字列を返す.メソッドformat()の別名. - - - format指定子はC言語のsprintf()が受け付けるもの(ただし,Rubyに - は unsignedがないので,%uは除く)に加えて, %b, %B, %O, %Xを使う - ことができる.%bは数値の2進表示,%B, %O, %Xはそれぞれ2進,8進, - 16進数の表示を行なうが,負の数の処理の際に2の補数表現ではなく, - その絶対値表記の先頭に`-'をつけたものを表示する. - - srand([初期値]) - - 乱数の初期値を設定し,古い初期値を返す.初期値が省略された時に - はtime(3)の返す値をデフォルトとする. - - sub(pattern[, replace]) - - システム変数`$_'の指す文字列で最初にpatternにマッチする部分を - replaceに置き換える.引数replace が省略された時にはイテレータ - として動作し,ブロックを評価した結果で置換する.subメソッドは - `$_'の値を更新する.その他の詳細に関してはStringクラスのsubメ - ソッドの解説を参照のこと. - - syscall(num, arg..) - - numで指定された番号のシステムコールを実行する.第2引数以降をシ - ステムコールの引数として渡す.引数は文字列または整数でなければ - ならない. - - system(command) - - コマンドを実行し,その終了ステータスを返す. - - trap(command, signal..) - - signalの割り込みがかかった時にcommandを実行する.signalはシグ - ナル名かシグナルの番号.commandとして"SIG_IGN"または"IGNORE"を - 指定した時にはそのシグナルを無視する(可能ならば)."SIG_DFL"ま - たは"DEFAULT"を指定した時はデフォルトの動作を行なう. - - wait() - - 子プロセスが終了するのを待ち,終了した子プロセスのpidを返す. - 子プロセスが一つもなければnilを返す. - - waitpid(pid, flags) - - 特定の子プロセスの終了を待ち,そのプロセスが終了した時に真を返 - す.子プロセスが存在しないか,ノンブロッキングモードで子プロセ - スがまだ終了していない時にはnilを返す.waitpid(2)かwait4(2)の - 実装されていないマシンではflagsはいつもnilまたは0でなければな - らない. - -* 組み込み変数と定数 - - $! エラーメッセージ.failで設定する. - - $@ エラーが発生した時点のファイル名と行番号が - - "ファイル:行番号[:メソッド名(あれば)]" - - の形式で格納される. - - $& 最後に成功したパターンマッチ - - $` 最後のパターンマッチでマッチした文字列の前の文字列 - - $' 最後のパターンマッチでマッチした文字列の後に続く文字列 - - $+ 最後の検索パターンでマッチした最後の括弧 - - $1..$9 最後に成功したパターンマッチでn番目の括弧にマッチした - 値が格納される.該当する括弧がなければnilが入っている. - - $~ 最後のマッチに関する情報.これをセットすると$&や$1..$9 - の値が変化する. - - $= この変数の値がnilでない時,パターンマッチや文字列の比 - 較でアルファベットの大文字小文字を区別しない.デフォル - トはnil(区別する). - - $/ 入力レコードセパレータ.ファイルや文字列に対してeachを - 行なう時の分割文字を指定する.$/に空文字列("")を指定す - ると段落単位で入力を行ない,nilを指定すると全体を一度 - に読み込む.$/には正規表現は使えない.デフォルトは - "\n". - - $\ 出力レコードセパレータ.この変数に文字列を指定すると - write()やprint()の度に最後にこの文字列を付加して出力す - る.デフォルトはnil(なにも追加しない). - - $, Array:join()のデフォルトの区切り文字列.print()の各引 - 数の間に出力される文字列. - - $; String:split()のデフォルトの区切り文字. - - $. 最後に読んだ入力ファイルの行番号. - - $< 引数(なければ標準入力)で構成される仮想ファイル.つまり - gets()は$<.gets()と同じ意味である.$<.fileで現在読み込 - み中のファイルオブジェクトが,$<.filenameでそのファイ - ル名が得られる.(覚え方: `<'はシェルの入力元指定) - - $> printやprintfのデフォルトの出力先.初期値は$stdout.-i - オプションを指定した場合には読み込み元と同じ名前のファ - イル.(覚え方: `>'はシェルの出力先指定) - - $_ 最後にgets()などで読み込んだ文字列. - - $0 rubyスクリプトの名前.この変数に代入するとps(1)の出力 - が変化する. - - $* rubyスクリプトに与えられた引数.ruby自身に対する引数は - 取り除かれている. - - $$ 現在実行中のrubyプロセスのpid. - - $? 最後に実行した子プロセスのstatus. - - $: ファイルをロードする時に検索するディレクトリへのパスを - 含む配列.起動時にはデフォルト値(コンパイル時に指定す - る)に加えて,環境変数RUBYLIBの値とruby起動時の-Iオプショ - ンで指定された値が追加される.(覚え方: コロンは環境変 - 数PATHの区切り文字である) - - $" 既にロードされたファイル名を含む配列.require()で同じ - ファイルを2回ロードしないために用いられる.(覚え方: - prevent files to be doubly quoted(loaded)) - - $ARGF $<の別名. - - $ARGV $*の別名. - - $DEBUG `-d'フラグの状態(真偽値). - - $FILENAME 仮想ファイル`$<'で現在読み込み中の(メソッドgets()が今 - 読んでいる)ファイル名.$<.filenameと同じ. - - $LOAD_PATH $:の別名. - - $stdin 標準入力 - $stdout 標準出力 - $stderr 標準エラー出力 - - $VERBOSE `-v'フラグの状態(真偽値) - - TRUE t - FALSE nil - - それぞれ真偽値を表す.条件判断はnilを偽,それ以外の全ての値を - 真として判断するため,TRUEの値は代表的な真の値という以上の意味 - を持たない.よって,あるメソッドの返値が真であるということと, - それがTRUEを返すということは厳密には同じではない(述語的に用い - られるメソッドは大抵真の値としてTRUEを返すようにはなっているが). - つまり - - if some.method() then ... else ... end - - と - - if some.method() == TRUE then ... else ... end - - は完全には同義ではない.FALSEに関しては,このような問題は生じ - ない. - - STDIN 標準入力 - STDOUT 標準出力 - STDERR 標準エラー出力 - - ENV 環境変数にアクセスする連想配列.文字列をキーとして与え - ると対応する環境変数の値が得られる.環境変数が存在しな - い場合はnilが返る. - - KCODE 対応している漢字コードを表す文字列."EUC","SJIS"または - "NONE". - - VERSION rubyのバージョンを示す文字列 - -* 組み込みクラスとモジュール - -** Array(クラス) - -数字を添字とした配列のクラスである.生成は一般的には配列式``[..]''で -行なわれる. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self[nth] - self[start..end] - self[start, length] - - 配列の要素にアクセスする.最初の形式では配列のnth番目の要素を - 返し,2番目の形式ではstart番目の要素からend番目の要素を含む部 - 分配列を返す.3番目の形式ではstart番目からlength個の要素を含む - 部分配列を返す. - - self[nth] = val - self[start..end] = val - self[start, length] = val - - 配列の要素を変更する.最初の形式では配列のnth番目の要素をvalに - 変更する.2番目の形式はstart番目の要素からend番目の要素までを - valに変更する.3番目の形式ではstart番目からlength個の要素をval - に変更する. - - 2番目,3番目の形式ではvalは配列でなければならない. - - 例: - - ary = [1, 2, 3, 4, 5] - ary[0..2] = [0, 0] # 配列の内容は [0, 0, 4, 5] - ary[1, 0] = [7] # 配列の内容は [0, 7, 0, 6, 5] - - self + other - - 配列の連結.selfとotherの両方の配列の内容を繋げた新しい配列を - 返す. - - self * times - - 配列の繰り返し. - - self - other - - 集合の差演算.selfからotherの要素を取り除いた内容の新しい配列 - を返す.重複する要素は1度だけ現れる. - - self * other - - 集合の積演算.両方の配列に含まれる要素からなる新しい配列を返す. - 重複する要素は1度だけ現れる. - - self | other - - 集合の和演算.両方の配列にいずれかに含まれる要素を全て含む新し - い配列を返す.重複する要素は1度だけ現れる. - - self << obj - - objを配列の末尾に追加する.selfを返すのでC++的に連鎖できる. - - assoc(key) - - 連想リスト(assocペアを要素とする配列)を検索し,第1要素がkeyと - 等しい ("=="で比較する)配列を返す. - - clear - - 配列の大きさを0にする. - - delete(val) - - valと一致する要素を削除する. - - delete_if - - 要素を削除するイテレータ.ブロックを評価した値が真の時,対応す - る要素を配列から削除する. - - each - - 配列の各要素を順に与えるイテレータ. - - fill(val) - fill(val, start[, length]) - fill(val, start..end) - - 配列(の指定された部分)の要素の値をvalに設定する.2番めの形式で - lengthが省略された時は配列の終りまでの長さをとる.指定された部 - 分配列が元の配列の範囲を越える時は自動的に拡張される. - - index(val) - - valと等しい最初の要素のインデックスを返す.該当する要素が存在 - しない場合はnilを返す. - - indexes(ary) - indexes(index-1, ..., index-n) - - 1番目の形式では整数の配列を引数として受けて,その要素をインデッ - クスとする要素を含む配列を返す.2番目の形式では各引数の値をイ - ンデックスとする要素を含む配列を返す. - - join([sep]) - - 配列の要素を連結した文字列を返す.各要素は文字列に変換され,間 - にsepを挟んで連結される.sepが省略された時にはシステム変数`$,' - の値が用いられる. - - length - size - - 配列の長さ(要素数)を返す. - - push(obj) - - objを配列の末尾に追加する. - - pack(template) - - 配列の内容をtemplate文字列にしたがって,1つの文字列にパックす - る.パックした文字列を返す.テンプレートは型指定文字列とその長 - さ(省略時は1)を並べたものである.長さとして`*'が指定された時は - 「残りのデータ全て」の長さを表す. - - 型指定文字は以下のものがある. - - a ASCII文字列(null文字を詰める) - A ASCII文字列(スペースを詰める) - b ビットストリング(下位ビットから上位ビット) - B ビットストリング(上位ビットから下位ビット) - h 16進文字列(下位ニブルが先) - H 16進文字列(上位ニブルが先) - c char - C unsigned char - s sort - S unsigned sort - i int - I unsigned int - l long - L unsigned int - n ネットワークバイトオーダーのshort - N ネットワークバイトオーダーのlong - f 単精度浮動小数点数(機種依存) - d 倍精度浮動小数点数(機種依存) - x ナルバイト - X 1バイト後退 - @ 絶対位置への移動 - - pop - - 配列の末尾の要素を取り除いて,それを返す. - - rassoc(value) - - 連想リスト(2要素の配列を要素とする配列)を検索し,第2要素が - valueと等しい("=="で比較する)配列を返す. - - shift - - 配列の先頭の要素を取り除いて,それを返す. - - sort - - 配列の内容をソートする.イテレータとして呼び出された場合はブロッ - クを評価した値で要素の大小を決定する.大きい時に正,等しい時に - 0,小さき時に負.通常のメソッドとして呼び出された場合は各要素 - を`<=>'で比較する. - - to_a - - 自分自身を返す.対称性のために用意されているメソッドであまり面 - 白くない. - - unshift(obj) - - objを配列の先頭に追加する. - -Single Methods: - - Array[item..] - - 引数を要素とする配列を生成する. - -** Assoc(クラス) - -データの組(ペア)を表現するクラス.通常,生成は`::'演算子を用いて行なわ -れる. - -SuperClass: Object - -Methods: - - car - - CONSペアのデータのCAR部を返す. - - car=(val) - - CONSペアのCAR部のデータを変更する. - - cdr - - CONSペアのデータのCDR部を返す. - - cdr=(val) - - CONSペアのCDR部のデータを変更する. - -** Bignum(クラス) - -無限多倍長整数のクラス.演算の結果がこのFixnumの範囲内である場合には自 -動的にクラスはFixnumに変換される.一般的にRubyプログラムではFixnumと -Bignumの変換は暗黙のうちに行われるので,意識する必要は無い.Floatとの -混合に関しては,Bignumより Floatの方がgenericityが高いのにも関わらず, -Bignumの方が,大きな値を表現できるので,変換時に桁落ちが生じる可能性が -ある. - - -SuperClass: Integer - -Methods: - - self + other - self - other - self * other - self / other - self % other - self ** other - - 算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. - - ~ self - self | other - self & other - self ^ other - - ビット演算.それぞれビット反転,論理和,論理積,排他的論理和を - 返す. - - self << bits - self >> bits - - シフト演算.それぞれbitsビットだけ左右にビットシフトを行なう. - - divmod(other) - - 商と剰余の2要素の配列を返す. - -** Block(クラス) - -イテレータに渡される手続きをまとめたオブジェクト.実行するコードだけで -なくコンテキスト(ローカル変数)なども保存する. - -SuperClass: Object - -Methods: - - call(arg[,..]) - - ブロックを実行する. - -Single Methods: - - new - - 新しいブロックを生成する.yieldを実行できる場所でこのメソッド - が呼ばれると,その時点で実行されるべきコードを包み込んだオブジェ - クト(Block)を生成する. - -** Class(クラス) - -クラスのクラス.より厳密に説明するとクラスは特異メソッドを継承するため -に,それぞれメタクラスと呼ばれる名前のないクラスをクラスとして持ち, -Classはそのメタクラスのクラスである(分かったかな?).が,この解説が理解 -できなくても,Rubyを使うことに何の支障もない.クラスには特異メソッドを -定義できる事と,スーパークラスで定義された特異メソッドはそのサブクラス -でも有効である事を知れば十分である. - -SuperClass: Module - -Private Methods: - - attr(name[, public]) - - そのクラスのインスタンスに対してnameで指定される属性を定義する. - 詳しくはModuleのattrメソッドの項を参照のこと. - -Methods: - - new(..) - - クラスのインスタンスを生成する.多くの場合このメソッドはサブク - ラスの特異メソッドによってオーバーライドされ,クラスによって引 - 数が異なる. - -** Comparable(モジュール) - -比較演算を許すクラスのためのMixin.このモジュールをインクルードするこ -とによって,`<=>'演算子を定義するだけで他の演算子はその定義を利用して -派生できる. - -Methods: - - self == other - - selfがotherと等しい時真を返す. - - self > other - - selfがotherより大きい時真を返す. - - self >= other - - selfがotherより大きいか等しい時真を返す. - - self < other - - selfがotherより小さい時真を返す. - - self <= other - - selfがotherより小さいか等しい時真を返す. - - between(min, max) - - selfがminとmaxの範囲内にある時真を返す. - -** Dir(クラス) - -ディレクトリ内の要素を順に返すディレクトリストリーム操作のためのクラス. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - close - - ディレクトリストリームをクローズする.以後の操作は例外を発生さ - せる. - - each - - ディレクトリ内の各要素を順に与えるイテレータ. - - getwd - pwd - - カレントディレクトリを返す. - - rewind - - ディレクトリストリームを先頭にリセットする. - - seek(pos) - - ディレクトリストリームの位置をposに設定する. - - tell - - ディレクトリストリームの現在の位置を返す. - -Single Methods: - - self[pat] - glob(pat) - - 文字列patをsh形式のワイルドカードとして展開した結果を文字列の - 配列として返す.書式は以下の通りである. - - * 任意の文字列(空文字列を含む)と一致 - ? 任意の1文字と一致 - [ ] []内のいずれか1文字と一致 - {..} {}内の(コンマで区切られた)いずれかの文字列と - 一致 - - chdir(path) - - カレントディレクトリをpathに変更する. - - chroot(path) - - プロセスのルートディレクトリを変更する,同名のシステムコールと - 同じ働きをする.この操作は実効uidがスーパユーザである時だけに - 制限されている.ルートディレクトリを元に戻す(ルートディレクト - リを上方に変更する)方法は提供されていない. - - mkdir(path[, mode]) - - modeで指定されたモードを持つディレクトリpathを作成する.モード - はumaskによって修正される.modeのデフォルト値は0777. - - open(path) - - pathに対するディレクトリストリームをオープンする. - - rmdir(path) - - pathで指定されたディレクトリを削除する.ディレクトリは空である - 必要がある. - -** Enumerable(モジュール) - -要素に対する繰り返しを行なうクラスのためのMixin.このモジュールをイン -クルードするためには,メソッド`each'を定義する必要がある. - -Methods: - - collect - - 各要素に対してブロックを評価した結果を全て含む配列を返す - - find - - 要素に対してブロックを評価した値が真になった最初の要素を返す. - - find_all - - 各要素に対してブロックを評価した値が真であった要素を全て含む配 - 列を返す. - - grep(pattern) - - 「要素 =~ pattern」が成立する全ての要素を含む配列を返す.イテ - レータとして用いられた時は上記の条件の成立した要素に対してブロッ - クを実行する. - - includes(val) - - valと`=='の関係にある要素を持つ時,真を返す. - - index(val) - - valと`=='の関係にあるオブジェクトが何番目に現れたかを返す.一 - 番最初の要素が0になる.要素が存在しない時にはnilを返す.順序の - ないクラスに対してはあまり意味がない. - - length - - 要素の数を返す. - - min - - 最小の要素を返す.全ての要素がお互いに`<=>'メソッドで比較でき - ることを仮定している. - - max - - 最大の要素を返す.各要素が`<=>'メソッドで比較できることを仮定 - している. - - reverse - - 全ての要素を逆順に並べた配列を返す. - - sort - - 全ての要素をソートした配列を返す. - -** Etc(モジュール) - -/etcディレクトリ以下の情報を得るためのモジュール.クラスにインクルード -して使うこともできる. - -Methods: -Single Methods: - - getlogin - - 自分のlogin名を返す.これが失敗した場合はgetpwuid()を用いると - 良い. - - getpwnam(name) - - /etc/passwdファイル(あるいはDBMファイルやNISデータベース)を検 - 索し,nameの名前を持つpasswdエントリを返す.戻り値はpasswd構造 - 体で以下のメンバを持つ. - - struct passwd - name # ユーザ名(文字列) - passwd # パスワード(文字列) - uid # ユーザID(整数) - gid # グループID(整数) - gecos # gecosフィールド(文字列) - dir # ホームディレクトリ(文字列) - shell # ログインシェル(文字列) - # 以降のメンバはシステムによっては提供されない. - change # パスワード変更時間(整数) - quota # クォータ(整数) - age # エージ(整数) - class # ユーザアクセスクラス(文字列) - comment # コメント(文字列) - expire # アカウント有効期限(整数) - end - - 詳細はgetpwnam(3)を参照のこと. - - getpwuid([uid]) - - uidをユーザIDとするpasswdエントリを返す.戻り値はgetpwnam()と - 同様である.引数を省略した場合にはgetuid()の値を用いる.詳細は - getpwuid(3)を参照のこと. - - getgrgid(gid) - - /etc/groupファイル(あるいは…getpwnam参照)を検索し,gidをグルー - プIDとするグループエントリを返す.戻り値はgroup構造体で以下の - メンバを持つ. - - struct group - name # グループ名(文字列) - passwd # グループのパスワード(文字列) - gid # グループID(整数) - mem # グループメンバ名の配列 - end - - 詳細はgetgrgid(3)を参照のこと. - - getgrnam(name) - - nameという名前のグループエントリを返す.戻り値はgetgrgid()と同 - 様である.詳細はgetgrnam(3)を参照. - - group - - 全てのグループエントリを順にアクセスするためのイテレータ. - - passwd - - 全てのpasswdエントリを順にアクセスするためのイテレータ. - -** File(クラス) - -ファイルアクセスのためのクラス.メソッドopen()で生成される.また,この -クラスの特異メソッドとしてtestのファイルテスト演算子相当のメソッドが定 -義されている(FileTestモジュールのメソッド郡). - -SuperClass: IO - -Methods: - - atime - - ファイルの最終アクセス時刻を返す. - - ctime - - ファイルの最終ステータス変更時刻を返す. - - chmod(mode) - - ファイルのパーミッションを変更する(cf chmod(2)). - - chown(owner, group) - - ファイルの所有者とグループを変更する(cf chown(2)).nilか-1を - 指定することによって所有者やグループを現在のまま変えないでおく - ことができる. - - eof - - ファイルの終端に到達した時に真を返す. - - lstat - - ファイルに関するStat構造体を返す.lstatはファイルがシンボリッ - クリンクであればリンクそのものに関するStat構造体を返す.構造体 - の内容についてはstatを参照のこと. - - mtime - - ファイルの最終修正時刻を返す. - - rewind - - ファイルのファイルポインタの位置を先頭に移動する. - - path - - ファイルのパス名を返す. - - seek(offset, ptrname) - - ファイルのファイルポインタの位置をoffsetに移動する.ptrnameは - 0,1,2のいずれかであって,それぞれファイルの先頭,現在位置, - ファイルの終端のうちのいずれかからの相対を示す. - - stat - - ファイルに関するStat構造体を返す(Struct を参照). - - struct stat - dev # ファイルの存在するデバイス - ino # ファイルのi-node番号 - mode # モード - nlink # ハードリンクの数 - uid # 所有者のユーザID - gid # 所有者のグループID - rdev # デバイスのID(スペシャルファイルのみ) - size # ファイルサイズ(byte数) - blksize # ファイルシステムにおいて適切なブロックサイズ - blocks # ブロック数 - atime # 最終アクセス時間 - mtime # 最終更新時間 - ctime # 最終状態変更時間 - end - - 詳細な説明はfstat(2)を参照のこと.システム上で定義されている - stat構造体に該当するメンバがない場合は0が設定されている. - - tell - - ファイルの現在のファイルポインタの位置を返す. - - truncate(length) - - ファイルを切り捨てて最大lengthバイトにする.ファイルはwriteモー - ドでオープンされていなければならない. - -Single Methods: - - atime(filename) - - filenameの最終アクセス時刻を返す. - - ctime(filename) - - filenameの最終ステータス変更時刻を返す. - - chmod(mode, path, file..) - - ファイルのパーミッションを変更する(cf chmod(2)).変更したファ - イル数を返す.fileにはワイルドカードも許す. - - chown(owner, group, file..) - - ファイルの所有者とグループを変更する(cf chown(2)).nilか-1を指 - 定することによって所有者やグループを現在のまま変えないでおくこ - とができる.変更したファイル数を返す.fileにはワイルドカードも - 許す. - - link(old, new) - - oldへのハードリンクnewを生成する.link(2)と同じ制限がある. - - mtime(filename) - - filenameの最終修正時刻を返す. - - readlink(path) - - シンボリックリンクpathの内容を文字列として返す. - - rename(from, to) - - ファイル名fromをtoに変更する.rename(2)参照.既にtoという名前 - のファイルが存在する時にはまずそのファイルが削除される. - - stat(filename) - - filenameのファイルのStat構造体を返す. - - symlink(old, new) - - oldへのシンボリックリンクnewを生成する. - - truncate(path, length) - - pathで指定されたファイルを切り捨てて最大lengthバイトにする. - - type(filename) - - filenameのファイルのタイプを表す文字列を返す.文字列は"file", - "directory","characterSpecial","blockSpecial","fifo", - "link","socket"のうちのいずれか一つである. - - unlink(file..) - - ファイルを削除する.ディレクトリの削除にはDir.rmdirを使うこと. - fileにはワイルドカードも許す. - - utime(atime, mtime, file..) - - ファイルのアクセス時刻をatimeに,修正時刻をmtimeに設定する. - atime,mtimeは数またはTimeクラスのインスタンスでなければならな - い.fileにはワイルドカードも許す. - - - これ以外にFileTestモジュールのメソッドも特異メソッドとして持つ. - -** FileTest(モジュール) - -ファイルテスト用メソッドを集めたモジュール.インクルードして用いること -もできる. - -Methods: -Single Methods: - - b(filename) - - filenameのファイルがブロックスペシャルファイルである時,真を返 - す. - - c(filename) - - filenameのファイルがキャラクタスペシャルファイルである時,真を - 返す. - - executable(filename) - x(filename) - - filenameのファイルが実行可能の時,真を返す. - - exists(filename) - e(filename) - a(filename) - - filenameのファイルが存在する時,真を返す. - - G(filename) - - filenameのファイルのgidが実効グループのgidと同じ時,真を返す. - - isdirectory(filename) - d(filename) - - filenameがディレクトリの時,真を返す. - - isfile(filename) - f(filename) - - filenameのファイルが通常ファイルの時,真を返す. - - islink(filename) - l(filename) - - filenameのファイルがシンボリックリンクである時,真を返す. - - ispipe(filename) - p(filename) - - filenameのファイルが名前つきパイプ(FIFO)である時,真を返す. - - issocket(filename) - S(filename) - - filenameのファイルがソケットである時,真を返す. - - owned(filename) - O(filename) - - filenameのファイルを実効ユーザが所有している時,真を返す. - - readable(filename) - r(filename) - - filenameのファイルを読みとり可能の時,真を返す. - - R(filename) - - filenameのファイルを実uid/gidで読みとり可能の時,真を返す. - - setuid(filename) - u(filename) - - filenameのファイルのsetuidビットがセットされている時,真を返す. - - setuid(filename) - g(filename) - - filenameのファイルのsetuidビットがセットされている時,真を返す. - - size(filename) - s(filename) - - filenameのファイルが存在する時,ファイルの大きさを返す.存在し - ない時はnilを返す. - - sticky(filename) - g(filename) - - filenameのファイルのstickyビットがセットされている時,真を返す. - - writable(filename) - w(filename) - - filenameのファイルが実uid/gidで書き込み可能の時,真を返す. - - W(filename) - - filenameのファイルが書き込み可能の時,真を返す. - - z(filename) - - filenameのファイルが存在し,大きさが0である時,真を返す. - -** Fixnum(クラス) - -31bit(マシンのlongの長さ-1 bit)整数のクラス.builtin classである.この -クラスはpointer内の即値であるためcall by valueで呼び出される点が特徴的 -である(他のクラスはcall by reference).演算の結果が31bitを越える場合に -は自動的にBignum(無限多倍長整数)に拡張される. - -イテレータupto(),downto(),step()は繰り返しのために用いられ,一般に -Rangeクラスを用いるより高速である. - -SuperClass: Integer - -Methods: - - self + other - self - other - self * other - self / other - self % other - self ** other - - 算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. - - ~ self - self | other - self & other - self ^ other - - ビット演算.それぞれビット反転,論理和,論理積,排他的論理和を - 返す. - - self << bits - self >> bits - - シフト演算.それぞれbitsビットだけ左右にビットシフトを行なう. - - self .. max - - 範囲指定.self から maxまでの範囲オブジェクトを返す. - - downto(min) - - イテレータ.selfからminまで下向きに繰り返す. - - id2name - - 整数値をIDだとみなして,相当する文字列を返す.相当する文字列が - 存在しない場合はnilを返す. - - step(max, step) - - イテレータ.selfからmaxまでstepずつ変化しながら,繰り返す. - - to_f - - selfをFloatに変換したものを返す. - - to_i - - selfをそのまま返す. - - upto(max) - - イテレータ.selfからmaxまで繰り返す. - -** Float(クラス) - - 浮動小数点数のクラス. - -SuperClass: Numeric - -Methods: - - self + other - self - other - self * other - self / other - self % other - self ** other - - 算術演算.それぞれ和,差,積,商,剰余,冪乗を返す. - - self == other - self > other - - 比較演算. - - coerce(num) - - numをfloatに変換する.ただし現時点でFloatが理解できる他の数は - FixnumとBignumだけである. - - to_f - - selfをそのまま返す. - - to_i - - selfを整数に変換した結果を返す. - -Single Methods: - - new(float) - - floatと同じ値を持つ新しいFloatオブジェクトを返す. - -** GC(モジュール) - -Ruby組み込みのgarbage collectorの制御を行なうためのモジュール.このモ -ジュールのメソッドをを用いることによって,一時的にGCを止めたり,GCの起 -きるタイミングを制御したりできる. - -Methods: - - garbage_collect - - GCを開始する.「GC.start」と同義. - -Single Methods: - - disable - - GCを禁止する. - - enable - - GCを許可する. - - start - - GCを開始する. - -** Glob(クラス) - -ワイルドカードのクラス.ワイルドカードのリテラルは<..>という形式であ -る.正規表現とほぼ同じように使えるが,こちらは機能が少ない.ただし,ワ -イルドカードの展開機能がある. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self =~ string - - ワイルドカードが文字列にマッチした場合には真を,しない場合は - nilを返す. - - each - - ワイルドカードにマッチするファイル名を与えるイテレータ. - -Single Methods: - - new(string) - - 文字列をワイルドカードに変換したオブジェクトを返す. - -** Hash(クラス) - -連想配列あるいはハッシュ表.任意のオブジェクトを添字とできる配列のクラ -スである.連想配列オブジェクトの生成は一般的には連想配列式 - - ``{a=>b,..}'' - -で行なわれる. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self [key] - - keyをキーとする値を返す. - - self [key]= value - - keyをキーとして,valueを格納する.valueとしてnilを指定するとそ - のkeyに対する項目の削除となる.つまり,Hashは値としてnilを持つ - ことはできない. - - clear - - 連想配列を空にする. - - delete(key) - - keyをキーとする組を削除する. - - delete_if - - 要素を削除するイテレータ.key::valueというペアを与えて,ブロッ - クを評価した値が真の時,該当する項目を削除する. - - each - each_pair - - key::valueなるペアを与えるイテレータ. - - each_key - - 全てのkeyに対して繰り返すイテレータ. - - each_value - - 全てのvalueに対して繰り返すイテレータ. - - has_key(key) - includes(key) - - keyが連想配列中に存在する時,真を返す - - has_value(value) - - valueを値とする組が連想配列中に存在する時,真を返す - - indexes(ary) - indexes(key-1, ..., key-n) - - 1番目の形式では配列を引数として受けて,その要素をキーとする要 - 素を含む配列を返す.2番目の形式では各引数の値をキーとする要素 - を含む配列を返す. - - keys - - 連想配列中に存在するキー全てを含む配列を返す. - - length - size - - 連想配列中の要素の数を返す. - - shift - - 連想配列中の要素を一つ取り出し(削除して),key::valueなるペアを - 返す. - - to_a - - 連想配列中のkey-valueペアを要素とする配列を返す. - - values - - 連想配列中に存在する値全てを含む配列を返す. - -Single Methods: - - Hash[key, value..] - - 奇数番目の引数をkey,偶数番目の引数をvalueとする連想配列を生成 - する. - - new - - 新しい(空の)連想配列オブジェクトを返す. - -** Integer(クラス) - -整数クラス.実際はその大きさによってFixnumとBignumいう二つのサブクラス -で実現されている.Integerはそれらのスーパークラスとなる抽象クラスであ -る.Rubyではほとんどの場合,FixnumとBignumの区別は必要なく,相互の変換 -は自動的に行なわれる.整数をビット列だとみなす場合には,無限の長さをも -つビット列と考えて構わない. - -SuperClass: Numeric - -Methods: - - self[idx] - - 整数のidxビット目がセットされていれば1,セットされていなければ - 0を返す. - - chr - - その数をコードとする文字だけを含む1文字の文字列を返す.一般に - 長さ1以上の文字列について,次の関係が常に成立する. - - str[0].chr == str[0,1] - - 整数が文字の範囲内(0..255)になければ例外が発生する. - - is_integer - - いつも真を返す. - -** IO(クラス) - -入出力のための基本クラス. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self << object - - objectを出力する.objectが文字列でない時にはメソッドto_sを用い - て文字列に変換する.selfを戻り値とするので,C++のような`<<'の - 連鎖を使える. - - 例: - - $stdout << 1 << " is a " << Fixnum << "\n" - - close - - 入出力ポートをクローズする.以後のこのオブジェクトに対する入出 - 力操作はエラーになる. - - each - - 一行ずつ読み込んでくるためのイテレータ.行の区切りはシステム変 - 数`$/'によって変更できる.読み込んだ文字列はシステム変数`$_'に - もセットされる. - - each_byte() - - 一文字ずつ読み込んでくるためのイテレータ.文字は文字コードを表 - すFixnumである. - - fileno - to_i - - IOオブジェクトが使っているファイルディスクリプタ(Fixnum)を返す. - - flush - - バッファをフラッシュする. - - getc - - 一行読み込んで,読み込みに成功した時にはその文字列を返す.ファ - イルの終りに到達した時にはnilを返す.カーネルメソッドgetc()は - $stdin.getcと同じ意味である. - - gets - - 一行読み込んで,読み込みに成功した時にはその文字列を返す.ファ - イルの終りに到達した時にはnilを返す. - - isatty - - 入出力ポートがttyである時,真を返す. - - puts(obj) - - objを出力する.「self << obj」と同じ意味である. - - read([length]) - - lengthバイト読み込んで,その文字列を返す.lengthが省略された時 - には,全てのデータを読み込む. - - readlines - - ファイルを全て読み込んで各行を要素としてもつ配列を返す. - - sync - - 現在の出力同期モードを真偽値で返す.同期モードが真の時は出力関 - 数の呼出毎にバッファがフラッシュされる. - - sync=(newstate) - - 出力同期モードを設定する. - - sysread(length) - - stdioを経由せずにread(2)を用いて入力を行なう.入力されたデータ - を含む文字列を返す.ファイルの終りに到達した時にはnilを返す. - read(2)の性質により必ずlengthバイトの文字列が読み込まれるわけ - ではない.gets()やgetc()などstdioを経由するメソッドと混用する - ことはバッファリングの不整合などで思わぬ動作をすることがある. - - syswrite(str) - - stdioを経由せずに,write(2)を用いて出力を行なう.このメソッド - はバッファリングなどstdioがしてくれることは一切行なわない. - syswriteは実際に書き込んだバイト数を返す.print()やprintf()と - syswrite()を混用するのは推奨できない. - - write(str) - - strを出力する.出力したバイト数を返す. - -** Kernel(クラス) - -全てのクラスの基底クラス.Ruby組み込みの全ての関数メソッドはこのクラス -で定義されている.関数メソッドについては「関数」の項目を参照のこと. - -SuperClass: なし - -Methods: - - ! self - - 否定.Non-nilのオブジェクトの場合常に偽(nil)を返す.このメソッ - ドはNilクラスでは再定義され真を返す. - - self == other - equal(other) - - オブジェクトの一致判定.レシーバと引数の引数が一致する時,真を - 返す.Kernelクラスの定義では双方のオブジェクトが同一の時真を返 - す."=="メソッドは各オブジェクトの性質に応じて再定義する必要が - ある."=="メソッドを再定義した時には,hashメソッドもそれに合わ - せて再定義する必要がある. - - equal()メソッドは"=="メソッドの別名で,"=="を再定義した後でも - オブジェクトの同一性判定を行なうために用いられる.よって - equal()メソッドはサブクラスで再定義するべきではない. - - self != other - - "=="の否定.内部で"=="メソッドを呼び出しているので,こちらは再 - 定義する必要はない. - - self =~ other - - マッチ.デフォルトの動作は"=="と同じである."=~"はcaseの比較に - も用いられる. - - !~ - - "=~"の否定.内部で"=~"メソッドを呼び出しているので,こちらは再 - 定義する必要はない. - - self :: other - - selfとotherを要素とするassocペアを返す.この演算子は右結合であ - るので,a::b::c は (a::(b::c)) と解釈される. - - is_nil - - オブジェクトがnilであるかどうか.Kernelクラスの定義では真を返 - す.Nilクラスで再定義されている. - - id - - 各オブジェクトに対して一意のFixnumを返す.が,Fixnumは自分自身を返 - すので,idが一致しても同じオブジェクトであることは保証されない. - つまり, - - obj1.id == obj2.id - - が成立しても,どちらかがFixnumであれば,obj1とobj2が同じである - とは限らない.ただし,両方がFixnumでないことが保証できれば,2 - つのオブジェクトが同一であることは確実である. - - hash - - オブジェクトのハッシュ値(Fixnum)を返す.Hashクラスでキーとなる - オブジェクトを格納するのに用いられている.「A == B」が成立する - 時は必ず「A.hash == B.hash」が成立する必要があるので,"=="を再 - 定義した時には必ずこちらもそれに合わせて再定義すること. - - -** Math(モジュール) - -浮動小数点演算をサポートするクラス.Mathモジュールは同じ定義のメソッド -と特異メソッドとの両方が定義されているので,特異メソッドを呼び出して使 -う使い方と,クラスにインクルードして使う使い方との両方ができる. - - 例: - - pi = Math.atan2(1, 1) * 4; - include Math - pi2 = atan2(1, 1) - -Methods: -Single Methods: - - atan2(x, y) - - π〜-πの範囲でX/Yのアークタンジェントを返す. - - cos(x) - sin(x) - tan(x) - - ラジアンで表されたxの三角関数の値を返す. - - exp(x) - - xの指数関数の値を返す. - - log(x) - - xの自然対数を返す. - - log10(x) - - xの常用対数を返す. - - sqrt(x) - - xの平方根を返す.xの値が負である時には例外を発生させる. - - cbrt(x) - - xの立方根を返す. - -** Module(クラス) - -モジュールのクラス. - -SuperClass: Object - -Private Methods: - - attr(name[, public]) - - そのモジュールをインクルードしたクラスのインスタンスに対して - nameで指定される属性を付加し,属性に対するアクセスメソッドを定 - 義する.attr("attr")はクラス定義に以下に示すコードを追加するの - とほぼ同義である. - - def attr; @attr; end - - 省略可能な第2引数publicが与えられて,かつその値がnilでない時に - はその属性には属性設定メソッドも用意され,外部から代入可能にな - る.attr("attr", TRUE)はクラス定義に以下のコードを追加するの - とほぼ同義である. - - def attr; @attr; end - def attr=(val); @attr = val; end - - 属性を構成するメソッドを再定義することによって,アクセス時の動 - 作を変更できる.例えば - - attr("test", TRUE) - def test=(val) - print("test was ", @test, "\n") - print("and now is ", @test = val, "\n") - end - - のように設定時に属性の値を表示するようなことが可能である.attr - はアクセスメソッドがすでに定義されている場合は,デフォルトのア - クセスメソッドを定義しない. - -Methods: - - include(module[, module..]) - - 引数で指定したモジュールをインクルードして,メソッド,定数を追 - 加する.クラス,モジュールに別のモジュールをインクルードするこ - とによって,限定された多重継承(Mixin)を実現できる. - - to_s - - モジュールの文字列表現を返す.モジュールの場合の文字列表現はモ - ジュール名である. - - private(name[, name..]) - - nameで指定されたメソッドを関数形式でだけ呼び出し可能にする.す - でにprivateメソッドである場合には何もしない. - - public(name[, name..]) - - nameで指定されたメソッドを通常形式で呼び出し可能にする.すでに - publicメソッドである場合には何もしない. - -** Nil(クラス) - -偽を表すオブジェクトnilのクラス.偽変数(の値)nilはNilクラスの唯一のイ -ンスタンスである. - -SuperClass: Kernel - -Methods: - - self + other - - otherが整数,浮動小数点数,文字列,配列である時,otherを返す. - - ! self - - 常に真を返す. - - is_nil - - 常に真を返す. - -** Numeric(クラス) - -数一般の性質を表す抽象クラス. - -SuperClass: Object - -Included Modules: Comparable - -Methods: - - + self - - オブジェクトselfそのものを返す - - - self - - 「0 - self」の値を返す.サブクラスでより効率的に再定義されるこ - とが期待される. - - abs - - 絶対値を返す. - - divmod(other) - - 商と剰余のペアを返す. - - next - - 次の数を返す.次の数とはその数を越える最小の整数である. - -** Object(クラス) - -全ての通常クラスのスーパクラス.通常クラスのインスタンスの一般的な振舞 -いを定義している.このクラスのサブクラスでないクラスはKernelとNilと, -組み込み関数を定義しているBuiltinだけである. - -SuperClass: Builtin - -Methods: - - extened(module[, module..]) - - 引数で指定したモジュールをselfにインクルードする.モジュールで - 定義されているメソッドが特異メソッドとして追加される. - - initialize - - オブジェクトの生成時に呼び出される.デフォルトの定義は何もしな - い.サブクラスで必要に応じて再定義されることが期待されている. - - is_member_of(class) - - オブジェクトselfがクラスclassのインスタンスである時,真を返す. - - is_kind_of(class) - - オブジェクトselfがクラスclassかそのサブクラスのインスタンスで - ある時,真を返す. - - clone - - オブジェクトの複製を作る.インスタンスが即値であるFixnumクラス - 以外のクラスの場合,「obj.equal(obj.clone)」は偽であるが,多く - の場合「obj == obj.clone」は真である. - - to_s - - オブジェクトの文字列表現を返す.このメソッドは内部的にprint() - やformat()メソッドで用いられている. - - to_a - - オブジェクトを配列に変換する.カーネルクラスで定義されているデ - フォルトは,そのオブジェクト自身を含む1要素の配列を返す. - -** Process(モジュール) - -プロセスに関する操作を行なうためのモジュール.Mathモジュールと同様に全 -てのメソッドは特異メソッドとしても通常のメソッドとしても使える. -Processはプロセスオブジェクトのクラスではなくて,プロセス操作のメソッ -ドをまとめたものであることに注意すること. - -Methods: -Single Methods: - - egid - - プロセスの現在の実効GIDを返す. - - egid=(gid) - - プロセスの現在の実効GIDをgidにセットする. - - euid - - プロセスの現在の実効UIDを返す. - - euid=(uid) - - プロセスの現在の実効UIDをuidにセットする. - - getpgrp([pid]) - - pidで指定されたプロセスが現在所属しているプロセスグループのid - を返す.pidを省略した時とpidに0を与えた時は現在実行しているプ - ロセスを対象にする. - - getpriority(which, who) - - whichとwhoで指定されるプロセス,プロセスグループ,ユーザの現在 - の優先順位を返す.詳細はgetpriority(2)を参照.Processモジュー - ルではwhichとして指定できる定数PRIO_PROCESS,PRIO_PGRP, - PRIO_USERが定義されている. - - gid - - プロセスの現在の実GIDを返す. - - gid= - - プロセスの現在の実GIDをgidにセットする. - - pid - - プロセスのプロセスIDを返す.これはシステム変数`$$'の値と同じで - ある. - - ppid - - 親プロセスのプロセスのプロセスIDを返す.UNIXでは直接の親プロセ - スが終了した場合,親プロセスのpidは1(initのpid)になる. - - setpgrp(pid, pgrp) - - pidで指定されたプロセスのプロセスグループをpgrpにする.pidに0 - を与えると現在実行中のプロセスを対象にする. - - setpriority(which, who, prio) - - whichとwhoで指定されるプロセス,プロセスグループ,ユーザの現在 - の優先順位をprioに設定する.詳細はsetpriority(2)を参照のこと. - - uid - - プロセスの現在の実UIDを返す. - - uid= - - プロセスの現在の実UIDをuidにセットする. - -** Range(クラス) - -範囲オブジェクトのクラス.範囲オブジェクトは`..'演算子によって生成され, -一般的には以下のような使い方をする - - for i in 1..5 - ... - end - -しかし,この場合は以下の方が速い. - - 1.upto(5) { - ... - } - -範囲オブジェクトを生成する`..'演算子の両辺はComparableを含むクラスのイ -ンスタンスであれば何でも構わない.範囲は始点と終点を含むことに注意する -こと. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self =~ other - - selfがotherと同じクラスに対する範囲オブジェクトで,その範囲内 - にotherがある時(start <= other <= end),真を返す.これはcase式 - で範囲指定する時に便利である.例えば - - case i - when 1, 3..5 - ... - end case - - のようなコードを書くことができる. - - each - - 範囲内に存在するオブジェクトを与えるイテレータ.主にfor式のた - めに用いられる. - - end - - 範囲の終点を返す - - start - - 範囲の始点を返す. - -** Regexp(クラス) - -正規表現のクラス.正規表現のリテラルは/.../という形式で表すが,動的に -生成するためには - - Regexp.new(文字列) - -とする.ただし,Stringクラスの`=~'を始めとして多くのメソッドは正規表現 -の替わりに文字列が与えられた時には内部的に正規表現を生成するので,生成 -コストを節約したいと思う時や,正規表現の大文字小文字の区別を明示的に指 -定したい時など以外は明示的に生成したいと思うことは少ないはずだ. - -SuperClass: Object - -Methods: - - self =~ string - - 正規表現が文字列にマッチした場合,マッチした位置を返す.マッチ - しない場合はnilを返す. - - ~ self - - 「$_ =~ self」と同義. - -Single Methods: - - complie(string[, casefold]) - new(string[, casefold]) - - 文字列を正規表現に変換したオブジェクトを返す.省略可能な第2引 - 数が与えられ,その値がnilでない時には,生成された正規表現オブ - ジェクトはシステム変数`$='の値に関わらず,マッチする時に大文字 - 小文字の違いを無視する. - - quote(str) - - 文字列の中の正規表現で意味を持つ文字をエスケープする.新しい文 - 字列を返す. - -** String(クラス) - -文字列クラス.Rubyの文字列はヌルターミネートではないので,バイナリデー -タも扱える.従ってどちらかというと単なる文字列というよりバイト列である. -その思想に基づいて,正規表現に関するメソッド以外は2byte系の文字を意識 -していない.これは作者の手抜きではなく意図的にそうしているのである(信 -じてくれ). - -SuperClass: Object - -Included Modules: Comparable, Enumerable - -Methods: - - self + other - - 文字列の連結.連結された文字列を返す. - - self * times - - 文字列の繰り返し.例えば - - "x" * 4 == "xxxx" - - である. - - self == other - self > other - - 文字列の比較.システム変数$=がnilでない時には大文字小文字を区 - 別せずに比較を行なう. - - self =~ other - - 文字列のマッチ.otherは正規表現か文字列.otherが文字列の場合に - は動的に正規表現に変換される.マッチした場合はマッチした位置, - しなかった場合はnilが返る. - - ~ self - - 「$_ =~ self」と同義. - - self[nth] - self[beg..end] - self[beg,len] - - 内容の取り出し.1番目の形式ではnthバイト目のデータをFixnumとし - て返す.2番目の形式ではbegバイト目からendバイト目までの部分文 - 字列を返す(両端を含む).3番目の形式ではbegバイト目からlenバイ - ト分の部分文字列を返す. - - self[nth] = val - self[beg..end] = val - self[beg,len] = val - - 内容の更新.1番目の形式ではnthバイト目のデータをval (整数)に変 - 更する.2番目の形式はbegバイト目からendバイト目までの部分文字 - 列をvalとして与えられた文字列で置き換える.3番目の形式はbegバ - イト目からlenバイト分の部分文字列をvalとして与えられた文字列で - 置き換える. - - capitalize - - 文字列中の最初の文字を(それがアルファベットであれば),大文字に - 変換し,残る文字列中のアルファベットを小文字に置き換える. - - chop - - 文字列の最後のバイトを切り落とす.元の文字列を変更することに注 - 意すること. - - crypt(salt) - - crypt(3)を用いて暗号化した文字列を返す.saltは2バイト以上の長 - さの任意の文字列である. - - delete(str) - - 文字列のうち,strに含まれる文字を削除する.文字列の指定はtrと - 同様であり,a-bでaからbまでの範囲を,先頭の^で文字列の否定(含 - まれてないものを指定)を意味する.元の文字列を変更することに注 - 意すること. - - downcase - - 文字列中のアルファベットを全て小文字に置き換えた文字列を返す. - - each - - 文字列から1行ずつ読み込んでくるイテレータ. - - each_byte - - 文字列のそれぞれのバイトについて繰り返すイテレータ. - - gsub(pattern[, replace]) - - 文字列中でpatternにマッチする部分を全てreplaceに置き換える.置 - 換文字列replace中の&と\0はマッチした文字列に,\1..\9はn番目の - 括弧の内容に置き換えられる.引数replaceが省略された時にはイテ - レータとして動作し,ブロックを評価した結果で置換する. - - hex - - 文字列を16進数を表す文字列と解釈して,整数に変換する. - - index(substr[, pos]) - - substrが最初に出現する位置を返す.posを与えるとその位置から検 - 索を開始する.見つからない時にはnilを返す. - - intern - - 文字列に一意に対応する整数を返す.文字列はナル文字を含んではな - らない. - - length - size - - 文字列の長さ(バイト数)を返す. - - ljust(width) - rjust(width) - center(width) - - 文字列をそれぞれ,右詰め,左詰め,真中寄せした幅widthの文字列 - を返す.文字列長がwidthより長い場合は元の文字列を返し,切り詰 - めない. - - next - - selfからendまで「次の」文字列を返す.次の文字列とは数字は数字 - として,英文字は英文字として増加し,桁上がりの処理が行なわれた - ものである. - - "aa".next => "ab" - "99".next => "100" - "a9".next => "b0" - - oct - - 文字列を8進数を表す文字列と解釈して,整数に変換する.8進数の - 定義は/[0-7]+/であり,文字列の先頭からこのパターンにマッチする - 部分を整数に変換する.この定義に全く当てはまらない文字列に対し - ては0を返す.perlとは違って文字列が0xから始まっているからといっ - て 16進数だと見なしてくれたりはしない.それらは先頭の0が8進数 - と認識され,0を返す. - - reverse - - 文字列の各バイトを逆順に並べた文字列を返す.文字列が2バイトで - 構成される文字を含んでいてもお構いなしにバイト単位で反転する. - splitは2バイト文字を理解するので,2バイト文字を含む文字列を文 - 字単位に反転するには - - "全角文字列".split(//).reverse.join("") - - とすればよい. - - rindex(substr[, pos]) - - 文字列substrが最後に出現する位置を返す.posを与えるとその位置 - で検索を終了する.見つからない時にはnilを返す.indexとの相違点 - は1)文字列の末尾から検索する.2)substrとして正規表現を受け付け - ない.の2点である. - - split([sep[, limit]]) - - 文字列をsepで指定されたパターンによって,フィールドに分割する. - sepが省略された時のデフォルトはシステム変数`$;'の値が用いられ - る.limitが指定された時には最大limit個のフィールドに分割する. - split()は分割された文字列を含む配列を返す.sepで指定されたパター - ンが空文字列とマッチする場合は文字列が1文字ずつに分割される. - - squeeze([str]) - - 文字列のうち,strに含まれる文字が連続していた場合,一文字に圧 - 縮する.strが省略された場合,すべての文字を対象とする.文字列 - の指定はtrと同様であり,`a-b'でaからbまでの範囲を,先頭の`^'で - 文字列の否定(含まれてないものを指定)を意味する.元の文字列を変 - 更することに注意すること. - - strip - - 文字列の前後の空白を取り除く. - - sub(pattern[, replace]) - - 文字列のpatternにマッチする最初の部分をreplaceに置き換える.置 - 換文字列replace中の&と\0はマッチした文字列に,\1..\9は n番目の - 括弧の内容に置き換えられる.引数replaceが省略された時にはイテ - レータとして動作し,ブロックを評価した結果で置換する. - - sum([bits]) - - 文字列のbitsビットのチェックサムを得る.省略値は16である.ruby - では以下のコードでSystem Vの`sum'プログラムと同じ値を得られる. - - while gets() - sum += $_.sum - end - sum %= 65536 - - swapcase - - 文字列中のアルファベットのうち大文字を小文字に,小文字を大文字 - に置き換える. - - to_f - - 文字列をFloatに変換する. - - to_i - - 文字列を10進数を表す文字列と解釈して,整数に変換する. - - toupper - - 文字列中のアルファベットを全て大文字に置き換えた文字列を返す. - tr("a-z", "A-Z")より少し速い. - - tolower - - 文字列中のアルファベットを全て小文字に置き換えた文字列を返す. - tr("A-Z", "a-z")より少し速い. - - tr(search, replace) - - 文字列の中にsearch文字列に含まれる文字が存在すれば,replace文 - 字列の対応する文字で置き換える.replace文字列が省略された場合 - は空文字列が与えられたと見なす.replace文字列がsearch文字列よ - りも短い時はreplace文字列の最後の文字が繰り返されていると見な - す.search文字列の方が短い時には対応する文字のないreplace部は - 単に無視される(BSDのtrの動作). - - search文字列,replace文字列中に`a-b'という形式が現れた場合,そ - のaからbまでの範囲の文字をASCIIの昇順で指定したことになる.ま - た,search文字列の最初の文字が`^'である場合,続く文字列に*含ま - れない*文字列が置換の対象になる. - - tr(1)の機能のうち,文字を削除する機能,連続する文字を圧縮する - 機能は別のメソッドに分割されている.それらの機能については - delete,squeezeを参照のこと. - - 簡便のため,str.tr(src,repl).squeeze(repl)に相当するメソッド - tr_s(src,repl) が提供されている. - - unpack(template) - - 文字列をtemplate文字列にしたがってアンパックし,それらの要素を - 含む配列を返す.template文字列はArrayクラスのpackメソッドとほ - ぼ同様である. - - a ASCII文字列(後続するnull文字やスペースを残す) - A ASCII文字列(後続するnull文字やスペースを削除) - b ビットストリング(下位ビットから上位ビット) - B ビットストリング(上位ビットから下位ビット) - h 16進文字列(下位ニブルが先) - H 16進文字列(上位ニブルが先) - c char - C unsigned char - s sort - S unsigned sort - i int - I unsigned int - l long - L unsigned int - n ネットワークバイトオーダーのshort - N ネットワークバイトオーダーのlong - f 単精度浮動小数点数(機種依存) - d 倍精度浮動小数点数(機種依存) - x 1バイト読み飛ばす - X 1バイト後退 - @ 絶対位置への移動 - - rubyのunpackはperlと違ってチェックサムの計算機能がないことに注 - 意すること. - - - upcase - - 文字列中のアルファベットを全て大文字に置き換えた文字列を返す. - - upto(end) - - selfから始まって,endまで「次の」文字列を順に与えるイテレータ. - 次の文字列とはstr.nextで与えられる文字列である. - - このメソッドはRange:eachで用いられているので,以下のような処理 - が可能である. - - for i in "a" .. "ba" - print(i, "\n"); - end - - これはa, b, c, ... aa, ... az, baまでを各行に出力する. - - - 気を付けなければいけないのは,この終了判定は大小関係ではなく - `=='で判定されているため,`..'演算子の左辺の値に続く文字列に右 - 辺の文字列が含まれていない,以下の例のような場合は無限ループに - 陥ってしまう. - - for i in "0" .. "1a" - print(i, "\n"); - end - - 作者はこう書くことによって責任を逃れてようとしていると考える人 - もいるかもしれない.その推測は正しい. - -Single Methods: - - new(string) - - stringと同じ内容を持つ新しい文字列を返す. - -** Struct(クラス) - -構造体クラス.複数のデータをまとめる時に用いられる(例: Time::times). -データをまとめる時には配列クラスが用いられることもあるが(例: select), -構造体を使うべき時は以下のような場合である. - - (1) 要素の数が固定 - - 要素の数が変動するものは構造体を使うのには向かない. - - (2) 要素の数が多い - - 人間が一度に容易に扱える概念の数は7つまでであるという仮説がある. - この仮説に従えば,要素が4つ以上あるデータの場合は配列を用いた場 - 合,要素数*2(つまりオフセットとその意味)が7を越える.よって,そ - のような場合には構造体を使った方が理解しやすいと思われる. - - (3) 同時に大量に生成されない - - 構造体は配列よりも若干生成コストが高いので,速度が問題になる場合 - (例えば同時に大量に生成される場合など)は構造体の使用が適切でない - 可能性がある. - -各構造体にはメンバ名と同名の引数のないメソッドが定義される. - -本ドキュメント内で,構造体を表現するためには以下の形式を使う. - - struct 構造体名 - メンバ... - end - -しかし,プログラム中でこの形式で構造体を生成するわけではない. - -SuperClass: Object - -Included Modules: Enumerable - -Methods: - - self[idx] - - idxが数の時はidx番目の要素を返す.idxが文字列の時はidxと同じ名 - 前のメンバの要素を返す. - - values - - 構造体のメンバの値を要素に持つ配列を返す.例えば以下のコードで - 自分のpasswdエントリを出力することができる. - - print(Etc.getpwuid().values.join(":"), "\n") - - この出力は`grep "$USER" /etc/passwd'の出力と余分なフィールドい - くつか(システムによって異なる)がある以外は同じである. - -Single Methods: - - new(name, member::value..) - - nameという名前を持つ構造体を生成する.memberは構造体のメンバを - 表す文字列であり,valueはその値である.生成された構造体はメン - バで指定された名前の特異メソッドが定義されていて,そのメソッド - によってメンバの内容を得ることができる. - -** Time(クラス) - -時間を表すクラス.大小比較などができる.Time.nowで現在の時間を得ること -ができる.またファイルのタイムスタンプを得るメソッドの戻り値もこのクラ -スのインスタンスである. - -SuperClass: Object - -Included Modules: Comparable - -Methods: - - self <=> other - - otherはTimeのインスタンスか整数.整数が与えられた場 - 合には1970年 1月 1日 00:00:00 GMTからの秒数であると - して時刻との比較を行なう. - - asctime - ctime - to_s - - 時刻をdate(1)形式の文字列に変換する. - - gmtime - - タイムゾーンの修正を行なわないGMTでの時刻を得る.このメソッド - を受けとったTimeクラスのインスタンスは,以後の時刻変換をGMTで - 行なう.gmtimeは自分自身を返す. - - ロンドンの時刻を表示するには - - print(Time.now.gmtime, "\n") - - とすればよい. - - localtime - - タイムゾーンの修正を行なった時刻を得る(デフォルト).localtime - は自分自身を返す. - - to_i - tv_sec - - 1970年 1月 1日 00:00:00 GMTから時刻までの秒数を整数で返す.時 - 刻のsecondの部分でもある. - - sec - min - hour - mday - year - wday - yday - zone - isdst - - 内部的に保持しているtm構造体の内容を返す.zone以外は整数を返す. - zoneはタイムゾーンを表す文字列を返す.(cf localtime(3)) - - strftime(format) - - 時刻をformat文字列に従って文字列に変換した結果を返す.format - 文字列として指定できるものは 以下の通りである. - - %A 曜日の名称(Sunday, Monday,..) - %a 曜日の省略名(Sun, Mon,..) - %B 月の名称(January, February,..) - %b 月の省略名(Jan, Feb,..) - %c 時刻表現(cf ctime(3)) - %d 十進数での日(01-31) - %H 24時間制の時(00-23) - %I 12時間制の時(01-12) - %j 年中の通算日(001-366) - %M 分(00-59) - %m 月を表す数字(01-12) - %p 午前または午後(AM,PM) - %S 秒(00-61) - %U 週を表す数字.最初の日曜日が第1週の - 始まり(00-53) - %W 週を表す数字.最初の月曜日が第1週の - 始まり(00-53) - %w 曜日を表す数字.日曜日が0(0-6) - %X 時刻(例: 15:01:06) - %x 日付(例: Fri Jan 14 1994) - %Y 西暦を表す数字 - %y 西暦の下2桁(00-99) - %Z タイムゾーン - %% %自身 - - usec - tv_usec - - 時刻のmicro secondの部分を返す. - -Single Methods: - - now - - 現在の時刻を表すTimeクラスのインスタンスを生成する. - - at(time) - - timeと同じ時刻を表すTimeクラスのインスタンスを生成する.timeは - Timeクラスのインスタンスかあるいは数(整数/浮動小数点数)であり, - 数の場合は1970年 1月 1日 00:00:00 GMTからの秒数であるとして時 - 刻を計算する. - - times - - 現在のプロセスとその子プロセスが消費したユーザ/システムCPUタイ - ムの積算を構造体として返す(Structを参照). - - struct tms - utime # プロセスのユーザ時間 - stime # プロセスのシステム時間 - cutime # 子プロセスのユーザ時間 - cstime # 子プロセスのシステム時間 - end - - 時間の単位は秒であり,浮動小数点数で与えられる.詳細は - times(3)を参照のこと. - -* C言語とのインタフェース - -rubyはC言語とのインターフェースを提供し,C言語からのクラス,モジュール -の定義,C言語で記述したメソッドの定義,rubyのメソッドの呼び出し,イテ -レータの呼び出し,例外処理などを行なうことが出来る.また,OSが許せば実 -行時にCで書かれたモジュールをロードすることも出来る. - -具体的なインタフェースに関しては,別ドキュメント(添付ファイル C-IF)を -参照のこと. - -* 文法 - -以下は疑似BNFで記述したrubyの文法である.より正確な記述はparse.yを参照 -されたい. - -PROGRAM : COMPEXPR - -COMPEXPR : EXPR (TERM EXPR)* [TERM] - -EXPR : MLHS `=' ARGS - | ASSOCS - | return ARGS - | fail ARGS - | yield ARGS - | identifier CALL_ARGS - | PRIMARY `.' identifier CALL_ARGS - | super CALL_ARGS - | undef FNAME - | alias FNAME FNAME - | include identifier (`,' identifier)* - | EXPR if EXPR - | EXPR while EXPR - | EXPR and EXPR - | EXPR or EXPR - | ARG - -ARG : LHS `=' ARG - | LHS OP_ASGN ARG - | ARG `..' ARG - | ARG `...' ARG - | ARG `+' ARG - | ARG `-' ARG - | ARG `*' ARG - | ARG `/' ARG - | ARG `%' ARG - | ARG `**' ARG - | `+' ARG - | `-' ARG - | ARG `|' ARG - | ARG `^' ARG - | ARG `&' ARG - | ARG `<=>' ARG - | ARG `>' ARG - | ARG `>=' ARG - | ARG `<' ARG - | ARG `<=' ARG - | ARG `==' ARG - | ARG `!=' ARG - | ARG `=~' ARG - | ARG `!~' ARG - | `!' ARG - | `~' ARG - | ARG `<<' ARG - | ARG `>>' ARG - | ARG `::' ARG - | ARG `&&' ARG - | ARG `||' ARG - | PRIMARY - -PRIMARY : `(' EXPR `)' - | LITERAL - | VARIABLE - | super `(' [CALL_ARGS] `)' - | super - | PRIMARY `[' [ARGS] `]' - | `[' [ARGS [`,']] `]' - | `{' [ (ARGS|ASSOCS) [`,'] ] `}' - | redo - | break - | continue - | retry - | return - | fail `(' ARGS `)' - | fail `(' `)' - | fail - | yield `(' ARGS `)' - | yield `(' `)' - | yield - | PRIMARY `{' [ITER_VAR] `|' COMPEXPR `}' - | identifier `(' [CALL_ARGS] `)' - | PRIMARY `.' identifier `(' [CALL_ARGS] `)' - | PRIMARY `.' identifier - | if EXPR THEN - COMPEXPR - (elsif EXPR THEN COMPEXPR)* - [else COMPEXPR] - end - | while EXPR TERM COMPEXPR end - | case COMPEXPR - (when ARGS THEN)+ - [else COMPEXPR] - end - | for ITER_VAR in EXPR TERM - COMPEXPR - end - | begin - COMPEXPR - [resque COMPEXPR] - [ensure COMPEXPR] - end - | class identifier `:' identifier - COMPEXPR - end - | module identifier - COMPEXPR - end - | def FNAME ARGLIST - COMPEXPR - end - | def SINGLETON `.' FNAME ARGLIST - COMPEXPR - end - -THEN : TERM - | then - | TERM then - -ITER_VAR : LHS - | MLHS - -MLHS : LHS `,' [LHS (`,' LHS)*] [`*' LHS] - -LHS : VARIABLE - | PRIMARY `[' [ARGS] `]' - | PRIMARY `.' identifier - -CALL_ARGS : ARGS - | ASSOCS - | ARGS `,' ASSOCS - | ARGS `,' `*' ARG - -ARGS : ARG - | ARGS `,' ARG - -ARGLIST : `('[identifier(`,'identifier)*][`*'identifier]`)' - | TERM - -SINGLETON : VARIABLE - | `(' COMPEXPR `)' - -ASSOCS : ASSOC (`,' ASSOC)* - -ASSOC : ARG `=>' ARG - -VARIABLE : VARNAME - | nil - | self - | `__FILE__' - | `__LINE__' - -LITERAL : numeric - | SYMBOL - | STRING - | REGEXP - | GLOB - -TERM : `;' - | `\n' - -ここより下は字句解析部で認識される. - -SYMBOL : `:'FNAME - | `:'VARNAME - -FNAME : identifier | `::' | `..' | `|' | `^' | `&' - | `<=>' | `==' | `=~' | `>' | `>=' | `<' | `<=' - | `<<' | `>>' | `+' | `-' | `*' | `/' | `%' | `**' - | `~' | `+@' | `-@' | `[]' | `[]=' - -VARNAME : GLOBAL - | `@'identifier - | identifier - -GLOBAL : `$'identifier - | `$'any_char - -STRING : `"' any_char* `"' - | `'' any_char* `'' - | ``' any_char* ``' - -REGEXP : `/' any_char* `/'[i] - -GLOB : `<' any_char* `>' - -* 謝辞 - -Rubyの言語仕様は数多くの言語の影響を受けている. 以下にあげるのはその主 -な言語である. - - C, Perl, CLU, Sather, CLOS, Eiffel, Icon, tcl, AWK, bourne shell, - Smalltalk, Emacs Lisp. - -またrubyの言語仕様を決定するために協力して下さった方々を以下にあげる. - - 石塚圭樹,大庭康生,伊藤純一郎,中村@NEC.関根@日本DEC, - たなか@赤坂.富士通(敬称略). -------------------------------------------------------- -Local variables: -fill-column: 70 -end: @@ -16,13 +16,13 @@ static void fmt_setup(); VALUE -Fsprintf(argc, argv) +f_sprintf(argc, argv) int argc; VALUE *argv; { struct RString *fmt; char *buf, *p, *end; - int i, blen, bsiz; + int blen, bsiz; VALUE result; #define FNONE 0 @@ -51,7 +51,7 @@ Fsprintf(argc, argv) } #define GETARG() \ - ((argc == 0)?Fail("too few argument."):(argc--, (argv++)[0])) + ((argc == 0)?Fail("too few argument."),0:(argc--, (argv++)[0])) fmt = (struct RString*)GETARG(); Check_Type(fmt, T_STRING); @@ -110,7 +110,7 @@ Fsprintf(argc, argv) case '5': case '6': case '7': case '8': case '9': flags |= FWIDTH; width = 0; - for (p; p < end && isdigit(*p); p++) { + for (; p < end && isdigit(*p); p++) { width = 10 * width + (*p - '0'); } if (p >= end) { @@ -150,7 +150,7 @@ Fsprintf(argc, argv) goto retry; } - for (p; p < end && isdigit(*p); p++) { + for (; p < end && isdigit(*p); p++) { prec = 10 * prec + (*p - '0'); } if (p >= end) { @@ -178,22 +178,39 @@ Fsprintf(argc, argv) { VALUE arg = GETARG(); int len; - char fbuf[32]; -#define MIN(a,b) ((a)<(b)?(a):(b)) str = obj_as_string(arg); - fmt_setup(fbuf, 's', flags, width, prec); + len = RSTRING(str)->len; if (flags&FPREC) { - CHECK(prec); - } - else if ((flags&FWIDTH) && width > RSTRING(str)->len) { - CHECK(width); + if (prec < len) { + CHECK(prec); + memcpy(&buf[blen], RSTRING(str)->ptr, prec); + blen += prec; + break; + } } - else { - CHECK(RSTRING(str)->len); + if (flags&FWIDTH) { + if (width > len) { + width -= len; + CHECK(width); + if (!(flags&FMINUS)) { + while (width--) { + buf[blen++] = ' '; + } + } + memcpy(&buf[blen], RSTRING(str)->ptr, len); + blen += len; + if (flags&FMINUS) { + while (width--) { + buf[blen++] = ' '; + } + } + break; + } } - sprintf(&buf[blen], fbuf, RSTRING(str)->ptr); - blen += strlen(&buf[blen]); + CHECK(len); + memcpy(&buf[blen], RSTRING(str)->ptr, len); + blen += len; } break; @@ -10,7 +10,7 @@ static char *rcsid = "$Header: /usr/ext/cvsroot/ruby/st.c,v 1.3 1994/12/09 01:28 #include "st.h" extern void *xmalloc(); -static rehash(); +static void rehash(); #define max(a,b) ((a) > (b) ? (a) : (b)) #define nil(type) ((type *) 0) @@ -25,8 +25,6 @@ static rehash(); * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins * allocated initially * - * DEFAULT_GROW_FACTOR is the amount the hash table is expanded after - * the density has reached max_density */ #define EQUAL(func, x, y) \ @@ -39,14 +37,13 @@ static rehash(); (table->hash == ST_NUMHASH) ? ((int) (key) % table->num_bins) :\ (*table->hash)((key), table->num_bins)) -st_table *st_init_table_with_params(compare, hash, size, density, grow_factor, - reorder_flag) -int (*compare)(); -int (*hash)(); -int size; -int density; -double grow_factor; -int reorder_flag; +st_table* +st_init_table_with_params(compare, hash, size, density, reorder_flag) + int (*compare)(); + int (*hash)(); + int size; + int density; + int reorder_flag; { st_table *tbl; @@ -55,7 +52,6 @@ int reorder_flag; tbl->hash = hash; tbl->num_entries = 0; tbl->max_density = density; - tbl->grow_factor = grow_factor; tbl->reorder_flag = reorder_flag; tbl->num_bins = size; tbl->bins = @@ -63,18 +59,19 @@ int reorder_flag; return tbl; } -st_table *st_init_table(compare, hash) -int (*compare)(); -int (*hash)(); +st_table* +st_init_table(compare, hash) + int (*compare)(); + int (*hash)(); { return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE, ST_DEFAULT_MAX_DENSITY, - ST_DEFAULT_GROW_FACTOR, ST_DEFAULT_REORDER_FLAG); } +int st_free_table(table) -st_table *table; + st_table *table; { register st_table_entry *ptr, *next; int i; @@ -111,10 +108,11 @@ if (PTR_NOT_EQUAL(table, ptr, key)) {\ }\ } +int st_lookup(table, key, value) -st_table *table; -register char *key; -char **value; + st_table *table; + register char *key; + char **value; { int hash_val; register st_table_entry *ptr; @@ -147,10 +145,11 @@ char **value; table->num_entries++;\ } +int st_insert(table, key, value) -register st_table *table; -register char *key; -char *value; + register st_table *table; + register char *key; + char *value; { int hash_val; st_table_entry *tbl; @@ -169,10 +168,11 @@ char *value; } } +int st_add_direct(table, key, value) -st_table *table; -char *key; -char *value; + st_table *table; + char *key; + char *value; { int hash_val; st_table_entry *tbl; @@ -181,10 +181,11 @@ char *value; ADD_DIRECT(table, key, value, hash_val, tbl); } +int st_find_or_add(table, key, slot) -st_table *table; -char *key; -char ***slot; + st_table *table; + char *key; + char ***slot; { int hash_val; st_table_entry *tbl, *ptr; @@ -203,13 +204,14 @@ char ***slot; } } -static rehash(table) -register st_table *table; +static void +rehash(table) + register st_table *table; { register st_table_entry *ptr, *next, **old_bins = table->bins; int i, old_num_bins = table->num_bins, hash_val; - table->num_bins = table->grow_factor*old_num_bins; + table->num_bins = 2*old_num_bins; if (table->num_bins%2 == 0) { table->num_bins += 1; @@ -234,8 +236,9 @@ register st_table *table; free((char *) old_bins); } -st_table *st_copy(old_table) -st_table *old_table; +st_table* +st_copy(old_table) + st_table *old_table; { st_table *new_table; st_table_entry *ptr, *tbl; @@ -275,10 +278,11 @@ st_table *old_table; return new_table; } +int st_delete(table, key, value) -register st_table *table; -register char **key; -char **value; + register st_table *table; + register char **key; + char **value; { int hash_val; st_table_entry *tmp; @@ -317,10 +321,11 @@ char **value; return 0; } +int st_foreach(table, func, arg) -st_table *table; -enum st_retval (*func)(); -char *arg; + st_table *table; + enum st_retval (*func)(); + char *arg; { st_table_entry *ptr, *last, *tmp; enum st_retval retval; @@ -346,14 +351,16 @@ char *arg; } ptr = ptr->next; free((char *) tmp); + table->num_entries--; } } } } +int st_strhash(string, modulus) -register char *string; -int modulus; + register char *string; + int modulus; { register int val = 0; register int c; @@ -23,7 +23,6 @@ struct st_table { int num_entries; int max_density; int reorder_flag; - double grow_factor; st_table_entry **bins; }; @@ -49,7 +48,6 @@ st_table *st_copy(); #define ST_DEFAULT_MAX_DENSITY 5 #define ST_DEFAULT_INIT_TABLE_SIZE 11 -#define ST_DEFAULT_GROW_FACTOR 2.0 #define ST_DEFAULT_REORDER_FLAG 0 int st_strhash(); @@ -13,10 +13,13 @@ #include "ruby.h" #include "re.h" +#define BEG(no) regs.beg[no] +#define END(no) regs.end[no] + #include <stdio.h> #include <ctype.h> -VALUE C_String; +VALUE cString; #define STRLEN(s) RSTRING(s)->len @@ -26,7 +29,7 @@ str_new(ptr, len) UINT len; { NEWOBJ(str, struct RString); - OBJSETUP(str, C_String, T_STRING); + OBJSETUP(str, cString, T_STRING); str->len = len; str->orig = Qnil; @@ -50,7 +53,7 @@ str_new3(str) struct RString *str; { NEWOBJ(str2, struct RString); - OBJSETUP(str2, C_String, T_STRING); + OBJSETUP(str2, cString, T_STRING); str2->len = str->len; str2->ptr = str->ptr; @@ -74,11 +77,11 @@ obj_as_string(obj) } str = rb_funcall(obj, pr_str, 0); if (TYPE(str) != T_STRING) - return Fkrn_to_s(obj); + return krn_to_s(obj); return str; } -VALUE +static VALUE str_clone(str) struct RString *str; { @@ -92,36 +95,41 @@ str_clone(str) return obj; } +VALUE +str_dup(str) + struct RString *str; +{ + return str_new(str->ptr, str->len); +} + static VALUE -Sstr_new(class, str) +str_s_new(class, str) VALUE class; struct RString *str; { - Check_Type(str, T_STRING); - { - NEWOBJ(str2, struct RString); - OBJSETUP(str2, class, T_STRING); + NEWOBJ(str2, struct RString); + OBJSETUP(str2, class, T_STRING); - str2->len = str->len; - str2->ptr = ALLOC_N(char, str->len+1); - if (str2->ptr) { - memcpy(str2->ptr, str->ptr, str->len); - } - str2->ptr[str->len] = '\0'; - str2->orig = Qnil; - return (VALUE)str2; + str = as_str(str); + str2->len = str->len; + str2->ptr = ALLOC_N(char, str->len+1); + if (str2->ptr) { + memcpy(str2->ptr, str->ptr, str->len); } + str2->ptr[str->len] = '\0'; + str2->orig = Qnil; + return (VALUE)str2; } static VALUE -Fstr_length(str) +str_length(str) struct RString *str; { return INT2FIX(str->len); } VALUE -Fstr_plus(str1, str2) +str_plus(str1, str2) struct RString *str1, *str2; { struct RString *str3; @@ -136,7 +144,7 @@ Fstr_plus(str1, str2) } VALUE -Fstr_times(str, times) +str_times(str, times) struct RString *str; VALUE times; { @@ -154,20 +162,6 @@ Fstr_times(str, times) return (VALUE)str2; } -extern VALUE C_Range; - -static VALUE -Fstr_dot2(left, right) - VALUE left, right; -{ - extern VALUE C_Range; - VALUE str; - - Check_Type(right, T_STRING); - str = range_new(left, right); - return str; -} - VALUE str_substr(str, start, len) struct RString *str; @@ -206,13 +200,6 @@ str_subseq(str, beg, end) if (end < 0) end = 0; } - if (beg > end) { - int tmp; - - Warning("start %d is bigger than end %d", beg, end); - tmp = beg; beg = end; end = tmp; - } - if (beg >= str->len) { return str_new(0, 0); } @@ -222,7 +209,7 @@ str_subseq(str, beg, end) len = end - beg + 1; if (len < 0) { - Fail("end %d too small(size %d)", end, str->len); + len = 0; } return str_substr(str, beg, len); @@ -230,10 +217,13 @@ str_subseq(str, beg, end) extern VALUE ignorecase; +#define STR_FREEZE FL_USER1 + void str_modify(str) struct RString *str; { + if (FL_TEST(str, STR_FREEZE)) Fail("can't modify frozen string"); if (str->orig == Qnil) return; str->ptr = ALLOC_N(char, str->len+1); if (str->ptr) { @@ -242,6 +232,32 @@ str_modify(str) str->orig = Qnil; } +static VALUE +str_freeze(str) + VALUE str; +{ + FL_SET(str, STR_FREEZE); + return str; +} + +static VALUE +str_frozen(str) + VALUE str; +{ + if (FL_TEST(str, STR_FREEZE)) + return TRUE; + return FALSE; +} + +VALUE +str_dup_freezed(str) + VALUE str; +{ + str = str_dup(str); + str_freeze(str); + return str; +} + VALUE str_grow(str, len) struct RString *str; @@ -275,7 +291,7 @@ str_cat(str, ptr, len) } static VALUE -Fstr_concat(str1, str2) +str_concat(str1, str2) struct RString *str1, *str2; { str2 = as_str(str2); @@ -283,7 +299,7 @@ Fstr_concat(str1, str2) return (VALUE)str1; } -static +static int str_hash(str) struct RString *str; { @@ -305,7 +321,7 @@ str_hash(str) } static VALUE -Fstr_hash(str) +str_hash_method(str) VALUE str; { int key = str_hash(str); @@ -321,7 +337,7 @@ str_cmp(str1, str2) UINT len; int retval; - if (ignorecase != Qnil) { + if (ignorecase != FALSE) { return str_cicmp(str1, str2); } @@ -334,7 +350,7 @@ str_cmp(str1, str2) } static VALUE -Fstr_equal(str1, str2) +str_equal(str1, str2) struct RString *str1, *str2; { if (TYPE(str2) != T_STRING) @@ -348,12 +364,12 @@ Fstr_equal(str1, str2) } static VALUE -Fstr_cmp(str1, str2) +str_cmp_method(str1, str2) VALUE str1, str2; { int result; - Check_Type(str2, T_STRING); + str2 = obj_as_string(str2); result = str_cmp(str1, str2); return INT2FIX(result); } @@ -361,7 +377,7 @@ Fstr_cmp(str1, str2) VALUE Freg_match(); static VALUE -Fstr_match(x, y) +str_match(x, y) struct RString *x, *y; { VALUE reg; @@ -369,11 +385,11 @@ Fstr_match(x, y) switch (TYPE(y)) { case T_REGEXP: - return Freg_match(y, x); + return reg_match(y, x); case T_STRING: - reg = re_regcomp(y); - start = research(reg, x, 0); + reg = reg_regcomp(y); + start = reg_search(reg, x, 0, 0); if (start == -1) { return FALSE; } @@ -386,7 +402,7 @@ Fstr_match(x, y) } static VALUE -Fstr_match2(str) +str_match2(str) struct RString *str; { extern VALUE rb_lastline; @@ -396,8 +412,8 @@ Fstr_match2(str) if (TYPE(rb_lastline) != T_STRING) Fail("$_ is not a string"); - reg = re_regcomp(str); - start = research(reg, rb_lastline, 0); + reg = reg_regcomp(str); + start = reg_search(reg, rb_lastline, 0, 0); if (start == -1) { return Qnil; } @@ -427,7 +443,7 @@ str_index(str, sub, offset) } static VALUE -Fstr_index(argc, argv, str) +str_index_method(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -445,7 +461,7 @@ Fstr_index(argc, argv, str) switch (TYPE(sub)) { case T_REGEXP: - pos = research(sub, str, pos); + pos = reg_search(sub, str, pos, (struct re_registers *)-1); break; case T_STRING: @@ -461,7 +477,7 @@ Fstr_index(argc, argv, str) } static VALUE -Fstr_rindex(argc, argv, str) +str_rindex(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -519,7 +535,7 @@ str_next(s) } static VALUE -Fstr_next(orig) +str_next_method(orig) struct RString *orig; { struct RString *str, *str2; @@ -545,7 +561,7 @@ Fstr_next(orig) } VALUE -Fstr_upto(beg, end) +str_upto(beg, end) VALUE beg, end; { VALUE current; @@ -553,8 +569,10 @@ Fstr_upto(beg, end) current = beg; for (;;) { rb_yield(current); - if (Fstr_equal(current, end)) break; - current = Fstr_next(current); + if (str_equal(current, end)) break; + current = str_next_method(current); + if (RSTRING(current)->len > RSTRING(end)->len) + break; } return Qnil; @@ -580,8 +598,8 @@ str_aref(str, indx) return (VALUE)INT2FIX(str->ptr[idx] & 0xff); case T_REGEXP: - if (Fstr_index(str, indx)) - return re_last_match(0); + if (str_index(str, indx)) + return reg_last_match(0); return Qnil; case T_STRING: @@ -590,26 +608,18 @@ str_aref(str, indx) default: /* check if indx is Range */ - if (obj_is_kind_of(indx, C_Range)) { + { int beg, end; - - beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg); - end = rb_iv_get(indx, "end"); end = NUM2INT(end); - if (beg > end) { - int tmp; - - Warning("start %d is bigger than end %d", beg, end); - tmp = beg; beg = end; end = tmp; + if (range_beg_end(indx, &beg, &end)) { + return str_subseq(str, beg, end); } - - return str_subseq(str, beg, end); } Fail("Invalid index for string"); } } static VALUE -Fstr_aref(argc, argv, str) +str_aref_method(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -674,9 +684,10 @@ str_sub(str, pat, val, once) VALUE val; int once; { - int beg, end, offset, n; + int beg, offset, n; + struct re_registers regs; - Check_Type(val, T_STRING); + val = obj_as_string(val); str_modify(str); switch (TYPE(pat)) { @@ -684,19 +695,19 @@ str_sub(str, pat, val, once) break; case T_STRING: - return str_sub(str, re_regcomp(pat), val, once); + pat = (struct RRegexp*)reg_regcomp(pat); + break; default: /* type failed */ Check_Type(pat, T_REGEXP); } + regs.allocated = 0; for (offset=0, n=0; - (beg=research(pat, str, offset)) >= 0; - offset=BEG(0)+STRLEN(val)) { - end = END(0)-1; - val = re_regsub(val); - str_replace2(str, beg, END(0)-1, val); + (beg=reg_search(pat, str, offset, ®s)) >= 0; + offset=END(0)+1) { + str_replace2(str, beg, END(0)-1, reg_regsub(val, str, ®s)); n++; if (once) break; } @@ -739,27 +750,19 @@ str_aset(str, indx, val) default: /* check if indx is Range */ - if (obj_is_kind_of(indx, C_Range)) { - Check_Type(val, T_STRING); - - beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg); - end = rb_iv_get(indx, "end"); end = NUM2INT(end); - if (beg > end) { - int tmp; - - Warning("start %d is bigger than end %d", beg, end); - tmp = beg; beg = end; end = tmp; + { + int beg, end; + if (range_beg_end(indx, &beg, &end)) { + str_replace2(str, beg, end, val); + return val; } - - str_replace2(str, beg, end, val); - return val; } Fail("Invalid index for string"); } } static VALUE -Fstr_aset(argc, argv, str) +str_aset_method(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -792,9 +795,11 @@ Fstr_aset(argc, argv, str) static VALUE str_sub_iter(str, pat, once) VALUE str, pat; + int once; { VALUE val; - int beg, end, offset, n; + int beg, offset; + struct re_registers regs; if (!iterator_p()) { Fail("Wrong # of arguments(1 for 2)"); @@ -806,7 +811,7 @@ str_sub_iter(str, pat, once) break; case T_STRING: - pat = re_regcomp(pat); + pat = reg_regcomp(pat); break; default: @@ -815,18 +820,20 @@ str_sub_iter(str, pat, once) } offset=0; - while ((beg=research(pat, str, offset)) >= 0) { - val = rb_yield(re_nth_match(0)); + regs.allocated = 0; + while ((beg=reg_search(pat, str, offset, ®s)) >= 0) { + val = rb_yield(reg_nth_match(0, backref_get())); val = obj_as_string(val); str_replace2(str, beg, END(0)-1, val); offset=BEG(0)+STRLEN(val); if (once) break; } + re_free_registers(®s); return (VALUE)str; } static VALUE -Fstr_sub(argc, argv, str) +str_sub_bang(argc, argv, str) int argc; VALUE *argv; VALUE str; @@ -840,7 +847,16 @@ Fstr_sub(argc, argv, str) } static VALUE -Fstr_gsub(argc, argv, str) +str_sub_method(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + return str_sub_bang(argc, argv, str_dup(str)); +} + +static VALUE +str_gsub_bang(argc, argv, str) int argc; VALUE *argv; VALUE str; @@ -853,10 +869,21 @@ Fstr_gsub(argc, argv, str) return str_sub(str, pat, val, 0); } +static VALUE +str_gsub(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE v = str_gsub_bang(argc, argv, str_dup(str)); + if (v) return v; + return str; +} + extern VALUE rb_lastline; static VALUE -Fsub(argc, argv) +f_sub_bang(argc, argv) int argc; VALUE *argv; { @@ -870,7 +897,23 @@ Fsub(argc, argv) } static VALUE -Fgsub(argc, argv) +f_sub(argc, argv) + int argc; + VALUE *argv; +{ + VALUE v; + + Check_Type(rb_lastline, T_STRING); + v = f_sub_bang(argc, argv, str_dup(rb_lastline)); + if (v) { + rb_lastline = v; + return v; + } + return rb_lastline; +} + +static VALUE +f_gsub_bang(argc, argv) int argc; VALUE *argv; { @@ -884,7 +927,41 @@ Fgsub(argc, argv) } static VALUE -Fstr_reverse(str) +f_gsub(argc, argv) + int argc; + VALUE *argv; +{ + VALUE v; + + Check_Type(rb_lastline, T_STRING); + v = f_gsub_bang(argc, argv, str_dup(rb_lastline)); + if (v) { + rb_lastline = v; + return v; + } + return rb_lastline; +} + +static VALUE +str_reverse_bang(str) + struct RString *str; +{ + char *s, *e, *p; + + s = str->ptr; + e = s + str->len - 1; + p = ALLOCA_N(char, str->len); + + while (e >= s) { + *p++ = *e--; + } + MEMCPY(str->ptr, p, char, str->len); + + return (VALUE)str; +} + +static VALUE +str_reverse(str) struct RString *str; { VALUE obj = str_new(0, str->len); @@ -901,14 +978,14 @@ Fstr_reverse(str) } static VALUE -Fstr_to_i(str) +str_to_i(str) struct RString *str; { return str2inum(str->ptr, 10); } static VALUE -Fstr_to_f(str) +str_to_f(str) struct RString *str; { double atof(); @@ -918,35 +995,50 @@ Fstr_to_f(str) } static VALUE -Fstr_to_s(str) +str_to_s(str) VALUE str; { return str; } static VALUE -Fstr_inspect(str) +str_inspect(str) struct RString *str; { - char buf[160]; + struct RString *str0; char *p, *pend; - char *b, *bend; + char *b; + int offset; -#define CHECK(n) if (b+n > bend) break; + str0 = (struct RString*)str_new2("\""); + offset = 1; +#define CHECK(n) do {\ + str_cat(str0, 0, n);\ + b = str0->ptr + offset;\ + offset += n;\ +} while (0) p = str->ptr; pend = p + str->len; - b = buf; bend = b + sizeof buf - (str->len>150?4:2); - *b++ = '"'; while (p < pend) { char c = *p++; - if (isprint(c)) { - CHECK(1); + if (ismbchar(c) && p+1 < pend) { + CHECK(2); *b++ = c; + *b++ = *p++; + } + else if (c == '"') { + CHECK(2); + *b++ = '\\'; + *b++ = '"'; } - else if (ismbchar(c)) { + else if (c == '\\') { CHECK(2); + *b++ = '\\'; + *b++ = '\\'; + } + else if (isprint(c)) { + CHECK(1); *b++ = c; - *b++ = *p++; } else if (c == '\n') { CHECK(2); @@ -973,7 +1065,7 @@ Fstr_inspect(str) *b++ = '\\'; *b++ = 'v'; } - else if (c == '\1') { + else if (c == '\007') { CHECK(2); *b++ = '\\'; *b++ = 'a'; @@ -983,28 +1075,19 @@ Fstr_inspect(str) *b++ = '\\'; *b++ = 'e'; } - else if (iscntrl(c)) { - CHECK(2); - *b++ = '^'; - *b++ = c; - } else { - CHECK(1); - *b++ = c; - } - } - *b++ = '"'; - if (p < pend) { - bend = buf + sizeof buf; - while (b < bend) { - *b++ = '.'; + CHECK(4); + *b++ = '\\'; + sprintf(b, "%03o", c); + b += 3; } } - return str_new(buf, b - buf); + str_cat(str0, "\"", 1); + return (VALUE)str0; } static VALUE -Fstr_upcase(str) +str_upcase_bang(str) struct RString *str; { char *s, *send; @@ -1022,7 +1105,14 @@ Fstr_upcase(str) } static VALUE -Fstr_downcase(str) +str_upcase(str) + struct RString *str; +{ + return str_upcase_bang(str_dup(str)); +} + +static VALUE +str_downcase_bang(str) struct RString *str; { char *s, *send; @@ -1040,7 +1130,14 @@ Fstr_downcase(str) } static VALUE -Fstr_capitalize(str) +str_downcase(str) + struct RString *str; +{ + return str_downcase_bang(str_dup(str)); +} + +static VALUE +str_capitalize_bang(str) struct RString *str; { char *s, *send; @@ -1058,7 +1155,14 @@ Fstr_capitalize(str) } static VALUE -Fstr_swapcase(str) +str_capitalize(str) + struct RString *str; +{ + return str_capitalize_bang(str_dup(str)); +} + +static VALUE +str_swapcase_bang(str) struct RString *str; { char *s, *send; @@ -1079,25 +1183,20 @@ Fstr_swapcase(str) } static VALUE -Fstr_toupper(str) +str_swapcase(str) struct RString *str; { - return Fstr_upcase(str_new(str->ptr, str->len)); + return str_swapcase_bang(str_dup(str)); } -static VALUE -Fstr_tolower(str) - struct RString *str; -{ - return Fstr_downcase(str_new(str->ptr, str->len)); -} +typedef unsigned char *USTR; struct tr { - unsigned char gen, now, max; + int gen, now, max; char *p, *pend; } trsrc, trrepl; -static char +static int trnext(t) struct tr *t; { @@ -1108,12 +1207,12 @@ trnext(t) if (t->p < t->pend && *t->p == '-') { t->p++; if (t->p < t->pend) { - if (t->now > *t->p) { + if (t->now > *(USTR)t->p) { t->p++; continue; } t->gen = 1; - t->max = *t->p++; + t->max = *(USTR)t->p++; } } return t->now; @@ -1129,13 +1228,13 @@ trnext(t) } static VALUE -Fstr_tr(str, src, repl) +str_tr_bang(str, src, repl) struct RString *str, *src, *repl; { struct tr trsrc, trrepl; int cflag = 0; char trans[256]; - int i, c, save; + int i, c; char *s, *send, *t; Check_Type(src, T_STRING); @@ -1176,7 +1275,7 @@ Fstr_tr(str, src, repl) char r; for (i=0; i<256; i++) { - trans[i] = 0; + trans[i] = i; } while ((c = trnext(&trsrc)) >= 0) { r = trnext(&trrepl); @@ -1197,6 +1296,13 @@ Fstr_tr(str, src, repl) return (VALUE)str; } +static VALUE +str_tr(str, src, repl) + struct RString *str, *src, *repl; +{ + return str_tr_bang(str_dup(str), src, repl); +} + static void tr_setup_table(str, table) struct RString *str; @@ -1222,7 +1328,7 @@ tr_setup_table(str, table) } static VALUE -Fstr_delete(str1, str2) +str_delete_bang(str1, str2) struct RString *str1, *str2; { char *s, *send, *t; @@ -1248,6 +1354,13 @@ Fstr_delete(str1, str2) } static VALUE +str_delete(str1, str2) + struct RString *str1, *str2; +{ + return str_delete_bang(str_dup(str1), str2); +} + +static VALUE tr_squeeze(str1, str2) struct RString *str1, *str2; { @@ -1284,7 +1397,7 @@ tr_squeeze(str1, str2) } static VALUE -Fstr_squeeze(argc, argv, str1) +str_squeeze_bang(argc, argv, str1) int argc; VALUE *argv; VALUE str1; @@ -1299,18 +1412,34 @@ Fstr_squeeze(argc, argv, str1) } static VALUE -Fstr_tr_s(str, src, repl) +str_squeeze(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + return str_squeeze_bang(argc, argv, str_dup(str)); +} + +static VALUE +str_tr_s_bang(str, src, repl) VALUE str, src, repl; { Check_Type(src, T_STRING); Check_Type(repl, T_STRING); - Fstr_tr(str, src, repl); + str_tr(str, src, repl); tr_squeeze(str, repl); return str; } static VALUE -Fstr_split(argc, argv, str) +str_tr_s(str, src, repl) + VALUE str, src, repl; +{ + return str_tr_s_bang(str_dup(str), src, repl); +} + +static VALUE +str_split_method(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -1343,7 +1472,7 @@ Fstr_split(argc, argv, str) char_sep = RSTRING(spat)->ptr[0]; } else { - spat = (struct RRegexp*)re_regcomp(spat); + spat = (struct RRegexp*)reg_regcomp(spat); } break; case T_REGEXP: @@ -1401,9 +1530,11 @@ Fstr_split(argc, argv, str) int start = beg; int last_null = 0; int idx; + struct re_registers regs; - while ((end = research(spat, str, start)) >= 0) { - if (start == end && BEG(0) == END(0)) { + regs.allocated = 0; + while ((end = reg_search(spat, str, start, ®s)) >= 0) { + if (start == end && regs.beg[0] == regs.end[0]) { if (last_null == 1) { if (ismbchar(str->ptr[beg])) ary_push(result, str_substr(str, beg, 2)); @@ -1420,22 +1551,23 @@ Fstr_split(argc, argv, str) } else { ary_push(result, str_substr(str, beg, end-beg)); - beg = start = END(0); + beg = start = regs.end[0]; if (limit && lim <= ++i) break; } last_null = 0; for (idx=1; idx < 10; idx++) { - if (BEG(idx) == -1) break; - if (BEG(idx) == END(idx)) + if (regs.beg[idx] == -1) break; + if (regs.beg[idx] == regs.end[idx]) tmp = str_new(0, 0); else - tmp = str_subseq(str, BEG(idx), END(idx)-1); + tmp = str_subseq(str, regs.beg[idx], regs.end[idx]-1); ary_push(result, tmp); if (limit && lim <= ++i) break; } } + re_free_registers(®s); } if (str->len > beg) { ary_push(result, str_subseq(str, beg, -1)); @@ -1444,8 +1576,19 @@ Fstr_split(argc, argv, str) return result; } +VALUE +str_split(str, sep0) + struct RString* str; + char *sep0; +{ + VALUE sep; + + sep = str_new2(sep0); + return str_split_method(1, &sep, str); +} + static VALUE -Fstr_each(str) +str_each_line(str) struct RString* str; { extern VALUE RS; @@ -1453,6 +1596,7 @@ Fstr_each(str) int rslen; char *p = str->ptr, *pend = p + str->len, *s; char *ptr = p; + int len = str->len; if (RS == Qnil) { rb_yield(str); @@ -1478,7 +1622,8 @@ Fstr_each(str) memcmp(RSTRING(RS)->ptr, p-rslen+1, rslen) == 0)) { rb_lastline = str_new(s, p - s + 1); rb_yield(rb_lastline); - if (str->ptr != ptr) Fail("string modified"); + if (str->ptr != ptr || str->len != len) + Fail("string modified"); s = p + 1; } } @@ -1492,7 +1637,7 @@ Fstr_each(str) } static VALUE -Fstr_each_byte(str) +str_each_byte(str) struct RString* str; { int i; @@ -1504,7 +1649,7 @@ Fstr_each_byte(str) } static VALUE -Fstr_chop(str) +str_chop_bang(str) struct RString *str; { str_modify(str); @@ -1516,11 +1661,20 @@ Fstr_chop(str) } static VALUE -Fstr_strip(str) +str_chop(str) + struct RString *str; +{ + return str_chop_bang(str_dup(str)); +} + +static VALUE +str_strip_bang(str) struct RString *str; { char *s, *t, *e; + str_modify(str); + s = str->ptr; e = t = s + str->len; /* remove spaces at head */ @@ -1531,49 +1685,55 @@ Fstr_strip(str) while (s <= t && isspace(*t)) t--; t++; - if (s > str->ptr || t < e) { + str->len = t-s; + if (s > str->ptr) { char *p = str->ptr; - int len = t-s; - str->ptr = ALLOC_N(char, len+1); - memcpy(str->ptr, p, len); - str->ptr[len] = '\0'; - if (str->orig) { - str->orig = Qnil; - } - else { - free(p); - } + str->ptr = ALLOC_N(char, str->len+1); + memcpy(str->ptr, s, str->len); + str->ptr[str->len] = '\0'; + free(p); + } + else if (t < e) { + str->ptr[str->len] = '\0'; } + return (VALUE)str; } static VALUE -Fstr_hex(str) +str_strip(str) + struct RString *str; +{ + return str_strip_bang(str_dup(str)); +} + +static VALUE +str_hex(str) struct RString *str; { return str2inum(str->ptr, 16); } static VALUE -Fstr_oct(str) +str_oct(str) struct RString *str; { return str2inum(str->ptr, 8); } static VALUE -Fstr_crypt(str, salt) +str_crypt(str, salt) struct RString *str, *salt; { - Check_Type(salt, T_STRING); + salt = as_str(salt); if (salt->len < 2) - Fail("salt too short(need 2 byte)"); + Fail("salt too short(need >2 bytes)"); return str_new2(crypt(str->ptr, salt->ptr)); } static VALUE -Fstr_intern(str) +str_intern(str) struct RString *str; { if (strlen(str->ptr) != str->len) @@ -1583,7 +1743,7 @@ Fstr_intern(str) } static VALUE -Fstr_sum(argc, argv, str) +str_sum(argc, argv, str) int argc; VALUE *argv; struct RString *str; @@ -1624,7 +1784,8 @@ Fstr_sum(argc, argv, str) } } -Fstr_ljust(str, w) +VALUE +str_ljust(str, w) struct RString *str; VALUE w; { @@ -1642,7 +1803,8 @@ Fstr_ljust(str, w) return (VALUE)res; } -Fstr_rjust(str, w) +VALUE +str_rjust(str, w) struct RString *str; VALUE w; { @@ -1660,7 +1822,8 @@ Fstr_rjust(str, w) return (VALUE)res; } -Fstr_center(str, w) +VALUE +str_center(str, w) struct RString *str; VALUE w; { @@ -1684,76 +1847,97 @@ Fstr_center(str, w) return (VALUE)res; } -extern VALUE C_Kernel; -extern VALUE M_Comparable; -extern VALUE M_Enumerable; +extern VALUE cKernel; +extern VALUE mComparable; +extern VALUE mEnumerable; +void Init_String() { - C_String = rb_define_class("String", C_Object); - rb_include_module(C_String, M_Comparable); - rb_include_module(C_String, M_Enumerable); - rb_define_single_method(C_String, "new", Sstr_new, 1); - rb_define_method(C_String, "clone", str_clone, 0); - rb_define_method(C_String, "<=>", Fstr_cmp, 1); - rb_define_method(C_String, "==", Fstr_equal, 1); - rb_define_method(C_String, "hash", Fstr_hash, 0); - rb_define_method(C_String, "+", Fstr_plus, 1); - rb_define_method(C_String, "*", Fstr_times, 1); - rb_define_method(C_String, "..", Fstr_dot2, 1); - rb_define_method(C_String, "[]", Fstr_aref, -1); - rb_define_method(C_String, "[]=", Fstr_aset, -1); - rb_define_method(C_String, "length", Fstr_length, 0); - rb_define_alias(C_String, "size", "length"); - rb_define_method(C_String, "=~", Fstr_match, 1); - rb_define_method(C_String, "~", Fstr_match2, 0); - rb_define_method(C_String, "next", Fstr_next, 0); - rb_define_method(C_String, "upto", Fstr_next, 1); - rb_define_method(C_String, "index", Fstr_index, -1); - rb_define_method(C_String, "rindex", Fstr_rindex, -1); - - rb_define_method(C_String, "to_i", Fstr_to_i, 0); - rb_define_method(C_String, "to_f", Fstr_to_f, 0); - rb_define_method(C_String, "to_s", Fstr_to_s, 0); - rb_define_method(C_String, "_inspect", Fstr_inspect, 0); - - rb_define_method(C_String, "toupper", Fstr_toupper, 0); - rb_define_method(C_String, "tolower", Fstr_tolower, 0); - - rb_define_method(C_String, "upcase", Fstr_upcase, 0); - rb_define_method(C_String, "downcase", Fstr_downcase, 0); - rb_define_method(C_String, "capitalize", Fstr_capitalize, 0); - rb_define_method(C_String, "swapcase", Fstr_swapcase, 0); - - rb_define_method(C_String, "hex", Fstr_hex, 0); - rb_define_method(C_String, "oct", Fstr_oct, 0); - rb_define_method(C_String, "split", Fstr_split, -1); - rb_define_method(C_String, "reverse", Fstr_reverse, 0); - rb_define_method(C_String, "concat", Fstr_concat, 1); - rb_define_method(C_String, "crypt", Fstr_crypt, 1); - rb_define_method(C_String, "intern", Fstr_intern, 0); - - rb_define_method(C_String, "ljust", Fstr_ljust, 1); - rb_define_method(C_String, "rjust", Fstr_rjust, 1); - rb_define_method(C_String, "center", Fstr_center, 1); - - rb_define_method(C_String, "sub", Fstr_sub, -1); - rb_define_method(C_String, "gsub", Fstr_gsub, -1); - rb_define_method(C_String, "chop", Fstr_chop, 0); - rb_define_method(C_String, "strip", Fstr_strip, 0); - - rb_define_method(C_String, "tr", Fstr_tr, 2); - rb_define_method(C_String, "tr_s", Fstr_tr_s, 2); - rb_define_method(C_String, "delete", Fstr_delete, 1); - rb_define_method(C_String, "squeeze", Fstr_squeeze, -1); - - rb_define_method(C_String, "each", Fstr_each, 0); - rb_define_method(C_String, "each_byte", Fstr_each_byte, 0); - - rb_define_method(C_String, "sum", Fstr_sum, -1); - - rb_define_private_method(C_Kernel, "sub", Fsub, -1); - rb_define_private_method(C_Kernel, "gsub", Fgsub, -1); + cString = rb_define_class("String", cObject); + rb_include_module(cString, mComparable); + rb_include_module(cString, mEnumerable); + rb_define_singleton_method(cString, "new", str_s_new, 1); + rb_define_method(cString, "clone", str_clone, 0); + rb_define_method(cString, "dup", str_dup, 0); + rb_define_method(cString, "<=>", str_cmp_method, 1); + rb_define_method(cString, "==", str_equal, 1); + rb_define_method(cString, "hash", str_hash_method, 0); + rb_define_method(cString, "+", str_plus, 1); + rb_define_method(cString, "*", str_times, 1); + rb_define_method(cString, "[]", str_aref_method, -1); + rb_define_method(cString, "[]=", str_aset_method, -1); + rb_define_method(cString, "length", str_length, 0); + rb_define_alias(cString, "size", "length"); + rb_define_method(cString, "=~", str_match, 1); + rb_define_method(cString, "~", str_match2, 0); + rb_define_method(cString, "next", str_next_method, 0); + rb_define_method(cString, "upto", str_next, 1); + rb_define_method(cString, "index", str_index_method, -1); + rb_define_method(cString, "rindex", str_rindex, -1); + + rb_define_method(cString, "freeze", str_freeze, 0); + rb_define_method(cString, "frozen?", str_frozen, 0); + + rb_define_method(cString, "to_i", str_to_i, 0); + rb_define_method(cString, "to_f", str_to_f, 0); + rb_define_method(cString, "to_s", str_to_s, 0); + rb_define_method(cString, "inspect", str_inspect, 0); + + rb_define_method(cString, "upcase", str_upcase, 0); + rb_define_method(cString, "downcase", str_downcase, 0); + rb_define_method(cString, "capitalize", str_capitalize, 0); + rb_define_method(cString, "swapcase", str_swapcase, 0); + + rb_define_method(cString, "upcase!", str_upcase_bang, 0); + rb_define_method(cString, "downcase!", str_downcase_bang, 0); + rb_define_method(cString, "capitalize!", str_capitalize_bang, 0); + rb_define_method(cString, "swapcase!", str_swapcase_bang, 0); + + rb_define_method(cString, "hex", str_hex, 0); + rb_define_method(cString, "oct", str_oct, 0); + rb_define_method(cString, "split", str_split_method, -1); + rb_define_method(cString, "reverse", str_reverse, 0); + rb_define_method(cString, "reverse!", str_reverse_bang, 0); + rb_define_method(cString, "concat", str_concat, 1); + rb_define_method(cString, "crypt", str_crypt, 1); + rb_define_method(cString, "intern", str_intern, 0); + + rb_define_method(cString, "ljust", str_ljust, 1); + rb_define_method(cString, "rjust", str_rjust, 1); + rb_define_method(cString, "center", str_center, 1); + + rb_define_method(cString, "sub", str_sub_method, -1); + rb_define_method(cString, "gsub", str_gsub, -1); + rb_define_method(cString, "chop", str_chop, 0); + rb_define_method(cString, "strip", str_strip, 0); + + rb_define_method(cString, "sub!", str_sub_bang, -1); + rb_define_method(cString, "gsub!", str_gsub_bang, -1); + rb_define_method(cString, "strip!", str_strip_bang, 0); + rb_define_method(cString, "chop!", str_chop_bang, 0); + + rb_define_method(cString, "tr", str_tr, 2); + rb_define_method(cString, "tr_s", str_tr_s, 2); + rb_define_method(cString, "delete", str_delete, 1); + rb_define_method(cString, "squeeze", str_squeeze, -1); + + rb_define_method(cString, "tr!", str_tr_bang, 2); + rb_define_method(cString, "tr_s!", str_tr_s_bang, 2); + rb_define_method(cString, "delete!", str_delete_bang, 1); + rb_define_method(cString, "squeeze!", str_squeeze_bang, -1); + + rb_define_method(cString, "each_line", str_each_line, 0); + rb_define_method(cString, "each_byte", str_each_byte, 0); + rb_define_method(cString, "each", str_each_byte, 0); + + rb_define_method(cString, "sum", str_sum, -1); + + rb_define_private_method(cKernel, "sub", f_sub, -1); + rb_define_private_method(cKernel, "gsub", f_gsub, -1); + + rb_define_private_method(cKernel, "sub!", f_sub_bang, -1); + rb_define_private_method(cKernel, "gsub!", f_gsub_bang, -1); pr_str = rb_intern("to_s"); } @@ -9,203 +9,263 @@ ************************************************/ #include "ruby.h" -#include "env.h" -VALUE C_Struct; -extern VALUE M_Enumerable; - -char *strdup(); +ID rb_frame_last_func(); +VALUE cStruct; +extern VALUE mEnumerable; static VALUE -struct_alloc(class, name) - VALUE class; - char *name; +struct_ref(obj) + struct RStruct *obj; { - NEWOBJ(st, struct RStruct); - OBJSETUP(st, class, T_STRUCT); - - if (name) st->name = strdup(name); - else st->name = Qnil; - st->len = 0; - st->tbl = Qnil; + VALUE nstr, member, slot; + int i; - return (VALUE)st; + nstr = CLASS_OF(obj); + member = rb_ivar_get(nstr, rb_intern("__member__")); + if (member == Qnil) { + Fail("non-initialized struct"); + } + slot = INT2FIX(rb_frame_last_func()); + for (i=0; i<RARRAY(member)->len; i++) { + if (RARRAY(member)->ptr[i] == slot) { + return obj->ptr[i]; + } + } + Fail("not struct member"); + return Qnil; /* not reached */ } +static VALUE struct_ref0(obj) struct RStruct *obj; {return obj->ptr[0];} +static VALUE struct_ref1(obj) struct RStruct *obj; {return obj->ptr[1];} +static VALUE struct_ref2(obj) struct RStruct *obj; {return obj->ptr[2];} +static VALUE struct_ref3(obj) struct RStruct *obj; {return obj->ptr[3];} +static VALUE struct_ref4(obj) struct RStruct *obj; {return obj->ptr[4];} +static VALUE struct_ref5(obj) struct RStruct *obj; {return obj->ptr[5];} +static VALUE struct_ref6(obj) struct RStruct *obj; {return obj->ptr[6];} +static VALUE struct_ref7(obj) struct RStruct *obj; {return obj->ptr[7];} +static VALUE struct_ref8(obj) struct RStruct *obj; {return obj->ptr[8];} +static VALUE struct_ref9(obj) struct RStruct *obj; {return obj->ptr[9];} + +VALUE (*ref_func[10])() = { + struct_ref0, + struct_ref1, + struct_ref2, + struct_ref3, + struct_ref4, + struct_ref5, + struct_ref6, + struct_ref7, + struct_ref8, + struct_ref9, +}; + static VALUE -struct_find(s, id) - struct RStruct *s; - ID id; +struct_set(obj, val) + struct RStruct *obj; + VALUE val; { - struct kv_pair *t, *tend; + VALUE nstr, member, slot; + int i; - t = s->tbl; - tend = t + s->len; - while (t < tend) { - if (t->key == id) return t->value; - t++; + nstr = CLASS_OF(obj); + member = rb_ivar_get(nstr, rb_intern("__member__")); + if (member == Qnil) { + Fail("non-initialized struct"); } - Fail("struct %s has no member %s", s->name, rb_id2name(id)); + for (i=0; i<RARRAY(member)->len; i++) { + slot = RARRAY(member)->ptr[i]; + if (id_attrset(FIX2INT(slot)) == rb_frame_last_func()) { + return obj->ptr[i] = val; + } + } + Fail("not struct member"); + return Qnil; /* not reached */ } -static VALUE -Fstruct_access(s) - struct RStruct *s; -{ - return struct_find(s, the_env->last_func); -} +static VALUE struct_s_new(); static VALUE -struct_add(s, mem, val) - struct RStruct *s; - char *mem; - VALUE val; +make_struct(name, member) + struct RString *name; + struct RArray *member; { - int pos = s->len; + VALUE nstr; + int i; - s->len++; - if (s->tbl == Qnil) { - s->tbl = ALLOC_N(struct kv_pair, 1); - } - else { - REALLOC_N(s->tbl, struct kv_pair, s->len); + nstr = rb_define_class_under(cStruct, name->ptr, cStruct); + rb_ivar_set(nstr, rb_intern("__size__"), INT2FIX(member->len)); + rb_ivar_set(nstr, rb_intern("__member__"), member); + + rb_define_singleton_method(nstr, "new", struct_s_new, -1); + for (i=0; i< member->len; i++) { + ID id = FIX2INT(member->ptr[i]); + if (i<10) { + rb_define_method_id(nstr, id, ref_func[i], 0); + } + else { + rb_define_method_id(nstr, id, struct_ref, 0); + } + rb_define_method_id(nstr, id_attrset(id), struct_set, 1); } - s->tbl[pos].key = rb_intern(mem); - s->tbl[pos].value = val; - rb_define_single_method(s, mem, Fstruct_access, 0); + return nstr; } #include <varargs.h> VALUE -struct_new(name, va_alist) +struct_define(name, va_alist) char *name; va_dcl { - VALUE st; - va_list args; + va_list ar; + VALUE nm, ary; char *mem; - st = struct_alloc(C_Struct,name); - va_start(args); - while (mem = va_arg(args, char*)) { - struct_add(st, mem, va_arg(args, VALUE)); + nm = str_new2(name); + ary = ary_new(); + + va_start(ar); + while (mem = va_arg(ar, char*)) { + ID slot = rb_intern(mem); + ary_push(ary, INT2FIX(slot)); } - va_end(vargs); + va_end(ar); - return st; + return make_struct(nm, ary); } -#define ASSOC_KEY(a) RASSOC(a)->car -#define ASSOC_VAL(a) RASSOC(a)->cdr - static VALUE -Sstruct_new(argc, argv, class) +struct_s_def(argc, argv) int argc; VALUE *argv; - VALUE class; { - VALUE name, st; - struct RArray *tbl; - int i, max; + struct RString *name; + struct RArray *rest; + VALUE nstr; + int i; - rb_scan_args(argc, argv, "1*", &name, &tbl); + rb_scan_args(argc, argv, "1*", &name, &rest); Check_Type(name, T_STRING); + for (i=0; i<rest->len; i++) { + Check_Type(rest->ptr[i], T_FIXNUM); + } + return make_struct(name, rest); +} - st = struct_alloc(class, RSTRING(name)->ptr); - for (i=0, max=tbl->len; i<max; i++) { - VALUE assoc = tbl->ptr[i]; +VALUE +struct_alloc(class, values) + VALUE class; + struct RArray *values; +{ + VALUE size; + int n; - Check_Type(assoc, T_ASSOC); - Check_Type(ASSOC_KEY(assoc), T_STRING); - struct_add(st, RSTRING(ASSOC_KEY(assoc))->ptr, ASSOC_VAL(assoc)); + size = rb_ivar_get(class, rb_intern("__size__")); + n = FIX2INT(size); + if (n < values->len) { + Fail("struct size differs"); } - - return st; + else { + NEWOBJ(st, struct RStruct); + OBJSETUP(st, class, T_STRUCT); + st->len = n; + st->ptr = ALLOC_N(VALUE, n); + MEMCPY(st->ptr, values->ptr, VALUE, values->len); + MEMZERO(st->ptr+values->len, VALUE, n - values->len); + + return (VALUE)st; + } + return Qnil; /* not reached */ } -static VALUE -Fstruct_each(s) - struct RStruct *s; +VALUE +struct_new(class, va_alist) + VALUE class; + va_dcl { - struct kv_pair *t, *tend; + VALUE val, mem; + va_list args; - t = s->tbl; - tend = t + s->len; - while (t < tend) { - rb_yield(t->value); - t++; + mem = ary_new(); + va_start(args); + while (val = va_arg(args, VALUE)) { + ary_push(mem, val); } + va_end(args); + + return struct_alloc(class, mem); } static VALUE -Fstruct_values(s) - struct RStruct *s; +struct_s_new(argc, argv, obj) + int argc; + VALUE *argv; { - VALUE ary; - struct kv_pair *t, *tend; + VALUE member, slot; - ary = ary_new(); - t = s->tbl; - tend = t + s->len; - while (t < tend) { - ary_push(ary, t->value); - t++; - } - - return ary; + member = ary_new4(argc, argv); + return struct_alloc(obj, member); } static VALUE -Fstruct_aref(s, idx) +struct_each(s) struct RStruct *s; - VALUE idx; { - struct RArray *ary; int i; - if (TYPE(idx) == T_STRING) - return struct_find(rb_intern(RSTRING(idx)->ptr)); - - i = NUM2INT(idx); - if (s->len <= i) - Fail("offset %d too large for struct(size:%d)", i, s->len); - return s->tbl[i].value; + for (i=0; i<s->len; i++) { + rb_yield(s->ptr[i]); + } + return Qnil; } +char *rb_class2name(); #define HDR "struct " static VALUE -Fstruct_to_s(s) +struct_to_s(s) struct RStruct *s; { - char *buf; + char *name, *buf; - buf = ALLOCA_N(char, strlen(s->name)+sizeof(HDR)+1); - sprintf(buf, "%s%s", HDR, s->name); + name = rb_class2name(CLASS_OF(s)); + buf = ALLOCA_N(char, strlen(name)+sizeof(HDR)+1); + sprintf(buf, "%s%s", HDR, name); return str_new2(buf); } static VALUE -Fstruct_inspect(s) +struct_inspect(s) struct RStruct *s; { - VALUE str, str2; - char buf[256], *p; + char *name = rb_class2name(CLASS_OF(s)); + ID inspect = rb_intern("inspect"); + VALUE str, member; + char buf[256]; int i; - ID inspect = rb_intern("_inspect"); - sprintf(buf, "#<%s%s: ", HDR, s->name); + member = rb_ivar_get(CLASS_OF(s), rb_intern("__member__")); + if (member == Qnil) { + Fail("non-initialized struct"); + } + + sprintf(buf, "#<%s%s: ", HDR, name); str = str_new2(buf); for (i=0; i<s->len; i++) { + VALUE str2, slot; + char *p; + if (i > 0) { str_cat(str, ", ", 2); } - p = rb_id2name(s->tbl[i].key); + slot = RARRAY(member)->ptr[i]; + p = rb_id2name(FIX2INT(slot)); str_cat(str, p, strlen(p)); str_cat(str, "=", 1); - str2 = rb_funcall(s->tbl[i].value, inspect, 0, Qnil); + str2 = rb_funcall(s->ptr[i], inspect, 0, 0); + str2 = obj_as_string(str2); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); } str_cat(str, ">", 1); @@ -214,47 +274,108 @@ Fstruct_inspect(s) } static VALUE -Fstruct_to_a(s) +struct_to_a(s) + struct RStruct *s; +{ + return ary_new4(s->len, s->ptr); +} + +static VALUE +struct_clone(s) + struct RStruct *s; +{ + NEWOBJ(st, struct RStruct); + CLONESETUP(st, s); + st->len = s->len; + st->ptr = ALLOC_N(VALUE, s->len); + MEMCPY(st->ptr, s->ptr, VALUE, st->len); + + return (VALUE)st; +} + +static VALUE +struct_aref(s, idx) struct RStruct *s; + VALUE idx; { - VALUE ary; int i; - ary = ary_new2(s->len); - for (i=0; i<s->len; i++) { - ary_push(ary, s->tbl[i].value); + i = NUM2INT(idx); + if (i < 0) i = s->len - i; + if (i < 0) + Fail("offset %d too small for struct(size:%d)", i, s->len); + if (s->len <= i) + Fail("offset %d too large for struct(size:%d)", i, s->len); + return s->ptr[i]; +} + +static VALUE +struct_aset(s, idx, val) + struct RStruct *s; + VALUE idx, val; +{ + int i; + + i = NUM2INT(idx); + if (i < 0) i = s->len - i; + if (i < 0) + Fail("offset %d too small for struct(size:%d)", i, s->len); + if (s->len <= i) + Fail("offset %d too large for struct(size:%d)", i, s->len); + return s->ptr[i] = val; +} + +static VALUE +struct_equal(s, s2) + struct RStruct *s, *s2; +{ + int i; + + if (TYPE(s2) != T_STRUCT) return FALSE; + if (CLASS_OF(s) != CLASS_OF(s2)) return FALSE; + if (s->len != s2->len) { + Fail("incomsistent struct"); } - return ary; + for (i=0; i<s->len; i++) { + if (!rb_equal(s->ptr[i], s2->ptr[i])) return FALSE; + } + return TRUE; } static VALUE -Fstruct_clone(s) +struct_hash(s) struct RStruct *s; { - struct RStruct *st = (struct RStruct*)struct_alloc(s->name); + int i, h; + ID hash = rb_intern("hash"); - CLONESETUP(st, s); - st->len = s->len; - st->tbl = ALLOC_N(struct kv_pair, s->len); - MEMCPY(st->tbl, s->tbl, struct kv_pair, st->len); - RBASIC(st)->class = single_class_clone(RBASIC(s)->class); - return (VALUE)st; + h = CLASS_OF(s); + for (i=0; i<s->len; i++) { + h ^= rb_funcall(s->ptr[i], hash, 0); + } + return INT2FIX(h); } +void Init_Struct() { - C_Struct = rb_define_class("Struct", C_Object); - rb_include_module(C_Struct, M_Enumerable); + cStruct = rb_define_class("Struct", cObject); + rb_include_module(cStruct, mEnumerable); + + rb_define_singleton_method(cStruct, "new", struct_s_def, -1); + + rb_define_method(cStruct, "clone", struct_clone, 0); - rb_define_single_method(C_Struct, "new", Sstruct_new, -1); - rb_define_method(C_Struct, "clone", Fstruct_clone, 0); + rb_define_method(cStruct, "==", struct_equal, 1); + rb_define_method(cStruct, "hash", struct_hash, 0); - rb_define_method(C_Struct, "to_s", Fstruct_to_s, 0); - rb_define_method(C_Struct, "_inspect", Fstruct_inspect, 0); - rb_define_method(C_Struct, "to_a", Fstruct_to_a, 0); + rb_define_method(cStruct, "to_s", struct_to_s, 0); + rb_define_method(cStruct, "inspect", struct_inspect, 0); + rb_define_method(cStruct, "to_a", struct_to_a, 0); + rb_define_method(cStruct, "values", struct_to_a, 0); - rb_define_method(C_Struct, "each", Fstruct_each, 0); - rb_define_method(C_Struct, "values", Fstruct_values, 0); - rb_define_method(C_Struct, "[]", Fstruct_aref, 1); + rb_define_method(cStruct, "each", struct_each, 0); + rb_define_method(cStruct, "[]", struct_aref, 1); + rb_define_method(cStruct, "[]=", struct_aset, 2); } @@ -12,12 +12,27 @@ #include "ruby.h" #include <sys/types.h> -#include <sys/time.h> + +#include <time.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif + +#ifdef HAVE_SYS_TIMES_H #include <sys/times.h> +#endif #include <math.h> -static VALUE C_Time; -extern VALUE M_Comparable; +static VALUE cTime; +#if defined(HAVE_TIMES) || defined(NT) +static VALUE S_Tms; +#endif +extern VALUE mComparable; struct time_object { struct timeval tv; @@ -37,12 +52,12 @@ static ID id_tv; #define MakeTimeval(obj,tobj) {\ if (!id_tv) id_tv = rb_intern("tv");\ - Make_Data_Struct(obj, id_tv, struct time_object, Qnil, Qnil, tobj);\ + Make_Data_Struct(obj, id_tv, struct time_object, 0, 0, tobj);\ tobj->tm_got=0;\ } static VALUE -Stime_now(class) +time_s_now(class) VALUE class; { VALUE obj = obj_alloc(class); @@ -59,6 +74,7 @@ Stime_now(class) static VALUE time_new_internal(class, sec, usec) + VALUE class; int sec, usec; { VALUE obj = obj_alloc(class); @@ -75,7 +91,7 @@ VALUE time_new(sec, usec) int sec, usec; { - return time_new_internal(C_Time, sec, usec); + return time_new_internal(cTime, sec, usec); } struct timeval* @@ -107,7 +123,7 @@ time_timeval(time) break; default: - if (!obj_is_kind_of(time, C_Time)) { + if (!obj_is_kind_of(time, cTime)) { Fail("Can't convert %s into Time", rb_class2name(CLASS_OF(time))); } GetTimeval(time, tobj); @@ -118,12 +134,9 @@ time_timeval(time) } static VALUE -Stime_at(class, time) +time_s_at(class, time) VALUE class, time; { - VALUE obj; - int sec, usec; - struct time_object *tobj; struct timeval *tp; tp = time_timeval(time); @@ -132,7 +145,7 @@ Stime_at(class, time) } static VALUE -Ftime_to_i(time) +time_to_i(time) VALUE time; { struct time_object *tobj; @@ -142,7 +155,7 @@ Ftime_to_i(time) } static VALUE -Ftime_to_f(time) +time_to_f(time) VALUE time; { struct time_object *tobj; @@ -152,7 +165,7 @@ Ftime_to_f(time) } static VALUE -Ftime_usec(time) +time_usec(time) VALUE time; { struct time_object *tobj; @@ -162,14 +175,14 @@ Ftime_usec(time) } static VALUE -Ftime_cmp(time1, time2) +time_cmp(time1, time2) VALUE time1, time2; { struct time_object *tobj1, *tobj2; int i; GetTimeval(time1, tobj1); - if (obj_is_member_of(time2, C_Time)) { + if (obj_is_instance_of(time2, cTime)) { GetTimeval(time2, tobj2); if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) { if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0); @@ -186,7 +199,7 @@ Ftime_cmp(time1, time2) } static VALUE -Ftime_hash(time) +time_hash(time) VALUE time; { struct time_object *tobj; @@ -198,7 +211,7 @@ Ftime_hash(time) } static VALUE -Ftime_localtime(time) +time_localtime(time) VALUE time; { struct time_object *tobj; @@ -215,7 +228,7 @@ Ftime_localtime(time) } static VALUE -Ftime_gmtime(time) +time_gmtime(time) VALUE time; { struct time_object *tobj; @@ -232,17 +245,16 @@ Ftime_gmtime(time) } static VALUE -Ftime_asctime(time) +time_asctime(time) VALUE time; { struct time_object *tobj; - char *ct; char buf[32]; int len; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } #ifndef HAVE_TM_ZONE if (tobj->gmt == 1) @@ -256,21 +268,21 @@ Ftime_asctime(time) } static VALUE -Ftime_coerce(time1, time2) +time_coerce(time1, time2) VALUE time1, time2; { return time_new(CLASS_OF(time1), NUM2INT(time2), 0); } static VALUE -Ftime_plus(time1, time2) +time_plus(time1, time2) VALUE time1, time2; { struct time_object *tobj1, *tobj2; int sec, usec; GetTimeval(time1, tobj1); - if (obj_is_member_of(time2, C_Time)) { + if (obj_is_instance_of(time2, cTime)) { GetTimeval(time2, tobj2); sec = tobj1->tv.tv_sec + tobj2->tv.tv_sec; usec = tobj1->tv.tv_usec + tobj2->tv.tv_usec; @@ -287,14 +299,14 @@ Ftime_plus(time1, time2) } static VALUE -Ftime_minus(time1, time2) +time_minus(time1, time2) VALUE time1, time2; { struct time_object *tobj1, *tobj2; int sec, usec; GetTimeval(time1, tobj1); - if (obj_is_member_of(time2, C_Time)) { + if (obj_is_instance_of(time2, cTime)) { GetTimeval(time2, tobj2); sec = tobj1->tv.tv_sec - tobj2->tv.tv_sec; usec = tobj1->tv.tv_usec - tobj2->tv.tv_usec; @@ -311,124 +323,124 @@ Ftime_minus(time1, time2) } static VALUE -Ftime_sec(time) +time_sec(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_sec); } static VALUE -Ftime_min(time) +time_min(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_min); } static VALUE -Ftime_hour(time) +time_hour(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_hour); } static VALUE -Ftime_mday(time) +time_mday(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_mday); } static VALUE -Ftime_mon(time) +time_mon(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_mon); } static VALUE -Ftime_year(time) +time_year(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_year); } static VALUE -Ftime_wday(time) +time_wday(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_wday); } static VALUE -Ftime_yday(time) +time_yday(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_yday); } static VALUE -Ftime_isdst(time) +time_isdst(time) VALUE time; { struct time_object *tobj; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } return INT2FIX(tobj->tm.tm_isdst); } static VALUE -Ftime_zone(time) +time_zone(time) VALUE time; { struct time_object *tobj; @@ -437,7 +449,7 @@ Ftime_zone(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } len = strftime(buf, 10, "%Z", &(tobj->tm)); @@ -445,17 +457,15 @@ Ftime_zone(time) } static VALUE -Ftime_to_a(time) +time_to_a(time) VALUE time; { struct time_object *tobj; - struct tm *tm; - char buf[10]; VALUE ary; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } ary = ary_new3(9, INT2FIX(tobj->tm.tm_sec), @@ -471,7 +481,7 @@ Ftime_to_a(time) } static VALUE -Ftime_strftime(time, format) +time_strftime(time, format) VALUE time, format; { struct time_object *tobj; @@ -481,12 +491,12 @@ Ftime_strftime(time, format) Check_Type(format, T_STRING); GetTimeval(time, tobj); if (tobj->tm_got == 0) { - Ftime_localtime(time); + time_localtime(time); } if (strlen(RSTRING(format)->ptr) < RSTRING(format)->len) { /* Ruby string contains \0. */ VALUE str; - int l, total = 0; + int l; char *p = RSTRING(format)->ptr, *pe = p + RSTRING(format)->len; str = str_new(0, 0); @@ -503,60 +513,89 @@ Ftime_strftime(time, format) } static VALUE -Stime_times(obj) +time_s_times(obj) VALUE obj; { +#ifdef HAVE_TIMES +#ifndef HZ +#define HZ 60 /* Universal constant :-) */ +#endif /* HZ */ struct tms buf; if (times(&buf) == -1) rb_sys_fail(Qnil); - return struct_new("tms", - "utime", float_new((double)buf.tms_utime / 60.0), - "stime", float_new((double)buf.tms_stime / 60.0), - "cutime", float_new((double)buf.tms_cutime / 60.0), - "cstime", float_new((double)buf.tms_cstime / 60.0), + return struct_new(S_Tms, + float_new((double)buf.tms_utime / HZ), + float_new((double)buf.tms_stime / HZ), + float_new((double)buf.tms_cutime / HZ), + float_new((double)buf.tms_cstime / HZ), Qnil); +#else +#ifdef NT + FILETIME create, exit, kernel, user; + HANDLE hProc; + + hProc = GetCurrentProcess(); + GetProcessTimes(hProc,&create, &exit, &kernel, &user); + return struct_new(S_Tms, + float_new((double)(kernel.dwHighDateTime*2E32+kernel.dwLowDateTime)/2E6), + float_new((double)(user.dwHighDateTime*2E32+user.dwLowDateTime)/2E6), + float_new((double)0), + float_new((double)0), + Qnil); +#else + Fail("can't call times"); + return Qnil; +#endif +#endif } +void Init_Time() { - C_Time = rb_define_class("Time", C_Object); - rb_include_module(C_Time, M_Comparable); - - rb_define_single_method(C_Time, "now", Stime_now, 0); - rb_define_single_method(C_Time, "new", Stime_now, 0); - rb_define_single_method(C_Time, "at", Stime_at, 1); - - rb_define_single_method(C_Time, "times", Stime_times, 0); - - rb_define_method(C_Time, "to_i", Ftime_to_i, 0); - rb_define_method(C_Time, "to_f", Ftime_to_f, 0); - rb_define_method(C_Time, "<=>", Ftime_cmp, 1); - rb_define_method(C_Time, "hash", Ftime_hash, 0); - - rb_define_method(C_Time, "localtime", Ftime_localtime, 0); - rb_define_method(C_Time, "gmtime", Ftime_gmtime, 0); - rb_define_method(C_Time, "ctime", Ftime_asctime, 0); - rb_define_method(C_Time, "asctime", Ftime_asctime, 0); - rb_define_method(C_Time, "to_s", Ftime_asctime, 0); - rb_define_method(C_Time, "_inspect", Ftime_asctime, 0); - rb_define_method(C_Time, "to_a", Ftime_to_a, 0); - rb_define_method(C_Time, "coerce", Ftime_coerce, 1); - - rb_define_method(C_Time, "+", Ftime_plus, 1); - rb_define_method(C_Time, "-", Ftime_minus, 1); - - rb_define_method(C_Time, "sec", Ftime_sec, 0); - rb_define_method(C_Time, "min", Ftime_min, 0); - rb_define_method(C_Time, "hour", Ftime_hour, 0); - rb_define_method(C_Time, "mday", Ftime_mday, 0); - rb_define_method(C_Time, "year", Ftime_year, 0); - rb_define_method(C_Time, "wday", Ftime_wday, 0); - rb_define_method(C_Time, "yday", Ftime_yday, 0); - rb_define_method(C_Time, "isdst", Ftime_isdst, 0); - - rb_define_method(C_Time, "tv_sec", Ftime_to_i, 0); - rb_define_method(C_Time, "tv_usec", Ftime_usec, 0); - rb_define_method(C_Time, "usec", Ftime_usec, 0); - - rb_define_method(C_Time, "strftime", Ftime_strftime, 1); + cTime = rb_define_class("Time", cObject); + rb_include_module(cTime, mComparable); + + rb_define_singleton_method(cTime, "now", time_s_now, 0); + rb_define_singleton_method(cTime, "new", time_s_now, 0); + rb_define_singleton_method(cTime, "at", time_s_at, 1); + + rb_define_singleton_method(cTime, "times", time_s_times, 0); + + rb_define_method(cTime, "to_i", time_to_i, 0); + rb_define_method(cTime, "to_f", time_to_f, 0); + rb_define_method(cTime, "<=>", time_cmp, 1); + rb_define_method(cTime, "hash", time_hash, 0); + + rb_define_method(cTime, "localtime", time_localtime, 0); + rb_define_method(cTime, "gmtime", time_gmtime, 0); + rb_define_method(cTime, "ctime", time_asctime, 0); + rb_define_method(cTime, "asctime", time_asctime, 0); + rb_define_method(cTime, "to_s", time_asctime, 0); + rb_define_method(cTime, "inspect", time_asctime, 0); + rb_define_method(cTime, "to_a", time_to_a, 0); + rb_define_method(cTime, "coerce", time_coerce, 1); + + rb_define_method(cTime, "+", time_plus, 1); + rb_define_method(cTime, "-", time_minus, 1); + + rb_define_method(cTime, "sec", time_sec, 0); + rb_define_method(cTime, "min", time_min, 0); + rb_define_method(cTime, "hour", time_hour, 0); + rb_define_method(cTime, "mday", time_mday, 0); + rb_define_method(cTime, "mon", time_mon, 0); + rb_define_method(cTime, "year", time_year, 0); + rb_define_method(cTime, "wday", time_wday, 0); + rb_define_method(cTime, "yday", time_yday, 0); + rb_define_method(cTime, "isdst", time_isdst, 0); + rb_define_method(cTime, "zone", time_zone, 0); + + rb_define_method(cTime, "tv_sec", time_to_i, 0); + rb_define_method(cTime, "tv_usec", time_usec, 0); + rb_define_method(cTime, "usec", time_usec, 0); + + rb_define_method(cTime, "strftime", time_strftime, 1); + +#if defined(HAVE_TIMES) || defined(NT) + S_Tms = struct_define("Tms", "utime", "stime", "cutime", "cstime", Qnil); +#endif } @@ -3,7 +3,6 @@ util.c - $Author$ - $Revision$ $Date$ created at: Fri Mar 10 17:22:34 JST 1995 diff --git a/variable.c b/variable.c index c6b180736d..9721c1c2d4 100644 --- a/variable.c +++ b/variable.c @@ -10,16 +10,14 @@ #include "ruby.h" #include "env.h" -#include "ident.h" #include "st.h" st_table *rb_global_tbl; st_table *rb_class_tbl; #define global_tbl rb_global_tbl #define class_tbl rb_class_tbl -#define instance_tbl (RBASIC(Qself)->iv_tbl) -VALUE rb_const_bound(); +VALUE rb_const_defined(); VALUE rb_const_get(); st_table * @@ -39,11 +37,31 @@ char * rb_class2path(class) VALUE class; { - VALUE path = rb_ivar_get_1(class, rb_intern("__classpath__")); + VALUE path; + + while (TYPE(class) == T_ICLASS) { + class = (VALUE)RCLASS(class)->super; + } + path = rb_ivar_get(class, rb_intern("__classpath__")); if (TYPE(path) != T_STRING) Bug("class path does not set properly"); return RSTRING(path)->ptr; } +VALUE +rb_class_path(class) + VALUE class; +{ + char *name = rb_class2path(class); + + if (strchr(name, ':')) { + VALUE ary = str_split(str_new2(name), ":"); + ary_pop(ary); + ary = ary_reverse(ary); + return ary_join(ary, str_new2("::")); + } + return str_new2(name); +} + void rb_set_class_path(class, under, name) VALUE class, under; @@ -58,7 +76,7 @@ rb_set_class_path(class, under, name) s = rb_class2path(under); str_cat(str, s, strlen(s)); } - rb_ivar_set_1(class, rb_intern("__classpath__"), str); + rb_ivar_set(class, rb_intern("__classpath__"), str); } VALUE @@ -88,7 +106,7 @@ rb_path2class(path) } *s = '\0'; id = rb_intern(name); - if (!rb_const_bound(class, id)) + if (!rb_const_defined(class, id)) Fail("%s not defined", name); class = rb_const_get(class, id); switch (TYPE(class)) { @@ -106,7 +124,39 @@ rb_name_class(class, id) VALUE class; ID id; { - rb_ivar_set_1(class, rb_intern("__classname__"), INT2FIX(id)); + rb_ivar_set(class, rb_intern("__classname__"), INT2FIX(id)); +} + +static st_table *autoload_tbl = 0; + +static void +rb_autoload_id(id, filename) + ID id; + char *filename; +{ + if (!autoload_tbl) { + autoload_tbl = new_idhash(); + } + st_insert(autoload_tbl, id, strdup(filename)); +} + +void +rb_autoload(class, filename) + char *class, *filename; +{ + rb_autoload_id(rb_intern(class), filename); +} + +VALUE +f_autoload(obj, class, file) + VALUE obj, class; + struct RString *file; +{ + ID id = rb_to_id(class); + + Check_Type(file, T_STRING); + rb_autoload_id(id, file->ptr); + return Qnil; } char * @@ -130,7 +180,7 @@ rb_class2name(class) class = (struct RClass*)class->super; } - name = rb_ivar_get_1(class, rb_intern("__classname__")); + name = rb_ivar_get(class, rb_intern("__classname__")); if (name) { name = FIX2INT(name); return rb_id2name((ID)name); @@ -138,45 +188,33 @@ rb_class2name(class) Bug("class 0x%x not named", class); } +struct trace_var { + void (*func)(); + void *data; + struct trace_var *next; +}; + struct global_entry { - enum { GLOBAL_VAL, GLOBAL_VAR, GLOBAL_UNDEF } mode; ID id; - union { - VALUE val; - VALUE *var; - } v; - VALUE (*get_hook)(); - VALUE (*set_hook)(); void *data; + VALUE (*getter)(); + void (*setter)(); + void (*marker)(); + int block_trace; + struct trace_var *trace; }; -static -mark_global_entry(key, entry) - ID key; - struct global_entry *entry; -{ - switch (entry->mode) { - case GLOBAL_VAL: - gc_mark(entry->v.val); /* normal global value */ - break; - case GLOBAL_VAR: - if (entry->v.var) - gc_mark(*entry->v.var); /* c variable pointer */ - break; - default: - break; - } - if (entry->data) { - gc_mark_maybe(entry->data); - } - return ST_CONTINUE; -} +static VALUE undef_getter(); +static void undef_setter(); +static void undef_marker(); -void -gc_mark_global_tbl() -{ - st_foreach(global_tbl, mark_global_entry, 0); -} +static VALUE val_getter(); +static void val_setter(); +static void val_marker(); + +static VALUE var_getter(); +static void var_setter(); +static void var_marker(); struct global_entry* rb_global_entry(id) @@ -188,22 +226,129 @@ rb_global_entry(id) entry = ALLOC(struct global_entry); st_insert(global_tbl, id, entry); entry->id = id; - entry->mode = GLOBAL_UNDEF; - entry->v.var = Qnil; - entry->get_hook = entry->set_hook = Qnil; + entry->data = 0; + entry->getter = undef_getter; + entry->setter = undef_setter; + entry->marker = undef_marker; + + entry->block_trace = 0; + entry->trace = 0; } return entry; } -void -rb_define_variable(name, var, get_hook, set_hook, data) - char *name; - VALUE *var; - VALUE (*get_hook)(); - VALUE (*set_hook)(); +static VALUE +undef_getter(id) + ID id; +{ + Warning("global var %s not initialized", rb_id2name(id)); + return Qnil; +} + +static void +undef_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->getter = val_getter; + entry->setter = val_setter; + entry->marker = val_marker; + + entry->data = (void*)val; +} + +static void +undef_marker() +{ +} + +static VALUE +val_getter(id, val) + ID id; + VALUE val; +{ + return val; +} + +static void +val_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->data = (void*)val; +} + +static void +val_marker(data) void *data; { + if (data) gc_mark_maybe(data); +} + +static VALUE +var_getter(id, var) + ID id; + VALUE *var; +{ + if (!var || !*var) return Qnil; + return *var; +} + +static void +var_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + *var = val; +} + +static void +var_marker(var) + VALUE **var; +{ + if (var) gc_mark_maybe(*var); +} + +static void +readonly_setter(id, var, val) + ID id; + void *var; + VALUE val; +{ + Fail("Can't set variable %s", rb_id2name(id)); +} + +static int +mark_global_entry(key, entry) + ID key; struct global_entry *entry; +{ + struct trace_var *trace; + + (*entry->marker)(entry->data); + trace = entry->trace; + while (trace) { + if (trace->data) gc_mark_maybe(trace->data); + trace = trace->next; + } + return ST_CONTINUE; +} + +void +gc_mark_global_tbl() +{ + st_foreach(global_tbl, mark_global_entry, 0); +} + +static ID +global_id(name) + char *name; +{ ID id; if (name[0] == '$') id = rb_intern(name); @@ -213,80 +358,143 @@ rb_define_variable(name, var, get_hook, set_hook, data) strcpy(buf+1, name); id = rb_intern(buf); } + return id; +} + +void +rb_define_hooked_variable(name, var, getter, setter) + char *name; + VALUE *var; + VALUE (*getter)(); + void (*setter)(); +{ + struct global_entry *entry; + ID id = global_id(name); entry = rb_global_entry(id); - entry->mode = GLOBAL_VAR; - entry->v.var = var; - entry->get_hook = get_hook; - entry->set_hook = set_hook; - entry->data = data; + entry->data = (void*)var; + entry->getter = getter?getter:var_getter; + entry->setter = setter?setter:var_setter; + entry->marker = var_marker; } void -rb_define_varhook(name, get_hook, set_hook, data) +rb_define_variable(name, var) char *name; - VALUE (*get_hook)(); - VALUE (*set_hook)(); - void *data; + VALUE *var; { - struct global_entry *entry; + rb_define_hooked_variable(name, var, 0, 0); +} + +void +rb_define_readonly_variable(name, var) + char *name; + VALUE *var; +{ + rb_define_hooked_variable(name, var, 0, readonly_setter); +} + +void +rb_define_virtual_variable(name, getter, setter) + char *name; + VALUE (*getter)(); + void (*setter)(); +{ + if (!getter) getter = val_getter; + if (!setter) setter = readonly_setter; + rb_define_hooked_variable(name, 0, getter, setter); +} + +void rb_trace_eval(); + +void +rb_trace_eval(cmd, val) + VALUE cmd, val; +{ + rb_eval_cmd(cmd, ary_new3(1, val)); +} + +VALUE +f_trace_var(argc, argv) + int argc; + VALUE *argv; +{ + VALUE var, cmd; ID id; + struct global_entry *entry; + struct trace_var *trace; - if (name[0] == '$') id = rb_intern(name); - else { - char *buf = ALLOCA_N(char, strlen(name)+2); - buf[0] = '$'; - strcpy(buf+1, name); - id = rb_intern(buf); + if (rb_scan_args(argc, argv, "11", &var, &cmd) == 1) { + cmd = f_lambda(); } - + id = rb_to_id(var); if (!st_lookup(global_tbl, id, &entry)) { - entry = ALLOC(struct global_entry); - entry->id = id; - entry->mode = GLOBAL_VAL; - st_insert(global_tbl, id, entry); - } - else if (entry->mode == GLOBAL_UNDEF) { - entry->mode = GLOBAL_VAL; - } - entry->v.val = Qnil; - entry->get_hook = get_hook; - entry->set_hook = set_hook; - if (data) { - entry->data = data; + Fail("undefined global variable %s", rb_id2name(id)); } + trace = ALLOC(struct trace_var); + trace->next = entry->trace; + trace->func = rb_trace_eval; + trace->data = (void*)cmd; + entry->trace = trace; + + return Qnil; } VALUE -rb_readonly_hook(val, id) - VALUE val; - ID id; +f_untrace_var(obj, var) + VALUE obj, var; { - Fail("Can't set variable %s", rb_id2name(id)); - /* not reached */ + ID id; + struct global_entry *entry; + struct trace_var *trace; + VALUE ary; + + id = rb_to_id(var); + if (!st_lookup(global_tbl, id, &entry)) { + Fail("undefined global variable %s", rb_id2name(id)); + } + ary = ary_new(); + trace = entry->trace; + while (trace) { + struct trace_var *next = trace->next; + ary_push(ary, trace->data); + free(trace); + trace = next; + } + entry->trace = 0; + + return ary; } VALUE rb_gvar_get(entry) struct global_entry *entry; { - VALUE val; - - if (entry->get_hook) - val = (*entry->get_hook)(entry->id, entry->data); - switch (entry->mode) { - case GLOBAL_VAL: - return entry->v.val; + return (*entry->getter)(entry->id, entry->data, entry); +} - case GLOBAL_VAR: - if (entry->v.var == Qnil) return val; - return *entry->v.var; +struct trace_data { + struct trace_var *trace; + VALUE val; +}; + +static void +trace_ev(data) + struct trace_data *data; +{ + struct trace_var *trace = data->trace; - default: - break; + while (trace) { + (*trace->func)(trace->data, data->val); + trace = trace->next; } - Warning("global var %s not initialized", rb_id2name(entry->id)); - return Qnil; +} + +static void +trace_en(entry) + struct global_entry *entry; +{ + entry->block_trace = 0; } VALUE @@ -294,19 +502,15 @@ rb_gvar_set(entry, val) struct global_entry *entry; VALUE val; { - if (entry->set_hook) - (*entry->set_hook)(val, entry->id, entry->data); + struct trace_data trace; - if (entry->mode == GLOBAL_VAR) { - if (entry->v.var) { - *entry->v.var = val; - } - } - else { - if (entry->mode == GLOBAL_UNDEF) { - entry->mode = GLOBAL_VAL; - } - entry->v.val = val; + (*entry->setter)(val, entry->id, entry->data, entry); + + if (!entry->block_trace) { + entry->block_trace = 1; + trace.trace = entry->trace; + trace.val = val; + rb_ensure(trace_ev, &trace, trace_en, entry); } return val; } @@ -317,19 +521,21 @@ rb_gvar_set2(name, val) VALUE val; { struct global_entry *entry; - ID id; - id = rb_intern(name); - if (!st_lookup(global_tbl, id, &entry)) { - entry = rb_global_entry(id); - } - rb_gvar_set(entry, val); + entry = rb_global_entry(global_id(name)); + return rb_gvar_set(entry, val); +} - return val; +VALUE +rb_gvar_defined(entry) + struct global_entry *entry; +{ + if (entry->getter == undef_getter) return FALSE; + return TRUE; } VALUE -rb_ivar_get_1(obj, id) +rb_ivar_get(obj, id) struct RObject *obj; ID id; { @@ -352,14 +558,7 @@ rb_ivar_get_1(obj, id) } VALUE -rb_ivar_get(id) - ID id; -{ - return rb_ivar_get_1(Qself, id); -} - -VALUE -rb_ivar_set_1(obj, id, val) +rb_ivar_set(obj, id, val) struct RObject *obj; ID id; VALUE val; @@ -368,7 +567,7 @@ rb_ivar_set_1(obj, id, val) case T_OBJECT: case T_CLASS: case T_MODULE: - if (obj->iv_tbl == Qnil) obj->iv_tbl = new_idhash(); + if (!obj->iv_tbl) obj->iv_tbl = new_idhash(); st_insert(obj->iv_tbl, id, val); break; default: @@ -380,11 +579,19 @@ rb_ivar_set_1(obj, id, val) } VALUE -rb_ivar_set(id, val) +rb_ivar_defined(obj, id) + struct RObject *obj; ID id; - VALUE val; { - return rb_ivar_set_1(Qself, id, val); + switch (TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + if (obj->iv_tbl && st_lookup(obj->iv_tbl, id, 0)) + return TRUE; + break; + } + return FALSE; } VALUE @@ -393,40 +600,58 @@ rb_const_get(class, id) ID id; { VALUE value; + struct RClass *tmp; - while (class) { - if (class->iv_tbl && st_lookup(class->iv_tbl, id, &value)) { + tmp = class; + while (tmp) { + if (tmp->iv_tbl && st_lookup(tmp->iv_tbl, id, &value)) { return value; } - if (BUILTIN_TYPE(class) == T_MODULE) { - class = RCLASS(C_Object); - } - else { - class = class->super; - } + tmp = tmp->super; + } + if (BUILTIN_TYPE(class) == T_MODULE) { + return rb_const_get(cObject, id); } /* pre-defined class */ if (st_lookup(class_tbl, id, &value)) return value; - /* here comes autoload code in the future. */ + /* autoload */ + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { + char *modname; + VALUE module; - Fail("Uninitialized constant %s", rb_id2name(id)); + st_delete(autoload_tbl, &id, &modname); + module = str_new2(modname); + free(modname); + f_require(Qnil, module); + return rb_const_get(class, id); + } + + /* Uninitialized constant */ + if (class && (VALUE)class != cObject) + Fail("Uninitialized constant %s::%s", + RSTRING(rb_class_path(class))->ptr, + rb_id2name(id)); + else + Fail("Uninitialized constant %s",rb_id2name(id)); /* not reached */ } VALUE -rb_const_bound(class, id) +rb_const_defined(class, id) struct RClass *class; ID id; { while (class) { - if (class->iv_tbl && st_lookup(class->iv_tbl, id, Qnil)) { + if (class->iv_tbl && st_lookup(class->iv_tbl, id, 0)) { return TRUE; } class = class->super; } - if (st_lookup(class_tbl, id, Qnil)) + if (st_lookup(class_tbl, id, 0)) + return TRUE; + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) return TRUE; return FALSE; } @@ -437,10 +662,10 @@ rb_const_set(class, id, val) ID id; VALUE val; { - if (rb_const_bound(class, id)) - Fail("already initialized constnant"); + if (rb_const_defined(class, id)) + Fail("already initialized constnant %s", rb_id2name(id)); - if (class->iv_tbl == Qnil) class->iv_tbl = new_idhash(); + if (!class->iv_tbl) class->iv_tbl = new_idhash(); st_insert(class->iv_tbl, id, val); } @@ -460,7 +685,7 @@ rb_iv_get(obj, name) { ID id = rb_intern(name); - return rb_ivar_get_1(obj, id); + return rb_ivar_get(obj, id); } VALUE @@ -471,5 +696,35 @@ rb_iv_set(obj, name, val) { ID id = rb_intern(name); - return rb_ivar_set_1(obj, id, val); + return rb_ivar_set(obj, id, val); +} + +VALUE +backref_get() +{ + int cnt, max; + + if (!the_scope->local_vars) return Qnil; + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == '~') { + cnt--; + if (the_scope->local_vars[cnt]) + return the_scope->local_vars[cnt]; + else + return 1; + } + } + return Qnil; +} + +void +backref_set(val) + VALUE val; +{ + int cnt, max; + + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == '~') break; + } + the_scope->local_vars[cnt-1] = val; } @@ -15,18 +15,21 @@ #include "version.h" #include <stdio.h> -extern VALUE C_Kernel; +extern VALUE cKernel; +void Init_version() { - rb_define_const(C_Kernel, "VERSION", str_new2(RUBY_VERSION)); + rb_define_const(cKernel, "VERSION", str_new2(RUBY_VERSION)); } +void show_version() { fprintf(stderr, "ruby - version %s (%s)\n", RUBY_VERSION, VERSION_DATE); } +void show_copyright() { fprintf(stderr, "ruby - Copyright (C) 1993-1995 Yukihiro Matsumoto\n"); @@ -1,2 +1,2 @@ -#define RUBY_VERSION "0.76" -#define VERSION_DATE "95/05/19" +#define RUBY_VERSION "0.95" +#define VERSION_DATE "95/12/21" |