summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1799
-rw-r--r--MANIFEST30
-rw-r--r--Makefile.in30
-rw-r--r--README182
-rw-r--r--README.EXT973
-rw-r--r--README.jp159
-rw-r--r--ToDo8
-rw-r--r--array.c459
-rw-r--r--bignum.c440
-rw-r--r--class.c45
-rw-r--r--compar.c10
-rw-r--r--config.dj35
-rwxr-xr-xconfig.guess139
-rwxr-xr-xconfig.sub177
-rw-r--r--configure.bat5
-rw-r--r--configure.in242
-rw-r--r--defines.h13
-rw-r--r--dir.c64
-rw-r--r--dln.c161
-rw-r--r--enum.c100
-rw-r--r--env.h7
-rw-r--r--error.c714
-rw-r--r--eval.c3252
-rw-r--r--ext/Setup4
-rw-r--r--ext/Setup.dj8
-rw-r--r--ext/dbm/MANIFEST1
-rw-r--r--ext/dbm/dbm.c188
-rw-r--r--ext/dbm/dbm.doc107
-rw-r--r--ext/dbm/extconf.rb2
-rw-r--r--ext/etc/etc.c36
-rw-r--r--ext/extmk.rb.in148
-rw-r--r--ext/kconv/MANIFEST2
-rw-r--r--ext/kconv/kconv.c1934
-rw-r--r--ext/marshal/MANIFEST1
-rw-r--r--ext/marshal/extconf.rb2
-rw-r--r--ext/marshal/marshal.c519
-rw-r--r--ext/marshal/marshal.doc5
-rw-r--r--ext/md5/MANIFEST6
-rw-r--r--ext/md5/depend2
-rw-r--r--ext/md5/md5.doc36
-rw-r--r--ext/md5/md5.h86
-rw-r--r--ext/md5/md5c.c337
-rw-r--r--ext/md5/md5init.c90
-rw-r--r--ext/socket/MANIFEST1
-rw-r--r--ext/socket/extconf.rb7
-rw-r--r--ext/socket/socket.c426
-rw-r--r--ext/socket/socket.doc227
-rw-r--r--ext/tkutil/MANIFEST2
-rw-r--r--ext/tkutil/depend1
-rw-r--r--ext/tkutil/extconf.rb11
-rw-r--r--ext/tkutil/tkutil.c1
-rw-r--r--file.c403
-rw-r--r--gc.c171
-rw-r--r--glob.c8
-rw-r--r--hash.c137
-rw-r--r--inits.c8
-rw-r--r--io.c885
-rw-r--r--io.h14
-rw-r--r--lib/base64.rb2
-rw-r--r--lib/cgi-lib.rb56
-rw-r--r--lib/complex.rb490
-rw-r--r--lib/find.rb47
-rw-r--r--lib/getopts.rb173
-rw-r--r--lib/jcode.rb174
-rw-r--r--lib/mailread.rb19
-rw-r--r--lib/mathn.rb307
-rw-r--r--lib/observer.rb40
-rw-r--r--lib/parsearg.rb103
-rw-r--r--lib/rational.rb361
-rw-r--r--lib/safe.rb78
-rw-r--r--lib/thread.rb153
-rw-r--r--lib/tk.rb557
-rw-r--r--lib/tkcanvas.rb47
-rw-r--r--lib/tkcore.rb521
-rw-r--r--lib/tkentry.rb2
-rw-r--r--lib/tkscrollbox.rb27
-rw-r--r--lib/tktext.rb18
-rw-r--r--lib/tkthcore.rb546
-rw-r--r--main.c4
-rw-r--r--math.c9
-rw-r--r--missing/flock.c90
-rw-r--r--missing/mkdir.c4
-rw-r--r--missing/setenv.c4
-rw-r--r--missing/strftime.c1094
-rw-r--r--node.h84
-rw-r--r--numeric.c417
-rw-r--r--object.c384
-rw-r--r--pack.c74
-rw-r--r--parse.y1586
-rw-r--r--process.c386
-rw-r--r--random.c28
-rw-r--r--range.c153
-rw-r--r--re.c247
-rw-r--r--re.h2
-rw-r--r--regex.c76
-rw-r--r--regex.h2
-rw-r--r--ruby.c239
-rw-r--r--ruby.h123
-rw-r--r--ruby.texi5044
-rw-r--r--sample/clnt.rb6
-rw-r--r--sample/dir.rb4
-rw-r--r--sample/eval.rb41
-rw-r--r--sample/evaldef.rb26
-rw-r--r--sample/export.rb2
-rw-r--r--sample/fact.rb8
-rwxr-xr-xsample/from.rb14
-rw-r--r--sample/fullpath.pl22
-rw-r--r--sample/fullpath.rb6
-rwxr-xr-xsample/getopts.test25
-rw-r--r--sample/io.rb4
-rwxr-xr-xsample/less.rb4
-rw-r--r--sample/list.rb14
-rwxr-xr-xsample/mpart.rb6
-rw-r--r--sample/observ.rb31
-rw-r--r--sample/philos.rb54
-rw-r--r--sample/pi.rb18
-rw-r--r--sample/rcs.rb2
-rw-r--r--sample/regx.rb23
-rw-r--r--sample/ruby-mode.el575
-rw-r--r--sample/sieve.rb2
-rw-r--r--sample/svr.rb2
-rw-r--r--sample/test.rb981
-rwxr-xr-xsample/time.rb2
-rw-r--r--sample/tkbiff.rb46
-rw-r--r--sample/tkbrowse.rb8
-rw-r--r--sample/tkfrom.rb13
-rw-r--r--sample/tkline.rb25
-rw-r--r--sample/trojan.pl12
-rw-r--r--sample/tsvr.rb23
-rwxr-xr-xsample/uumerge.rb8
-rw-r--r--sig.h46
-rw-r--r--signal.c161
-rw-r--r--sprintf.c72
-rw-r--r--st.c202
-rw-r--r--st.h22
-rw-r--r--string.c674
-rw-r--r--struct.c95
-rw-r--r--time.c251
-rw-r--r--top.sed37
-rw-r--r--util.c2
-rw-r--r--util.h2
-rw-r--r--variable.c206
-rw-r--r--version.c8
-rw-r--r--version.h4
144 files changed, 20373 insertions, 12047 deletions
diff --git a/ChangeLog b/ChangeLog
index 9a7479823d..78867f67d9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,1771 @@
+Tue Dec 24 15:20:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.4-961224
+
+ * configure.in: charがunsignedかどうかもチェック
+
+ * regex.c (SIGN_EXTEND_CHAR): __CHAR_UNSIGNED__にも対応
+
+ * pack.c (pack_unpack): 明示的にsigned charを指定.
+
+Mon Dec 23 14:41:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ruby.c (load_file): 標準入力からのスクリプトで一時ファイルを使わ
+ ないように
+
+ * object.c (f_integer): `0x', `0'などでbaseを解釈するように.
+
+Fri Dec 20 01:44:39 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * Makefile.in (flock.o): flockに対応
+
+Thu Dec 19 20:13:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.4-961219
+
+Wed Dec 18 00:06:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * glob.c (glob_filename): strrchrがマクロの場合に対応
+
+ * configure.in: <sys/select.h>をチェック
+
+ * ext/kconv/kconv.c: 1.62ベースに
+
+ * ext/kconv/kconv.c: Kconvモジュール
+
+ * string.c (str_substr): lenが元の文字列より長い時に対応
+
+ * parse.y (iterator): 「$bar do .. end」などは許さないように
+
+ * parse.y (iterator): FID(foo!,foo?)をdo形式のイテレータにできる.
+
+ * missing/flock.c (flock): lockf()を使って代替
+
+ * file.c (file_flock): flockを実装
+
+Tue Dec 17 12:13:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.4-961217
+
+Fri Dec 13 02:05:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * configure.in: RUBYLIBのカレントを後回し(@mix/awk offline)
+
+ * dln.c: AIXに対応した?(@mix/awk offline)
+
+ * eval.c (thread_schedule): critical sectionでも明示的なコンテキス
+ トスイッチは起きないとまずい
+
+ * re.c (reg_search): matchに失敗した時に$~をnilに.
+
+ * re.c (reg_search): 毎回matchを生成するように
+
+Thu Dec 12 17:03:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * numeric.c (flo_to_s): 2.0.to_s -> 2.0に
+
+ * eval.c (thread_save_context): $_, $~をthread毎に保存
+
+ * eval.c (thread_kill): main threadではexit(0)
+
+ * string.c (str_split_method): 間違った結果を返していた
+
+Thu Dec 12 15:32:48 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
+
+ * dir.c: CYGWIN32対応
+
+ * ext/socket/socket.c: CYGWIN32対応
+
+ * io.c: CYGWIN32対応
+
+Thu Dec 12 14:43:51 1996 Jun Kuroda <j_kuro@pluto.ai.kutech.ac.jp>
+
+ * lib/tk.rb: wish4.2も探索候補に含める
+
+ * config.guess: JCC対応
+
+Thu Dec 12 00:41:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.4-961212
+
+ * parse.y (parse_string): """..."""はやはり無くすことにした
+
+ * parse.y (parse_regx): %r|...|でterminatorを \ でエスケープできる
+ ように
+
+ * signal.c (posix_signal): sigactionを使うsignal
+
+ * configure.in: posix signal/bsd signalの検出
+
+Wed Dec 11 17:47:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_schedule): critical sectionではコンテキストスイッ
+ チが起きないように
+
+ * lib/thread.rb: SharedMutexクラス
+
+ * lib/jcode.rb: String#scanを使うように
+
+Tue Dec 10 12:21:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961210
+
+ * string.c (str_split_method): 正規表現に()を含む時にバグ
+
+ * lib/jcode.rb: ちょっとましになった
+
+ * string.c (tr_setup_table): 置換文字が短すぎる(2文字)のときのバグ
+
+Mon Dec 9 11:38:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_scan): 文字列のマッチを行う.イテレータとしても動
+ 作する
+
+ * regex.c (re_copy_registers): allocatedが初期化されていなかった
+
+ * re.c (match_to_s): $~の文字列化
+
+ * re.c (match_to_a): $~を配列化できるように
+
+ * re.c (match_getter): レジスタが初期化されていなかった
+
+Thu Dec 5 11:06:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_split_method): マッチしなかった括弧は空文字列を
+ pushするべきではない
+
+ * string.c (str_succ): アルファベットを含まない文字に対応
+
+Wed Dec 4 10:48:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961204
+
+ * io.c (io_binmode): DJGPPでのbinmode対応
+
+ * sprintf.c (f_sprintf): intの範囲の数値は直接sprintfで変換する
+
+ * sprintf.c (f_sprintf): "%02s"に頼らない
+
+ * re.c (reg_search): indexでSEGV
+
+Tue Dec 3 10:09:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961203
+
+ * ext/extmk.rb.in (install): INSTALL_DATAからINSTALLに変更
+
+ * dln.c: hpux対応
+
+ * string.c (str_aset_method): 負の値を含む範囲でも例外を起こさない
+
+ * array.c (ary_replace): 負の値を含む範囲でも例外を起こさない
+
+ * array.c (beg_len): beg==endの時,長さ0に
+
+Mon Dec 2 14:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * configure.in: HP shl対応
+
+ * string.c (str_upto): beg > endの時無限ループに落ちるのを止めた
+
+ * range.c (range_each): String#uptoが再定義された場合に対応
+
+ * string.c (str_split_method): "ABC".split(/(B)/)が誤動作
+
+Sat Nov 30 01:43:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): undefでSEGV
+
+Fri Nov 29 12:17:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-parse-region): %Q#..#などに対応.しか
+ し,区切り文字が演算子で行末にある場合には対応できなかった.
+
+ * re.c (reg_raise): 例外でもスラッシュをエスケープ
+
+ * re.c (reg_inspect): スラッシュをエスケープ
+
+ * parse.y (parse_string): `%[QqXxRr](.)..\1'なる文字列形式(テスト
+ 採用)
+
+ * parse.y (parse_qstring): '''...'''の形式
+
+ * ext/dbm/dbm.c (Init_dbm): 述語key?,value?の追加
+
+ * ext/dbm/dbm.c (Init_dbm): includes->include?
+
+ * hash.c (Init_Hash): 述語key?,value?,include?の追加
+
+ * eval.c (rb_eval): else節が実行されない(うーん)
+
+ * string.c (str_sub_iter_s): イテレータブロック内でマッチが行われ
+ ると位置がずれる(時に無限ループに落ちる)
+
+ * string.c (str_resize): lenが0の時sizeの調整が行われなかった
+
+Thu Nov 28 00:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961128
+
+ * parse.y (parse_string): 3-quote styleの文字列(例:"""abc"d"e""")
+
+ * configure.in (EXTSTATIC): extを静的にリンクする時にはrubyはdllを
+ 使うように
+
+ * io.c (Init_IO): getsの引数が間違っていた
+
+ * string.c (str_each_line): RSを明示的に指定できるように
+
+Wed Nov 27 12:37:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961127
+
+ * eval.c (rb_eval): iver defined? でselfを指定するのを忘れた
+
+ * io.c: gets等でRSを明示的に指定できるように
+
+ * ext/extmk.rb.in (install): static linkに失敗
+
+Tue Nov 26 10:33:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961126
+
+ * string.c (str_sub_s): 置換後の文字列長さが間違っていた
+
+Mon Nov 25 09:11:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * numeric.c (fix_rshift): 32以上の右シフトで0を返すように(Cの
+ rshiftは(x>>(y%32))を返していた).
+
+ * string.c (str_gsub): 置換が行われない場合があった
+
+ * string.c (str_resize): 本当に必要な時だけrealloc
+
+Thu Nov 21 04:13:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * configure.in (EXTSTATIC): --with-static-linked-extで全てのモジュー
+ ルを静的リンクするように
+
+ * pack.c (pack_unpack): 行末の改行がない時にもチェックサムをスキッ
+ プするように
+
+Wed Nov 20 96 21:42:51 1996 Yasuo OHBA <jammy@shljapan.co.jp>
+
+ * configure.in: freebsd対応
+
+Wed Nov 20 10:24:24 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/extmk.rb.in (install): 通常リンク用のLDFLAGSとダイナミックリ
+ ンク用のDLDFALGSを分離
+
+ * ext/extmk.rb.in (install): コンパイルの成功したものを静的リンク
+ のリストに追加する
+
+ * eval.c (f_missing): オブジェクトの文字列表現が長すぎる時バッファ
+ を書き潰していた
+
+ * process.c (proc_exec_v): forkした後例外を発生させてはいけない
+
+Tue Nov 19 13:28:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961119
+
+ * eval.c (mod_method_defined): Module#method_defined? の追加
+
+ * parse.y (call_args): 引数が唯一のコマンドコールである時のバグ(戻
+ り値が展開されてしまう)
+
+Mon Nov 18 13:28:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_sub): 失敗した時にnilを返していた
+
+ * string.c (str_split_method): 検索開始位置が移動してなかった
+
+ * ext/socket/socket.c (sock_s_getservbyaname): まだ間違っていた
+
+ * version 0.99.3-961118
+
+ * string.c (str_sub_s): 元の文字列を置換するのを止めた
+
+ * pack.c (encodes): 領域外をアクセスしていた
+
+Fri Nov 15 17:10:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * bignum.c (big_divmod): Bignumが引数の場合の対応忘れ
+
+ * sample/ruby-mode.el (ruby-expr-beg): word?形式への対応が不完全
+
+Wed Nov 13 15:42:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_tr_s_bang): tr_sでtrが行われていなかった
+
+ * eval.c (rb_eval): autoloadクラスのチェック
+
+ * string.c (f_sub): subがsub!と同じ動作になっていた
+
+ * eval.c (thread_sleep): stopとsleepの分離
+
+Mon Nov 11 13:53:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961111
+
+ * numeric.c (fix_step): to, stepが整数以外の場合に対応
+
+ * eval.c (rb_call): dynamic varがdynamic scopingになっていた(これ
+ はまずい)
+
+ * string.c (str_chop_bang): 長さ0の文字列のchopで,領域外のアクセ
+ スが発生していた.
+
+ * parse.y (yyerror): 割り当てた領域外をアクセスしていた
+
+Fri Nov 8 11:54:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_yield): scopeをheapにコピー
+
+Thu Nov 7 09:56:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * numeric.c (num_coerce): とりあえず両辺をFloatに変換することに
+
+Wed Nov 6 10:45:13 1996 Yasuo OHBA <jammy@shljapan.co.jp>
+
+ * lib/parsearg.rb: 第2引数を変更.
+
+Tue Nov 5 14:21:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961105
+
+Sat Nov 2 01:11:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * bignum.c (big_pow): typo (dy -> dx)
+
+ * bignum.c (big_divmod): 知らない型はfloatに変換してみる
+
+ * numeric.c (fix_lshift): 境界条件のバグ(負になっていた)
+
+ * bignum.c (big_pow): 無駄なfloatへの変換をなくした
+
+ * math.c (math_atan2): typo(x -> y)
+
+Fri Nov 1 15:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/socket/socket.c (sock_gethostname): gethostnameがない時には
+ unameを使ってホスト名を得る
+
+ * ext/etc/etc.c (etc_getlogin): getloginがNULLを返しても環境変数を
+ 調べるように
+
+ * object.c (krn_clone): オブジェクトのフラグもコピー
+
+ * hash.c (rb_cmp): ハッシュの比較を`=='でなく`eql?'に変更
+
+ * math.c (Need_Float): Float()を使って変換する
+
+ * compar.c (cmp_gt): 以前の右辺を返す仕様の名残が残っていた
+
+Thu Oct 31 12:55:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961031
+
+ * numeric.c (Init_Numeric): typo
+
+ * eval.c (error_print): 長すぎるtrace backを途中省略する
+
+ * regex.c (re_compile_pattern): 全角のrangeに対応
+
+Wed Oct 30 03:03:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.3-961030
+
+ * io.c (f_ungetc): 関数を追加
+
+ * eval.c (dyna_var_asgn): return値忘れ
+
+Tue Oct 29 10:05:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (f_split): 関数splitを追加
+
+ * eval.c (rb_call): ネストした外側のクラス/モジュールの定数を参照
+ できるように
+
+Mon Oct 28 09:51:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_sub): offsetが文字の末尾にある時のチェック
+
+ * regex.c (re_match): 割り当てるレジスタの数が1多かった
+
+ * io.c (io_gets): $/ = ""の動作をperlに合わせる(awkとはちょっと違
+ うらしい)
+
+ * io.c (io_gets): $/ = nilの時少し高速化
+
+ * string.c (str_split_method): 括弧がnullにマッチした時にも無視し
+ ないように
+
+ * string.c (str_split_method): 括弧にマッチした分はlimitの数に含め
+ ないように.
+
+ * numeric.c (num_coerce_bin): coerceの定義を変更,2要素の配列
+ [x,y]を返すように
+
+ * sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"の対応を改
+ 善した.
+
+Sat Oct 26 01:43:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/marshal/marshal.c (w_object): ビルトインクラスのサブクラスを
+ 正しく復旧できるように
+
+ * ext/marshal/marshal.c (w_object): ユーザ定義dumpの優先
+
+ * numeric.c (flo_coerce): Float()を使って定義
+
+ * numeric.c (Init_Numeric): Numericのnewのundefはまずい
+
+ * ext/marshal/marshal.c (w_symbol): シンボルの内容(文字列)は一度し
+ かファイルに書き出さない.
+
+ * sample/ruby-mode.el (ruby-parse-region): if/while修飾子に対応し
+ なくなっていた
+
+ * bignum.c (Init_Bignum): Bignum.newを除く
+
+ * eval.c (rb_eval): 引数評価後にファイル名と行番号を再設定
+
+ * numeric.c (flo_div): typo
+
+ * sample/ruby-mode.el (ruby-parse-region): def /, def `に対応
+
+Fri Oct 25 09:26:29 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"に対応
+
+ * array.c (ary_aset): indexがfixnumの場合ちょっと高速化
+
+ * eval.c (thread_fd_writable): 書き込み前のselectチェック
+
+ * array.c (ary_assoc): 無限ループに落ちた
+
+ * eval.c (thread_wait_for): selectがエラー終了した時,linux以外で
+ の動作が正しくなかった.
+
+Thu Oct 24 08:26:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (backtrace): `$@'を文字列から配列に変更した.
+
+ * eval.c (eval): eval中の例外発生位置を保存する
+
+ * bignum.c (bigsub): オペランドの大小比較の失敗
+
+ * re.c (reg_search): 直接参照がない時にも`$~'がセットされるように
+
+Wed Oct 23 10:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-961023
+
+ * ext/marshal/marshal.c (r_bytes): mallocをやめ,allocaを使う
+
+ * sample/ruby-mode.el (ruby-calculate-indent): 括弧の対応を変更.
+ ()内ではインデントをレベルを合わせるように
+
+Tue Oct 22 12:59:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * hash.c (hash_s_new): sizeを指定できるように
+
+ * ext/marshal/marshal.c (w_object): dumpする深さ制限を指定できるよ
+ うに
+
+ * array.c (ary_s_new): sizeを指定した時の初期化忘れ
+
+ * object.c (f_float): big2dblの宣言忘れ.
+
+ * bignum.c (bigsub): 大きさの近いBignum同士の演算で結果が負になる
+ 場合に間違いがあった.
+
+ * array.c (ary_aset): 置換先と置換元が同じ長さの時内容を
+ shift(memmove)しないように.
+
+ * ext/marshal/marshal.c (marshal_dump): ファイルフォーマットにバー
+ ジョンを埋め込むように
+
+ * ext/marshal/marshal.c (tmpnam): linux-aout-dln用に定義
+
+Mon Oct 21 08:40:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/socket/socket.c (sock_s_gethostbyname): hostent構造体の情報
+ を返す
+ (sock_s_gethostbyaddr): IPアドレスからhostent構造体を得る
+ (sock_s_getservbyaname): getservbyname(3)
+
+Fri Oct 18 10:37:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-indent-to): 移動先カラムが負になるバグ
+
+ * eval.c (compile): evalで元ソースの行番号でエラーを表示する
+
+Thu Oct 17 09:52:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (eval): evalで文法エラーがあった時にSEGV
+
+ * lib/safe.rb: Restricted.evalの中だけ制限を加える.
+
+ * eval.c (error_print): バックトレースの出力.callerで例外発生位置
+ を調整した時に問題が出る(そんなことをしなければ良いのだが…)
+
+ * eval.c (make_backtrace): バックトレースの生成
+
+Wed Oct 16 12:56:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ruby-man-0.99.2-jp/index.html: 日本語版ドキュメントの完成(長かった…)
+
+ * re.c (reg_regcomp): $=がnilの時の処理
+
+ * string.c (f_chop): $_に対するchop
+
+Tue Oct 15 11:04:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-961015
+
+Mon Oct 14 18:22:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_schedule): BOW対応.selectが-1を返した時にバグ(実
+ はdo .. whileがcontinueで先頭にジャンプすると思い込んでいた.条
+ 件の直前だったのね ^^);;;;;
+
+ * sample/ruby-mode.el (ruby-mode-syntax-table): ?のsyntaxが"/"では
+ まずいらしい
+
+ * hash.c (rb_hash): name conflict
+
+Fri Oct 11 00:23:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-961011
+
+ * ext/marshal/marshal.c (w_object): 結局動いていなかった循環オブジェ
+ クト対応を外した.
+
+ * hash.c (rb_hash): Fixnumと文字列の高速化
+
+ * ext/marshal/marshal.c (w_object): 無駄なデータの削除(フォーマッ
+ トの非互換性)
+
+ * io.c (io_readline): 戻り値の不備
+
+ * ext/marshal/marshal.c (marshal_dumps): MSDOS対応
+
+ * ruby.c (load_file): MSDOS対応
+
+Wed Oct 9 17:46:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/extmk.rb.in (install): 無駄なコピーを避ける
+
+ * string.c (str_sub_method): マッチがなかった時のString#subの値が
+ 違っていた.
+
+ * eval.c (obj_extend): extendした時にobject_extendedを呼ぶように
+
+Tue Oct 8 00:55:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_alloc): 割当の平均化
+
+ * eval.c (thread_schedule): joinのバグを修正
+
+ * eval.c (thread_wait_for): selectへの割込みなどに対応
+
+ * eval.c (thread_select): linuxのselectの挙動に対応(timeoutが変化
+ する)
+
+Mon Oct 7 09:47:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-961007
+
+ * eval.c (PUSH_BLOCK): the_classの保存を忘れていた.
+
+ * ext/dbm/dbm.c (fdbm_store): sizeの保存する場所が間違っていた
+
+ * ext/socket/socket.c (s_accept): thread対応していなかった
+
+Sat Oct 5 01:32:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * io.c (io_readchar): EOFで例外を発生させる
+
+Fri Oct 4 11:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/marshal/marshal.c (w_object): HashとObjectの復旧に必要なハッ
+ シュテーブルが渡されていなかった.
+
+ * variable.c (rb_path2class): ユーザ定義クラスの復旧に失敗していた
+
+ * variable.c (rb_path2class): クラスが存在しない時のエラーをFatal
+ からNameErrorへ.
+
+ * range.c (range_s_new): first,lastが両方Numericの時エラーになって
+ いた.
+
+ * range.c: start->first, end->last
+
+Wed Oct 2 02:02:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * file.c: DJGPPでchmod,chownを使えるように(ってDOSにchownがあるのか?)
+
+ * class.c (rb_singleton_class): ビルトインクラスもextendしたり特異
+ メソッドを追加したりできるように
+
+ * variable.c (rb_set_class_path): ユーザ定義のトップレベルクラスに
+ pathを設定しない
+
+ * eval.c (eval): 例外がRuntimeErrorに化けていた
+
+ * eval.c (eval): eval中の例外の表現の改善
+
+ * eval.c (eval): eval_with_bindingとの一本化
+
+ * eval.c (rb_eval): クラス/モジュール定義の中から定義中のクラス/モ
+ ジュールが参照できるように
+
+Tue Oct 1 01:40:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-961001
+
+ * parse.y: cur_crefが2度宣言されていた
+
+ * signal.c (trap): SIGSEGV,SIGBUSのない機種に対応
+
+ * io.c (Init_IO): 引数タイプの指定間違い
+
+Mon Sep 30 15:28:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-960930
+
+ * config.guess,config.sub: $host_osが正しく設定されない
+
+ * eval.c (rb_eval): yieldで正しくないselfが設定されていた
+
+ * eval.c (ruby_run): toplevelの例外処理のバグ
+
+Mon Sep 30 09:13:26 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
+
+ * djgpp対応
+
+Sat Sep 28 02:45:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-960928
+
+ * sample/ruby-mode.el (ruby-beginning-of-block): ブロックの先頭に
+ 移動(正しくインデントしていないと動作しない)
+ (ruby-end-of-block): 同上
+
+ * eval.c (class_s_new): Class#newがイテレータとして呼ばれた時は
+ initializeもイテレータとして呼ばれるように
+
+ * signal.c (sigsegv): SEGVでbacktraceを表示するように
+
+Fri Sep 27 09:51:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-960927
+
+ * eval.c (error_print): 引数のないraiseでメッセージが正しく表示さ
+ れるように.
+
+ * eval.c (rb_longjmp): mesgがnilの時RuntimeErrorを生成する.
+
+ * eval.c (f_raise): 引数がない時に対応
+
+ * eval.c (thread_mark): stack上にないデータのアドレス変換を行って
+ いた.
+
+ * eval.c (Init_Thread): 割込みの間隔が1秒と長すぎた.
+
+Thu Sep 26 16:02:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_schedule): 一度ペンディングになるとフラグがクリア
+ されていなかった.
+
+ * process.c (rb_proc_exec): system/execの引数が空文字列であった場
+ 合,例外を発生すべきだった.
+
+ * config.sub/config.guess: 新しいものに置き換え
+
+Thu Sep 26 15:41:35 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
+
+ * io.c (next_argv): -i.bakをBOWとDOSに対応.
+
+Thu Sep 26 01:31:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * io.c (io_sysread): EOFで例外
+
+ * io.c (f_readline): EOFで例外を発生するように.getsは互換性のため
+ nilを返すままにする
+
+ * eval.c (proc_call): lambdaからのreturnでIN_BLOCKフラグが立ったま
+ まだった
+
+ * eval.c (PUSH_BLOCK2): threadに対応するためBlockを一度stackにコピー
+
+Wed Sep 25 11:54:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (method_call): Const::method()形式を使えるようにしてみた.
+ 引数括弧は省略できない.
+
+ * sample/test.rb: Process.killの存在を確かめてからテストを行う
+
+ * eval.c (eval_with_binding): 第2引数としてbinding(またはlambda)を
+ 与えるとその環境でevalを実行するようにした
+
+ * eval.c (f_binding): 現在のbindingを返す関数
+
+ * eval.c: block構造体にthe_classを保存するメンバを追加
+
+ * process.c (Init_process): kill,wait,waitpidをProcessに移動
+
+Tue Sep 24 02:44:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el: いろいろ問題が多いので以前の高速化は破棄.
+ 別のアプローチを使った.
+
+ * lib/tk.rb (Tk.pack): 複数のウィンドウを受け付けるpack
+
+Sat Sep 21 11:08:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (exprs): 空文も受け付けるように文法を変更.今までは改行
+ の連続だけが許されていた.
+
+Fri Sep 20 11:39:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * Failの大半を名前つき例外に変更.
+
+ * re.c (Init_Regexp): 名前つき例外を導入.
+
+ * eval.c (f_missing): Objectはinspectしない.
+
+ * object.c (inspect_i): Object#inspectでloopに対応.
+
+ * regex.c (re_search): /^$/が""にマッチしなかった.
+
+Thu Sep 19 19:25:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * regex.c (re_search): /^$/が非空行にマッチしていた.
+
+Tue Sep 17 10:28:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-960917
+
+Mon Sep 16 10:47:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-calculate-indent): 演算子継続の場合の
+ 文字列の判定のバグ
+
+ * sample/ruby-mode.el (ruby-calculate-indent): elseなどの次の行の
+ インデント計算を正しく.
+
+Sat Sep 14 08:37:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.2-960914
+
+Fri Sep 13 08:06:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/socket/socket.c (tcpaddr): port番号にntohsをつけ忘れ
+
+ * dln.c (link_undef): テーブルの種類が間違っていた.
+
+ * bignum.c (bigadd): 引き算が発生する時に計算違いが起きていた.
+
+ * parse.y (iter_do_block): do..endでもdynamic variableを.
+
+ * bignum.c (big_pow): より正確な計算を(整数同士ではfloatに変換しな
+ い).
+
+Thu Sep 12 13:11:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * variable.c (rb_set_class_path): Stringクラスが初期化される前に
+ Stringを作っていた.組込みクラスにはpathはいらない
+
+ * parse.y (yylex): 0.1が0になっていた
+
+ * parse.y (yylex): 行番号の不整合
+
+ * gc.c (oblist_live_obj): 今「生きている」全部のオブジェクトを返す
+ イテレータ.そのクラス(またはサブクラス)の全部のインスタンスを返
+ すeach_object_ofも定義した.
+
+ * class.c (rb_define_class_id): 無駄なクラスを割り当てていた.結果
+ として未初期化のクラスオブジェクトが存在していた.
+
+Wed Sep 11 00:56:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (yylex): octalの定数の検出をより正確に(090はエラーとか).
+
+ * bignum.c (big_minus): yがxより大きい場合にエラー.
+
+ * parse.y (yylex): エラー行番号の表示をより正確に
+
+ * sample/ruby-mode.el (ruby-expr-beg): 変数名が1文字の時誤動作して
+ いた.
+
+ * sample/ruby-mode.el (ruby-calculate-indent): ?/でループに落ちい
+ たバグを修正.
+
+ * enum.c (enum_min,enum_max): sortのようにイテレータとしても動作す
+ るように.
+
+ * enum.c (enum_find_all): typo
+
+Tue Sep 10 12:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * node.h (nd_line): NODEのlineをflagsに押し込めてオブジェクトサイ
+ ズを小さくした.制限:32bit intのマシンの場合,ファイルの行数が
+ 32767を越えると正常に表示されない.
+
+ * st.c: hashとcompareの関数メンバを構造体にパック,クラス的な使い
+ 方を行う.1 tableあたり4 byteの節約.
+
+Mon Sep 9 16:35:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * file.c (file_truncate): 提供されない時には特別な例外を発生するよ
+ うに.
+
+ * eval.c (Init_Proc): 不適切な位置のlocal-jumpを例外に.
+
+Sat Sep 7 17:06:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (proc_call): まだスコープがスタック上にある時には局所脱出
+ を有効にする.これで,procを生成してcallすることは,スコープを脱
+ 出しない限り,yieldと同じ意味を持つことになる.
+
+Fri Sep 6 13:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-indent-to): インデントが変わらない時に
+ はバッファを変更しない.
+ (ruby-calculate-indent): まず文字列の内部か判断してから,前の行
+ からパーズを行う.defunが大きくなった時の高速化.
+ (ruby-in-string-p): 文字列の内部かどうかを判断する関数(以前の
+ parseから分離)
+ (ruby-parse-region): 文字列に対する処理をはずす.
+ (ruby-beginning-of-block): ブロックの先頭に
+ (ruby-end-of-block): ブロックの末尾に(遅い…)
+
+Thu Sep 5 14:23:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * file.c (file_s_split): [dirname,basename]にsplitする.
+
+ * eval.c (rb_eval): evalの中でも定数の値が正しくなるように.これで
+ 定数に関しては静的なスコープが保証されるようになった.
+
+ * st.c (rehash): ハッシュ拡大の系数を2から1.79に.割算がより良い値
+ を返すように.
+
+Thu Sep 5 00:32:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (class_superclass) クラスのスーパークラスを返すメソッド.
+
+Wed Sep 4 16:54:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * random.c (f_rand): Bignumやlongの範囲を越えるFloatに対する乱数も
+ 発生できるように.
+
+ * struct.c (struct_alloc): Fatalではなく例外を発生させるように(通
+ 常の使用で発生しうる).
+
+ * struct.c (struct_s_members): Structの特異メソッドではなく,生成
+ されたStructクラスの特異メソッドにした.
+
+ * st.c (st_init_table): ruby専用にパラメタを固定にした(サイ
+ ズが減った)
+
+Mon Sep 2 11:37:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c (ary_shift): capaがあまりにも大きい時には領域をREALLOC
+ (ary_pop): 同上
+
+ * string.c (str_inspect): multibyte character 対応にミス.
+ (str_inspect): unsigned charにしないと符号展開されてしまう
+
+ * parse.y (primary): `::'をprimaryに移動 Foo::Bar.Bazがエラーにな
+ らないように.
+
+ * parse.y (primary): オペレータ形式の特異メソッドが定義できない
+
+ * random.c (f_rand): maxが0の時に対応
+
+ * io.c (io_printf): 関数を定義していたがインタプリタに登録していな
+ かった.
+
+ * file.c (file_s_basename): 第2引数が無い時にエラー.
+
+Thu Aug 29 10:49:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (expr): イテレータの新形式に「method do .. end」形式を採
+ 用した.もちろん昔の形式も有効.
+
+ * sample/ruby-mode.el (ruby-calculate-indent): endの数の方が多い場
+ 合にもエラーを起こさないように.
+
+Wed Aug 28 09:41:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * numeric.c (upto,downto,step,times): 対象がfixnumの範囲を越えても
+ 動作するように.
+
+Mon Aug 26 10:04:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * missing/setenv.c (envix): typo(missing `== 0' for memcmp)
+
+ * dir.c (dir_foreach): foreach(dir open -> read loop -> closeまで)
+
+ * io.c (io_foreach): foreach(file open -> read loop -> closeまで)
+
+ * Fatalのうち捕捉可能ないくつかを例外に.
+
+Sat Aug 24 23:56:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * bignum.c (bigdivmod): FIX2INT -> INT2FIX 大間違い
+
+Fri Aug 23 18:13:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * regex.c (re_free_registers): allocateしていない時には当然 free
+ してはいけない.
+
+Thu Aug 22 01:20:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_create): 外側から強制終了させられたthreadは
+ cleanupする必要が無い.
+
+Wed Aug 21 09:57:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_create): threadを終了させた大域脱出の情報を
+ main_threadに渡すように.
+
+ * parse.y (call_args): 最終引数に括弧を省略したメソッド呼出しを置
+ けるように(例: print foo bar, baz == print(foo(bar,baz)))
+
+Tue Aug 20 13:37:16 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (masign): 多重代入とrest引数の動作を合わせて空の配列を代
+ 入するように.
+
+ * parse.y (arg): defined?の強度をもうちょっと強く
+
+ * eval.c (error_print): -wで例外名も表示するように
+
+ * eval.c (rb_eval): 新構文に対応
+ (handle_rescue): 捕捉する例外を kind_of? で同定
+
+ * parse.y (primary): rescueの構文を変更(同定引数の追加,複数rescue)
+
+ * Fail()のかなりを適当な例外を使うように
+
+ * eval.c (thread_interrupt): Interrupt(今はnon-local jump)は
+ main-threadに送られるように.
+
+ * eval.c (rb_longjmp): $! の内容を文字列から例外クラスに変更
+ (rb_raise): rb_fail から名称変更
+ (rb_interrupt): 例外化
+ (rb_exit): 例外化
+
+ * error.c (Init_Exception): 例外クラスの新設(文字列のサブクラス)
+
+Mon Aug 19 19:40:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * signal.c (trap): 古いハンドラを返すように.
+
+Wed Aug 14 00:07:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_trap_eval): ハンドラのためにthreadをforkすることを止
+ めた.
+
+ * eval.c (thread_mark): thread毎の $!, $@ をマークし忘れ
+
+ * ext/dbm/dbm.c (fdbm_delete): イテレータとして呼ばれた場合,要素
+ が無ければブロックを評価する.
+
+ * hash.c (hash_delete): イテレータとして呼ばれた場合,要素が無けれ
+ ばブロックを評価する.
+
+ * array.c (ary_delete): イテレータとして呼ばれた場合,要素が無けれ
+ ばブロックを評価する.
+
+ * eval.c (rb_interrupt): SIGINTのデフォルトをexitから特別な大域脱
+ 出に.やはり割り込まれた位置の表示が無いのは寂しいので.
+
+Tue Aug 13 01:34:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_exit): sub-thread内でのexitもstatusを保存するように
+ (thread_create): 自thread内のexitに対応
+
+ * signal.c (sighandle): SIGINTのデフォルトハンドラはexitするように
+ (以前は例外を発生していた).
+
+ * 例外の一部をFatalに.
+
+ * string.c (str_aset): 文字列の置換の対象が部分文字列でなかった時,
+ 例外を発生させないように
+
+ * eval.c (proc_call): Procの中からbreak/nextは通し,他のものは通さ
+ ないように
+
+Mon Aug 12 14:15:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * object.c (krn_type): 文字列を返す
+
+ * eval.c (thread_create): sub-thread内でのexitに対応
+
+ * numeric.c (fix_type): 文字列を返す
+
+ * io.c (f_p): デバッグ用データ表示メソッド
+
+ * eval.c (f_missing): nil/TRUE/FALSEを特別扱い
+
+ * string.c (str_inspect): 長い文字列を短縮表示.inspectの働きを
+ human readable stringの生成に統一(re-generatable string は正式に
+ 無くなった).
+
+Sat Aug 10 16:54:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * object.c (Init_Object): kernel/nil/false/trueのクラス名を変更(小
+ 文字に),rubyスクリプトからアクセスできないように.
+
+ * eval.c (rb_eval): CONSTANTのアクセス先を単純化.crefを使わない.
+
+ * eval.c (f_eval): 特異メソッド内でも定数の値が正しくなるように
+
+Fri Aug 9 12:23:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c (ary_concat): append -> concat Stringに合わせた
+
+ * parse.y (yylex): `$;'が使えなかった.
+
+ * array.c (ary_push_method): 複数引数を受け付けるように.
+ (ary_unshift): 複数引数を受け付けるように.
+
+ * io.c (io_popen): IO.popenでcommand pipeが開けるように.
+
+ * object.c (Init_Object): KernelとNilをruby scriptからアクセスでき
+ ないように.
+
+Thu Aug 8 01:21:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * object.c (f_integer): 整数への変換関数
+ (f_float): 実数への変換関数
+ (f_string): 文字列への変換関数
+ (f_array): 配列への変換関数
+
+ * bignum.c (big_to_i): FIXNUMの範囲でない時はBignumのまま返すよう
+ に変更.
+
+Wed Aug 7 09:28:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99.1-960807
+
+ * parse.y (mlhs): 「*foo = 1,2,3」タイプの多重代入も可能に.
+
+ * object.c (Init_Object): クラスTrue/Falseをruby scriptからアクセ
+ スできないように.
+
+ * object.c (nil_inspect): inspect表現は"nil"に
+
+ * io.c (io_print): nilのprintをnilに.
+
+ * object.c (nil_to_s): nilの文字列表現を""に.
+
+Tue Aug 6 01:12:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * dir.c (dir_s_open): file descripterが足りない時にはgcしてからも
+ う一度openしてみる.
+
+ * io.c (rb_fopen): すべてのfopen()についてfile descripterが足りな
+ い時にはgcしてからもう一度openしてみる.
+
+ * ext/socket/socket.c (Init_socket): 定数の追加.
+
+ * sample/ruby-mode.el (ruby-indent-to): インデント後のカーソル位置
+ の調整を正しく.
+
+ * gc.c (gc): 割込みチェックを行わない(Cコードの中で安心して
+ malloc()が使えなくなるので).
+
+ * st.c (call_hash_func): signalとthreadによる割込みに対応.
+
+ * sig.h (DEFER_INTS): 割込み禁止区間の指定
+
+ * eval.c (f_require): threadによるrequireの競合に対応(最初の
+ requireが終了するまで他のthreadは待つ).
+
+ * bignum.c (str2inum): 0x80000000の値が負になっていた
+
+ * sprintf.c (f_sprintf): 文字列末尾,行末の単独の`%'に対応
+
+ * bignum.c (big_cmp): 比較の結果が逆になる時があった.
+
+Mon Aug 5 10:58:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * process.c (proc_exec_v): 例外のメッセージを分かりやすく.
+
+ * ext/dbm/dbm.c (fdbm_store): nilを格納すると要素の削除になる
+
+ * ext/dbm/dbm.c: サイズをキャッシュ.
+
+Sat Aug 3 01:52:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_fail): `fail'が引数無しで呼ばれた時だけ以前の`$@'を保
+ 存するように.
+
+ * eval.c (f_fail): frameの調整
+
+Fri Aug 2 11:26:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/socket/socket.c (bsock_setopt): valとしてTRUE/FALSE/Fixnumも
+ 受け付けるように.
+
+ * ext/socket/socket.c (Init_socket): SO_REUSEADDR等の定数の追加
+
+ * ext/md5/md5init.c: md5モジュール(初の複数ファイルからなるモジュー
+ ルでもある)
+
+ * ruby.h (Make_Data_Struct): Data: objectのinstance変数に格納 ->
+ Data型のObjectに(Dir,Time,Proc,Thread,DBM)
+
+Thu Aug 1 11:38:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/dbm/dbm.c (fdbm_store): valueが文字で無い時に対応
+
+Wed Jul 31 10:53:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ext/socket/socket.c (open_inet): htonsが必要であった
+ (tcpaddr): ntohlで変換した
+
+ * process.c (rb_proc_exec): execvp -> execv
+
+Tue Jul 30 17:48:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c: `$?'をthread localに
+
+ * Makefile.in (install): install時にstripを行う
+
+ * configure.in: install時のstripの検出
+
+ * configure.in: NEXTSTEP対応
+
+ * version 0.99.1-960730
+
+Tue Jul 30 16:40:35 1996 SHIROYAMA Takayuki <psi@fortune.nest.or.jp>
+
+ * dln.c (dln_load): NeXT dln(mach-o)対応.configureは未対応
+
+Tue Jul 30 09:46:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * process.c (f_system): 複数引数もとれるように
+
+ * process.c (f_exec): 複数引数もとれるように
+
+ * array.c (ary_append): 配列(またはEnum)の要素を破壊的に追加
+
+ * array.c (ary_plus): Enumはその要素を追加
+
+ * file.c (file_s_open): File.openを追加
+
+ * struct.c (struct_new): FIX2INTを忘れていた
+
+ * file.c (Init_File): exists? -> exist?
+
+ * object.c (obj_is_kind_of): is_kind_of? -> kind_of?, is_a?
+
+ * object.c (obj_is_instance_of): is_instance_of? -> instance_of?
+
+Mon Jul 29 16:40:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (parse_regx): 式展開を行った場合,casefoldの設定ができて
+ いなかった.
+
+ * object.c (true_type): TRUE/FALSEにtypeを実装.
+
+ * parse.y (read_escape): 3文字以内のoctalに対応(\0とか)
+
+Fri Jul 26 00:31:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c (ary_reverse_bang): in-placeで配列を反転させる
+ (ary_sort_bang): in-placeでsortする
+ (ary_sort): sortした配列を返すように
+ (ary_delete_at): 指定した位置の要素を削除する
+
+ * eval.c (rb_call): stack深さチェックを毎回は行わないように
+
+ * error.c (Warning): 実行中のwarningが表示されていなかった
+
+ * eval.c (compile): 例外発生を分離.
+
+ * eval.c (f_eval): 変数rb_in_evalを正しく管理するように
+
+ * ext/dbm/dbm.c (fdbm_store): 格納するkeyを文字列に変換
+
+ * eval.c (rb_call): 無限再帰のチェックを大域脱出を行うC methodにも
+ 対応させた.threadのstack深さチェックルーチンを流用.
+
+ * parse.y (yylex): 第1引数のunary -/+の判定が間違っていた.
+
+ * parse.y (yylex): unary +で数字を余計に読んでいた(ex. +5 -> 55)
+
+Thu Jul 25 12:15:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (yylex): 曖昧でない引数に対して警告を出していた.
+
+ * eval.c (iterator_p): 引数で呼んでも正しい結果を返すように.
+
+ * parse.y: break/next/redo/retryのメソッド化.
+
+ * sample/ruby-mode.el (ruby-calculate-indent): nestのチェックミス
+
+ * sample/ruby-mode.el (ruby-parse-region): 予約語のチェックを強化
+
+ * parse.y (primary): unless/untilの復活
+
+Tue Jul 23 18:50:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c (Array#empty?), Hash.c (Hash#empty?), ext/dbm/dbm.c (DBM#empty?):
+ 空の判定述語
+
+ * eval.c (f_unless): ifの逆をするイテレータ
+
+ * eval.c (f_until): whileの逆をするイテレータ
+
+ * parse.y: notの優先順位をand/orより高く
+
+ * parse.y (expr): `!'を引数括弧を省略したcallでも有効に
+
+Mon Jul 22 10:15:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960722
+
+ * array.c (ary_print_on): OFSのNILチェックが不完全
+
+ * ruby.c (load_file): 標準入力からのスクリプトが空の時に対応.
+
+ * ruby.c (proc_options): -wでは引数無しの時には標準入力からスクリ
+ プトをとる(-vではたんに終了する).
+
+ * array.c (ary_compact): nilの要素を取り除くメソッド
+
+ * array.c (ary_nitems): nilでない要素を数えるメソッド
+
+Sun Jul 20 00:51:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ruby.c (proc_options): -w optionを追加
+
+ * parse.y: {}が閉じていない時には展開しない文字列を
+
+Fri Jul 19 16:16:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960719
+
+ * lib/find.rb: 石塚版(pruneの拡張付き)
+
+ * file.c (test_l): lstatで調べないとね.
+
+ * eval.c (f_throw): 第2引数を省略可能に.
+
+ * parse.y (str_extend): {}のネストに対応
+
+Thu Jul 18 18:25:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960718
+
+ * parse.y (str_extend): 文字列中の式展開に \" ' ` / を含む事ができ
+ るように.
+
+Tue Jul 16 15:55:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-parse-region): 正規表現内のエスケープ
+ に対応
+
+ * version 0.99-960716
+
+Fri Jul 12 10:06:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * io.c (f_select): 引数のclose check.
+
+ * ruby.c (load_file): #!行の引数チェックを第1引数に限定(実をいうと
+ DOS改行対策)
+
+Wed Jul 10 17:18:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960710
+
+ * time.c (time_s_timegm/time_s_timelocal): 時間を生成するメソッド
+
+Mon Jun 17 15:59:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960617
+
+ * parse.y (yyerror): エラー表示の簡略化.
+
+Wed Jun 12 14:11:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * signal.c (rb_trap_exit): trap 0はthreadを生成せずに処理する.
+
+Fri Jun 7 10:17:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c/hash.c (indexes): 配列1引数のパターンを無くした.配列の
+ 場合は`*ary'を使ってもらおう.
+
+ * eval.c (thread_wait_threads): main_threadが終了する前に他の
+ threadを待つ(強制的には終了させない).
+ (ruby_run): 他のthreadを待っている間にシグナルが来たら,全thread
+ を強制終了させる.
+
+ * eval.c (rb_fail): メソッド名を`$!'に埋め込む.
+
+ * eval.c (thread_create): main_threadのコンテクストがセーブされな
+ い場合があった.
+
+ * process.c (f_sleep): 時間を指定せず,threadがひとつしかない状況
+ にも対応.
+
+ * eval.c (thread_create): create後,fnを呼び出す前にcontext switch
+ が起きると違うcontextでfnが実行されてしまうバグ.
+
+Mon Jun 3 08:03:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * struct.c (struct_s_def): メンバの指定を文字列,シンボル(FIXNUM)
+ 双方で可能にした.
+
+ * ext/etc/etc.c (Init_etc): 構造体オブジェクトをGCから保護した.
+
+ * error.c (rb_sys_fail): nil/FALSEを引数として受け付けるように.
+
+Thu May 30 16:19:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_select): EINTRに対応.
+
+Wed May 29 11:04:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (f_catch): catch/throwを実装した.
+
+Tue May 28 13:30:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960528
+
+ * eval.c (thread_cleanup): main threadが終了すると他のthreadも終了
+ することの明確化.
+
+ * signal.c (trap): SIGINTのデフォルトの設定ミス(本当にSIG_DFLでは
+ まずかった).rubyではちゃんとハンドルしないと.
+
+ * eval.c (thread_interrupt): SIGINTはmain_threadに例外を発生させる
+ ように.
+
+Mon May 27 15:13:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_status): threadの状態を返すメソッド.threadの終了
+ を待たない.
+
+ * eval.c (thread_value): 一種のpromiseを実装するためのメソッド.
+
+ * eval.c (thread_join): 待っているthreadが例外を起こした時には,
+ joinがその例外を発生するように.
+
+ * eval.c (thread_create): threadでの例外をpropagateしないように.
+
+Fri May 24 10:47:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * enum.c (Init_Enumerable): `size' as alias to the `length'
+
+ * eval.c (thread_save_context): `$@', `$!'をスレッド毎にセーブ.
+
+ * eval.c (superclass): エラー表示をより親切に.
+
+Thu May 23 10:38:41 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.99-960523
+
+ * eval.c (superclass): エラー時にスーパークラス名を(分かれば)表示
+ するように.
+
+Wed May 22 19:48:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (superclass): スーパークラスの指定子を`:'から`<'に変更.
+
+Tue May 21 09:27:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * lib/thread.rb: threadをサポートするクラス(Mutex, Queue).
+
+Mon May 20 09:39:49 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * time.c (time_cmp): 浮動小数点数も扱えるように.
+ (time_minus): Time - Timeが浮動小数点数を返すように.
+
+Fri May 17 15:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * process.c (rb_proc_exec): Thread対応時にexecの直前に
+ ITIMER_VIRTUALをリセットする.
+
+Tue May 14 02:12:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * signal.c (sighandle): SIGINTに対してデフォルトで例外を発生させる
+ のをやめ,status 130でexitするようにした.
+
+ * eval.c (thread_schedule): Threadのバグはほとんどとれたようだ.
+
+Fri May 10 11:21:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (thread_schedule): ユーザレベルThread機能.効率はともかく
+ 移植性はある.今後,thread間の通信機能を実装する予定.
+
+Thu May 2 21:22:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * time.c (time_timeval): struct timevalを直接返すように(static変数
+ を使わない).
+
+Wed May 1 17:27:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * process.c (f_sleep): 整数以外のtimeを指定できるように.
+
+Thu Apr 25 08:19:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * file.c (file_s_dirname): ファイル名が"/"を含まない時,"."を返す
+ ように(GNU dirnameの仕様).
+
+ * file.c (file_s_basename): まだnilと0を混同しているソースが残って
+ いた.
+
+ * parse.y (exprs): エラーリカバリを追加.
+
+Wed Apr 24 15:51:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_chop_bang): CRLFの場合2 bytesをchop!するように.
+
+ * ext/socket/socket.c (tcp_svr_s_open): まだnilと0を混同しているソー
+ スが残っていた.
+
+Tue Apr 23 18:14:25 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * pack.c (pack_pack): "A/a"のバグ.余計なpaddingが入っていた.
+
+Thu Apr 18 13:02:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * configure.in: アーキテクチャ依存部を別ディレクトリにインストール
+ するように.
+
+ * parse.y (yyerror): エラー発生時にエラー行とその位置を表示するよ
+ うに.
+
+Wed Apr 17 14:22:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * defines.h: SAFE_SIGHANDLEを無くし,危険な選択はできないように.
+
+ * io.c (io_ungetc): 新機能.
+
+ * ruby.c (load_file): ファイルからの読み込み方式が変わったのに対応.
+
+ * parse.y (compile_file): ファイルからの入力を一度全部読み込むのを
+ 止めて,getsを使うことにした.
+
+Wed Apr 10 17:40:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.98
+
+Tue Apr 9 09:54:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (iter_block): イテレータブロックの指定をメソッド呼び出し
+ に限定.文法の明確化.
+
+ * eval.c (rb_eval): 条件式の正規表現の比較をinline化.
+
+ * eval.c (rb_eval): defined? の 定義情報(種別)を文字列で返す.
+
+ * node.h: NODE_BEGIN -> NODE_RESCUE, NODE_ENSUREに分離.
+
+ * eval.c (rb_eval): option -n/-pのトップレベルループのinline展開.
+
+ * parse.y (cond0): 条件式中の文字列は比較の対象としない
+
+Wed Mar 27 12:33:54 1996 Tairo Nomura <tairo@hucom.tp.titech.ac.jp>
+
+ * defines.h: NeXT対応
+
+Wed Mar 27 10:02:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y: 予約語の変更 continue -> next
+
+Mon Mar 25 07:34:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (parse_regx): o(once)オプションを追加.
+
+Fri Mar 22 14:25:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.97d
+
+ * eval.c (dyna_var_defined): 動的ローカル変数の定義チェック用ルー
+ チン.
+
+ * parse.y (gettable): eval()の中での動的ローカル変数(既に値を持っ
+ ているもの)の検出に失敗していた.
+
+Tue Mar 19 10:46:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.97c
+
+ * re.c (reg_s_new): compile時にsegmentation fault.
+
+ * parse.y (str_extend): いつもevalするように.
+
+Wed Mar 13 11:00:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (str_extend): 文字列中の式展開の不備を無くした.
+
+ * parse.y: 下手なエラーリカバリを外した.
+
+Tue Mar 12 12:30:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rescue): 間違ってensureでも例外を捕捉していた.
+
+Wed Mar 6 12:11:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * parse.y (var_extend): 変数展開"#{}"で,任意の式を書けるようにし
+ た,これで「変数」展開では無くなっちゃったなあ.
+
+ * regex.c (init_syntax_once): `_'をwordに追加.
+
+ * regex.c (re_compile_pattern): `\w',`\W'の判定をsyntax tableを使
+ うように.
+
+Tue Feb 27 10:15:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * object.c (obj_inspect): 表示するインスタンス変数が無い時には,
+ to_sを使う.
+
+ * configure.in: dlnの検出を自動的に.
+
+Mon Feb 26 19:55:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ruby.c (readin): read(2)で一度にファイルが読み込めない場合に対応.
+
+Sat Feb 24 14:47:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.97b
+
+Fri Feb 23 11:26:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * class.c (rb_define_module): C言語で定義されたモジュールのPATHの
+ 設定忘れ.文字列化でcore dump.
+
+ * eval.c (mod_include): 戻り値をnilに.
+
+ * version 0.97a
+
+Thu Feb 22 21:03:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * array.c (ary_times): 「配列*文字列」がjoinと同じ働きをするように.
+
+Wed Feb 21 11:18:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * configure.in : fileCountをcache.
+
+ * configure.in : LinuxでELF環境を自動的に検出できるよう.
+
+Tue Feb 20 11:18:09 1996 Mitsuhide Satou <mit-sato@aries.bekkoame.or.jp>
+
+ * FreeBSD dynamic link対応.
+
+Fri Feb 16 08:50:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * object.c (obj_inspect): インスタンス変数を持たないオブジェクトも
+ 正しく表示されるように.
+
+Wed Feb 14 16:56:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): 条件式の`2..2'など左辺成立直後に右辺が成立する
+ パターンにバグ.
+
+Tue Feb 13 18:22:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.97
+
+Fri Feb 9 21:32:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * lib/tkscrollbox.rb: スクロールでtclの設定を行い,ruby<->wishの不
+ 要な通信を無くした.
+
+Wed Feb 7 10:26:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * string.c (str_aref): indexをunsigned intでとっていた.
+
+ * string.c (str_aref): 範囲外のindexに対してnilを返す.
+
+ * parse.y (special_local_set): `$_'が宣言無しに使われた場合に対応.
+ 関数をvariable.cから移動.
+
+ * string.c (str_sub): 置換開始位置が間違っていた.
+
+Tue Feb 6 16:17:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-parse-region): コメントの読み飛ばしの
+ バグ.
+
+Fri Feb 2 18:35:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * variable.c (lastline_get): `$_'を`$~'と同じようにSCOPEローカルな
+ 変数にした.
+
+Thu Feb 1 14:14:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * file.c: statのcacheをやめた.
+
+Wed Jan 31 07:13:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (proc_s_new): procの中でyieldを呼ばれた時にcore dumpして
+ いた.とりあえず例外を発生させる.
+
+ * variable.c (rb_class2path): singleton classに対応.
+
+ * ext/etc/etc.c (Init_etc): struct_defineのターミネータがnilだった
+ (0でなければならない).
+
+ * ext/marshal/marshal.c: TRUE/FALSEを吐き出せるように.
+
+ * eval.c (rb_get_method_body): キャッシュのalias対応,いままでは
+ aliasはキャッシュに入っていなかった.
+
+Tue Jan 30 09:55:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): NODE_BLOCK - tail recursive(というほどでもない
+ が).
+
+ * io.c (io_pipe): pipe(2)を実装した.
+
+ * eval.c (rb_eval): Qselfをなくした.thread対応への第一歩.先は遠
+ いが….
+
+ * eval.c (proc_call): procの中でのreturnはprocの終了を意味するよう
+ に.ただし,procからのyieldの中でのreturnは例外を発生する.
+
+Wed Jan 24 11:33:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.96a
+
+ * dir.c (dir_each): `$_'の値を変更するのをやめた.
+
+ * io.c (f_readlines): nilとFALSEの分離のあおりで無限ループに落ちて
+ いた.
+
+ * ruby.c (ruby_options): $0の設定ミス.
+
+Tue Jan 23 15:28:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): ``は文字列を引数とするメソッド(`)呼び出しのシ
+ ンタックスシュガーであるとした.
+
+ * ruby.c (addpath): `-I'オプションでディレクトリが「前に」追加され
+ るように変更.
+
+Fri Jan 19 11:23:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * dln.c (load_1): N_INDR対応(出来たような気がする).
+
+Thu Jan 18 18:14:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * ruby.texi: FALSEとnilの分離を反映した.
+
+Tue Jan 16 17:39:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.96 - とりあえずnilとFALSEを区別する版
+
+Wed Jan 10 15:31:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * re.c (reg_match): マッチしなかった時の戻り値はFALSE.
+
+ * object.c (rb_equal): `0 == nil'がTRUEになるバグ.
+
+Tue Jan 9 00:44:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * nilとFALSEが分離可能に変更.
+
+ * nilとFALSEと0の区別を厳密に.
+
+ * struct.c (struct_new): 引数を0で終る必要が無くなった.
+
+ * object.c (inspect_i): オブジェクトのチェックのバグ(Fixnumでcore
+ dumpしていた).
+
+ * range.c (range_to_s): Rangeの表示を改善.
+
+ * object.c (true_inspect): TRUEの表示を`TRUE'に.
+
+Mon Jan 8 15:02:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * numeric.c (fix_mul): divide by zero errorが発生した(オーバーフロー
+ 検出のバグ)
+
+ * texinfo.texをパッケージに含めた.
+
+Sun Dec 31 00:08:49 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): `::'では,そのクラスで定義された定数を参照する
+ ように変更.
+
+ * string.c (Init_String): eachをeach_lineに戻した.
+
+Thu Dec 28 12:31:55 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * eval.c (rb_eval): caseの演算子を`=~'から`==='に.
+
+ * variable.c (rb_const_set): クラス定数の再定義を許す(同じクラスで
+ は不可).警告は出す.
+
+Wed Dec 27 13:27:52 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.95c
+
+ * ext/tkutil/tkutil.c: wishがあってもなくても一応コンパイルだけは
+ するように.
+
+ * lib/tk.rb: 環境変数PATHから{wish|wish4.0}を探すように.
+
+Tue Dec 26 01:03:42 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * sample/ruby-mode.el (ruby-parse-region): 正規表現の検出強化.
+
+ * numeric.c (fix_mul): 乗算のオーバーフロー検出アルゴリズムのバグ.
+
+ * ext/extmk.rb.in: ./install-shを使う場合のPATHを調整.
+
+ * Makefile.in (install): lib/*.rbを一つずつインストール.
+
+ * io.c (io_each_line): イテレータの戻り値をnilで統一.
+
+Fri Dec 22 10:34:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+
+ * version 0.95b
+
+ * variable.c (f_untrace_var): 第2引数を指定すると特定のtraceを削除
+ できるように.
+
+ * variable.c (f_trace_var): 第2引数がnilの時,traceを削除する.
+
+ * lib/tk.rb (file_readable/file_writable): 第2引数をnilにすること
+ によるevent handlerの削除.
+
+ * parse.y (variable): ドキュメントに`__FILE__'と`__LINE__'が残って
+ いた.`caller(0)'で代用したはずだったのに.
+
+ * eval.c (f_eval): $!のリセット.
+
+ * error.c (err_sprintf): 勝手に"\n"を付加するのを止めた.
+
+ * parse.y (f_arglist): 引数リスト直後のif/whileの読み間違い.
+ lex_stateの値が設定されていなかった.
+
Thu Dec 21 00:56:57 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
+ * version 0.95a - ^^;;;
+
+ * lib/tkscrollbox.rb: パッケージに入ってなかった.
+
+ * configure.in: FILE structureのチェックにバグ.
+
+ * Makefile.in (clean): ext以下をinstallしていた.
+
+ * ext/socket/extconf.rb: Solarisにおける-lnlsのチェック.
+
+ * array.c (beg_len): バグがあった….悲しい.
+
* version 0.95 - fj.sourcesに
* eval.c (rb_eval): rescueのロジックをrb_rescue()に一元化.
@@ -204,8 +1970,8 @@ Fri Oct 13 13:19:19 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (str_strip_bang): 文字列の後ろの長さの調整が行われてい
なかった.
- * re.c (reg_search): $&, $1...のはローカルに束縛するようになった.
- 呼び出したメソッドでのマッチは現スコープの$&などの値に影響しない.
+ * re.c (reg_search): $&, $1...はローカルに束縛するようになった.呼
+ び出したメソッドでのマッチは現スコープの$&などの値に影響しない.
マッチの情報をスコープ外で得たいときには$~を使って束縛情報を持ち
出す必要がある.
@@ -352,7 +2118,7 @@ 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>
+ Wed Sep 13 15:44:35 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* string.c (Fstr_tr_bang): 範囲外の文字に対する変換バグ.
@@ -441,7 +2207,7 @@ Fri Aug 11 14:37:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* io.c: マクロREAD_DATA_PENDINGの定義を変更(Linux対応)
- * io.c (io_fptr_finalize): ftprの開放時の処理を指定できるように.
+ * io.c (io_fptr_finalize): fptrの開放時の処理を指定できるように.
Wed Aug 9 16:52:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
@@ -722,8 +2488,8 @@ Thu May 18 12:27:23 1995 Yukihiro Matsumoto <matz@ix-02>
い問題もあった.結果としてtrを書き換えたので,copyrightの問題は
無くなった(と思う).
- * gc.c (gc): the_scopeをマークしていなかったので,ローカル変数が間
- 違って開放される場合があった.
+ * gc.c (gc): the_scopeをマークしていなかったので,ローカル変数の指
+ しているオブジェクトが間違って開放される場合があった.
* gc.c (mark_locations_array): 若干の高速化.
@@ -789,8 +2555,8 @@ Thu Apr 20 12:31:24 1995 Yukihiro Matsumoto (matz@ix-02)
* env.h, gc.c, regex.c: IRIXへの移植対応
- * configure: picを生成するoptionの検出のため,システムタイプをチェッ
- クするように.
+ * configure: dlopen用にpicを生成するoptionの検出のため,システムタ
+ イプをチェックするように.
Tue Apr 18 19:08:17 1995 Yukihiro Matsumoto (matz@ix-02)
@@ -808,7 +2574,7 @@ Mon Apr 10 18:36:06 1995 Yukihiro Matsumoto (matz@ix-02)
Fri Apr 7 13:51:08 1995 Yukihiro Matsumoto (matz@ix-02)
* cons.c->assoc.c: consの余計な機能は外してpairとしての機能だけを
- 残した.enumerableをincludeするのもやめた.
+ 残した.Enumerableをincludeするのもやめた.
* string.c(esub): 文字列置換イテレータ.perlのs///eの相当する.
@@ -885,8 +2651,8 @@ Fri Mar 10 18:35:46 1995 Yukihiro Matsumoto (matz@ix-02)
* eval.c: Mathのようなモジュールは自分自身でextendする.
- * eval.c: クラスやモジュールを定義した既に同名のものがあれば追加定
- 義となるように.ただし.superクラスの違いなどはチェックする.
+ * eval.c: クラスやモジュールを定義する時,既に同名のものがあれば追
+ 加定義となるように.ただし.superクラスの違いなどはチェックする.
* regex.c: debug.
@@ -933,8 +2699,8 @@ Thu Feb 23 11:19:19 1995 Yukihiro Matsumoto (matz@ix-02)
* eval.c(rb_clear_cache): キャッシュのクリアし忘れがあった.
* eval.c: 定数のスコープをクラス内の静的スコープに変更した.これに
- よって,特異メソッドからは参照される定数は,レシーバのクラスでは
- なく,定義されたスコープのクラスの定数となる.
+ よって,特異メソッドから参照される定数は,レシーバのクラスではな
+ く,定義されたスコープのクラスの定数となる.
Wed Feb 22 00:51:38 1995 Yukihiro Matsumoto (matz@dyna)
@@ -985,8 +2751,9 @@ Fri Feb 10 16:30:00 1995 Yukihiro Matsumoto (matz@ix-02)
* ruby.c(load_file): scriptを読み込む時だけ"#!"の解析を行うように.
- * ruby.c(readin): ファイル読み込み時に先頭に"#!"があり,rubyに引数
- が与えられていれば,その引数も有効になる.
+ * ruby.c(readin): ファイル読み込み時に先頭に"#!"があり,その行が
+ "ruby"という文字列を含む時,rubyに引数が与えられていれば,その引
+ 数も有効になる.
* parse.y(yylex): コメント行の終りが`\'であった時,次の行に継続し
ているとみなすようにした.
@@ -1056,7 +2823,7 @@ Tue Jan 17 11:11:27 1995 Yukihiro Matsumoto (matz@ix-02)
性を継承する.最初の定義の時は今までと同じデフォルト(トップレベ
ルで関数的,クラス定義内で通常メソッド).
- * object.c(Class::new): オブジェクトの生成時に関数的メソッド
+ * object.c(Class#new): オブジェクトの生成時に関数的メソッド
init_objectが必ず呼ばれるように変更.
* eval.c: 未定義のメソッドに対してunknownメソッドが呼ばれるように
diff --git a/MANIFEST b/MANIFEST
index 81c7a3a646..3b0ba3e0d0 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2,6 +2,7 @@ ChangeLog
MANIFEST
Makefile.in
README
+README.jp
README.EXT
ToDo
array.c
@@ -9,7 +10,9 @@ bignum.c
class.c
compar.c
configure
+configure.bat
configure.in
+config.dj
config.guess
config.sub
defines.h
@@ -47,7 +50,6 @@ regex.c
regex.h
ruby.c
ruby.h
-ruby.texi
sig.h
signal.c
sprintf.c
@@ -56,27 +58,41 @@ st.h
string.c
struct.c
time.c
+top.sed
util.h
util.c
variable.c
version.c
version.h
ext/Setup
+ext/Setup.dj
ext/extmk.rb.in
lib/base64.rb
+lib/cgi-lib.rb
+lib/complex.rb
lib/find.rb
lib/getopts.rb
+lib/jcode.rb
lib/mailread.rb
+lib/mathn.rb
+lib/observer.rb
lib/parsearg.rb
lib/parsedate.rb
+lib/rational.rb
+lib/safe.rb
+lib/thread.rb
lib/tk.rb
+lib/tkcore.rb
lib/tkcanvas.rb
+lib/tkclass.rb
lib/tkentry.rb
+lib/tkscrollbox.rb
lib/tktext.rb
-lib/tkclass.rb
+lib/tkthcore.rb
missing/alloca.c
missing/crypt.c
missing/dup2.c
+missing/flock.c
missing/memmove.c
missing/mkdir.c
missing/nt.c
@@ -93,16 +109,16 @@ sample/cbreak.rb
sample/clnt.rb
sample/dbm.rb
sample/dir.rb
-sample/evaldef.rb
+sample/eval.rb
sample/export.rb
sample/exyacc.rb
+sample/fact.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
@@ -113,12 +129,16 @@ sample/list3.rb
sample/marshal.rb
sample/mkproto.rb
sample/mpart.rb
+sample/observ.rb
sample/occur.pl
sample/occur.rb
sample/occur2.rb
+sample/philos.rb
+sample/pi.rb
sample/rcs.awk
sample/rcs.dat
sample/rcs.rb
+sample/regx.rb
sample/ruby-mode.el
sample/sieve.rb
sample/svr.rb
@@ -131,6 +151,6 @@ sample/tkfrom.rb
sample/tkhello.rb
sample/tkline.rb
sample/tktimer.rb
-sample/trojan.pl
sample/trojan.rb
+sample/tsvr.rb
sample/uumerge.rb
diff --git a/Makefile.in b/Makefile.in
index 81e3feca79..aa018968ff 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -77,30 +77,30 @@ 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
+ cd ext; ../miniruby ./extmk.rb @EXTSTATIC@; fi
$(PROGRAM): $(OBJS)
@rm -f $(PROGRAM)
$(PURIFY) $(CC) $(STATIC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
-$(bindir)/ruby: extruby
- $(INSTALL_PROGRAM) ruby $(bindir)/ruby
- strip $(bindir)/ruby
-
-install: $(bindir)/ruby
+install:; $(INSTALL_PROGRAM) ruby $(bindir)/ruby
+ @-@STRIP@ $(bindir)/ruby
+ @test -d $(libdir) || mkdir $(libdir)
cd ext; ../miniruby ./extmk.rb install
- $(INSTALL_DATA) lib/*.rb $(libdir)
+ @for rb in `grep '^lib/' MANIFEST`; do \
+ $(INSTALL_DATA) $$rb $(libdir); \
+ done
clean:; @rm -f $(OBJS)
@rm -f ext/extinit.c ext/extinit.o
- cd ext; ../ruby ./extmk.rb install
+ cd ext; ../miniruby ./extmk.rb clean
realclean: clean
@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;\
+test:; @-./ruby sample/test.rb > ./ruby_test 2>&1; \
if grep '^end of test' ./ruby_test > /dev/null; then \
echo "test succeeded"; \
else \
@@ -122,6 +122,9 @@ crypt.o: missing/crypt.c
dup2.o: missing/dup2.c
$(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/dup2.c
+flock.o: missing/flock.c
+ $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/flock.c
+
memmove.o: missing/memmove.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c missing/memmove.c
@@ -162,22 +165,21 @@ bignum.o: bignum.c ruby.h config.h defines.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
dir.o: dir.c ruby.h config.h defines.h
-dln.o: dln.c config.h defines.h dln.h
+dln.o: dln.c config.h defines.h dln.h st.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
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 re.h regex.h
+gc.o: gc.c ruby.h config.h defines.h env.h sig.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 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
+numeric.o: numeric.c ruby.h config.h defines.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 sig.h st.h
@@ -190,7 +192,7 @@ 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
+struct.o: struct.c ruby.h config.h defines.h
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 st.h
diff --git a/README b/README
index ad239f98d2..debf3e4b1c 100644
--- a/README
+++ b/README
@@ -1,151 +1,115 @@
-* Rubyとは
+* What's Ruby
-Rubyはシンプルかつ強力なオブジェクト指向スクリプト言語です.
-Rubyは最初から純粋なオブジェクト指向言語として設計されていま
-すから,オブジェクト指向プログラミングを手軽に行う事が出来ま
-す.もちろん通常の手続き型のプログラミングも可能です.
+Ruby is the interpreted scripting language for quick and
+easy object-oriented programming. It has many features to
+process text files and to do system management tasks (as in
+perl). It is simple, straight-forward, and extensible.
-Rubyはテキスト処理関係の能力などに優れ,perlと同じくらい強力
-です.さらにシンプルな文法と,例外処理やイテレータなどの機構
-によって,より分かりやすいプログラミングが出来ます.
+* Features of ruby
-* Rubyの特長.
+ + Simple Syntax
+ + *Normal* Object-Oriented features(ex. class, method calls)
+ + *Advanced* Object-Oriented features(ex. Mix-in, Singleton-method)
+ + Operator Overloading
+ + Exception Handling
+ + Iterators and Closures
+ + Garbage Collection
+ + Dynamic Loading of Object files(on some architecture)
+ + Highly Portable(works on many UNIX machines)
- + シンプルな文法
- + 普通のオブジェクト指向機能(クラス,メソッドコールなど)
- + 特殊なオブジェクト指向機能(Mixin, 特異メソッドなど)
- + 演算子オーバーロード
- + 例外処理機能
- + イテレータとクロージャ
- + ガーベージコレクタ
- + ダイナミックローディング (アーキテクチャによる)
- + 移植性が高い.多くのUNIX上で動く
+* How to get ruby
-* 入手法
+** by ftp
-** ftpで
+The ruby distribution can be found on
-以下の場所においてあります.
+ ftp://ftp.caelum.co.jp/pub/lang/ruby/
- ftp://ftp.kk.info.kanagawa-u.ac.jp/pub/languages/ruby/
+** by mail
-** メイルで
-
-以下のアドレスに`send'というSubjectのメイルを送って下さい.
+Send the mail which subject is 'send' to the address below.
ruby-archive@caelum.co.jp
-本文には何を書いても構いません.折り返し,最新版のrubyが送っ
-て来ます.
-
-* メイリングリスト
-
- Rubyに関わる話題のためのメイリングリストを解説しました.ア
- ドレスは
-
- ruby-list@caelum.co.jp
-
- です.このアドレスにメイルを送れば,自動的に登録されます.
-
-* コンパイル・インストール
-
-以下の手順で行ってください.
-
- 1. configureを実行してMakefileなどを生成する
-
- 2. (必要ならば)defines.hを編集する
-
- 多分,必要無いと思います.
-
- 3. (必要ならば)ext/Setupに静的にリンクする拡張モジュールを
- 指定する
+You will receive the uuencoded gzipped tar file of the newest ruby
+distribution.
- ext/Setupに記述したモジュールは静的にリンクされます.
+* How to compile and install
- ダイナミックローディングをサポートしていないアーキテク
- チャではSetupの1行目の「option nodynamic」という行のコ
- メントを外す必要があります.また,このアーキテクチャで
- 拡張モジュールを利用するためには,あらかじめ静的にリン
- クしておく必要があります.
+This is what you need to do to compile and install ruby:
- 4. makeを実行してコンパイルする
+ 1. Run ./configure, which will generate config.h and Makefile.
- 5. make testでテストを行う.
+ 2. Edit defines.h if you need. Probably this step will not need.
- 「test succeeded」と表示されれば成功です.
+ 3. Remove comment mark(#) before the module names from ext/Setup, if
+ you want to link modules statically.
- 6. make install
+ If you want to link all the extension modules, remove comment
+ mark from the line "#option nodynamic".
-もし,コンパイル時にエラーが発生した場合にはエラーのログとマ
-シン,OSの種類を含むできるだけ詳しいレポートを作者に送ってく
-ださると他の方のためにもなります.
+ 4. Run make.
-* 移植
+ 5. Optionally, run 'make test' to check that the compiled ruby
+ interpreter works well. If you see the message "test succeeded",
+ your ruby works as it should.
-UNIXであればconfigureがほとんどの差異を吸収してくれるはずで
-すが,思わぬ見落としがあった場合(あるに違いない),作者にその
-ことをレポートすれば,解決できるかも知れません.
+ 6. Run 'make install'
-アークテクチャにもっとも依存するのはGC部です.rubyのGCは対象
-のアーキテクチャがsetjmp()によって,全てのレジスタを jmp_buf
-に格納することと,jmp_bufとスタックが32bitアラインメントされ
-ていることを仮定しています.前者が成立しない場合の対応は困難
-を極めるでしょう.後者の解決は比較的簡単で,gc.cでスタックを
-マークしている部分にアラインメントのバイト数だけずらしてマー
-クするコードを追加するだけで済みます.「defined(THINK_C)」で
-括られている部分を参考にしてください
+If you fail to compile ruby, please send the detailed error report with
+the error log and machine/OS type, to help others.
-# 実際にはrubyはThink Cでコンパイルできません.
+* Copying
-sparc以外のレジスタウィンドウを持つCPUでは,レジスタウィンド
-ウをフラッシュするコードを追加する必要があるかも知れません.
+Ruby is copyrighted by Yukihiro Matsumoto <matz@caelum.co.jp>.
-* 配布条件
+This source is distributed under the conditions blow:
-Rubyの配布に関して著作権保持者である作者<matz@caelum.co.jp>
-は以下の条件をつけます.
+ 1. You may make and give away verbatim copies of the source form of
+ the software without restriction, provided that you do not modify
+ the original distribution file.
- + 更新
+ If you want to distribute the modified version in any way, contact
+ the author.
- いかなる目的であれ自由です.ただし,機能拡張やバグ修正は
- 作者へのフィードバックを期待します(もちろん強制ではあり
- ません).
+ 2. You may distribute the software in object code or executable
+ form, provided that you distribute it with instructions on where
+ to get the software.
- + 他のプログラムへの引用
+ 3. You may modify the software in any way, provided that you do not
+ distribute the modified version.
- いかなる目的であれ自由です.ただし,rubyに含まれる他の作
- 者によるコードは,それぞれの作者の意向による制限が加えら
- れます.具体的にはgc.c(一部),regex.[ch],fnmatch.[ch],
- glob.c, st.[ch]とmissingディレクトリ下のファイル群が該当
- します.
+ 4. You may modify and include the part of the software into any other
+ software (possibly commercial). But some files in the
+ distribution are not written by the author, so that they are not
+ under this terms. They are gc.c(partly),regex.[ch],fnmatch.[ch],
+ glob.c, st.[ch] and somme files under ./missing directory. See
+ each files for the condition.
- + 再配布
+ 5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
- 配布した状態を維持する限り自由です.変更を行ったものを再
- 配布することを希望する時には作者に連絡してください.オリ
- ジナルのrubyと明確に区別できるようであれば,再配布を認め
- る方針です.
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
- 変更を行なわないrubyをコンパイルしたバイナリの配布は禁止
- しませんが,コンパイル条件に起因するトラブルを減らすため
- に,コンパイル時の情報をできるだけ詳しく明記する事を希望
- します.
+* ruby home-page
- + Rubyスクリプトの権利
+ The URL of the ruby home-page is:
- 全てのrubyスクリプトの権利はそれぞれの著作者に属します.
- 作者はこれらに関して一切の権利を主張しません.またrubyに
- 組み込むための拡張モジュールに関しても同様です.
+ http://www.caelum.co.jp/~matz/ruby/index-en.html
- + 無保証
+* The Author
- Rubyは無保証です.作者はrubyをサポートする意志はあります
- が,ruby自身のバグあるいはrubyスクリプトのバグなどから発
- 生するいかなる損害に対しても責任を持ちません.
+Feel free to send comments and bug reports to the author. Here is the
+author's latest mail address:
-* 著者
+ matz@ruby.club.or.jp
-コメント,バグレポートその他は matz@caelum.co.jp まで.
-------------------------------------------------------
created at: Thu Aug 3 11:57:36 JST 1995
Local variables:
diff --git a/README.EXT b/README.EXT
index efa627a24a..fdf8c96af7 100644
--- a/README.EXT
+++ b/README.EXT
@@ -1,308 +1,1029 @@
.\" README.EXT - -*- Text -*- created at: Mon Aug 7 16:45:54 JST 1995
-Rubyを拡張するための方法を解説する.
+rubyの拡張モジュールの作り方を説明します.
+
+1.基礎知識
+
+Cの変数には型があり,データには型がありません.ですから,た
+とえばポインタをintの変数に代入すると,その値は整数として取
+り扱われます.逆にrubyの変数には型がなく,データに型がありま
+す.この違いのため,Cとrubyは相互に変換しなければ,お互いの
+データをアクセスできません.
+
+rubyのデータはVALUEというCの型で表現されます.VALUE型のデー
+タはそのデータタイプを自分で知っています.このデータタイプと
+いうのはデータ(オブジェクト)の実際の構造を意味していて,ruby
+のクラスとはまた違ったものです.
+
+VALUEからCにとって意味のあるデータを取り出すためには
+
+ (1) VALUEのデータタイプを知る
+ (2) VALUEをCのデータに変換する
+
+の両方が必要です.(1)を忘れると間違ったデータの変換が行われ
+て,最悪プログラムがcore dumpします.
+
+1.1 データタイプ
+
+rubyにはユーザが使う可能性のある以下のタイプがあります.
-RubyはCコードを書くことによって,簡単に機能を追加できる.おおまかな手
-順は以下の通りである.
+ T_NIL nil
+ T_OBJECT 通常のオブジェクト
+ T_CLASS クラス
+ T_MODULE モジュール
+ T_FLOAT 浮動小数点数
+ T_STRING 文字列
+ T_REGEXP 正規表現
+ T_ARRAY 配列
+ T_FIXNUM Fixnum(31bit長整数)
+ T_HASH 連想配列
+ T_STRUCT (rubyの)構造体
+ T_BIGNUM 多倍長整数
+ T_TRUE 真
+ T_FALSE 偽
+ T_DATA データ
- 1. ファイルを用意する
+その他に内部で利用されている以下のタイプがあります.
- extディレクトリの下に拡張モジュール用のディレクトリを用意して,そ
- の配下に以下のファイルを用意する必要がある.
+ T_ICLASS
+ T_MATCH
+ T_VARMAP
+ T_SCOPE
+ T_NODE
- + MANIFEST.必要なファイルの一覧.
+いくつかのタイプはCの構造体で実装されています.
- 必ず必要.一時的なファイル以外の全てのファイル名を1行1ファイル
- の形式で記述すること.
+1.2 VALUEのデータタイプをチェックする
- + Cのソースファイル.
+ruby.hではTYPE()というマクロが定義されていて,VALUEのデータ
+タイプを知ることが出来ます.TYPE()マクロは上で紹介したT_XXXX
+の形式の定数を返します.VALUEのデータタイプに応じて処理する
+場合には,TYPE()の値で分岐することになります.
- モジュールが1ファイルだけからなる時はモジュール名と同じ名前のファ
- イル名(モジュール.c)をつける.逆にモジュールが複数からなる時は
- モジュール名のついたソースファイルは避けること.
+ switch (TYPE(obj)) {
+ case T_FIXNUM:
+ /* FIXNUMの処理 */
+ break;
+ case T_STRING:
+ /* 文字列の処理 */
+ break;
+ case T_ARRAY:
+ /* 配列の処理 */
+ break;
+ default:
+ /* 例外を発生させる */
+ Fail("not valid value");
+ break;
+ }
- + extconf.rb(optional).設定用ファイル.
+それとデータタイプをチェックして,正しくなければ例外を発生す
+る関数が用意されています.
- 関数やライブラリ,ヘッダの存在チェックをしたり,モジュール名な
- どを設定する.このファイルが無ければ全てデフォルトでコンパイル
- される.
+ void Check_Type(VALUE value, int type)
- + depend(optional).Makefileにインクルードするためのファ
- イルの依存関係を記述したファイル.
+この関数はvalueがtypeで無ければ,例外を発生させます.引数と
+して与えられたVALUEのデータタイプが正しいかどうかチェックす
+るためには,この関数を使います.
- `gcc -MM *.c > depend'とすれば自動的に生成できる.
+1.3 VALUEをCのデータに変換する
- 2. Cのソースファイルを用意する
+データタイプがT_NIL, T_FALSE, T_TRUEである時,データはそれぞ
+れnil, FALSE, TRUEです.このデータタイプのオブジェクトはひと
+つずつしか存在しません.
- 必ず「Init_モジュール名」という関数を用意し,その中で,変数やクラ
- スの定義や,クラスへのメソッドの登録などの初期化を行うこと.この
- 関数の呼び出しはインタプリタの初期化時(静的リンクの場合)かモジュー
- ルのロード時(動的リンクの場合)に自動的に行われる.
+データタイプがT_FIXNUMの時,これは31bitのサイズを持つ整数で
+す.FIXNUMをCの整数に変換するためにはマクロ「FIX2INT()」を使
+います.それから,FIXNUMに限らずrubyのデータを整数に変換する
+「NUM2INT()」というマクロがあります.このマクロはデータタイ
+プのチェック無しで使えます(整数に変換できない場合には例外が
+発生する).
-* Ruby API
+それ以外のデータタイプは対応するCの構造体があります.対応す
+る構造体のあるVALUEはそのままキャスト(型変換)すれば構造体の
+ポインタに変換できます.
-C言語からRubyの機能を利用するAPIは以下の通りである.
+構造体は「struct RXxxxx」という名前でruby.hで定義されていま
+す.例えば文字列は「struct RString」です.実際に使う可能性が
+あるのは文字列と配列くらいだと思います.
+
+ruby.hでは構造体へキャストするマクロも「RXXXXX()」(全部大文
+字にしたもの)という名前で提供されています(例: RSTRING()).
+
+例えば,文字列strの長さを得るためには「RSTRING(str)->len」と
+し,文字列strをchar*として得るためには「RSTRING(str)->ptr」
+とします.配列の場合には,それぞれ「RARRAT(str)->len」,
+「RARRAT(str)->ptr」となります.
+
+rubyの構造体を直接アクセスする時に気をつけなければならないこ
+とは,配列や文字列の構造体の中身は参照するだけで,直接変更し
+ないことです.直接変更した場合,オブジェクトの内容の整合性が
+とれなくなって,思わぬバグの原因になります.
+
+1.4 CのデータをVALUEに変換する
+
+VALUEの実際の構造は
+
+ * FIXNUMの場合
+
+ 1bit右シフトして,LSBを立てる.
+
+ * その他のポインタの場合
+
+ そのままVALUEにキャストする.
+
+となっています.よって,LSBをチェックすればVALUEがFIXNUMかど
+うかわかるわけです(ポインタのLSBが立っていないことを仮定して
+いる).
+
+ですから,FIXNUM以外のrubyのオブジェクトの構造体は単にVALUE
+にキャストするだけでVALUEに変換出来ます.ただし,任意の構造
+体がVALUEにキャスト出来るわけではありません.キャストするの
+はrubyの知っている構造体(ruby.hで定義されているstruct RXxxx
+のもの)だけにしておいてください.
+
+FIXNUMに関しては変換マクロを経由する必要があります.Cの整数
+からVALUEに変換するマクロは以下のものがあります.必要に応じ
+て使い分けてください.
+
+ INT2FIX() もとの整数が31bit以内に収まる時
+ INT2NUM() 任意の整数からVALUEへ
+
+INT2NUM()は整数がFIXNUMの範囲に収まらない場合,Bignumに変換
+してくれます(が,少し遅い).
+
+1.5 rubyのデータを操作する
+
+先程も述べた通り,rubyの構造体をアクセスする時に内容の更新を
+行うことは勧められません.で,rubyのデータを操作する時には
+rubyが用意している関数を用いてください.
+
+ここではもっとも使われるであろう文字列と配列の生成/操作を行
+い関数をあげます(全部ではないです).
+
+ 文字列に対する関数
+
+ str_new(char *ptr, int len)
+
+ 新しいrubyの文字列を生成する.
+
+ str_new2(char *ptr)
+
+ Cの文字列からrubyの文字列を生成する.この関数の機能は
+ str_new(ptr, strlen(ptr))と同等である.
+
+ str_cat(VALUE str, char *ptr, int len)
+
+ rubyの文字列strにlenバイトの文字列ptrを追加する.
+
+ 配列に対する関数
+
+ ary_new()
+
+ 要素が0の配列を生成する.
+
+ ary_new2(int len)
+
+ 要素が0の配列を生成する.len要素分の領域をあらかじめ割り
+ 当てておく.
+
+ ary_new3(int n, ...)
+
+ 引数で指定したn要素を含む配列を生成する.
+
+ ary_new4(int n, VALUE elts[])
+
+ 配列で与えたn要素の配列を生成する.
+
+ ary_push(VALUE ary)
+ ary_pop(VALUE ary, VALUE val)
+ ary_shift(VALUE ary)
+ ary_unshift(VALUE ary, VALUE val)
+ ary_entry(VALUE ary, int idx)
+
+ Arrayの同名のメソッドと同じ働きをする関数.第1引数は必ず
+ 配列でなければならない.
+
+2.rubyの機能を使う
+
+原理的にrubyで書けることはCでも書けます.rubyそのものがCで記
+述されているんですから,当然といえば当然なんですけど.ここで
+はrubyの拡張に使うことが多いだろうと予測される機能を中心に紹
+介します.
+
+2.1 rubyに機能を追加する
+
+rubyで提供されている関数を使えばrubyインタプリタに新しい機能
+を追加することができます.rubyでは以下の機能を追加する関数が
+提供されています.
+
+ * クラス,モジュール
+ * メソッド,特異メソッドなど
+ * 定数
+
+では順に紹介します.
+
+2.1.1 クラス/モジュール定義
+
+クラスやモジュールを定義するためには,以下の関数を使います.
+
+ VALUE rb_define_class(char *name, VALUE super)
+ VALUE rb_define_module(char *name)
+
+これらの関数は新しく定義されたクラスやモジュールを返します.
+メソッドや定数の定義にこれらの値が必要なので,ほとんどの場合
+は戻り値を変数に格納しておく必要があるでしょう.
+
+2.1.2 メソッド/特異メソッド定義
+
+メソッドや特異メソッドを定義するには以下の関数を使います.
+
+ void rb_define_method(VALUE class, char *name,
+ VALUE (*func)(), int argc)
+
+ void rb_define_sigleton_method(VALUE object, char *name,
+ VALUE (*func)(), int argc)
+
+
+念のため説明すると「特異メソッド」とは,その特定のオブジェク
+トに対してだけ有効なメソッドです.rubyではよくSmalltalkにお
+けるクラスメソッドとして,クラスに対する特異メソッドが使われ
+ます.
+
+これらの関数の argcという引数はCの関数へ渡される引数の数(と
+形式)を決めます.argcが正の時は関数に引き渡す引数の数を意味
+します.16個以上の引数は使えません(が,要りませんよね,そん
+なに).
+
+argcが負の時は引数の数ではなく,形式を指定したことになります.
+argcが-1の時は引数を配列に入れて渡されます.argcが-2の時は引
+数はrubyの配列として渡されます.
+
+メソッドを定義する関数はもう二つあります.ひとつはprivateメ
+ソッドを定義する関数で,引数はrb_define_method()と同じです.
+
+ void rb_define_private_method(VALUE class, char *name,
+ VALUE (*func)(), int argc)
+
+privateメソッドとは関数形式でしか呼び出すことの出来ないメソッ
+ドです.
+
+もうひとつはモジュール関数を定義するものです.モジュール関数
+とはモジュールの特異メソッドであり,同時にprivateメソッドで
+もあるものです.例をあげるとMathモジュールのsqrt()などがあげ
+られます.このメソッドは
+
+ Math.sqrt(4)
+
+という形式でも
+
+ include Math
+ sqrt(4)
+
+という形式でも使えます.モジュール関数を定義する関数は以下の
+通りです.
+
+ void rb_define_module_method(VALUE module, char *name,
+ VALUE (*func)(), int argc)
+
+2.1.3 定数定義
+
+拡張モジュールが必要な定数はあらかじめ定義しておいた方が良い
+でしょう.定数を定義する関数は二つあります.
+
+ void rb_define_const(VALUE class, char *name, VALUE val)
+ void rb_define_global_const(char *name, VALUE val)
+
+前者は特定のクラス/モジュールに属する定数を定義するもの,後
+者はグローバルな定数を定義するものです.
+
+2.2 rubyの機能をCから呼び出す
+
+既に『1.5 rubyのデータを操作する』で一部紹介したような関数を
+使えば,rubyの機能を実現している関数を直接呼び出すことが出来
+ます.
+
+# このような関数の一覧表はいまのところありません.ソースを見
+# るしかないですね.
+
+それ以外にもrubyの機能を呼び出す方法はいくつかあります.
+
+2.2.1 rubyのプログラムをevalする
+
+Cからrubyの機能を呼び出すもっとも簡単な方法として,文字列で
+与えられたrubyのプログラムを評価する関数があります.
+
+ VALUE rb_eval_string(char *str)
+
+この評価は現在の環境で行われます.つまり,現在のローカル変数
+やselfなどを受け継ぎます.
+
+2.2.2 IDまたはシンボル
+
+Cから文字列を経由せずにrubyのメソッドを呼び出すこともできま
+す.その前に,rubyインタプリタ内でメソッドや変数名を指定する
+時に使われているIDについて説明しておきましょう.
+
+IDとは変数名,メソッド名を表す整数です.rubyの中では
+
+ :識別子
+
+でアクセスできます.Cからこの整数を得るためには関数
+
+ rb_intern(char *name)
+
+を使います.
+
+2.2.3 Cからrubyのメソッドを呼び出す
+
+Cから文字列を経由せずにrubyのメソッドを呼び出すためには以下
+の関数を使います.
+
+ VALUE rb_funcall(VALUE recv, ID mid, int argc, ...)
+
+この関数はオブジェクトrecvのmidで指定されるメソッドを呼び出
+します.
+
+2.2.4 変数/定数を参照/更新する
+
+Cから関数を使って参照・更新できるのは,クラス定数,インスタ
+ンス変数です.大域変数は一部のものはCの大域変数としてアクセ
+スできます.ローカル変数を参照する方法は公開していません.
+
+オブジェクトのインスタンス変数を参照・更新する関数は以下の通
+りです.
+
+ VALUE rb_ivar_get(VALUE obj, ID id)
+ VALUE rb_ivar_get(VALUE obj, ID id, VALUE val)
+
+idはrb_intern()で得られるものを使ってください.
+
+クラス定数を参照するには以下の関数を使ってください.
+
+ VALUE rb_const_get(VALUE obj, ID id)
+
+クラス定数を新しく定義するためには『2.1.3 定数定義』で紹介さ
+れている関数を使ってください.
+
+3.rubyとCとの情報共有
+
+C言語とrubyの間で情報を共有する方法について解説します.
+
+3.1 Cから参照できるrubyの定数
+
+以下のrubyの定数はCのレベルから参照できる.
+
+ TRUE
+ FALSE
+
+真偽値.FALSEはC言語でも偽とみなされる.
+
+ Qnil
+
+C言語から見た「nil」.
+
+3.2 Cとrubyで共有される大域変数
+
+Cとrubyで大域変数を使って情報を共有できます.共有できる大域
+変数にはいくつかの種類があります.そのなかでもっとも良く使わ
+れると思われるのはrb_define_variable()です.
+
+ void rb_define_variable(char *name, VALUE *var)
+
+この関数はrubyとCとで共有する大域変数を定義します.変数名が
+`$'で始まらない時には自動的に追加されます.この変数の値を変
+更すると自動的にrubyの対応する変数の値も変わります.
+
+またruby側からは更新できない変数もあります.このread onlyの
+変数は以下の関数で定義します.
+
+ void rb_define_readonly_variable(char *name, VALUE *var)
+
+これら変数の他にhookをつけた大域変数を定義できます.hook付き
+の大域変数は以下の関数を用いて定義します.
+
+ void rb_define_hooked_variable(char *name, VALUE *var,
+ VALUE (*getter)(), VALUE (*setter)())
+
+この関数はCの関数によってhookのつけられた大域変数を定義しま
+す.変数が参照された時には関数getterが,変数に値がセットされ
+た時には関数setterが呼ばれます.hookを指定しない場合はgetter
+やsetterに0を指定してください.
+
+# getterもsetterも0ならばrb_define_variable()と同じ働きをし
+# ます.
+
+それから,Cの関数によって実現されるrubyの大域変数を定義する
+関数があります.
+
+ void rb_define_virtual_variable(char *name,
+ VALUE (*getter)(), VALUE (*setter)())
+
+この関数によって定義されたrubyの大域変数が参照された時には
+getterが,変数に値がセットされた時にはsetterが呼ばれます.
+
+3.3 Cのデータをrubyオブジェクトにする
+
+Cの世界で定義されたデータ(構造体)をrubyのオブジェクトとして
+取り扱いたい場合がありえます.このような場合には,Dataという
+rubyオブジェクトにCの構造体(へのポインタ)をくるむことでruby
+オブジェクトとして取り扱えるようになります.
+
+Dataオブジェクトを生成して構造体をrubyオブジェクトにカプセル
+化するためには,以下のマクロを使います.
+
+ Make_Data_Struct(class, type, mark, free, sval)
+
+ここでclassは新しく生成されるインスタンスのクラス,,typeは
+カプセル化するCのデータの型(構造体)です.markはこの構造体が
+rubyのオブジェクトへの参照がある時に使う関数です.そのような
+参照を含まない時には0を指定します.freeはこの構造体がもう不
+要になった時に呼ばれる関数です.この関数がガーベージコレクタ
+から呼ばれます.svalはtype型の変数で,Make_Data_Structの中で
+アロケートされます.
+
+マクロMake_Data_StructはDataオブジェクトを生成して,それを値
+として返します.
+
+このマクロを呼び出すとsvalに構造体がmalloc()されて代入され,
+かつその構造体をカプセル化したDataオブジェクトがインスタンス
+変数としてobjにセットされます.
+
+DataオブジェクトからCポインタを取り出すためには以下のマクロ
+を使います.
+
+ Get_Data_Struct(obj, type, sval)
+
+Dataオブジェクトからtype型のCポインタを取り出して,svalに代
+入します.
+
+これらのDataの使い方はちょっと分かりにくいので,後で説明する
+例題を参照してください.
+
+4.例題 - dbmパッケージを作る
+
+ここまでの説明でとりあえず拡張モジュールは作れるはずです.
+rubyのextディレクトリにすでに含まれているdbmモジュールを例に
+して段階的に説明します.
+
+(1) ディレクトリを作る
+
+ % mkdir ext/dbm
+
+rubyを展開したディレクトリの下,extディレクトリの中に拡張モ
+ジュール用のディレクトリを作ります.名前は適当に選んで構いま
+せん.
+
+(2) MANIFESTファイルを作る
+
+ % cd ext/dbm
+ % touch MANIFEST
+
+拡張モジュールのディレクトリの下にはMANIFESTというファイルが
+必要なので,とりあえず空のファイルを作っておきます.後でこの
+ファイルには必要なファイル一覧が入ることになります.
+
+MANIFESTというファイルは,makeの時にディレクトリが拡張モジュー
+ルを含んでいるかどうか判定するために使われれています.
+
+(3) 設計する
+
+まあ,当然なんですけど,どういう機能を実現するかどうかまず設
+計する必要があります.どんなクラスをつくるか,そのクラスには
+どんなメソッドがあるか,クラスが提供する定数などについて設計
+します.dbmクラスについてはext/dbm.docを参照してください.
+
+(4) Cコードを書く
+
+拡張モジュール本体となるC言語のソースを書きます.C言語のソー
+スがひとつの時には「モジュール名.c」を選ぶと良いでしょう.C
+言語のソースが複数の場合には逆に「モジュール名.c」というファ
+イル名は避ける必要があります.オブジェクトファイルとモジュー
+ル生成時に中間的に生成される「モジュール名.o」というファイル
+とが衝突するからです.
+
+rubyは拡張モジュールをロードする時に「Init_モジュール名」と
+いう関数を自動的に実行します.dbmモジュールの場合「Init_dbm」
+です.この関数の中でクラス,モジュール,メソッド,定数などの
+定義を行います.dbm.cから一部引用します.
+
+--
+Init_dbm()
+{
+ /* DBMクラスを定義する */
+ cDBM = rb_define_class("DBM", cObject);
+ /* DBMはEnumerateモジュールをインクルードする */
+ rb_include_module(cDBM, mEnumerable);
+
+ /* DBMクラスのクラスメソッドopen(): 引数はCの配列で受ける */
+ rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1);
+
+ /* DBMクラスのメソッドclose(): 引数はなし */
+ rb_define_method(cDBM, "close", fdbm_close, 0);
+ /* DBMクラスのメソッド[]: 引数は1個 */
+ rb_define_method(cDBM, "[]", fdbm_fetch, 1);
+ :
+}
+--
+
+DBMモジュールはdbmのデータと対応するオブジェクトになるはずで
+すから,Cの世界のdbmをrubyの世界に取り込む必要があります.
+
+dbm.cではDBMのデータを格納するために以下のような構造体を使っ
+ています.
+
+struct dbmdata {
+ int di_size;
+ DBM *di_dbm;
+};
+
+RubyのDBMオブジェクトを生成するためには以下のようなコードを
+使っています.
+
+ obj = Make_Data_Struct(class,struct dbmdata,0,free_dbm,dbmp);
+ dbmp->di_dbm = dbm;
+ dbmp->di_size = -1;
+
+DBMオブジェクトからCのDBMポインタを取り出すためには以下のよ
+うなマクロを使っています.
+
+#define GetDBM(obj, dbmp) {\
+ Get_Data_Struct(obj, struct dbmdata, dbmp);\
+ if (dbmp->di_dbm == 0) closeddbm();\
+}
+
+DBMクラスにはたくさんメソッドがありますが,分類すると3種類の
+引数の受け方があります.ひとつは引数の数が固定のもので,例と
+してはdeleteメソッドがあります.deleteメソッドを実装している
+fdbm_delete()はこのようになっています.
+
+--
+static VALUE
+fdbm_delete(obj, keystr)
+ VALUE obj, keystr;
+{
+ :
+}
+--
+
+引数の数が固定のタイプは第1引数がself,第2引数以降がメソッド
+の引数となります.
+
+引数の数が不定のものはCの配列で受けるものとrubyの配列で受け
+るものとがあります.dbmモジュールの中で,Cの配列で受けるもの
+はDBMのクラスメソッドであるopen()です.これを実装している関
+数fdbm_s_open()はこうなっています.
+
+--
+static VALUE
+fdbm_s_open(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ :
+ if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
+ mode = 0666; /* default value */
+ }
+ :
+}
+--
+
+このタイプの関数は第1引数が与えられた引数の数,第2引数が与え
+られた引数の入っている配列になります.selfは第3引数として与
+えられます.
+
+この配列で与えられた引数を解析するための関数がopen()でも使わ
+れているrb_scan_args()です.第3引数に指定したフォーマットに
+従い,第4変数以降に指定した変数に値を代入してくれます.この
+フォーマットは,第1文字目が省略できない引数の数,第2文字目が
+省略できる引数の数,第3文字目が対応する相手が無いあまりの引
+数があるかどうかを示す"*"です.2文字目と3文字目は省略できま
+す.dbm.cの例では,フォーマットは"11"ですから,引数は最低1つ
+で,2つまで許されるという意味になります.省略されている時の
+変数の値はnil(C言語のレベルではQnil)になります.
+
+rubyの配列で引数を受け取るものはindexesがあります.実装はこ
+うです.
+
+--
+static VALUE
+fdbm_indexes(obj, args)
+ VALUE obj;
+ struct RArray *args;
+{
+ :
+}
+--
+
+第1引数はself,第2引数はrubyの配列です.ここではキャストを減
+らすため struct RArray* で受けていますが,VALUEでも同じこと
+です.
+
+** 注意事項
+
+rubyと共有はしないがrubyのオブジェクトを格納する可能性のある
+Cの大域変数は以下の関数を使ってrubyインタプリタに変数の存在
+を教えてあげてください.でないとGCでトラブルを起こす可能性が
+あります.
+
+ void rb_global_variable(VALUE *var)
+
+(5) extconf.rbを用意する
+
+もしディレクトリに「extconf.rb」というファイルが存在すれば,
+make時に実行されます.なければ適当にMakefileが生成されます.
+
+extconf.rbはモジュールのコンパイルに必要な条件のチェックなど
+を行うことが目的です.extconf.rbの中では以下のruby関数を使う
+ことが出来ます.
+
+ have_library(lib, func): ライブラリの存在チェック
+ have_func(func): 関数の存在チェック
+ have_header(header): ヘッダファイルの存在チェック
+ create_makefile(target): Makefileの生成
+
+モジュールをコンパイルする条件が揃わなず,そのモジュールはコ
+ンパイルしない時にはcreate_makefileを呼ばなければ良い.
+
+(6) dependを用意する
+
+もし,ディレクトリにdependというファイルが存在すれば,
+Makefileが依存関係をチェックしてくれます.
+
+ % gcc -MM *.c > depend
+
+などで作ることが出来ます.あって損は無いでしょう.
+
+(7) MANIFESTファイルにファイル名を入れる
+
+ % ls > MANIFEST
+ % vi MANIFEST
+
+*.o, *~など不必要なファイル以外はMANIFESTに追加しておきます.
+make時にはMANIFESTの内容は参照しませんので,空のままでも問題
+は起きないんですけど,パッケージングの時に参照することがある
+し,必要なファイルを区別できるんで,用意しておいた方が良いで
+しょう.
+
+(8) makeする
+
+rubyのディレクトリでmakeを実行するとMakefileを生成してくれま
+す.一度Makefileが生成されれば拡張モジュールのディレクトリの
+中でmakeすることができます.extconf.rbを書き換えるなどして
+Makefileの再生成が必要な時はまたrubyディレクトリでmakeしてく
+ださい.
+
+(9) デバッグ
+
+まあ,デバッグしないと動かないでしょうね.ext/Setupにディレ
+クトリ名を書くと静的にリンクするのでデバッガが使えるようにな
+ります.その分コンパイルが遅くなりますけど.
+
+(10) できあがり
+
+後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお
+使いください.rubyの作者は拡張モジュールに関して一切の権利を
+主張しません.
+
+Appendix A. rubyのソースコードの分類
+
+rubyのソースはいくつかに分類することが出来ます.このうちクラ
+スライブラリの部分は基本的に拡張モジュールと同じ作り方になっ
+ています.これらのソースは今までの説明でほとんど理解できると
+思います.
+
+ruby言語のコア
+
+ class.c
+ error.c
+ eval.c
+ gc.c
+ object.c
+ parse.y
+ variable.c
+
+ユーティリティ関数
+
+ dln.c
+ fnmatch.c
+ glob.c
+ regex.c
+ st.c
+ util.c
+
+rubyコマンドの実装
+
+ dmyext.c
+ inits.c
+ main.c
+ ruby.c
+ version.c
+
+クラスライブラリ
+
+ array.c
+ bignum.c
+ compar.c
+ dir.c
+ enum.c
+ file.c
+ hash.c
+ io.c
+ math.c
+ numeric.c
+ pack.c
+ process.c
+ random.c
+ range.c
+ re.c
+ signal.c
+ sprintf.c
+ string.c
+ struct.c
+ time.c
+
+Appendix B. 拡張用関数リファレンス
+
+C言語からrubyの機能を利用するAPIは以下の通りである.
** 型
VALUE
- Rubyオブジェクトを表現する型.必要に応じてキャストして用いる.組み
- 込み型を表現するCの型はruby.hに記述してあるRで始まる構造体である.
- VALUE型をこれらにキャストするためにRで始まる構造体名を全て大文字に
- した名前のマクロが用意されている.
+rubyオブジェクトを表現する型.必要に応じてキャストして用いる.
+組み込み型を表現するCの型はruby.hに記述してあるRで始まる構造
+体である.VALUE型をこれらにキャストするためにRで始まる構造体
+名を全て大文字にした名前のマクロが用意されている.
** 変数・定数
Qnil
- 定数: nilオブジェクト
-
- Qself
-
- 変数: 現在のselfオブジェクトの値.一般にメソッドにはselfを指す引数
- が与えられるので, この変数にアクセスする必要はない.この変数の値を
- 変更する時は以後のselfの値そのものが変わってしまうので, 特別な事情
- がない限り代入してはならない.
+定数: nilオブジェクト
TRUE
- 定数: tオブジェクト(真のデフォルト値)
+定数: TRUEオブジェクト(真のデフォルト値)
FALSE
- 定数: nilオブジェクト
+定数: FALSEオブジェクト
** Cデータのカプセル化
VALUE data_new(void *sval, void (*mark)(), void (*free)())
- Cの任意のポインタをカプセル化したrubyオブジェクトを返す.このポイン
- タがrubyからアクセスされなくなった時,freeで指定した関数が呼ばれる.
- また,このポインタの指すデータが他のrubyオブジェクトを指している場
- 合,markに指定する関数でマークする必要がある.
+Cの任意のポインタをカプセル化したrubyオブジェクトを返す.こ
+のポインタがrubyからアクセスされなくなった時,freeで指定した
+関数が呼ばれる.また,このポインタの指すデータが他のrubyオブ
+ジェクトを指している場合,markに指定する関数でマークする必要
+がある.
Make_Data_Struct(obj, iv, type, mark, free, sval)
- type型のメモリをmallocし,変数svalに代入した後,それをカプセル化し
- たデータをobjのインスタンス変数ivに代入するマクロ.
+type型のメモリをmallocし,変数svalに代入した後,それをカプセ
+ル化したデータをobjのインスタンス変数ivに代入するマクロ.
Get_Data_Struct(obj, iv, type, sval)
- objのインスタンス変数ivが指すデータからtype型のポインタを取り出し
- 変数svalに代入するマクロ.
+objのインスタンス変数ivが指すデータからtype型のポインタを取
+り出し変数svalに代入するマクロ.
** クラス/モジュール定義
VALUE rb_define_class(char *name, VALUE super)
- superのサブクラスとして新しいRubyクラスを定義する.
+superのサブクラスとして新しいrubyクラスを定義する.
+
+ VALUE rb_define_class_under(VALUE module, char *name, VALUE super)
+
+superのサブクラスとして新しいrubyクラスを定義し,moduleの定
+数として定義する.
VALUE rb_define_module(char *name)
- 新しいRubyモジュールを定義する.
+新しいrubyモジュールを定義する.
+
+ VALUE rb_define_module_under(VALUE module, char *name, VALUE super)
+
+新しいrubyモジュールを定義し,moduleの定数として定義する.
void rb_include_module(VALUE class, VALUE module)
- モジュールをインクルードする.classがすでにmoduleをインクルードして
- いる時には何もしない(多重インクルードの禁止).
+モジュールをインクルードする.classがすでにmoduleをインクルー
+ドしている時には何もしない(多重インクルードの禁止).
void rb_extend_object(VALUE object, VALUE module)
- オブジェクトをモジュール(で定義されているメソッド)で拡張する.
+オブジェクトをモジュール(で定義されているメソッド)で拡張する.
** 大域変数定義
void rb_define_variable(char *name, VALUE *var)
- RubyとCとで共有するグローバル変数を定義する.変数名が`$'で始まらな
- い時には自動的に追加される.nameとしてrubyの識別子として許されない
- 文字(例えば` ')を含む場合にはrubyプログラムからは見えなくなる.
+rubyとCとで共有するグローバル変数を定義する.変数名が`$'で始
+まらない時には自動的に追加される.nameとしてrubyの識別子とし
+て許されない文字(例えば` ')を含む場合にはrubyプログラムから
+は見えなくなる.
void rb_define_readonly_variable(char *name, VALUE *var)
- RubyとCとで共有するread onlyのグローバル変数を定義する.read onlyで
- あること以外はrb_define_variable()と同じ.
+rubyとCとで共有するread onlyのグローバル変数を定義する.read
+onlyであること以外はrb_define_variable()と同じ.
void rb_define_virtual_variable(char *name,
VALUE (*getter)(), VALUE (*setter)())
- 関数によって実現されるRuby変数を定義する.変数が参照された時には
- getterが,関数に値がセットされた時にはsetterが呼ばれる.
+関数によって実現されるruby変数を定義する.変数が参照された時
+にはgetterが,変数に値がセットされた時にはsetterが呼ばれる.
void rb_define_hooked_variable(char *name, VALUE *var,
VALUE (*getter)(), VALUE (*setter)())
- 関数によってhookのつけられたグローバル変数を定義する.変数が参照さ
- れた時にはgetterが,関数に値がセットされた時にはsetterが呼ばれる.
- getterやsetterに0を指定した時にはhookを指定しないのと同じ事になる.
+関数によってhookのつけられたグローバル変数を定義する.変数が
+参照された時にはgetterが,関数に値がセットされた時にはsetter
+が呼ばれる.getterやsetterに0を指定した時にはhookを指定しな
+いのと同じ事になる.
void rb_global_variable(VALUE *var)
- GCのため,Rubyプログラムからはアクセスされないが, Rubyオブジェクト
- を含む大域変数をマークする.
+GCのため,rubyプログラムからはアクセスされないが, rubyオブジェ
+クトを含む大域変数をマークする.
** クラス定数
void rb_define_const(VALUE class, char *name, VALUE val)
- クラス定数を定義する.
+クラス定数を定義する.
+
+ void rb_define_global_const(char *name, VALUE val)
+
+大域定数を定義する.
+
+ rb_define_const(cKernal, name, 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の配列)という形式で与えられる.
+メソッドを定義する.argcはselfを除く引数の数.argcが-1の時,
+関数には引数の数(selfを含まない)を第1引数, 引数の配列を第2引
+数とする形式で与えられる(第3引数はself).argcが-2の時, 第1引
+数がself, 第2引数がargs(argsは引数を含むrubyの配列)という形
+式で与えられる.
rb_define_private_method(VALUE class, char *name, VALUE (*func)(), int argc)
- privateメソッドを定義する.引数はrb_define_method()と同じ.
+privateメソッドを定義する.引数はrb_define_method()と同じ.
rb_define_singleton_method(VALUE class, char *name, VALUE (*func)(), int argc)
- 特異メソッドを定義する.引数はrb_define_method()と同じ.
+特異メソッドを定義する.引数はrb_define_method()と同じ.
rb_scan_args(int atgc, VALUE *argv, char *fmt, ...)
- argc,argv形式で与えられた引数を分解する.fmtは必須引数の数, 付加引
- 数の数, 残りの引数があるかを指定する文字列で, "数字数字*"という形式
- である. 2 番目の数字と"*"はそれぞれ省略可能である.必須引数が一つ
- もない場合は0を指定する.第3引数以降は変数へのポインタで, 該当する
- 要素がその変数に格納される.付加引数に対応する引数が与えられていな
- い場合は変数にQnilが代入される.
+argc,argv形式で与えられた引数を分解する.fmtは必須引数の数,
+付加引数の数, 残りの引数があるかを指定する文字列で, "数字数
+字*"という形式である. 2 番目の数字と"*"はそれぞれ省略可能で
+ある.必須引数が一つもない場合は0を指定する.第3引数以降は変
+数へのポインタで, 該当する要素がその変数に格納される.付加引
+数に対応する引数が与えられていない場合は変数にQnilが代入され
+る.
-** Rubyメソッド呼び出し
+** rubyメソッド呼び出し
VALUE rb_funcall(VALUE recv, ID mid, int narg, ...)
- メソッド呼び出し.文字列からmidを得るためにはrb_intern()を使う.
+メソッド呼び出し.文字列からmidを得るためにはrb_intern()を使う.
VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv)
- メソッド呼び出し.引数をargc,argv形式で渡す.
+メソッド呼び出し.引数をargc,argv形式で渡す.
VALUE rb_eval_string(char *str)
- 文字列をrubyとスクリプトしてコンパイル・実行する.
+文字列をrubyとスクリプトしてコンパイル・実行する.
ID rb_intern(char *name)
- 文字列に対応するIDを返す.
+文字列に対応するIDを返す.
char *rb_id2name(ID id)
- IDに対応する文字列を返す(デバッグ用).
+IDに対応する文字列を返す(デバッグ用).
char *rb_class2name(VALUE class)
- classの名前を返す(デバッグ用).classが名前を持たない時には, 一番近
- い名前を持つクラスの名前を返す.
+classの名前を返す(デバッグ用).classが名前を持たない時には,
+祖先を遡って名前を持つクラスの名前を返す.
** インスタンス変数
VALUE rb_iv_get(VALUE obj, char *name)
- objのインスタンス変数の値を得る.`@'で始まらないインスタンス変数は
- Rubyプログラムからアクセスできない「隠れた」インスタンス変数になる.
+objのインスタンス変数の値を得る.`@'で始まらないインスタンス
+変数は rubyプログラムからアクセスできない「隠れた」インスタ
+ンス変数になる.
VALUE rb_iv_set(VALUE obj, char *name, VALUE val)
- objのインスタンス変数をvalにセットする.
+objのインスタンス変数をvalにセットする.
** 制御構造
VALUE rb_iterate(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2)
- func2をブロックとして設定し, func1をイテレータとして呼ぶ. func1に
- は arg1が引数として渡され, func2には第1引数にイテレータから与えられ
- た値, 第2引数にarg2が渡される.
+func2をブロックとして設定し, func1をイテレータとして呼ぶ.
+func1には arg1が引数として渡され, func2には第1引数にイテレー
+タから与えられた値, 第2引数にarg2が渡される.
VALUE rb_yield(VALUE val)
- valを値としてイテレータブロックを呼び出す.
+valを値としてイテレータブロックを呼び出す.
VALUE rb_rescue(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2)
- 関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生した時に
- は func2をarg2を引数として呼ぶ.戻り値は例外が発生しなかった時は
- func1の戻り値, 例外が発生した時にはfunc2の戻り値である.
+関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生し
+た時には func2をarg2を引数として呼ぶ.戻り値は例外が発生しな
+かった時はfunc1の戻り値, 例外が発生した時にはfunc2の戻り値で
+ある.
VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2)
- 関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発生して
- も) func2をarg2を引数として実行する.戻り値はfunc1の戻り値である(例
- 外が発生した時は戻らない).
+関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発
+生しても) func2をarg2を引数として実行する.戻り値はfunc1の戻
+り値である(例外が発生した時は戻らない).
** 例外・エラー
void Warning(char *fmt, ...)
- verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ.
+verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ.
void Fail(char *fmt, ...)
- 例外を発生させる.引数はprintf()と同じ.
+例外を発生させる.引数はprintf()と同じ.
void Fatal(char *fmt, ...)
- 致命的例外を発生させる.通常の例外処理は行なわれず, インタープリタ
- が終了する(ただしensureで指定されたコードは終了前に実行される).
+致命的例外を発生させる.通常の例外処理は行なわれず, インター
+プリタが終了する(ただしensureで指定されたコードは終了前に実
+行される).
void Bug(char *fmt, ...)
- インタープリタなどプログラムのバグでしか発生するはずのない状況の時
- 呼ぶ.インタープリタはコアダンプし直ちに終了する.例外処理は一切行
- なわれない.
+インタープリタなどプログラムのバグでしか発生するはずのない状
+況の時呼ぶ.インタープリタはコアダンプし直ちに終了する.例外
+処理は一切行なわれない.
** rubyの初期化・実行
-Rubyをアプリケーションに埋め込む場合には以下のインタフェースを使う.通
-常の拡張モジュールには必要ない.
+rubyをアプリケーションに埋め込む場合には以下のインタフェース
+を使う.通常の拡張モジュールには必要ない.
void ruby_init(int argc, char **argv, char **envp)
- rubyインタプリタの初期化を行なう.
+rubyインタプリタの初期化を行なう.
void ruby_run()
- rubyインタプリタを実行する.
+rubyインタプリタを実行する.
void ruby_script(char *name)
- rubyのスクリプト名($0)を設定する.
+rubyのスクリプト名($0)を設定する.
-* extconf.rbの記述
+Appendix B. extconf.rbで使える関数たち
-拡張モジュールのディレクトリに`extconf.rb'というファイルが存在する時に
-は,それが実行され,モジュールのコンパイルに必要な条件のチェックなどを
-行う事が出来る.extconf.rbの中では以下の関数を使う事ができる.
+extconf.rbの中では利用可能なコンパイル条件チェックの関数は以
+下の通りである.
have_library(lib, func)
- 関数funcを定義しているライブラリlibの存在をチェックする.ライブラリ
- が存在する時,TRUEを返す.
+関数funcを定義しているライブラリlibの存在をチェックする.ラ
+イブラリが存在する時,TRUEを返す.
have_func(func)
- 関数funcの存在をチェックする.funcが標準ではリンクされないライブラ
- リ内のものである時には先にhave_libraryでそのライブラリをチェックし
- ておく事.関数が存在する時,TRUEを返す.
+関数funcの存在をチェックする.funcが標準ではリンクされないラ
+イブラリ内のものである時には先にhave_libraryでそのライブラリ
+をチェックしておく事.関数が存在する時TRUEを返す.
have_header(header)
- ヘッダファイルの存在をチェックする.ヘッダファイルが存在する時TRUE
- を返す.
+ヘッダファイルの存在をチェックする.ヘッダファイルが存在する
+時TRUEを返す.
create_makefile(target)
- 拡張モジュール用のMakefileを生成する.この関数を呼ばなければそのモ
- ジュールはコンパイルされない.
+拡張モジュール用のMakefileを生成する.この関数を呼ばなければ
+そのモジュールはコンパイルされない.targetはモジュール名を表
+す.
/*
* Local variables:
- * fill-column: 70
+ * fill-column: 60
* end:
*/
diff --git a/README.jp b/README.jp
new file mode 100644
index 0000000000..06c0832b10
--- /dev/null
+++ b/README.jp
@@ -0,0 +1,159 @@
+* Rubyとは
+
+Rubyはシンプルかつ強力なオブジェクト指向スクリプト言語です.
+Rubyは最初から純粋なオブジェクト指向言語として設計されていま
+すから,オブジェクト指向プログラミングを手軽に行う事が出来ま
+す.もちろん通常の手続き型のプログラミングも可能です.
+
+Rubyはテキスト処理関係の能力などに優れ,perlと同じくらい強力
+です.さらにシンプルな文法と,例外処理やイテレータなどの機構
+によって,より分かりやすいプログラミングが出来ます.
+
+* Rubyの特長.
+
+ + シンプルな文法
+ + 普通のオブジェクト指向機能(クラス,メソッドコールなど)
+ + 特殊なオブジェクト指向機能(Mixin, 特異メソッドなど)
+ + 演算子オーバーロード
+ + 例外処理機能
+ + イテレータとクロージャ
+ + ガーベージコレクタ
+ + ダイナミックローディング (アーキテクチャによる)
+ + 移植性が高い.多くのUNIX上で動く
+
+* 入手法
+
+** ftpで
+
+以下の場所においてあります.
+
+ ftp://ftp.caelum.co.jp/pub/lang/ruby/
+
+** メイルで
+
+以下のアドレスに`send'というSubjectのメイルを送って下さい.
+
+ ruby-archive@caelum.co.jp
+
+本文には何を書いても構いません.折り返し,最新版のrubyが送っ
+て来ます.
+
+* ホームページ
+
+ RubyのホームページのURLは
+
+ http://www.caelum.co.jp/~matz/ruby/
+
+ です.
+
+* メイリングリスト
+
+ Rubyに関わる話題のためのメイリングリストを開設しました.ア
+ ドレスは
+
+ ruby-list@caelum.co.jp
+
+ です.このアドレスにメイルを送れば,自動的に登録されます.
+
+* コンパイル・インストール
+
+以下の手順で行ってください.
+
+ 1. configureを実行してMakefileなどを生成する
+
+ 2. (必要ならば)defines.hを編集する
+
+ 多分,必要無いと思います.
+
+ 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ではコンパイルできません.
+
+レジスタウィンドウを持つCPUでは,レジスタウィンドウをスタッ
+クにフラッシュするアセンブラコードを追加する必要があるかも知
+れません.
+
+* 配布条件
+
+作者は以下の条件のもとにrubyを配布します.
+
+ + 再配布
+
+ 配布した状態を維持する限り自由です.変更を行ったものを再
+ 配布することを希望する時には作者に連絡してください.
+
+ 変更を行なわないrubyをコンパイルしたバイナリの配布は禁止
+ しませんが,バイナリを受け取った人がソースを入手できるよ
+ うに,ソースの入手法を明示してください.
+
+ + 変更
+
+ 再配布を行わない限り,いかなる目的であれ自由です.ただし,
+ 機能拡張やバグ修正は作者へのフィードバックを期待します
+ (もちろん強制ではありません).
+
+ + 他のプログラムへの引用
+
+ いかなる目的であれ自由です.ただし,rubyに含まれる他の作
+ 者によるコードは,それぞれの作者の意向による制限が加えら
+ れます.具体的にはgc.c(一部),regex.[ch],fnmatch.[ch],
+ glob.c, st.[ch]とmissingディレクトリ下のファイル群が該当
+ します.
+
+ + Rubyスクリプトの権利
+
+ 全てのrubyスクリプトの権利はそれぞれの著作者に属します.
+ 作者はこれらに関して一切の権利を主張しません.またrubyに
+ 組み込むための拡張モジュールに関しても同様です.
+
+ + 無保証
+
+ Rubyは無保証です.作者はrubyをサポートする意志はあります
+ が,ruby自身のバグあるいはrubyスクリプトのバグなどから発
+ 生するいかなる損害に対しても責任を持ちません.
+
+* 著者
+
+コメント,バグレポートその他は matz@ruby.club.or.jp まで.
+-------------------------------------------------------
+created at: Thu Aug 3 11:57:36 JST 1995
+Local variables:
+mode: indented-text
+end:
diff --git a/ToDo b/ToDo
index 5322a83b25..c39e13625d 100644
--- a/ToDo
+++ b/ToDo
@@ -1,9 +1,7 @@
-* thread対応
-* Hand written parser(recursive decent)
-* クラスライブラリの見直し(UNIX依存を減らす)
+* """..."""と%Q#...#のどちらを残すか,あるいは両方活かすか
+* パッケージまたは大域変数のアクセス制御
* format機能
* here document
* perlのようなsetuid check
* write debugger for ruby
-* re-write regex code for speed
-* byte code interpretor
+* re-write regex code for speed and copyright
diff --git a/array.c b/array.c
index 91f7aace9f..040a18403c 100644
--- a/array.c
+++ b/array.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:18 $
created at: Fri Aug 6 09:46:12 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -18,6 +18,16 @@ VALUE rb_to_a();
#define ARY_DEFAULT_SIZE 16
+void
+memclear(mem, size)
+ VALUE *mem;
+ int size;
+{
+ while (size--) {
+ *mem++ = Qnil;
+ }
+}
+
VALUE
ary_new2(len)
int len;
@@ -29,8 +39,10 @@ ary_new2(len)
ary->capa = len;
if (len == 0)
ary->ptr = 0;
- else
+ else {
ary->ptr = ALLOC_N(VALUE, len);
+ memclear(ary->ptr, len);
+ }
return (VALUE)ary;
}
@@ -53,7 +65,7 @@ ary_new3(n, va_alist)
int i;
if (n < 0) {
- Fail("Negative number of items(%d)", n);
+ IndexError("Negative number of items(%d)", n);
}
ary = (struct RArray*)ary_new2(n<ARY_DEFAULT_SIZE?ARY_DEFAULT_SIZE:n);
@@ -96,15 +108,20 @@ assoc_new(car, cdr)
}
static VALUE
-ary_s_new(class)
+ary_s_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
VALUE class;
{
+ VALUE size;
NEWOBJ(ary, struct RArray);
OBJSETUP(ary, class, T_ARRAY);
+ rb_scan_args(argc, argv, "01", &size);
ary->len = 0;
- ary->capa = ARY_DEFAULT_SIZE;
- ary->ptr = ALLOC_N(VALUE, ARY_DEFAULT_SIZE);
+ ary->capa = NIL_P(size)?ARY_DEFAULT_SIZE:NUM2INT(size);
+ ary->ptr = ALLOC_N(VALUE, ary->capa);
+ memclear(ary->ptr, ary->capa);
return (VALUE)ary;
}
@@ -138,7 +155,7 @@ astore(ary, idx, val)
VALUE val;
{
if (idx < 0) {
- Fail("negative index for array");
+ IndexError("negative index for array");
}
if (idx >= ary->capa) {
@@ -146,7 +163,7 @@ astore(ary, idx, val)
REALLOC_N(ary->ptr, VALUE, ary->capa);
}
if (idx > ary->len) {
- MEMZERO(ary->ptr+ary->len, VALUE, idx-ary->len+1);
+ memclear(ary->ptr+ary->len, idx-ary->len+1);
}
if (idx >= ary->len) {
@@ -165,11 +182,14 @@ ary_push(ary, item)
}
static VALUE
-ary_append(ary, item)
+ary_push_method(argc, argv, ary)
+ int argc;
+ VALUE *argv;
struct RArray *ary;
- VALUE item;
{
- astore(ary, ary->len, item);
+ while (argc--) {
+ astore(ary, ary->len, *argv++);
+ }
return (VALUE)ary;
}
@@ -178,6 +198,10 @@ ary_pop(ary)
struct RArray *ary;
{
if (ary->len == 0) return Qnil;
+ if (ary->len * 10 < ary->capa && ary->capa > ARY_DEFAULT_SIZE) {
+ ary->capa = ary->len * 2;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
return ary->ptr[--ary->len];
}
@@ -194,6 +218,10 @@ ary_shift(ary)
/* sliding items */
MEMMOVE(ary->ptr, ary->ptr+1, VALUE, ary->len);
+ if (ary->len * 10 < ary->capa && ary->capa > ARY_DEFAULT_SIZE) {
+ ary->capa = ary->len * 2;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
return top;
}
@@ -215,6 +243,18 @@ ary_unshift(ary, item)
return ary->ptr[0] = item;
}
+static VALUE
+ary_unshift_method(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ while (argc--) {
+ ary_unshift(ary, argv[argc]);
+ }
+ return (VALUE)ary;
+}
+
VALUE
ary_entry(ary, offset)
struct RArray *ary;
@@ -244,10 +284,10 @@ ary_subseq(ary, beg, len)
if (beg < 0) beg = 0;
}
if (len < 0) {
- Fail("negative length for sub-array(size: %d)", ary->len);
+ IndexError("negative length %d", ary->len);
}
if (len == 0) {
- return ary_new();
+ return ary_new2(0);
}
if (beg + len > ary->len) {
len = ary->len - beg;
@@ -270,6 +310,10 @@ beg_len(range, begp, lenp, len)
if (!range_beg_end(range, &beg, &end)) return FALSE;
+ if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) {
+ IndexError("end smaller than beg [%d..%d]", beg, end);
+ }
+
if (beg < 0) {
beg = len + beg;
if (beg < 0) beg = 0;
@@ -281,10 +325,10 @@ beg_len(range, begp, lenp, len)
else {
if (end < 0) {
end = len + end;
- if (end < 0) end = 0;
+ if (end < 0) end = -1;
}
- if (len < end) end = len;
- if (beg < end) {
+ if (end > len) end = len;
+ if (beg > end) {
*lenp = 0;
}
else {
@@ -301,10 +345,9 @@ ary_aref(argc, argv, ary)
struct RArray *ary;
{
VALUE arg1, arg2;
+ int beg, len;
if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) {
- int beg, len;
-
beg = NUM2INT(arg1);
len = NUM2INT(arg2);
if (len <= 0) {
@@ -315,17 +358,17 @@ ary_aref(argc, argv, ary)
/* special case - speeding up */
if (FIXNUM_P(arg1)) {
- return ary_entry(ary, NUM2INT(arg1));
+ return ary_entry(ary, FIX2INT(arg1));
}
-
- /* check if idx is Range */
- {
- int beg, len;
-
+ else {
+ /* check if idx is Range */
if (beg_len(arg1, &beg, &len, ary->len)) {
return ary_subseq(ary, beg, len);
}
}
+ if (TYPE(arg1) == T_BIGNUM) {
+ IndexError("index too big");
+ }
return ary_entry(ary, NUM2INT(arg1));
}
@@ -340,7 +383,7 @@ ary_index(ary, val)
if (rb_equal(ary->ptr[i], val))
return INT2FIX(i);
}
- return Qnil; /* should be FALSE? */
+ return Qnil;
}
static VALUE
@@ -351,8 +394,8 @@ ary_indexes(ary, args)
VALUE new_ary;
int i = 0;
- if (!args || args->len == 1) {
- args = (struct RArray*)rb_to_a(args->ptr[0]);
+ if (!args || NIL_P(args)) {
+ return ary_new2(0);
}
new_ary = ary_new2(args->len);
@@ -365,6 +408,53 @@ ary_indexes(ary, args)
return new_ary;
}
+static void
+ary_replace(ary, beg, len, rpl)
+ struct RArray *ary, *rpl;
+ int beg, len;
+{
+ if (TYPE(rpl) != T_ARRAY) {
+ rpl = (struct RArray*)rb_to_a(rpl);
+ }
+ if (beg < 0) {
+ beg = ary->len + beg;
+ if (beg < 0) beg = 0;
+ }
+ if (beg >= ary->len) {
+ len = beg + rpl->len;
+ if (len >= ary->capa) {
+ ary->capa=len;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
+ memclear(ary->ptr+ary->len, beg-ary->len);
+ MEMCPY(ary->ptr+beg, rpl->ptr, VALUE, rpl->len);
+ ary->len = len;
+ }
+ else {
+ int alen;
+
+ if (beg + len > ary->len) {
+ len = ary->len - beg;
+ }
+ if (len < 0) {
+ IndexError("negative length %d", ary->len);
+ }
+
+ alen = ary->len + rpl->len - len;
+ if (alen >= ary->capa) {
+ ary->capa=alen;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
+
+ if (len != RARRAY(rpl)->len) {
+ MEMMOVE(ary->ptr+beg+rpl->len, ary->ptr+beg+len,
+ VALUE, ary->len-(beg+len));
+ ary->len = alen;
+ }
+ MEMCPY(ary->ptr+beg, rpl->ptr, VALUE, rpl->len);
+ }
+}
+
static VALUE
ary_aset(argc, argv, ary)
int argc;
@@ -374,90 +464,29 @@ ary_aset(argc, argv, ary)
VALUE arg1, arg2;
struct RArray *arg3;
int offset;
+ int beg, len;
if (rb_scan_args(argc, argv, "21", &arg1, &arg2, &arg3) == 3) {
- int beg, len;
-
beg = NUM2INT(arg1);
- if (TYPE(arg3) != T_ARRAY) {
- arg3 = (struct RArray*)rb_to_a(arg3);
- }
- if (beg < 0) {
- beg = ary->len + beg;
- if (beg < 0) {
- Fail("negative index for array(size: %d)", ary->len);
- }
- }
- if (beg >= ary->len) {
- len = beg + arg3->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, arg3->ptr, VALUE, arg3->len);
- ary->len = len;
- }
- else {
- int alen;
-
- len = NUM2INT(arg2);
- if (beg + len > ary->len) {
- len = ary->len - beg;
- }
- if (len < 0) {
- Fail("negative length for sub-array(size: %d)", ary->len);
- }
-
- alen = ary->len + arg3->len - len;
- if (alen >= ary->capa) {
- ary->capa=alen;
- REALLOC_N(ary->ptr, VALUE, ary->capa);
- }
-
- 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;
- }
+ len = NUM2INT(arg2);
+ ary_replace(ary, beg, len, arg3);
return (VALUE)arg3;
}
-
- /* check if idx is Range */
- {
- int beg, len;
-
- 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;
- }
- 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;
- }
- return arg2;
- }
+ else if (FIXNUM_P(arg1)) {
+ offset = FIX2INT(arg1);
+ goto fixnum;
+ }
+ else if (beg_len(arg1, &beg, &len, ary->len)) {
+ /* check if idx is Range */
+ ary_replace(ary, beg, len, arg2);
+ return arg2;
+ }
+ if (TYPE(arg1) == T_BIGNUM) {
+ IndexError("index too big");
}
offset = NUM2INT(arg1);
+ fixnum:
if (offset < 0) {
offset = ary->len + offset;
}
@@ -471,15 +500,10 @@ ary_each(ary)
{
int i;
- if (iterator_p()) {
- for (i=0; i<ary->len; i++) {
- rb_yield(ary->ptr[i]);
- }
- return Qnil;
- }
- else {
- return (VALUE)ary;
+ for (i=0; i<ary->len; i++) {
+ rb_yield(ary->ptr[i]);
}
+ return Qnil;
}
static VALUE
@@ -502,6 +526,15 @@ ary_length(ary)
}
static VALUE
+ary_empty_p(ary)
+ struct RArray *ary;
+{
+ if (ary->len == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static VALUE
ary_clone(ary)
struct RArray *ary;
{
@@ -540,7 +573,7 @@ ary_join(ary, sep)
default:
tmp = obj_as_string(tmp);
}
- if (sep) str_cat(result, sep->ptr, sep->len);
+ if (!NIL_P(sep)) str_cat(result, sep->ptr, sep->len);
str_cat(result, RSTRING(tmp)->ptr, RSTRING(tmp)->len);
}
@@ -556,9 +589,9 @@ ary_join_method(argc, argv, ary)
VALUE sep;
rb_scan_args(argc, argv, "01", &sep);
- if (sep == Qnil) sep = OFS;
+ if (NIL_P(sep)) sep = OFS;
- if (sep != Qnil)
+ if (!NIL_P(sep))
Check_Type(sep, T_STRING);
return ary_join(ary, sep);
@@ -569,7 +602,7 @@ ary_to_s(ary)
VALUE ary;
{
VALUE str = ary_join(ary, OFS);
- if (str == Qnil) return str_new(0, 0);
+ if (NIL_P(str)) return str_new(0, 0);
return str;
}
@@ -581,7 +614,7 @@ ary_print_on(ary, port)
int i;
for (i=0; i<ary->len; i++) {
- if (OFS && i>1) {
+ if (!NIL_P(OFS) && i>0) {
io_write(port, OFS);
}
io_write(port, ary->ptr[i]);
@@ -602,7 +635,7 @@ ary_inspect(ary)
len = 1;
for (i=0; i<ary->len; i++) {
- s = rb_funcall(ary->ptr[i], rb_intern("inspect"), 0, 0);
+ s = rb_inspect(ary->ptr[i]);
if (i > 0) str_cat(str, ", ", 2);
str_cat(str, RSTRING(s)->ptr, RSTRING(s)->len);
len += RSTRING(s)->len + 2;
@@ -635,15 +668,27 @@ VALUE
ary_reverse(ary)
struct RArray *ary;
{
- VALUE ary2 = ary_new2(ary->len);
- int i, j;
+ VALUE *p1, *p2;
+ VALUE tmp;
+
+ p1 = ary->ptr;
+ p2 = p1 + ary->len - 1; /* points last item */
- for (i=ary->len-1, j=0; i >=0; i--, j++) {
- RARRAY(ary2)->ptr[j] = ary->ptr[i];
+ while (p1 < p2) {
+ tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+ p1++; p2--;
}
- RARRAY(ary2)->len = ary->len;
- return ary2;
+ return (VALUE)ary;
+}
+
+static VALUE
+ary_reverse_method(ary)
+ struct RArray *ary;
+{
+ return ary_reverse(ary_clone(ary));
}
static ID cmp;
@@ -662,19 +707,25 @@ sort_2(a, b)
{
VALUE retval;
- if (!cmp) cmp = rb_intern("<=>");
retval = rb_funcall(*a, cmp, 1, *b);
return NUM2INT(retval);
}
VALUE
-ary_sort(ary)
+ary_sort_bang(ary)
struct RArray *ary;
{
qsort(ary->ptr, ary->len, sizeof(VALUE), iterator_p()?sort_1:sort_2);
return (VALUE)ary;
}
+VALUE
+ary_sort(ary)
+ VALUE ary;
+{
+ return ary_sort_bang(ary_clone(ary));
+}
+
static VALUE
ary_delete(ary, item)
struct RArray *ary;
@@ -689,11 +740,40 @@ ary_delete(ary, item)
}
i2++;
}
- ary->len = i2;
+ if (ary->len == i2) {
+ if (iterator_p()) rb_yield(Qnil);
+ }
+ else {
+ ary->len = i2;
+ }
return (VALUE)ary;
}
+VALUE
+ary_delete_at(ary, at)
+ struct RArray *ary;
+ VALUE at;
+{
+ int i1, i2, pos;
+ VALUE del = Qnil;
+
+ pos = NUM2INT(at);
+ for (i1 = i2 = 0; i1 < ary->len; i1++) {
+ if (i1 == pos) {
+ del = ary->ptr[i1];
+ continue;
+ }
+ if (i1 != i2) {
+ ary->ptr[i2] = ary->ptr[i1];
+ }
+ i2++;
+ }
+ ary->len = i2;
+
+ return del;
+}
+
static VALUE
ary_delete_if(ary)
struct RArray *ary;
@@ -717,6 +797,10 @@ ary_clear(ary)
struct RArray *ary;
{
ary->len = 0;
+ if (ARY_DEFAULT_SIZE*3 < ary->capa) {
+ ary->capa = ARY_DEFAULT_SIZE * 2;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
return (VALUE)ary;
}
@@ -731,7 +815,7 @@ ary_fill(argc, argv, ary)
VALUE *p, *pend;
rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
- if (arg2 == Qnil && beg_len(arg1, &beg, &len, ary->len)) {
+ if (NIL_P(arg2) && beg_len(arg1, &beg, &len, ary->len)) {
/* beg and len set already */
}
else {
@@ -754,7 +838,7 @@ ary_fill(argc, argv, ary)
REALLOC_N(ary->ptr, VALUE, ary->capa);
}
if (beg > ary->len) {
- MEMZERO(ary->ptr+ary->len, VALUE, end-ary->len);
+ memclear(ary->ptr+ary->len, end-ary->len);
}
ary->len = end;
}
@@ -766,28 +850,43 @@ ary_fill(argc, argv, ary)
return (VALUE)ary;
}
-static VALUE
+VALUE
ary_plus(x, y)
struct RArray *x, *y;
{
struct RArray *z;
- switch (TYPE(y)) {
- case T_ARRAY:
- z = (struct RArray*)ary_new2(x->len + y->len);
- MEMCPY(z->ptr, x->ptr, VALUE, x->len);
- MEMCPY(z->ptr+x->len, y->ptr, VALUE, y->len);
- z->len = x->len + RARRAY(y)->len;
- break;
-
- default:
- z = (struct RArray*)ary_clone(x);
- ary_push(z, y);
- break;
+ if (TYPE(y) != T_ARRAY) {
+ return ary_plus(x, rb_to_a(y));
}
+
+ z = (struct RArray*)ary_new2(x->len + y->len);
+ MEMCPY(z->ptr, x->ptr, VALUE, x->len);
+ MEMCPY(z->ptr+x->len, y->ptr, VALUE, y->len);
+ z->len = x->len + RARRAY(y)->len;
return (VALUE)z;
}
+VALUE
+ary_concat(x, y)
+ struct RArray *x, *y;
+{
+ struct RArray *z;
+ VALUE *p, *pend;
+
+ if (TYPE(y) != T_ARRAY) {
+ return ary_concat(x, rb_to_a(y));
+ }
+
+ p = y->ptr;
+ pend = p + y->len;
+ while (p < pend) {
+ astore(x, x->len, *p);
+ p++;
+ }
+ return (VALUE)x;
+}
+
static VALUE
ary_times(ary, times)
struct RArray *ary;
@@ -796,6 +895,10 @@ ary_times(ary, times)
struct RArray *ary2;
int i, len;
+ if (TYPE(times) == T_STRING) {
+ return ary_join(ary, times);
+ }
+
len = NUM2INT(times) * ary->len;
ary2 = (struct RArray*)ary_new2(len);
ary2->len = len;
@@ -820,8 +923,9 @@ ary_assoc(ary, key)
&& RARRAY(*p)->len > 1
&& rb_equal(RARRAY(*p)->ptr[0], key))
return *p;
+ p++;
}
- return Qnil; /* should be FALSE? */
+ return Qnil;
}
VALUE
@@ -834,11 +938,12 @@ ary_rassoc(ary, value)
p = ary->ptr; pend = p + ary->len;
while (p < pend) {
if (TYPE(*p) == T_ARRAY
- && RARRAY(*p)->len > 2
+ && RARRAY(*p)->len > 1
&& rb_equal(RARRAY(*p)->ptr[1], value))
return *p;
+ p++;
}
- return Qnil; /* should be FALSE? */
+ return Qnil;
}
static VALUE
@@ -944,7 +1049,47 @@ ary_or(ary1, ary2)
return ary3;
}
-extern VALUE cKernel;
+static VALUE
+ary_compact_bang(ary)
+ struct RArray *ary;
+{
+ VALUE *p, *t, *end;
+
+ p = t = ary->ptr;
+ end = p + ary->len;
+ while (t < end) {
+ if (NIL_P(*t)) t++;
+ else *p++ = *t++;
+ }
+ ary->len = ary->capa = (p - ary->ptr);
+ REALLOC_N(ary->ptr, VALUE, ary->len);
+
+ return (VALUE)ary;
+}
+
+static VALUE
+ary_compact(ary)
+ struct RArray *ary;
+{
+ return ary_compact_bang(ary_clone(ary));
+}
+
+static VALUE
+ary_nitems(ary)
+ struct RArray *ary;
+{
+ int n = 0;
+ VALUE *p, *pend;
+
+ p = ary->ptr;
+ pend = p + ary->len;
+ while (p < pend) {
+ if (!NIL_P(*p)) n++;
+ p++;
+ }
+ return INT2FIX(n);
+}
+
extern VALUE mEnumerable;
void
@@ -953,7 +1098,7 @@ Init_Array()
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, "new", ary_s_new, -1);
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);
@@ -965,26 +1110,32 @@ Init_Array()
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, "concat", ary_concat, 1);
+ rb_define_method(cArray, "<<", ary_push, 1);
+ rb_define_method(cArray, "push", ary_push_method, -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, "unshift", ary_unshift_method, -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, "empty?", ary_empty_p, 0);
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, "reverse", ary_reverse_method, 0);
+ rb_define_method(cArray, "reverse!", ary_reverse, 0);
rb_define_method(cArray, "sort", ary_sort, 0);
+ rb_define_method(cArray, "sort!", ary_sort_bang, 0);
rb_define_method(cArray, "delete", ary_delete, 1);
+ rb_define_method(cArray, "delete_at", ary_delete_at, 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, "include?", ary_includes, 1);
+ rb_define_method(cArray, "includes?", ary_includes, 1); /* obsolate */
rb_define_method(cArray, "assoc", ary_assoc, 1);
rb_define_method(cArray, "rassoc", ary_rassoc, 1);
@@ -995,4 +1146,10 @@ Init_Array()
rb_define_method(cArray, "-", ary_diff, 1);
rb_define_method(cArray, "&", ary_and, 1);
rb_define_method(cArray, "|", ary_or, 1);
+
+ rb_define_method(cArray, "compact", ary_compact, 0);
+ rb_define_method(cArray, "compact!", ary_compact_bang, 0);
+ rb_define_method(cArray, "nitems", ary_nitems, 0);
+
+ cmp = rb_intern("<=>");
}
diff --git a/bignum.c b/bignum.c
index a9bbe9b272..73316de894 100644
--- a/bignum.c
+++ b/bignum.c
@@ -40,15 +40,6 @@ bignew_1(class, len, sign)
#define bignew(len,sign) bignew_1(cBignum,len,sign)
-static VALUE
-big_s_new(class, y)
- VALUE class;
- struct RBignum *y;
-{
- Check_Type(y, T_BIGNUM);
- return bignew_1(class, y->len, y->sign);
-}
-
VALUE
big_clone(x)
struct RBignum *x;
@@ -136,7 +127,7 @@ int2big(n)
}
big = (struct RBignum*)uint2big(n);
if (neg) {
- big->sign = FALSE;
+ big->sign = 0;
}
return (VALUE)big;
}
@@ -198,11 +189,20 @@ str2inum(str, base)
}
if (len <= (sizeof(VALUE)*CHAR_BIT)) {
- int result = strtoul(str, 0, base);
+ UINT val = strtoul(str, 0, base);
- if (!sign) result = -result;
- if (FIXABLE(result)) return INT2FIX(result);
- return int2big(result);
+ if (POSFIXABLE(val)) {
+ if (sign) return INT2FIX(val);
+ else {
+ int result = -(int)val;
+ return INT2FIX(result);
+ }
+ }
+ else {
+ VALUE big = uint2big(val);
+ RBIGNUM(big)->sign = sign;
+ return big;
+ }
}
len = (len/(sizeof(USHORT)*CHAR_BIT))+1;
@@ -262,7 +262,7 @@ big2str(x, base)
return fix2str(x, base);
}
i = x->len;
- if (x->len == 0) return str_new2("0");
+ if (i == 0) return str_new2("0");
if (base == 10) {
j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i*241L)/800+2;
hbase = 10000;
@@ -332,7 +332,7 @@ big2int(x)
USHORT *ds;
if (len > sizeof(long)/sizeof(USHORT))
- Fail("Bignum too big to convert into fixnum");
+ ArgError("Bignum too big to convert into fixnum");
ds = BDIGITS(x);
num = 0;
while (len--) {
@@ -347,12 +347,7 @@ VALUE
big_to_i(x)
VALUE x;
{
- int v = big2int(x);
-
- if (FIXABLE(v)) {
- return INT2FIX(v);
- }
- return x;
+ return bignorm(x);
}
VALUE
@@ -389,7 +384,9 @@ big2dbl(x)
UINT i = x->len;
USHORT *ds = BDIGITS(x);
- while (i--) d = ds[i] + BIGRAD*d;
+ while (i--) {
+ d = ds[i] + BIGRAD*d;
+ }
if (!x->sign) d = -d;
return d;
}
@@ -402,6 +399,38 @@ big_to_f(x)
}
static VALUE
+big_cmp(x, y)
+ struct RBignum *x, *y;
+{
+ int xlen = x->len;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = (struct RBignum*)int2big(FIX2INT(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return num_coerce_bin(x, y);
+ }
+
+ if (x->sign > y->sign) return INT2FIX(1);
+ if (x->sign < y->sign) return INT2FIX(-1);
+ if (xlen < y->len)
+ return (x->sign) ? INT2FIX(-1) : INT2FIX(1);
+ if (xlen > y->len)
+ return (x->sign) ? INT2FIX(1) : INT2FIX(-1);
+
+ while(xlen-- && (BDIGITS(x)[xlen]==BDIGITS(y)[xlen]));
+ if (-1 == xlen) return INT2FIX(0);
+ return (BDIGITS(x)[xlen] > BDIGITS(y)[xlen]) ?
+ (x->sign ? INT2FIX(1) : INT2FIX(-1)) :
+ (x->sign ? INT2FIX(-1) : INT2FIX(1));
+}
+
+static VALUE
big_uminus(x)
struct RBignum *x;
{
@@ -413,6 +442,84 @@ big_uminus(x)
}
static VALUE
+big_neg(x)
+ struct RBignum *x;
+{
+ VALUE z = big_clone(x);
+ UINT i = x->len;
+ USHORT *ds = BDIGITS(z);
+
+ if (!x->sign) big_2comp(z);
+ while (i--) ds[i] = ~ds[i];
+ if (x->sign) big_2comp(z);
+ RBIGNUM(z)->sign = !RBIGNUM(z)->sign;
+
+ return bignorm(z);
+}
+
+static VALUE
+bigsub(x, y)
+ struct RBignum *x, *y;
+{
+ struct RBignum *z = 0;
+ USHORT *zds;
+ long num;
+ UINT i;
+
+ i = x->len;
+ /* if x is larger than y, swap */
+ if (x->len < y->len) {
+ z = x; x = y; y = z; /* swap x y */
+ }
+ else if (x->len == y->len) {
+ while (i > 0) {
+ i--;
+ if (BDIGITS(x)[i] > BDIGITS(y)[i]) {
+ break;
+ }
+ if (BDIGITS(x)[i] < BDIGITS(y)[i]) {
+ z = x; x = y; y = z; /* swap x y */
+ break;
+ }
+ }
+ }
+
+ z = (struct RBignum*)bignew(x->len, (z == 0)?1:0);
+ zds = BDIGITS(z);
+
+ i = x->len;
+ while (i--) zds[i] = BDIGITS(x)[i];
+
+ i = 0; num = 0;
+ do {
+ num += (long)zds[i] - BDIGITS(y)[i];
+ if (num < 0) {
+ zds[i] = num + BIGRAD;
+ num = -1;
+ }
+ else {
+ zds[i] = BIGLO(num);
+ num = 0;
+ }
+ } while (++i < y->len);
+ if (num) {
+ while (num && i < x->len) {
+ num += zds[i];
+ if (num < 0) {
+ zds[i++] = num + BIGRAD;
+ num = -1;
+ }
+ else {
+ zds[i++] = BIGLO(num);
+ num = 0;
+ }
+ }
+ }
+
+ return bignorm(z);
+}
+
+static VALUE
bigadd(x, y, sign)
struct RBignum *x, *y;
char sign;
@@ -422,6 +529,11 @@ bigadd(x, y, sign)
long num;
UINT i, len;
+ if (x->sign == (y->sign ^ sign)) {
+ if (y->sign == sign) return bigsub(y, x);
+ return bigsub(x, y);
+ }
+
if (x->len > y->len) {
len = x->len + 1;
}
@@ -437,53 +549,18 @@ bigadd(x, y, sign)
while (i--) zds[i] = BDIGITS(y)[i];
i = 0; num = 0;
- if (x->sign == z->sign) {
- do {
- num += (long)zds[i] + BDIGITS(x)[i];
- zds[i++] = BIGLO(num);
+ do {
+ num += (long)zds[i] + BDIGITS(x)[i];
+ zds[i++] = BIGLO(num);
num = BIGDN(num);
- } while (i < x->len);
- if (num) {
- while (i < y->len) {
- num += zds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- BDIGITS(z)[i] = num;
- }
- }
- else {
- do {
- num += (long)zds[i] - BDIGITS(x)[i];
- if (num < 0) {
- zds[i] = num + BIGRAD;
- num = -1;
- }
- else {
- zds[i] = BIGLO(num);
- num = 0;
- }
- } while (++i < x->len);
- if (num && x->len == y->len) {
- num = 1; i = 0;
- z->sign = 1;
- do {
- num += (BIGRAD-1) - zds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- } while (i < y->len);
- }
- else while (i < y->len) {
+ } while (i < x->len);
+ if (num) {
+ while (i < y->len) {
num += zds[i];
- if (num < 0) {
- zds[i++] = num + BIGRAD;
- num = -1;
- }
- else {
- zds[i++] = BIGLO(num);
- num = 0;
- }
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
}
+ BDIGITS(z)[i] = num;
}
return bignorm(z);
@@ -495,26 +572,40 @@ big_plus(x, y)
{
VALUE z;
- if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
- else {
- Check_Type(x, T_BIGNUM);
- }
- z = bigadd(x, y, 1);
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = int2big(FIX2INT(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bigadd(x, y, 1);
- return z;
+ case T_FLOAT:
+ return float_new(big2dbl(x) + RFLOAT(y)->value);
+
+ default:
+ return num_coerce_bin(x, y);
+ }
}
VALUE
big_minus(x, y)
VALUE x, y;
{
- if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
- else {
- Check_Type(y, T_BIGNUM);
- }
- x = bigadd(x, y, 0);
+ VALUE cmp;
- return x;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = int2big(FIX2INT(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bigadd(x, y, 0);
+
+ case T_FLOAT:
+ return float_new(big2dbl(x) - RFLOAT(y)->value);
+
+ default:
+ return num_coerce_bin(x, y);
+ }
}
VALUE
@@ -527,9 +618,19 @@ big_mul(x, y)
USHORT *zds;
if (FIXNUM_P(x)) x = (struct RBignum*)int2big(FIX2INT(x));
- if (FIXNUM_P(y)) y = (struct RBignum*)int2big(FIX2INT(y));
- else {
- Check_Type(y, T_BIGNUM);
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = (struct RBignum*)int2big(FIX2INT(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return float_new(big2dbl(x) * RFLOAT(y)->value);
+
+ default:
+ return num_coerce_bin(x, y);
}
j = x->len + y->len + 1;
@@ -567,7 +668,7 @@ bigdivmod(x, y, div, mod)
USHORT dd, q;
yds = BDIGITS(y);
- if (ny == 0 && yds[0] == 0) Fail("divided by 0");
+ if (ny == 0 && yds[0] == 0) num_zerodiv();
if (nx < ny) {
if (div) *div = INT2FIX(0);
if (mod) *mod = bignorm(x);
@@ -587,7 +688,7 @@ bigdivmod(x, y, div, mod)
if (div) *div = bignorm(z);
if (mod) {
if (!y->sign) t2 = -t2;
- *mod = FIX2INT(t2);
+ *mod = INT2FIX(t2);
}
return;
}
@@ -683,9 +784,19 @@ big_div(x, y)
{
VALUE z;
- if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
- else {
- Check_Type(y, T_BIGNUM);
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = int2big(FIX2INT(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return float_new(big2dbl(x) / RFLOAT(y)->value);
+
+ default:
+ return num_coerce_bin(x, y);
}
bigdivmod(x, y, &z, 0);
@@ -698,9 +809,20 @@ big_mod(x, y)
{
VALUE z;
- if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
- else {
- Check_Type(y, T_BIGNUM);
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = int2big(FIX2INT(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ y = dbl2big(RFLOAT(y)->value);
+ break;
+
+ default:
+ return num_coerce_bin(x, y);
}
bigdivmod(x, y, 0, &z);
@@ -713,9 +835,20 @@ big_divmod(x, y)
{
VALUE div, mod;
- if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
- else {
- Check_Type(y, T_BIGNUM);
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = int2big(FIX2INT(y));
+ break;
+
+ case T_FLOAT:
+ y = dbl2big(RFLOAT(y)->value);
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return num_coerce_bin(x, y);
}
bigdivmod(x, y, &div, &mod);
@@ -726,22 +859,37 @@ VALUE
big_pow(x, y)
VALUE x, y;
{
+ double d;
VALUE z;
- int n;
+
+ if (y == INT2FIX(0)) return INT2FIX(1);
+ switch (TYPE(y)) {
+ case T_FLOAT:
+ d = RFLOAT(y)->value;
+ break;
- if (TYPE(y) == T_FLOAT) {
- return float_new(pow(big2dbl(x), RFLOAT(y)->value));
- }
- n = NUM2INT(y);
- if (n == 0) return INT2FIX(1);
- if (n < 0) {
- return float_new(pow(big2dbl(x), (double)n));
+ case T_BIGNUM:
+ if (RBIGNUM(y)->sign) goto pos_big;
+ d = big2dbl(y);
+ break;
+
+ case T_FIXNUM:
+ if (FIX2INT(y) > 0) goto pos_big;
+ d = (double)FIX2INT(y);
+ break;
+
+ default:
+ return num_coerce_bin(x, y);
}
+ return float_new(pow(big2dbl(x), d));
+ pos_big:
z = x;
- while (--n) {
- while (!(n % 2)) {
- n = n /2;
+ for (;;) {
+ y = rb_funcall(y, '-', 1, INT2FIX(1));
+ if (y == INT2FIX(0)) break;
+ while (rb_funcall(y, '%', 1, INT2FIX(2)) == INT2FIX(0)) {
+ y = rb_funcall(y, '/', 1, INT2FIX(2));
x = big_mul(x, x);
}
z = big_mul(z, x);
@@ -906,22 +1054,6 @@ big_xor(x, y)
return bignorm(z);
}
-static VALUE
-big_neg(x)
- struct RBignum *x;
-{
- VALUE z = big_clone(x);
- UINT i = x->len;
- USHORT *ds = BDIGITS(z);
-
- if (!x->sign) big_2comp(z);
- while (i--) ds[i] = ~ds[i];
- if (x->sign) big_2comp(z);
- RBIGNUM(z)->sign = !RBIGNUM(z)->sign;
-
- return bignorm(z);
-}
-
static VALUE big_rshift();
VALUE
@@ -1014,32 +1146,6 @@ big_aref(x, y)
}
static VALUE
-big_cmp(x, y)
- struct RBignum *x, *y;
-{
- int xlen = x->len;
-
- 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)
- return (x->sign) ? INT2FIX(-1) : INT2FIX(1);
- if (xlen > y->len)
- return (x->sign) ? INT2FIX(1) : INT2FIX(-1);
-
- while(xlen-- && (BDIGITS(x)[xlen]==BDIGITS(y)[xlen]));
- if (-1 == xlen) return INT2FIX(0);
- return (BDIGITS(x)[xlen] < BDIGITS(y)[xlen]) ?
- (x->sign ? INT2FIX(1) : INT2FIX(-1)) :
- (x->sign ? INT2FIX(-1) : INT2FIX(1));
-}
-
-static VALUE
big_hash(x)
struct RBignum *x;
{
@@ -1059,13 +1165,12 @@ big_coerce(x, y)
VALUE y;
{
if (FIXNUM_P(y)) {
- return int2big(FIX2INT(y));
+ return assoc_new(int2big(FIX2INT(y)), x);
}
else {
- Fail("can't coerce %s to Bignum", rb_class2name(CLASS_OF(y)));
+ TypeError("can't coerce %s to Bignum", rb_class2name(CLASS_OF(y)));
}
/* not reached */
- return Qnil;
}
static VALUE
@@ -1079,11 +1184,43 @@ big_abs(x)
return (VALUE)x;
}
+/* !!!warnig!!!!
+ this is not really a random number!!
+*/
+
+VALUE
+big_rand(max)
+ struct RBignum *max;
+{
+ struct RBignum *v;
+ int len;
+
+ len = max->len;
+ v = RBIGNUM(bignew(len,1));
+ while (len--) {
+#ifdef HAVE_RANDOM
+ BDIGITS(v)[len] = random();
+#else
+ BDIGITS(v)[len] = rand();
+#endif
+ }
+
+ return big_mod(v, max);
+}
+
+static VALUE
+big_size(big)
+ struct RBignum *big;
+{
+ return INT2FIX(big->len*2);
+}
+
void
Init_Bignum()
{
cBignum = rb_define_class("Bignum", cInteger);
- rb_define_singleton_method(cBignum, "new", big_s_new, 1);
+
+ rb_undef_method(CLASS_OF(cBignum), "new");
rb_define_method(cBignum, "to_s", big_to_s, 0);
rb_define_method(cBignum, "coerce", big_coerce, 1);
@@ -1107,5 +1244,6 @@ Init_Bignum()
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);
+ rb_define_method(cBignum, "abs", big_abs, 0);
+ rb_define_method(cBignum, "size", big_size, 0);
}
diff --git a/class.c b/class.c
index 204c476fa7..3e8ce517a6 100644
--- a/class.c
+++ b/class.c
@@ -39,7 +39,7 @@ singleton_class_new(super)
{
struct RClass *cls = (struct RClass*)class_new(super);
- FL_SET(cls, FL_SINGLE);
+ FL_SET(cls, FL_SINGLETON);
return (VALUE)cls;
}
@@ -58,7 +58,7 @@ VALUE
singleton_class_clone(class)
struct RClass *class;
{
- if (!FL_TEST(class, FL_SINGLE))
+ if (!FL_TEST(class, FL_SINGLETON))
return (VALUE)class;
else {
/* copy singleton(unnamed) class */
@@ -68,7 +68,7 @@ singleton_class_clone(class)
clone->super = class->super;
clone->m_tbl = new_idhash();
st_foreach(class->m_tbl, clone_method, clone->m_tbl);
- FL_SET(clone, FL_SINGLE);
+ FL_SET(clone, FL_SINGLETON);
return (VALUE)clone;
}
}
@@ -78,7 +78,7 @@ rb_define_class_id(id, super)
ID id;
struct RBasic *super;
{
- struct RClass *cls = (struct RClass*)class_new(super);
+ struct RClass *cls;
if (!super) super = (struct RBasic*)cClass;
cls = (struct RClass*)class_new(super);
@@ -100,7 +100,6 @@ rb_define_class(name, super)
id = rb_intern(name);
class = rb_define_class_id(id, super);
st_add_direct(rb_class_tbl, id, class);
- rb_set_class_path(class, 0, name);
return class;
}
@@ -128,7 +127,7 @@ module_new()
NEWOBJ(mdl, struct RClass);
OBJSETUP(mdl, cModule, T_MODULE);
- mdl->super = Qnil;
+ mdl->super = 0;
mdl->m_tbl = new_idhash();
return (VALUE)mdl;
@@ -171,6 +170,7 @@ rb_define_module_under(under, name)
id = rb_intern(name);
module = rb_define_module_id(id);
rb_const_set(under, id, module);
+ rb_set_class_path(module, under, name);
return module;
}
@@ -201,7 +201,7 @@ rb_include_module(class, module)
{
struct RClass *p;
- if (!module) return;
+ if (NIL_P(module)) return;
switch (TYPE(module)) {
case T_MODULE:
@@ -212,10 +212,8 @@ rb_include_module(class, module)
}
if (class == module) return;
- if (BUILTIN_TYPE(class) == T_CLASS) {
- rb_clear_cache(class);
- }
-
+ rb_clear_cache();
+
while (module) {
/* ignore if the module included already in superclasses */
for (p = class->super; p; p = p->super) {
@@ -258,7 +256,7 @@ rb_undef_method(class, name)
struct RClass *class;
char *name;
{
- rb_add_method(class, rb_intern(name), Qnil, NOEX_PUBLIC);
+ rb_add_method(class, rb_intern(name), 0, NOEX_PUBLIC);
}
void
@@ -275,18 +273,7 @@ VALUE
rb_singleton_class(obj)
struct RBasic *obj;
{
- switch (TYPE(obj)) {
- case T_OBJECT:
- case T_CLASS:
- case T_MODULE:
- case T_STRUCT:
- break;
- default:
- Fail("can't define singleton method for built-in class");
- break;
- }
-
- if (FL_TEST(obj->class, FL_SINGLE)) {
+ if (FL_TEST(obj->class, FL_SINGLETON)) {
return (VALUE)obj->class;
}
return obj->class = singleton_class_new(obj->class);
@@ -338,10 +325,10 @@ rb_define_attr(class, id, pub)
attreq = rb_intern(buf);
sprintf(buf, "@%s", name);
attriv = rb_intern(buf);
- if (rb_method_boundp(class, attr) == FALSE) {
+ if (!rb_method_boundp(class, attr)) {
rb_add_method(class, attr, NEW_IVAR(attriv), 0);
}
- if (pub && rb_method_boundp(class, attreq) == FALSE) {
+ if (pub && !rb_method_boundp(class, attreq)) {
rb_add_method(class, attreq, NEW_ATTRSET(attriv), 0);
}
}
@@ -372,7 +359,7 @@ rb_scan_args(argc, argv, fmt, va_alist)
if (isdigit(*p)) {
n = *p - '0';
if (n > argc)
- Fail("Wrong number of arguments (%d for %d)", argc, n);
+ ArgError("Wrong # of arguments (%d for %d)", argc, n);
for (i=0; i<n; i++) {
var = va_arg(vargs, VALUE*);
*var = argv[i];
@@ -408,7 +395,7 @@ rb_scan_args(argc, argv, fmt, va_alist)
}
else if (*p == '\0') {
if (argc > i) {
- Fail("Wrong # of arguments(%d for %d)", argc, i);
+ ArgError("Wrong # of arguments(%d for %d)", argc, i);
}
}
else {
@@ -419,6 +406,6 @@ rb_scan_args(argc, argv, fmt, va_alist)
return argc;
error:
- Fail("bad scan arg format: %s", fmt);
+ Fatal("bad scan arg format: %s", fmt);
return 0;
}
diff --git a/compar.c b/compar.c
index e7d1e62d79..e406a0f0fe 100644
--- a/compar.c
+++ b/compar.c
@@ -6,7 +6,7 @@
$Date: 1994/10/14 06:19:05 $
created at: Thu Aug 26 14:39:48 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -34,7 +34,7 @@ cmp_gt(x, y)
VALUE c = rb_funcall(x, cmp, 1, y);
int t = NUM2INT(c);
- if (t > 0) return y;
+ if (t > 0) return TRUE;
return FALSE;
}
@@ -45,7 +45,7 @@ cmp_ge(x, y)
VALUE c = rb_funcall(x, cmp, 1, y);
int t = NUM2INT(c);
- if (t >= 0) return y;
+ if (t >= 0) return TRUE;
return FALSE;
}
@@ -56,7 +56,7 @@ cmp_lt(x, y)
VALUE c = rb_funcall(x, cmp, 1, y);
int t = NUM2INT(c);
- if (t < 0) return y;
+ if (t < 0) return TRUE;
return FALSE;
}
@@ -67,7 +67,7 @@ cmp_le(x, y)
VALUE c = rb_funcall(x, cmp, 1, y);
int t = NUM2INT(c);
- if (t <= 0) return y;
+ if (t <= 0) return TRUE;
return FALSE;
}
diff --git a/config.dj b/config.dj
new file mode 100644
index 0000000000..13f4e6ddb6
--- /dev/null
+++ b/config.dj
@@ -0,0 +1,35 @@
+#define THREAD 1
+#define HAVE_DIRENT_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_SYS_FILE_H 1
+#define HAVE_PWD_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TIMES_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_WAIT_H 1
+#define HAVE_STRING_H 1
+#define HAVE_UTIME_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_ST_BLKSIZE 1
+#define HAVE_ST_RDEV 1
+#define GETGROUPS_T gid_t
+#define RETSIGTYPE void
+#define HAVE_ALLOCA 1
+#define vfork fork
+#define HAVE_FMOD 1
+#define HAVE_RANDOM 1
+#define HAVE_WAITPID 1
+#define HAVE_GETCWD 1
+#define HAVE_TRUNCATE 1
+#define HAVE_CHSIZE 1
+#define HAVE_TIMES 1
+#define HAVE_UTIMES 1
+/* #define HAVE_FCNTL 1 */
+/* #define HAVE_SETITIMER 1 */
+#define HAVE_GETGROUPS 1
+#define HAVE_SIGPROCMASK 1
+#define FILE_COUNT _cnt
+#define DLEXT ".so"
+#define RUBY_LIB ";/usr/local/lib/ruby;."
+#define RUBY_ARCHLIB "/usr/local/lib/ruby/i386-msdos"
diff --git a/config.guess b/config.guess
index a3d6a9f1bf..ce6e12a8fd 100755
--- a/config.guess
+++ b/config.guess
@@ -1,6 +1,6 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+# Copyright (C) 1992, 93, 94, 95, 1996 Free Software Foundation, Inc.
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -51,14 +51,19 @@ trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15
# Note: order is significant - the case branches are not exclusive.
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
- alpha:OSF1:V*:*)
- # After 1.2, OSF1 uses "V1.3" for uname -r.
- echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^V//'`
- exit 0 ;;
alpha:OSF1:*:*)
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
- echo alpha-dec-osf${UNAME_RELEASE}
- exit 0 ;;
+ echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//'`
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
amiga:NetBSD:*:*)
echo m68k-cbm-netbsd${UNAME_RELEASE}
exit 0 ;;
@@ -75,6 +80,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
sun4*:SunOS:5.*:*)
echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
@@ -93,15 +101,30 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
sun3*:SunOS:*:*)
echo m68k-sun-sunos${UNAME_RELEASE}
exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
RISC*:ULTRIX:*:*)
echo mips-dec-ultrix${UNAME_RELEASE}
exit 0 ;;
VAX*:ULTRIX*:*:*)
echo vax-dec-ultrix${UNAME_RELEASE}
exit 0 ;;
+ mips:*:4*:UMIPS)
+ echo mips-mips-riscos4sysv
+ exit 0 ;;
mips:*:5*:RISCos)
echo mips-mips-riscos${UNAME_RELEASE}
exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
m88k:CX/UX:7*:*)
echo m88k-harris-cxux7
exit 0 ;;
@@ -112,12 +135,17 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo m88k-motorola-sysv3
exit 0 ;;
AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
-o ${TARGET_BINARY_INTERFACE}x = x ] ; then
echo m88k-dg-dgux${UNAME_RELEASE}
else
echo m88k-dg-dguxbcs${UNAME_RELEASE}
fi
+ else echo i586-dg-dgux${UNAME_RELEASE}
+ fi
exit 0 ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
@@ -169,10 +197,8 @@ EOF
else
IBM_ARCH=powerpc
fi
- if grep bos410 /usr/include/stdio.h >/dev/null 2>&1; then
- IBM_REV=4.1
- elif grep bos411 /usr/include/stdio.h >/dev/null 2>&1; then
- IBM_REV=4.1.1
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
else
IBM_REV=4.${UNAME_RELEASE}
fi
@@ -185,7 +211,7 @@ EOF
echo romp-ibm-bsd4.4
exit 0 ;;
ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
- echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
exit 0 ;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
echo rs6000-bull-bosx
@@ -203,7 +229,7 @@ EOF
case "${UNAME_MACHINE}" in
9000/31? ) HP_ARCH=m68000 ;;
9000/[34]?? ) HP_ARCH=m68k ;;
- 9000/7?? | 9000/8?7 ) HP_ARCH=hppa1.1 ;;
+ 9000/7?? | 9000/8?[679] ) HP_ARCH=hppa1.1 ;;
9000/8?? ) HP_ARCH=hppa1.0 ;;
esac
HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
@@ -239,18 +265,21 @@ EOF
rm -f dummy.c dummy
echo unknown-hitachi-hiuxwe2
exit 0 ;;
- 9000/7??:4.3bsd:*:* | 9000/8?7:4.3bsd:*:* )
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
echo hppa1.1-hp-bsd
exit 0 ;;
9000/8??:4.3bsd:*:*)
echo hppa1.0-hp-bsd
exit 0 ;;
- hp7??:OSF1:*:* | hp8?7:OSF1:*:* )
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
echo hppa1.1-hp-osf
exit 0 ;;
hp8??:OSF1:*:*)
echo hppa1.0-hp-osf
exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
exit 0 ;;
@@ -269,20 +298,23 @@ EOF
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
exit 0 ;;
- CRAY*X-MP:UNICOS:*:*)
+ CRAY*X-MP:*:*:*)
echo xmp-cray-unicos
exit 0 ;;
- CRAY*Y-MP:UNICOS:*:*)
- echo ymp-cray-unicos
- exit 0 ;;
- CRAY-2:UNICOS:*:*)
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*C90:*:*:*)
+ echo c90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
echo cray2-cray-unicos
exit 0 ;;
hp3[0-9][05]:NetBSD:*:*)
echo m68k-hp-netbsd${UNAME_RELEASE}
exit 0 ;;
i[34]86:BSD/386:*:* | *:BSD/OS:*:*)
- echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
exit 0 ;;
*:FreeBSD:*:*)
echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
@@ -290,11 +322,20 @@ EOF
*:NetBSD:*:*)
echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
exit 0 ;;
+ i*:CYGWIN*:*)
+ echo i386-pc-cygwin32
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin32
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
*:GNU:*:*)
echo `echo ${UNAME_MACHINE}|sed -e 's,/.*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
exit 0 ;;
*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux
+ echo ${UNAME_MACHINE}-pc-linux
exit 0 ;;
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
# are messed up and put the nodename in both sysname and nodename.
@@ -305,23 +346,25 @@ EOF
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
else
- echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
fi
exit 0 ;;
i[34]86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
- echo ${UNAME_MACHINE}-unknown-isc$UNAME_REL
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
elif /bin/uname -X 2>/dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
(/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
- echo ${UNAME_MACHINE}-unknown-sco$UNAME_REL
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
else
- echo ${UNAME_MACHINE}-unknown-sysv32
+ echo ${UNAME_MACHINE}-pc-sysv32
fi
exit 0 ;;
Intel:Mach:3*:*)
- echo i386-unknown-mach3
+ echo i386-pc-mach3
exit 0 ;;
paragon:*:*:*)
echo i860-intel-osf1
@@ -345,19 +388,19 @@ EOF
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
uname -p 2>/dev/null | grep 86 >/dev/null \
&& echo i486-ncr-sysv4 && exit 0 ;;
- m680[234]0:LynxOS:2.2*:*)
+ m680[234]0:LynxOS:2.[23]*:*)
echo m68k-lynx-lynxos${UNAME_RELEASE}
exit 0 ;;
mc68030:UNIX_System_V:4.*:*)
echo m68k-atari-sysv4
exit 0 ;;
- i[34]86:LynxOS:2.2*:*)
+ i[34]86:LynxOS:2.[23]*:*)
echo i386-lynx-lynxos${UNAME_RELEASE}
exit 0 ;;
- TSUNAMI:LynxOS:2.2*:*)
+ TSUNAMI:LynxOS:2.[23]*:*)
echo sparc-lynx-lynxos${UNAME_RELEASE}
exit 0 ;;
- rs6000:LynxOS:2.2*:*)
+ rs6000:LynxOS:2.[23]*:*)
echo rs6000-lynx-lynxos${UNAME_RELEASE}
exit 0 ;;
RM*:SINIX-*:*:*)
@@ -371,12 +414,29 @@ EOF
echo ns32k-sni-sysv
fi
exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ R3000:*System_V*:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ powerpc:JCC_BSD+:*:*)
+ echo powerpc-jcc-bsd4.4
+ exit 0 ;;
esac
#echo '(No uname command or uname output not recognized.)' 1>&2
#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
cat >dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
main ()
{
#if defined (sony)
@@ -427,7 +487,7 @@ main ()
#endif
#if defined (__386BSD__)
- printf ("i386-unknown-bsd\n"); exit (0);
+ printf ("i386-pc-bsd\n"); exit (0);
#endif
#if defined (sequent)
@@ -440,7 +500,18 @@ main ()
#endif
#if defined (_SEQUENT_)
- printf ("i386-sequent-ptx\n"); exit (0);
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
#endif
#if defined (vax)
diff --git a/config.sub b/config.sub
index 5641cc1ce6..27819cc8dc 100755
--- a/config.sub
+++ b/config.sub
@@ -1,9 +1,9 @@
#! /bin/sh
# Configuration validation subroutine script, version 1.1.
-# Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+# Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine. It does not imply ALL GNU software can.
+# can handle that machine. It does not imply ALL GNU software can.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,7 +17,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -40,6 +41,8 @@
# The goal of this file is to map all the various variations of a given
# machine specification into a single specification in the form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.
if [ x$1 = x ]
@@ -61,11 +64,21 @@ case $1 in
;;
esac
-# Separate what the user gave into CPU-COMPANY and OS (if any).
-basic_machine=`echo $1 | sed 's/-[^-]*$//'`
-if [ $basic_machine != $1 ]
-then os=`echo $1 | sed 's/.*-/-/'`
-else os=; fi
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
### Let's recognize common machines as not being operating systems so
### that things like config.sub decstation-3100 work. We also
@@ -80,38 +93,43 @@ case $os in
-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
- -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp )
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
os=
basic_machine=$1
;;
-hiux*)
os=-hiuxwe2
;;
+ -sco5)
+ os=sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
-sco4)
os=-sco3.2v4
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco3.2.[4-9]*)
os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco3.2v[4-9]*)
# Don't forget version if it is 3.2v4 or newer.
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco*)
os=-sco3.2v2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-isc)
os=-isc2.2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-clix*)
basic_machine=clipper-intergraph
;;
-isc*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-lynx*)
os=-lynxos
@@ -122,33 +140,43 @@ case $os in
-windowsnt*)
os=`echo $os | sed -e 's/windowsnt/winnt/'`
;;
+ -psos*)
+ os=-psos
+ ;;
esac
# Decode aliases for certain CPU-COMPANY combinations.
case $basic_machine in
# Recognize the basic CPU types without company name.
# Some are omitted here because they have special meanings below.
- tahoe | i[345]86 | i860 | m68k | m68000 | m88k | ns32k | arm | pyramid \
+ tahoe | i860 | m68k | m68000 | m88k | ns32k | arm \
+ | arme[lb] | pyramid \
| tron | a29k | 580 | i960 | h8300 | hppa1.0 | hppa1.1 \
- | alpha | we32k | ns16k | clipper | sparclite | i370 | sh \
- | powerpc | sparc64 | 1750a | dsp16xx | mips64 | mipsel \
+ | alpha | we32k | ns16k | clipper | i370 | sh \
+ | powerpc | powerpcle | 1750a | dsp16xx | mips64 | mipsel \
| pdp11 | mips64el | mips64orion | mips64orionel \
- | sparc)
- basic_machine=$basic_machine-unknown
- ;;
+ | sparc | sparclet | sparclite | sparc64)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[3456]86)
+ basic_machine=$basic_machine-pc
+ ;;
# Object if more than one company name word.
*-*-*)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
;;
# Recognize the basic CPU types with company name.
- vax-* | tahoe-* | i[345]86-* | i860-* | m68k-* | m68000-* | m88k-* \
+ vax-* | tahoe-* | i[3456]86-* | i860-* | m68k-* | m68000-* | m88k-* \
| sparc-* | ns32k-* | fx80-* | arm-* | c[123]* \
- | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* | power-* \
| none-* | 580-* | cray2-* | h8300-* | i960-* | xmp-* | ymp-* \
| hppa1.0-* | hppa1.1-* | alpha-* | we32k-* | cydra-* | ns16k-* \
| pn-* | np1-* | xps100-* | clipper-* | orion-* | sparclite-* \
- | pdp11-* | sh-* | powerpc-* | sparc64-* | mips64-* | mipsel-* \
+ | pdp11-* | sh-* | powerpc-* | powerpcle-* | sparc64-* | mips64-* | mipsel-* \
| mips64el-* | mips64orion-* | mips64orionel-*)
;;
# Recognize the various machine names and aliases which stand
@@ -188,6 +216,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-sysv
;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
balance)
basic_machine=ns32k-sequent
os=-dynix
@@ -220,6 +252,10 @@ case $basic_machine in
basic_machine=cray2-cray
os=-unicos
;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
crds | unos)
basic_machine=m68k-crds
;;
@@ -256,6 +292,10 @@ case $basic_machine in
encore | umax | mmax)
basic_machine=ns32k-encore
;;
+ ews4800)
+ basic_machine=mips-nec
+ os=-sysv4
+ ;;
fx2800)
basic_machine=i860-alliant
;;
@@ -306,20 +346,20 @@ case $basic_machine in
os=-mvs
;;
# I'm not sure what "Sysv32" means. Should this be sysv3.2?
- i[345]86v32)
- basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ i[3456]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv32
;;
- i[345]86v4*)
- basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ i[3456]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv4
;;
- i[345]86v)
- basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ i[3456]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv
;;
- i[345]86sol2)
- basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ i[3456]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-solaris2
;;
iris | iris4d)
@@ -417,14 +457,41 @@ case $basic_machine in
pc532 | pc532-*)
basic_machine=ns32k-pc532
;;
- pentium-*)
- # We will change tis to say i586 once there has been
- # time for various packages to start to recognize that.
- basic_machine=i486-`echo $basic_machine | sed 's/^[^-]*-//'`
+ pentium | p5)
+ basic_machine=i586-intel
+ ;;
+ pentiumpro | p6)
+ basic_machine=i686-intel
+ ;;
+ pentium-* | p5-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ k5)
+ # We don't have specific support for AMD's K5 yet, so just call it a Pentium
+ basic_machine=i586-amd
+ ;;
+ nexen)
+ # We don't have specific support for Nexgen yet, so just call it a Pentium
+ basic_machine=i586-nexgen
;;
pn)
basic_machine=pn-gould
;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
ps2)
basic_machine=i386-ibm
;;
@@ -519,6 +586,10 @@ case $basic_machine in
basic_machine=m68k-wrs
os=-vxworks
;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
xmp)
basic_machine=xmp-cray
os=-unicos
@@ -586,6 +657,8 @@ esac
if [ x"$os" != x"" ]
then
case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
@@ -597,21 +670,25 @@ case $os in
os=-sysv4
;;
-gnu/linux*)
- os=`echo $os | sed -e 's|gnu/linux|linux|'`
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
;;
# First accept the basic system types.
# The portable systems comes first.
- # Each alternative must end in a *, to match a version number.
+ # Each alternative MUST END IN A *, to match a version number.
# -sysv* is not here because it comes later, after sysvr4.
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
- | -vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[345]* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
- | -amigados* | -msdos* | -newsos* | -unicos* | -aos* \
+ | -amigados* | -msdos* | -newsos* | -unicos* | -aof* | -aos* \
| -nindy* | -vxworks* | -ebmon* | -hms* | -mvs* | -clix* \
- | -riscos* | -linux* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -netbsd* | -freebsd* | -riscix* \
| -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
- | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta | -udi | -eabi)
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin32* | -pe* | -psos* | -moss* | -proelf* \
+ | -linux*)
+ # Remember, each alternative MUST END IN *, to match a version number.
;;
-sunos5*)
os=`echo $os | sed -e 's|sunos5|solaris2|'`
@@ -637,6 +714,9 @@ case $os in
-ctix* | -uts*)
os=-sysv
;;
+ -ns2 )
+ os=-nextstep2
+ ;;
# Preserve the version number of sinix5.
-sinix5.*)
os=`echo $os | sed -e 's|sinix|sysv|'`
@@ -690,6 +770,9 @@ case $basic_machine in
*-acorn)
os=-riscix1.2
;;
+ arm*-semi)
+ os=-aout
+ ;;
pdp11-*)
os=-none
;;
@@ -741,6 +824,9 @@ case $basic_machine in
m88k-omron*)
os=-luna
;;
+ *-next )
+ os=-nextstep
+ ;;
*-sequent)
os=-ptx
;;
@@ -825,6 +911,9 @@ case $basic_machine in
-vxworks*)
vendor=wrs
;;
+ -aux*)
+ vendor=apple
+ ;;
esac
basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
;;
diff --git a/configure.bat b/configure.bat
new file mode 100644
index 0000000000..84e5191bbe
--- /dev/null
+++ b/configure.bat
@@ -0,0 +1,5 @@
+@echo off
+sed -f top.sed Makefile.in >Makefile
+sed -f top.sed ext/extmk.rb.in > ext\extmk.rb
+copy ext\Setup.dj ext\Setup
+copy config.dj config.h
diff --git a/configure.in b/configure.in
index fc87754cb8..7f89f5e69c 100644
--- a/configure.in
+++ b/configure.in
@@ -21,6 +21,17 @@ then
(it is also a good idea to do 'make clean' before compiling))
fi
+dnl checks for thread
+rb_thread=yes
+AC_ARG_ENABLE(thread, [--disable-thread never use user-level thread], [
+ rb_thread=$enableval
+])
+if test $rb_thread = yes; then
+ AC_DEFINE(THREAD)
+fi
+
+AC_CANONICAL_HOST
+
dnl Checks for programs.
AC_PROG_CC
AC_PROG_GCC_TRADITIONAL
@@ -29,19 +40,18 @@ 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(crypt, crypt)
-AC_CHECK_LIB(dl, dlopen, [:]) # Dynamic linking for SunOS/Solaris and SYSV
+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(limits.h sys/file.h sys/ioctl.h pwd.h\
- sys/time.h sys/times.h sys/param.h unistd.h\
+AC_CHECK_HEADERS(unistd.h limits.h sys/file.h sys/ioctl.h pwd.h sys/select.h\
+ sys/time.h sys/times.h sys/param.h sys/wait.h\
syscall.h a.out.h string.h utime.h memory.h)
dnl Checks for typedefs, structures, and compiler characteristics.
@@ -57,40 +67,75 @@ AC_TYPE_SIGNAL
AC_FUNC_ALLOCA
AC_FUNC_VFORK
AC_REPLACE_FUNCS(dup2 setenv memmove mkdir strerror strftime\
- strstr strtoul strdup crypt)
+ strstr strtoul strdup strcasecmp crypt flock)
AC_CHECK_FUNCS(fmod killpg random wait4 waitpid syscall getcwd\
- truncate chsize times utimes fcntl\
+ truncate chsize times utimes fcntl lockf setitimer\
setruid seteuid setreuid setrgid setegid setregid\
- getgroups getpriority sigprocmask dlopen)
-if test "$ac_cv_func strftime" = no; then
+ getgroups getpgid getpriority dlopen sigprocmask sigaction)
+if test "$ac_cv_func_strftime" = no; then
AC_STRUCT_TIMEZONE
AC_TRY_LINK([],
[extern int daylight; int i = daylight;], AC_DEFINE(HAVE_DAYLIGHT))
fi
+if test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes; then
+ AC_DEFINE(POSIX_SIGNAL)
+else
+ AC_MSG_CHECKING(for BSD signal semantics)
+ AC_CACHE_VAL(rb_cv_bsd_signal,
+ [AC_TRY_RUN([
+#include <stdio.h>
+#include <signal.h>
+
+void sig_handler(dummy)
+ int dummy;
+{
+}
+
+int main(argc, argv)
+ int argc;
+ char ** argv;
+{
+ signal(SIGINT, sig_handler);
+ kill(getpid(), SIGINT);
+ kill(getpid(), SIGINT);
+ return 0;
+}
+],
+ rb_cv_bsd_signal=yes,
+ rb_cv_bsd_signal=no,
+ [:])])
+ AC_MSG_RESULT($rb_cv_bsd_signal)
+ if test "$rb_cv_bsd_signal" = yes; then
+ AC_DEFINE(BSD_SIGNAL)
+ fi
+fi
+
AC_C_BIGENDIAN
+AC_CHAR_UNSIGNED
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
+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
+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
+if test "$rb_cv_fcnt" = ""; then
AC_TRY_COMPILE([#include <stdio.h>],
- [FILE *f = stdin; f->readCount = 0;], rb_cv_fcnt="readCount", )
+ [FILE *f = stdin; f->readCount = 0;],
+ rb_cv_fcnt="readCount", rb_cv_fcnt="not found")
fi])
-if test "$rb_cv_fcnt"; then
+if test "$rb_cv_fcnt" = "not found"; then
+ AC_MSG_RESULT([not found(OK if using GNU libc)])
+else
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
if test "$ac_cv_func_getpwent" = yes; then
@@ -105,31 +150,45 @@ if test "$ac_cv_func_getpwent" = yes; then
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], [
+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)
- cat confdefs.h > config.h
- AC_CACHE_VAL(rb_cv_dln_a_out,
- [AC_TRY_COMPILE([
-#define USE_DLN_A_OUT
-#include "dln.c"
+case "$host_os" in
+ linux*)
+ AC_MSG_CHECKING(whether ELF binaries are produced)
+ AC_CACHE_VAL(rb_cv_linux_elf,
+ [AC_TRY_RUN([
+/* Test for whether ELF binaries are produced */
+#include <fcntl.h>
+#include <stdlib.h>
+main() {
+ char buffer[4];
+ int i=open("conftest",O_RDONLY);
+ if(i==-1)
+ exit(1); /* fail */
+ if(read(i,&buffer[0],4)<4)
+ exit(1); /* fail */
+ if(buffer[0] != 127 || buffer[1] != 'E' ||
+ buffer[2] != 'L' || buffer[3] != 'F')
+ exit(1); /* fail */
+ exit(0); /* succeed (yes, it's ELF) */
+}
],
- [],
- 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
+ rb_cv_linux_elf=yes,
+ rb_cv_linux_elf=no,
+ [:])])
+ AC_MSG_RESULT($rb_cv_linux_elf)
+ if test "$rb_cv_linux_elf" = no; then
+ with_dln_a_out=yes
+ else
+ LDFLAGS="-rdynamic"
+ fi;;
+esac
+
+AC_SUBST(DLDFLAGS)dnl
AC_SUBST(STATIC)dnl
AC_SUBST(CCDLFLAGS)dnl
@@ -138,7 +197,63 @@ AC_SUBST(DLEXT)dnl
STATIC=
-if test "$rb_cv_dln_a_out" = yes; then
+if test "$with_dln_a_out" != yes; then
+ rb_cv_dlopen=unknown
+ AC_MSG_CHECKING(whether OS depend dynamic link works)
+ if test "$GCC" = yes; then
+ CCDLFLAGS=-fpic
+ else
+ case "$host_os" in
+ hpux*) CCDLFLAGS='+z';;
+ solaris*|irix*) CCDLFLAGS='-K pic' ;;
+ sunos*) CCDLFLAGS='-pic' ;;
+ svr4*|esix*) CCDLFLAGS='-Kpic' ;;
+ *) CCDLFLAGS='' ;;
+ esac
+ fi
+
+ case "$host_os" in
+ hpux*) DLDFLAGS="-E"
+ LDSHARED='ld -b'
+ LDFLAGS="-Wl,-E"
+ rb_cv_dlopen=yes;;
+ solaris*) LDSHARED='ld -G'
+ rb_cv_dlopen=yes;;
+ sunos*) LDSHARED='ld -assert nodefinitions'
+ rb_cv_dlopen=yes;;
+ svr4*|esix*) LDSHARED="ld -G"
+ rb_cv_dlopen=yes ;;
+ linux*) LDSHARED="gcc -shared"
+ rb_cv_dlopen=yes ;;
+ freebsd*) LDSHARED="ld -Bshareable"
+ rb_cv_dlopen=yes ;;
+ *) LDSHARED='ld' ;;
+ esac
+ AC_MSG_RESULT($rb_cv_dlopen)
+fi
+
+dln_a_out_works=no
+if test "$ac_cv_header_a_out_h" = yes; then
+ if test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown; then
+ AC_MSG_CHECKING(whether matz's dln works)
+ cat confdefs.h > config.h
+ AC_CACHE_VAL(rb_cv_dln_a_out,
+ [AC_TRY_COMPILE([
+#define USE_DLN_A_OUT
+#include "dln.c"
+],
+ [],
+ 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
+ dln_a_out_works=yes
+ AC_DEFINE(USE_DLN_A_OUT)
+ fi
+ fi
+fi
+
+if test "$dln_a_out_works" = yes; then
if test "$GCC" = yes; then
STATIC=-static
else
@@ -147,45 +262,48 @@ if test "$rb_cv_dln_a_out" = yes; then
DLEXT=o
AC_DEFINE(DLEXT, ".o")
CCDLFLAGS=
- LDCMD=
-
else
-
- AC_CANONICAL_HOST
case "$host_os" in
hpux*) DLEXT=sl
AC_DEFINE(DLEXT, ".sl");;
+ nextstep*) DLEXT=o
+ AC_DEFINE(DLEXT, ".o");;
*) DLEXT=so
AC_DEFINE(DLEXT, ".so");;
esac
+fi
- if test "$GCC" = yes; then
- CCDLFLAGS=-fpic
- else
- case "$host_os" in
- hpux*) CCDLFLAGS='+z';;
- solaris*|irix*) CCDLFLAGS='-K pic' ;;
- sunos*) CCDLFLAGS='-pic' ;;
- svr4*|esix*) CCDLFLAGS='-Kpic' ;;
- *) CCDLFLAGS='' ;;
- esac
- fi
-
- case "$host_os" in
- 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
+AC_SUBST(STRIP)dnl
+if test "$with_dln_a_out" = yes; then
+ STRIP=true
+else
+ STRIP=strip
fi
+case "$host_os" in
+ linux*)
+ STRIP='strip -S -x';;
+ nextstep*)
+ STRIP='strip -A -n';;
+esac
+
+EXTSTATIC=
+AC_SUBST(EXTSTATIC)dnl
+AC_ARG_WITH(static-linked-ext,
+ [--with-static-linked-ext link external modules statically],
+ [case $withval in
+ yes) STATIC=
+ EXTSTATIC=static;;
+ *) ;;
+ esac])
+
if test "$prefix" = NONE; then
- AC_DEFINE_UNQUOTED(RUBY_LIB, ".:${ac_default_prefix}/lib/ruby")
-else
- AC_DEFINE_UNQUOTED(RUBY_LIB, ".:${prefix}/lib/ruby")
+ prefix=$ac_default_prefix
fi
+AC_DEFINE_UNQUOTED(RUBY_LIB, "${prefix}/lib/ruby:.")
+AC_SUBST(archlib)dnl
+archlib="${prefix}/lib/ruby/${host_cpu}-${host_os}"
+AC_DEFINE_UNQUOTED(RUBY_ARCHLIB, "$archlib")
echo "creating config.h"
cat confdefs.h > config.h
diff --git a/defines.h b/defines.h
index 2eb509f214..bef61b00fa 100644
--- a/defines.h
+++ b/defines.h
@@ -16,10 +16,21 @@
#define EUC
#undef SJIS
-#define SAFE_SIGHANDLE
+#ifdef NeXT
+#define S_IXUSR _S_IXUSR /* execute/search permission, owner */
+#define S_IXGRP 0000010 /* execute/search permission, group */
+#define S_IXOTH 0000001 /* execute/search permission, other */
+#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG))
+#endif /* NeXT */
#ifdef NT
#include "missing/nt.h"
#endif
+#ifdef sparc
+#define FLUSH_REGISTER_WINDOWS asm("ta 3")
+#else
+#define FLUSH_REGISTER_WINDOWS /* empty */
+#endif
+
#endif
diff --git a/dir.c b/dir.c
index f291b94da2..a1a6114406 100644
--- a/dir.c
+++ b/dir.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:28 $
created at: Wed Jan 5 09:51:01 JST 1994
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -43,10 +43,11 @@
# endif
#endif
+#include <errno.h>
+
char *getenv();
static VALUE cDir;
-static ID id_dir;
static void
free_dir(dir)
@@ -66,11 +67,17 @@ dir_s_open(dir_class, dirname)
Check_Type(dirname, T_STRING);
dirp = opendir(dirname->ptr);
- if (dirp == NULL) Fail("Can't open directory %s", dirname->ptr);
+ if (dirp == NULL) {
+ if (errno == EMFILE || errno == ENFILE) {
+ gc();
+ dirp = opendir(dirname->ptr);
+ }
+ if (dirp == NULL) {
+ rb_sys_fail(dirname->ptr);
+ }
+ }
- obj = obj_alloc(dir_class);
- if (!id_dir) id_dir = rb_intern("dir");
- Make_Data_Struct(obj, id_dir, DIR*, 0, free_dir, d);
+ obj = Make_Data_Struct(dir_class, DIR*, 0, free_dir, d);
*d = dirp;
return obj;
@@ -84,8 +91,7 @@ closeddir()
#define GetDIR(obj, dirp) {\
DIR **_dp;\
- if (!id_dir) id_dir = rb_intern("dir");\
- Get_Data_Struct(obj, id_dir, DIR*, _dp);\
+ Get_Data_Struct(obj, DIR*, _dp);\
dirp = *_dp;\
if (dirp == NULL) closeddir();\
}
@@ -94,14 +100,14 @@ static VALUE
dir_each(dir)
VALUE dir;
{
- extern VALUE rb_lastline;
DIR *dirp;
struct dirent *dp;
+ VALUE file;
GetDIR(dir, dirp);
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
- rb_lastline = str_new(dp->d_name, NAMLEN(dp));
- rb_yield(rb_lastline);
+ file = str_new(dp->d_name, NAMLEN(dp));
+ rb_yield(file);
}
return dir;
}
@@ -113,9 +119,13 @@ dir_tell(dir)
DIR *dirp;
int pos;
+#if !defined(__CYGWIN32__)
GetDIR(dir, dirp);
pos = telldir(dirp);
return int2inum(pos);
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -124,9 +134,13 @@ dir_seek(dir, pos)
{
DIR *dirp;
+#if !defined(__CYGWIN32__)
GetDIR(dir, dirp);
seekdir(dirp, NUM2INT(pos));
return dir;
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -146,8 +160,8 @@ dir_close(dir)
{
DIR **dirpp;
- Get_Data_Struct(dir, id_dir, DIR*, dirpp);
- if (*dirpp == NULL) Fail("already closed directory");
+ Get_Data_Struct(dir, DIR*, dirpp);
+ if (*dirpp == NULL) closeddir();
closedir(*dirpp);
*dirpp = NULL;
@@ -176,7 +190,7 @@ dir_s_chdir(argc, argv, obj)
}
if (chdir(dist) < 0)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
return INT2FIX(0);
}
@@ -189,9 +203,9 @@ dir_s_getwd(dir)
char path[MAXPATHLEN];
#ifdef HAVE_GETCWD
- if (getcwd(path, sizeof(path)) == 0) Fail(path);
+ if (getcwd(path, sizeof(path)) == 0) rb_sys_fail(path);
#else
- if (getwd(path) == 0) Fail(path);
+ if (getwd(path) == 0) rb_sys_fail(path);
#endif
return str_new2(path);
@@ -201,12 +215,16 @@ static VALUE
dir_s_chroot(dir, path)
VALUE dir, path;
{
+#if !defined(DJGPP) && !defined(__CYGWIN32__)
Check_Type(path, T_STRING);
if (chroot(RSTRING(path)->ptr) == -1)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
return INT2FIX(0);
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -346,6 +364,17 @@ dir_s_glob(dir, str)
return ary;
}
+static VALUE
+dir_foreach(io, dirname)
+ VALUE io;
+ struct RString *dirname;
+{
+ VALUE dir;
+
+ dir = dir_s_open(cDir, dirname);
+ return rb_ensure(dir_each, dir, dir_close, dir);
+}
+
void
Init_Dir()
{
@@ -356,6 +385,7 @@ Init_Dir()
rb_include_module(cDir, mEnumerable);
rb_define_singleton_method(cDir, "open", dir_s_open, 1);
+ rb_define_singleton_method(cDir, "foreach", dir_foreach, 1);
rb_define_method(cDir,"each", dir_each, 0);
rb_define_method(cDir,"rewind", dir_rewind, 0);
diff --git a/dln.c b/dln.c
index 84ffef3aac..b4005e973c 100644
--- a/dln.c
+++ b/dln.c
@@ -6,10 +6,14 @@
$Date: 1994/12/09 01:28:23 $
created at: Tue Jan 18 17:05:06 JST 1994
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
+#ifdef _AIX
+#pragma alloca
+#endif
+
#include "config.h"
#include "defines.h"
#include "dln.h"
@@ -57,7 +61,7 @@ int eaccess();
#endif
#ifndef FUNCNAME_PATTERN
-# if defined(hpux) || defined(__NetBSD__) || defined(__BORLANDC__)
+# if defined(__hp9000s300) || defined(__NetBSD__) || defined(__BORLANDC__) || defined(__FreeBSD__)
# define FUNCNAME_PATTERN "_Init_%.200s"
# else
# define FUNCNAME_PATTERN "Init_%.200s"
@@ -276,7 +280,7 @@ sym_hash(hdrp, syms)
struct nlist *sym = syms;
struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist));
- tbl = st_init_table(strcmp, st_strhash);
+ tbl = st_init_strtable();
if (tbl == NULL) {
dln_errno = errno;
return NULL;
@@ -345,12 +349,11 @@ dln_init(prog)
p++;
}
*p = '\0';
- printf("%s\n", buf);
return dln_init(buf);
}
dln_init_p = 1;
- undef_tbl = st_init_table(strcmp, st_strhash);
+ undef_tbl = st_init_strtable();
close(fd);
return 0;
@@ -462,7 +465,7 @@ link_undef(name, base, reloc)
break;
}
if (reloc_tbl == NULL) {
- reloc_tbl = st_init_table(ST_NUMCMP, ST_NUMHASH);
+ reloc_tbl = st_init_numtable();
}
st_insert(reloc_tbl, u_no++, obj);
}
@@ -548,6 +551,25 @@ unlink_undef(name, value)
st_foreach(reloc_tbl, reloc_undef, &arg);
}
+#ifdef N_INDR
+struct indr_data {
+ char *name0, *name1;
+};
+
+static int
+reloc_repl(no, undef, data)
+ int no;
+ struct undef *undef;
+ struct indr_data *data;
+{
+ if (strcmp(data->name0, undef->name) == 0) {
+ free(undef->name);
+ undef->name = strdup(data->name1);
+ }
+ return ST_CONTINUE;
+}
+#endif
+
static int
load_1(fd, disp, need_init)
int fd;
@@ -581,6 +603,32 @@ load_1(fd, disp, need_init)
struct nlist *old_sym;
int value = sym->n_value;
+#ifdef N_INDR
+ if (sym->n_type == (N_INDR | N_EXT)) {
+ char *key = sym->n_un.n_name;
+
+ if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) {
+ if (st_delete(undef_tbl, &key, NULL)) {
+ unlink_undef(key, old_sym->n_value);
+ free(key);
+ }
+ }
+ else {
+ struct indr_data data;
+
+ data.name0 = sym->n_un.n_name;
+ data.name1 = sym[1].n_un.n_name;
+ st_foreach(reloc_tbl, reloc_repl, &data);
+
+ st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL);
+ if (st_delete(undef_tbl, &key, NULL)) {
+ free(key);
+ }
+ }
+ sym += 2;
+ continue;
+ }
+#endif
if (sym->n_type == (N_UNDF | N_EXT)) {
if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) {
old_sym = NULL;
@@ -624,7 +672,7 @@ load_1(fd, disp, need_init)
sym = syms;
while (sym < end) {
struct nlist *new_sym;
- char *key;
+ char *key, *name;
switch (sym->n_type) {
case N_COMM:
@@ -764,7 +812,7 @@ load_1(fd, disp, need_init)
if (strcmp(name+1, "libs_to_be_linked") == 0) {
libs_to_be_linked = (char**)sym->n_value;
}
- if (strcmp(name+1, buf) == 0) {
+ else if (strcmp(name+1, buf) == 0) {
init_p = 1;
((int (*)())sym->n_value)();
}
@@ -808,6 +856,7 @@ search_undef(key, value, lib_tbl)
int value;
st_table *lib_tbl;
{
+#if 0
static char *last = "";
int offset;
@@ -817,6 +866,13 @@ search_undef(key, value, lib_tbl)
target_offset = offset;
}
return ST_STOP;
+#else
+ int offset;
+
+ if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE;
+ target_offset = offset;
+ return ST_STOP;
+#endif
}
struct symdef {
@@ -879,7 +935,7 @@ load_lib(lib)
if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) {
/* make hash table from __.SYMDEF */
- lib_tbl = st_init_table(strcmp, st_strhash);
+ lib_tbl = st_init_strtable();
data = (int*)xmalloc(size);
if (data == NULL) goto syserr;
size = read(fd, data, size);
@@ -1020,6 +1076,11 @@ dln_sym(name)
#include <ctype.h> /* for isdigit() */
#include <errno.h> /* for global errno */
#include <string.h> /* for strerror() */
+#include <sys/ldr.h>
+#endif
+
+#ifdef NeXT
+/*#include <mach-o/rld.h>*/
#endif
static char *
@@ -1095,8 +1156,8 @@ aix_loaderror(char *pathname)
ERRBUF_APPEND("\n");
}
errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */
- Fail(errbuf);
-return;
+ LoadError(errbuf);
+ return;
}
#endif
@@ -1125,9 +1186,12 @@ dln_load(file)
# ifndef RTLD_LAZY
# define RTLD_LAZY 1
# endif
+# ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+# endif
/* Load file */
- if ((handle = dlopen(file, RTLD_LAZY)) == NULL) {
+ if ((handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) {
goto failed;
}
@@ -1150,13 +1214,14 @@ dln_load(file)
flags = BIND_DEFERRED;
lib = shl_load(file, flags, 0);
if (lib == NULL) {
- char buf[256];
- Fail("Failed to load %.200s", file);
+ rb_sys_fail(file);
}
shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct);
if (init_fct == NULL) {
- shl_findsym(&lib, buf, TYPE_DATA, (void*)&init_fct);
+ shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct);
if (init_fct == NULL) {
+ extern int errno;
+ errno = ENOSYM;
rb_sys_fail(file);
}
}
@@ -1179,14 +1244,54 @@ dln_load(file)
}
#endif /* _AIX */
+#ifdef NeXT
+#define DLN_DEFINED
+/*----------------------------------------------------
+ By SHIROYAMA Takayuki Psi@fortune.nest.or.jp
+
+ Special Thanks...
+ Yu tomoak-i@is.aist-nara.ac.jp,
+ Mi hisho@tasihara.nest.or.jp,
+ and... Miss ARAI Akino(^^;)
+ ----------------------------------------------------*/
+ {
+ unsigned long init_address;
+ char *object_files[2] = {NULL, NULL};
+
+ void (*init_fct)();
+ int len = strlen(file);
+ char *point;
+ char init_name[len +7];
+
+ object_files[0] = file;
+
+ /* Load object file, if return value ==0 , load failed*/
+ if(rld_load(NULL, NULL, object_files, NULL) == 0) {
+ LoadError("Failed to load %.200s", file);
+ }
+
+ /* lookup the initial function */
+ if(rld_lookup(NULL, buf, &init_address) == 0) {
+ LoadError("Failed to lookup Init function %.200s",file);
+ }
+
+ /* Cannot call *init_address directory, so copy this value to
+ funtion pointer */
+
+ init_fct = (void(*)())init_address;
+ (*init_fct)();
+ return ;
+ }
+#endif
+
#ifndef DLN_DEFINED
- Fail("dynamic link not supported");
+ rb_notimplement("dynamic link not supported");
#endif
#endif /* USE_DLN_A_OUT */
-#ifndef _AIX
+#if !defined(_AIX) && !defined(NeXT)
failed:
- Fail("%s - %s", dln_strerror(), file);
+ LoadError("%s - %s", dln_strerror(), file);
#endif
}
@@ -1228,8 +1333,7 @@ dln_find_1(fname, path, exe_flag)
if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0)
return fname;
- for (dp = path;; dp = ++ep)
- {
+ for (dp = path;; dp = ++ep) {
register int l;
int i;
int fspace;
@@ -1243,8 +1347,7 @@ dln_find_1(fname, path, exe_flag)
l = ep - dp;
bp = fbuf;
fspace = sizeof fbuf - 2;
- if (l > 0)
- {
+ if (l > 0) {
/*
** If the length of the component is zero length,
** start from the current directory. If the
@@ -1253,13 +1356,11 @@ dln_find_1(fname, path, exe_flag)
** take the path literally.
*/
- if (*dp == '~' && (l == 1 || dp[1] == '/'))
- {
+ if (*dp == '~' && (l == 1 || dp[1] == '/')) {
char *home;
home = getenv("HOME");
- if (home != NULL)
- {
+ if (home != NULL) {
i = strlen(home);
if ((fspace -= i) < 0)
goto toolong;
@@ -1269,8 +1370,7 @@ dln_find_1(fname, path, exe_flag)
dp++;
l--;
}
- if (l > 0)
- {
+ if (l > 0) {
if ((fspace -= l) < 0)
goto toolong;
memcpy(bp, dp, l);
@@ -1284,9 +1384,8 @@ dln_find_1(fname, path, exe_flag)
/* now append the file name */
i = strlen(fname);
- if ((fspace -= i) < 0)
- {
- toolong:
+ if ((fspace -= i) < 0) {
+ toolong:
fprintf(stderr, "openpath: pathname too long (ignored)\n");
*bp = '\0';
fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
diff --git a/enum.c b/enum.c
index df277a504a..39c09dbec8 100644
--- a/enum.c
+++ b/enum.c
@@ -6,20 +6,19 @@
$Date: 1995/01/10 10:42:29 $
created at: Fri Oct 1 15:15:19 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#include "ruby.h"
VALUE mEnumerable;
-static ID id_each, id_match, id_cmp;
+static ID id_each, id_eqq, id_cmp;
void
rb_each(obj)
VALUE obj;
{
- if (!id_each) id_each = rb_intern("each");
rb_funcall(obj, id_each, 0, 0);
}
@@ -27,8 +26,7 @@ static void
grep_i(i, arg)
VALUE i, *arg;
{
- if (!id_match) id_match = rb_intern("=~");
- if (rb_funcall(arg[0], id_match, 1, i)) {
+ if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) {
ary_push(arg[1], i);
}
}
@@ -37,8 +35,7 @@ static void
grep_iter_i(i, pat)
VALUE i, pat;
{
- if (!id_match) id_match = rb_intern("=~");
- if (rb_funcall(pat, id_match, 1, i)) {
+ if (RTEST(rb_funcall(pat, id_eqq, 1, i))) {
rb_yield(i);
}
}
@@ -61,33 +58,47 @@ enum_grep(obj, pat)
}
}
+struct find_arg {
+ int found;
+ VALUE val;
+};
+
static void
-find_i(i, foundp)
+find_i(i, arg)
VALUE i;
- int *foundp;
+ struct find_arg *arg;
{
- if (rb_yield(i)) {
- *foundp = TRUE;
+ if (RTEST(rb_yield(i))) {
+ arg->found = TRUE;
+ arg->val = i;
rb_break();
}
}
static VALUE
-enum_find(obj)
+enum_find(argc, argv, obj)
+ int argc;
+ VALUE argv;
VALUE obj;
{
- int enum_found;
-
- enum_found = FALSE;
- rb_iterate(rb_each, obj, find_i, &enum_found);
- return enum_found;
+ struct find_arg arg;
+ VALUE if_none;
+
+ rb_scan_args(argc, argv, "01", &if_none);
+ arg.found = FALSE;
+ rb_iterate(rb_each, obj, find_i, &arg);
+ if (arg.found) {
+ return arg.val;
+ }
+ if (NIL_P(if_none)) return Qnil;
+ return rb_eval_cmd(if_none, Qnil);
}
static void
find_all_i(i, tmp)
VALUE i, tmp;
{
- if (rb_yield(i)) {
+ if (RTEST(rb_yield(i))) {
ary_push(tmp, i);
}
}
@@ -99,7 +110,7 @@ enum_find_all(obj)
VALUE tmp;
tmp = ary_new();
- rb_iterate(rb_each, obj, find_all_i, 0);
+ rb_iterate(rb_each, obj, find_all_i, tmp);
return tmp;
}
@@ -111,7 +122,7 @@ collect_i(i, tmp)
VALUE retval;
retval = rb_yield(i);
- if (retval) {
+ if (RTEST(retval)) {
ary_push(tmp, retval);
}
}
@@ -179,23 +190,37 @@ min_i(i, min)
{
VALUE cmp;
- if (*min == Qnil)
+ if (NIL_P(*min))
*min = i;
else {
- if (!id_cmp) id_cmp = rb_intern("<=>");
cmp = rb_funcall(i, id_cmp, 1, *min);
if (FIX2INT(cmp) < 0)
*min = i;
}
}
+static void
+min_ii(i, min)
+ VALUE i, *min;
+{
+ VALUE cmp;
+
+ if (NIL_P(*min))
+ *min = i;
+ else {
+ cmp = rb_yield(assoc_new(i, *min));
+ if (FIX2INT(cmp) < 0)
+ *min = i;
+ }
+}
+
static VALUE
enum_min(obj)
VALUE obj;
{
VALUE min = Qnil;
- rb_iterate(rb_each, obj, min_i, &min);
+ rb_iterate(rb_each, obj, iterator_p()?min_ii:min_i, &min);
return min;
}
@@ -205,23 +230,37 @@ max_i(i, max)
{
VALUE cmp;
- if (*max == Qnil)
+ if (NIL_P(*max))
*max = i;
else {
- if (!id_cmp) id_cmp = rb_intern("<=>");
cmp = rb_funcall(i, id_cmp, 1, *max);
if (FIX2INT(cmp) > 0)
*max = i;
}
}
+static void
+max_ii(i, max)
+ VALUE i, *max;
+{
+ VALUE cmp;
+
+ if (NIL_P(*max))
+ *max = i;
+ else {
+ cmp = rb_yield(assoc_new(i, *max));
+ if (FIX2INT(cmp) > 0)
+ *max = i;
+ }
+}
+
static VALUE
enum_max(obj)
VALUE obj;
{
VALUE max = Qnil;
- rb_iterate(rb_each, obj, max_i, &max);
+ rb_iterate(rb_each, obj, iterator_p()?max_ii:max_i, &max);
return max;
}
@@ -291,7 +330,7 @@ length_i(i, length)
(*length)++;
}
-static VALUE
+VALUE
enum_length(obj)
VALUE obj;
{
@@ -310,7 +349,7 @@ Init_Enumerable()
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", enum_find, -1);
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);
@@ -319,4 +358,9 @@ Init_Enumerable()
rb_define_method(mEnumerable,"index", enum_index, 1);
rb_define_method(mEnumerable,"member?", enum_member, 1);
rb_define_method(mEnumerable,"length", enum_length, 0);
+ rb_define_method(mEnumerable,"size", enum_length, 0);
+
+ id_eqq = rb_intern("===");
+ id_each = rb_intern("each");
+ id_cmp = rb_intern("<=>");
}
diff --git a/env.h b/env.h
index 5f31e5cebc..ff53def099 100644
--- a/env.h
+++ b/env.h
@@ -16,9 +16,11 @@ extern struct FRAME {
VALUE *argv;
ID last_func;
struct RClass *last_class;
+ VALUE cbase;
struct FRAME *prev;
char *file;
int line;
+ int iter;
} *the_frame;
extern struct SCOPE {
@@ -28,8 +30,9 @@ extern struct SCOPE {
int flag;
} *the_scope;
-#define SCOPE_ALLOCA 0
-#define SCOPE_MALLOC 1
+#define SCOPE_ALLOCA 0
+#define SCOPE_MALLOC 1
+#define SCOPE_NOSTACK 2
extern int rb_in_eval;
diff --git a/error.c b/error.c
index 6634663eb1..61d5468c0d 100644
--- a/error.c
+++ b/error.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:31 $
created at: Mon Aug 9 16:11:34 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -32,32 +32,41 @@ err_sprintf(buf, fmt, args)
sprintf(buf, "%s:%d: ", sourcefile, sourceline);
vsprintf((char*)buf+strlen(buf), fmt, args);
}
- if (buf[strlen(buf)-1] != '\n')
- strcat(buf, "\n");
}
static void
-err_print(fmt, args)
- char *fmt;
- va_list args;
+err_append(s)
+ char *s;
{
- extern errstr;
- char buf[BUFSIZ];
+ extern VALUE errinfo;
- err_sprintf(buf, fmt, args);
if (rb_in_eval) {
- if (errstr == Qnil) {
- errstr = str_new2(buf);
+ if (NIL_P(errinfo)) {
+ errinfo = str_new2(s);
}
else {
- str_cat(errstr, buf, strlen(buf));
+ str_cat(errinfo, "\n", 1);
+ str_cat(errinfo, s, strlen(s));
}
}
else {
- fputs(buf, stderr);
+ fputs(s, stderr);
+ fputs("\n", stderr);
+ fflush(stderr);
}
}
+static void
+err_print(fmt, args)
+ char *fmt;
+ va_list args;
+{
+ char buf[BUFSIZ];
+
+ err_sprintf(buf, fmt, args);
+ err_append(buf);
+}
+
void
Error(fmt, va_alist)
char *fmt;
@@ -71,18 +80,18 @@ Error(fmt, va_alist)
nerrs++;
}
-int
-yyerror(msg)
- char *msg;
+void
+Error_Append(fmt, va_alist)
+ char *fmt;
+ va_dcl
{
- static char *f;
- static int line;
+ va_list args;
+ char buf[BUFSIZ];
- if (line == sourceline && strcmp(f, sourcefile) == 0)
- return;
- f = sourcefile; line = sourceline;
- Error("%s", msg);
- return 0;
+ va_start(args);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ err_append(buf);
}
void
@@ -103,32 +112,226 @@ Warning(fmt, va_alist)
}
void
-Fatal(fmt, va_alist)
+Bug(fmt, va_alist)
char *fmt;
va_dcl
{
+ char buf[BUFSIZ];
va_list args;
+ sprintf(buf, "[BUG] %s", fmt);
+ rb_in_eval = 0;
+
va_start(args);
- err_print(fmt, args);
+ err_print(buf, args);
va_end(args);
- rb_exit(1);
+ abort();
}
+static struct types {
+ int type;
+ char *name;
+} builtin_types[] = {
+ T_NIL, "nil",
+ T_OBJECT, "Object",
+ T_CLASS, "Class",
+ T_ICLASS, "iClass", /* internal use: mixed-in module holder */
+ T_MODULE, "Module",
+ T_FLOAT, "Float",
+ T_STRING, "String",
+ T_REGEXP, "Regexp",
+ T_ARRAY, "Array",
+ T_FIXNUM, "Fixnum",
+ T_HASH, "Hash",
+ T_STRUCT, "Struct",
+ T_BIGNUM, "Bignum",
+ T_FILE, "File",
+ T_TRUE, "TRUE",
+ T_FALSE, "FALSE",
+ T_DATA, "Data", /* internal use: wrapped C pointers */
+ T_MATCH, "Match", /* data of $~ */
+ T_VARMAP, "Varmap", /* internal use: dynamic variables */
+ T_SCOPE, "Scope", /* internal use: variable scope */
+ T_NODE, "Node", /* internal use: syntax tree node */
+ -1, 0,
+};
+
+extern void TypeError();
+
void
-Bug(fmt, va_alist)
+Check_Type(x, t)
+ VALUE x;
+ int t;
+{
+ struct types *type = builtin_types;
+
+ if (TYPE(x)!=(t)) {
+ while (type->type >= 0) {
+ if (type->type == t) {
+ TypeError("wrong argument type %s (expected %s)",
+ rb_class2name(CLASS_OF(x)), type->name);
+ }
+ type++;
+ }
+ Bug("unknown type 0x%x", t);
+ }
+}
+
+/* exception classes */
+#include "errno.h"
+
+extern VALUE cString;
+VALUE eGlobalExit, eException;
+VALUE eSystemExit, eInterrupt, eFatal;
+VALUE eRuntimeError;
+VALUE eSyntaxError;
+VALUE eTypeError;
+VALUE eArgError;
+VALUE eNameError;
+VALUE eIndexError;
+VALUE eNotImpError;
+VALUE eLoadError;
+
+VALUE eSystemCallError;
+VALUE mErrno;
+
+VALUE
+exc_new0(etype, ptr, len)
+ VALUE etype;
+ char *ptr;
+ UINT len;
+{
+ NEWOBJ(exc, struct RString);
+ OBJSETUP(exc, etype, T_STRING);
+
+ exc->len = len;
+ exc->orig = 0;
+ exc->ptr = ALLOC_N(char,len+1);
+ if (ptr) {
+ memcpy(exc->ptr, ptr, len);
+ }
+ exc->ptr[len] = '\0';
+ return (VALUE)exc;
+}
+
+VALUE
+exc_new(etype, s)
+ VALUE etype;
+ char *s;
+{
+ return exc_new0(etype, s, strlen(s));
+}
+
+VALUE
+exc_new2(etype, str)
+ VALUE etype;
+ struct RString *str;
+{
+ Check_Type(str, T_STRING);
+ return exc_new(etype, str->ptr, str->len);
+}
+
+static VALUE
+exc_s_new(argc, argv, etype)
+ int argc;
+ VALUE *argv;
+ VALUE etype;
+{
+ VALUE arg;
+
+ if (rb_scan_args(argc, argv, "01", &arg) == 0) {
+ return exc_new0(etype, 0, 0);
+ }
+ Check_Type(arg, T_STRING);
+ return exc_new2(etype, arg);
+}
+
+static VALUE *syserr_list;
+
+static void
+set_syserr(i, name)
+ int i;
+ char *name;
+{
+ syserr_list[i] = rb_define_class_under(mErrno, name, eSystemCallError);
+ rb_global_variable(&syserr_list[i]);
+}
+
+static void init_syserr();
+
+void
+Init_Exception()
+{
+ eGlobalExit = rb_define_class("GlobalExit", cString);
+ rb_define_method(eGlobalExit, "new", exc_s_new, -1);
+
+ eSystemExit = rb_define_class("SystemExit", eGlobalExit);
+ eFatal = rb_define_class("fatal", eGlobalExit);
+ eInterrupt = rb_define_class("Interrupt", eGlobalExit);
+
+ eException = rb_define_class("Exception", eGlobalExit);
+ eSyntaxError = rb_define_class("SyntaxError", eException);
+ eTypeError = rb_define_class("TypeError", eException);
+ eArgError = rb_define_class("ArgumentError", eException);
+ eNameError = rb_define_class("NameError", eException);
+ eIndexError = rb_define_class("IndexError", eException);
+ eNotImpError = rb_define_class("NotImplementError", eException);
+ eLoadError = rb_define_class("LoadError", eException);
+
+ eRuntimeError = rb_define_class("RuntimeError", eException);
+
+ init_syserr();
+}
+
+#define RAISE_ERROR(class) {\
+ va_list args;\
+ char buf[BUFSIZ];\
+\
+ va_start(args);\
+ vsprintf(buf, fmt, args);\
+ va_end(args);\
+\
+ rb_raise(exc_new(class, buf));\
+}
+
+void
+Raise(exc, fmt, va_alist)
char *fmt;
va_dcl
{
- char buf[BUFSIZ];
- va_list args;
+ RAISE_ERROR(exc);
+}
- sprintf(buf, "[BUG] %s", fmt);
+void
+TypeError(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ RAISE_ERROR(eTypeError);
+}
- va_start(args);
- err_print(buf, args);
- va_end(args);
- abort();
+void
+ArgError(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ RAISE_ERROR(eArgError);
+}
+
+void
+NameError(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ RAISE_ERROR(eNameError);
+}
+
+void
+IndexError(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ RAISE_ERROR(eIndexError);
}
void
@@ -136,6 +339,30 @@ Fail(fmt, va_alist)
char *fmt;
va_dcl
{
+ RAISE_ERROR(eRuntimeError);
+}
+
+void
+rb_notimplement()
+{
+ Raise(eNotImpError,
+ "The %s() function is unimplemented on this machine",
+ rb_id2name(the_frame->last_func));
+}
+
+void
+LoadError(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ RAISE_ERROR(eLoadError);
+}
+
+void
+Fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
va_list args;
char buf[BUFSIZ];
@@ -143,7 +370,8 @@ Fail(fmt, va_alist)
vsprintf(buf, fmt, args);
va_end(args);
- rb_fail(str_new2(buf));
+ rb_in_eval = 0;
+ rb_fatal(exc_new(eFatal, buf));
}
void
@@ -153,39 +381,397 @@ rb_sys_fail(mesg)
char *strerror();
char buf[BUFSIZ];
extern int errno;
+ int n = errno;
- if (mesg == Qnil)
- sprintf(buf, "%s", strerror(errno));
- else
+ if (RTEST(mesg))
sprintf(buf, "%s - %s", strerror(errno), mesg);
+ else
+ sprintf(buf, "%s", strerror(errno));
errno = 0;
- rb_fail(str_new2(buf));
-}
-
-static char *builtin_types[] = {
- "Nil",
- "Object",
- "Class",
- "iClass",
- "Module",
- "Float",
- "String",
- "Regexp",
- "Array",
- "Fixnum",
- "Hash",
- "Struct",
- "Bignum",
- "Data",
- "Match",
-};
+ if (!syserr_list[n]) {
+ char name[6];
-void
-WrongType(x, t)
- VALUE x;
- int t;
+ sprintf(name, "E%03d", n);
+ set_syserr(n, name);
+ }
+ rb_raise(exc_new(syserr_list[n], buf));
+}
+
+extern int sys_nerr;
+
+static void
+init_syserr()
{
- Fail("wrong argument type %s (expected %s)",
- rb_class2name(CLASS_OF(x)), builtin_types[t]);
+ eSystemCallError = rb_define_class("SystemCallError", eException);
+ mErrno = rb_define_module("Errno");
+ syserr_list = ALLOC_N(VALUE, sys_nerr+1);
+ MEMZERO(syserr_list, VALUE, sys_nerr+1);
+
+#ifdef EPERM
+ set_syserr(EPERM, "EPERM");
+#endif
+#ifdef ENOENT
+ set_syserr(ENOENT, "ENOENT");
+#endif
+#ifdef ESRCH
+ set_syserr(ESRCH, "ESRCH");
+#endif
+#ifdef EINTR
+ set_syserr(EINTR, "EINTR");
+#endif
+#ifdef EIO
+ set_syserr(EIO, "EIO");
+#endif
+#ifdef ENXIO
+ set_syserr(ENXIO, "ENXIO");
+#endif
+#ifdef E2BIG
+ set_syserr(E2BIG, "E2BIG");
+#endif
+#ifdef ENOEXEC
+ set_syserr(ENOEXEC, "ENOEXEC");
+#endif
+#ifdef EBADF
+ set_syserr(EBADF, "EBADF");
+#endif
+#ifdef ECHILD
+ set_syserr(ECHILD, "ECHILD");
+#endif
+#ifdef EAGAIN
+ set_syserr(EAGAIN, "EAGAIN");
+#endif
+#ifdef ENOMEM
+ set_syserr(ENOMEM, "ENOMEM");
+#endif
+#ifdef EACCES
+ set_syserr(EACCES, "EACCES");
+#endif
+#ifdef EFAULT
+ set_syserr(EFAULT, "EFAULT");
+#endif
+#ifdef ENOTBLK
+ set_syserr(ENOTBLK, "ENOTBLK");
+#endif
+#ifdef EBUSY
+ set_syserr(EBUSY, "EBUSY");
+#endif
+#ifdef EEXIST
+ set_syserr(EEXIST, "EEXIST");
+#endif
+#ifdef EXDEV
+ set_syserr(EXDEV, "EXDEV");
+#endif
+#ifdef ENODEV
+ set_syserr(ENODEV, "ENODEV");
+#endif
+#ifdef ENOTDIR
+ set_syserr(ENOTDIR, "ENOTDIR");
+#endif
+#ifdef EISDIR
+ set_syserr(EISDIR, "EISDIR");
+#endif
+#ifdef EINVAL
+ set_syserr(EINVAL, "EINVAL");
+#endif
+#ifdef ENFILE
+ set_syserr(ENFILE, "ENFILE");
+#endif
+#ifdef EMFILE
+ set_syserr(EMFILE, "EMFILE");
+#endif
+#ifdef ENOTTY
+ set_syserr(ENOTTY, "ENOTTY");
+#endif
+#ifdef ETXTBSY
+ set_syserr(ETXTBSY, "ETXTBSY");
+#endif
+#ifdef EFBIG
+ set_syserr(EFBIG, "EFBIG");
+#endif
+#ifdef ENOSPC
+ set_syserr(ENOSPC, "ENOSPC");
+#endif
+#ifdef ESPIPE
+ set_syserr(ESPIPE, "ESPIPE");
+#endif
+#ifdef EROFS
+ set_syserr(EROFS, "EROFS");
+#endif
+#ifdef EMLINK
+ set_syserr(EMLINK, "EMLINK");
+#endif
+#ifdef EPIPE
+ set_syserr(EPIPE, "EPIPE");
+#endif
+#ifdef EDOM
+ set_syserr(EDOM, "EDOM");
+#endif
+#ifdef ERANGE
+ set_syserr(ERANGE, "ERANGE");
+#endif
+#ifdef EDEADLK
+ set_syserr(EDEADLK, "EDEADLK");
+#endif
+#ifdef ENAMETOOLONG
+ set_syserr(ENAMETOOLONG, "ENAMETOOLONG");
+#endif
+#ifdef ENOLCK
+ set_syserr(ENOLCK, "ENOLCK");
+#endif
+#ifdef ENOSYS
+ set_syserr(ENOSYS, "ENOSYS");
+#endif
+#ifdef ENOTEMPTY
+ set_syserr(ENOTEMPTY, "ENOTEMPTY");
+#endif
+#ifdef ELOOP
+ set_syserr(ELOOP, "ELOOP");
+#endif
+#ifdef EWOULDBLOCK
+ set_syserr(EWOULDBLOCK, "EWOULDBLOCK");
+#endif
+#ifdef ENOMSG
+ set_syserr(ENOMSG, "ENOMSG");
+#endif
+#ifdef EIDRM
+ set_syserr(EIDRM, "EIDRM");
+#endif
+#ifdef ECHRNG
+ set_syserr(ECHRNG, "ECHRNG");
+#endif
+#ifdef EL2NSYNC
+ set_syserr(EL2NSYNC, "EL2NSYNC");
+#endif
+#ifdef EL3HLT
+ set_syserr(EL3HLT, "EL3HLT");
+#endif
+#ifdef EL3RST
+ set_syserr(EL3RST, "EL3RST");
+#endif
+#ifdef ELNRNG
+ set_syserr(ELNRNG, "ELNRNG");
+#endif
+#ifdef EUNATCH
+ set_syserr(EUNATCH, "EUNATCH");
+#endif
+#ifdef ENOCSI
+ set_syserr(ENOCSI, "ENOCSI");
+#endif
+#ifdef EL2HLT
+ set_syserr(EL2HLT, "EL2HLT");
+#endif
+#ifdef EBADE
+ set_syserr(EBADE, "EBADE");
+#endif
+#ifdef EBADR
+ set_syserr(EBADR, "EBADR");
+#endif
+#ifdef EXFULL
+ set_syserr(EXFULL, "EXFULL");
+#endif
+#ifdef ENOANO
+ set_syserr(ENOANO, "ENOANO");
+#endif
+#ifdef EBADRQC
+ set_syserr(EBADRQC, "EBADRQC");
+#endif
+#ifdef EBADSLT
+ set_syserr(EBADSLT, "EBADSLT");
+#endif
+#ifdef EDEADLOCK
+ set_syserr(EDEADLOCK, "EDEADLOCK");
+#endif
+#ifdef EBFONT
+ set_syserr(EBFONT, "EBFONT");
+#endif
+#ifdef ENOSTR
+ set_syserr(ENOSTR, "ENOSTR");
+#endif
+#ifdef ENODATA
+ set_syserr(ENODATA, "ENODATA");
+#endif
+#ifdef ETIME
+ set_syserr(ETIME, "ETIME");
+#endif
+#ifdef ENOSR
+ set_syserr(ENOSR, "ENOSR");
+#endif
+#ifdef ENONET
+ set_syserr(ENONET, "ENONET");
+#endif
+#ifdef ENOPKG
+ set_syserr(ENOPKG, "ENOPKG");
+#endif
+#ifdef EREMOTE
+ set_syserr(EREMOTE, "EREMOTE");
+#endif
+#ifdef ENOLINK
+ set_syserr(ENOLINK, "ENOLINK");
+#endif
+#ifdef EADV
+ set_syserr(EADV, "EADV");
+#endif
+#ifdef ESRMNT
+ set_syserr(ESRMNT, "ESRMNT");
+#endif
+#ifdef ECOMM
+ set_syserr(ECOMM, "ECOMM");
+#endif
+#ifdef EPROTO
+ set_syserr(EPROTO, "EPROTO");
+#endif
+#ifdef EMULTIHOP
+ set_syserr(EMULTIHOP, "EMULTIHOP");
+#endif
+#ifdef EDOTDOT
+ set_syserr(EDOTDOT, "EDOTDOT");
+#endif
+#ifdef EBADMSG
+ set_syserr(EBADMSG, "EBADMSG");
+#endif
+#ifdef EOVERFLOW
+ set_syserr(EOVERFLOW, "EOVERFLOW");
+#endif
+#ifdef ENOTUNIQ
+ set_syserr(ENOTUNIQ, "ENOTUNIQ");
+#endif
+#ifdef EBADFD
+ set_syserr(EBADFD, "EBADFD");
+#endif
+#ifdef EREMCHG
+ set_syserr(EREMCHG, "EREMCHG");
+#endif
+#ifdef ELIBACC
+ set_syserr(ELIBACC, "ELIBACC");
+#endif
+#ifdef ELIBBAD
+ set_syserr(ELIBBAD, "ELIBBAD");
+#endif
+#ifdef ELIBSCN
+ set_syserr(ELIBSCN, "ELIBSCN");
+#endif
+#ifdef ELIBMAX
+ set_syserr(ELIBMAX, "ELIBMAX");
+#endif
+#ifdef ELIBEXEC
+ set_syserr(ELIBEXEC, "ELIBEXEC");
+#endif
+#ifdef EILSEQ
+ set_syserr(EILSEQ, "EILSEQ");
+#endif
+#ifdef ERESTART
+ set_syserr(ERESTART, "ERESTART");
+#endif
+#ifdef ESTRPIPE
+ set_syserr(ESTRPIPE, "ESTRPIPE");
+#endif
+#ifdef EUSERS
+ set_syserr(EUSERS, "EUSERS");
+#endif
+#ifdef ENOTSOCK
+ set_syserr(ENOTSOCK, "ENOTSOCK");
+#endif
+#ifdef EDESTADDRREQ
+ set_syserr(EDESTADDRREQ, "EDESTADDRREQ");
+#endif
+#ifdef EMSGSIZE
+ set_syserr(EMSGSIZE, "EMSGSIZE");
+#endif
+#ifdef EPROTOTYPE
+ set_syserr(EPROTOTYPE, "EPROTOTYPE");
+#endif
+#ifdef ENOPROTOOPT
+ set_syserr(ENOPROTOOPT, "ENOPROTOOPT");
+#endif
+#ifdef EPROTONOSUPPORT
+ set_syserr(EPROTONOSUPPORT, "EPROTONOSUPPORT");
+#endif
+#ifdef ESOCKTNOSUPPORT
+ set_syserr(ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT");
+#endif
+#ifdef EOPNOTSUPP
+ set_syserr(EOPNOTSUPP, "EOPNOTSUPP");
+#endif
+#ifdef EPFNOSUPPORT
+ set_syserr(EPFNOSUPPORT, "EPFNOSUPPORT");
+#endif
+#ifdef EAFNOSUPPORT
+ set_syserr(EAFNOSUPPORT, "EAFNOSUPPORT");
+#endif
+#ifdef EADDRINUSE
+ set_syserr(EADDRINUSE, "EADDRINUSE");
+#endif
+#ifdef EADDRNOTAVAIL
+ set_syserr(EADDRNOTAVAIL, "EADDRNOTAVAIL");
+#endif
+#ifdef ENETDOWN
+ set_syserr(ENETDOWN, "ENETDOWN");
+#endif
+#ifdef ENETUNREACH
+ set_syserr(ENETUNREACH, "ENETUNREACH");
+#endif
+#ifdef ENETRESET
+ set_syserr(ENETRESET, "ENETRESET");
+#endif
+#ifdef ECONNABORTED
+ set_syserr(ECONNABORTED, "ECONNABORTED");
+#endif
+#ifdef ECONNRESET
+ set_syserr(ECONNRESET, "ECONNRESET");
+#endif
+#ifdef ENOBUFS
+ set_syserr(ENOBUFS, "ENOBUFS");
+#endif
+#ifdef EISCONN
+ set_syserr(EISCONN, "EISCONN");
+#endif
+#ifdef ENOTCONN
+ set_syserr(ENOTCONN, "ENOTCONN");
+#endif
+#ifdef ESHUTDOWN
+ set_syserr(ESHUTDOWN, "ESHUTDOWN");
+#endif
+#ifdef ETOOMANYREFS
+ set_syserr(ETOOMANYREFS, "ETOOMANYREFS");
+#endif
+#ifdef ETIMEDOUT
+ set_syserr(ETIMEDOUT, "ETIMEDOUT");
+#endif
+#ifdef ECONNREFUSED
+ set_syserr(ECONNREFUSED, "ECONNREFUSED");
+#endif
+#ifdef EHOSTDOWN
+ set_syserr(EHOSTDOWN, "EHOSTDOWN");
+#endif
+#ifdef EHOSTUNREACH
+ set_syserr(EHOSTUNREACH, "EHOSTUNREACH");
+#endif
+#ifdef EALREADY
+ set_syserr(EALREADY, "EALREADY");
+#endif
+#ifdef EINPROGRESS
+ set_syserr(EINPROGRESS, "EINPROGRESS");
+#endif
+#ifdef ESTALE
+ set_syserr(ESTALE, "ESTALE");
+#endif
+#ifdef EUCLEAN
+ set_syserr(EUCLEAN, "EUCLEAN");
+#endif
+#ifdef ENOTNAM
+ set_syserr(ENOTNAM, "ENOTNAM");
+#endif
+#ifdef ENAVAIL
+ set_syserr(ENAVAIL, "ENAVAIL");
+#endif
+#ifdef EISNAM
+ set_syserr(EISNAM, "EISNAM");
+#endif
+#ifdef EREMOTEIO
+ set_syserr(EREMOTEIO, "EREMOTEIO");
+#endif
+#ifdef EDQUOT
+ set_syserr(EDQUOT, "EDQUOT");
+#endif
}
diff --git a/eval.c b/eval.c
index c1363a6840..43a4bf07f6 100644
--- a/eval.c
+++ b/eval.c
@@ -11,8 +11,8 @@
************************************************/
#include "ruby.h"
-#include "env.h"
#include "node.h"
+#include "env.h"
#include "sig.h"
#include <stdio.h>
@@ -23,30 +23,21 @@
#ifdef HAVE_STRING_H
# include <string.h>
#else
-char *strchr();
char *strrchr();
#endif
-VALUE cProc;
+static VALUE cProc;
static VALUE proc_call();
-static void rb_clear_cache_body();
-static void rb_clear_cache_entry();
-
-/* #define TEST /* prints cache miss */
-#ifdef TEST
-#include <stdio.h>
-#endif
-
#define CACHE_SIZE 0x200
#define CACHE_MASK 0x1ff
#define EXPR1(c,m) ((((int)(c)>>3)^(m))&CACHE_MASK)
struct cache_entry { /* method hash table. */
ID mid; /* method's id */
+ ID mid0; /* method's original id */
struct RClass *class; /* receiver's class */
struct RClass *origin; /* where method defined */
- int nargs; /* # of args */
NODE *method;
int noex;
};
@@ -62,14 +53,7 @@ rb_add_method(class, mid, node, noex)
{
NODE *body;
- 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);
- }
- else {
- rb_clear_cache_entry(class, mid);
- }
+ if (NIL_P(class)) class = (struct RClass*)cObject;
body = NEW_METHOD(node, noex);
st_insert(class->m_tbl, mid, body);
}
@@ -83,7 +67,7 @@ search_method(class, id, origin)
while (!st_lookup(class->m_tbl, id, &body)) {
class = class->super;
- if (class == Qnil) return Qnil;
+ if (!class) return 0;
}
if (origin) *origin = class;
@@ -102,29 +86,25 @@ rb_get_method_body(classp, idp, noexp)
struct RClass *origin;
struct cache_entry *ent;
- if ((body = search_method(class, id, &origin)) == FALSE) {
- return Qnil;
+ if ((body = search_method(class, id, &origin)) == 0) {
+ return 0;
}
- if (body->nd_body == Qnil) return Qnil;
+ if (!body->nd_body) return 0;
- ent = cache + EXPR1(class, id);
-#ifdef TEST
- if (ent->mid != 0) {
- fprintf(stderr, "0x%x 0x%x %x\n", class, id, EXPR1(class, id));
- }
-#endif
/* store in cache */
+ ent = cache + EXPR1(class, id);
ent->class = class;
ent->noex = body->nd_noex;
body = body->nd_body;
if (nd_type(body) == NODE_FBODY) {
+ ent->mid = id;
*classp = ent->origin = (struct RClass*)body->nd_orig;
- *idp = ent->mid = body->nd_mid;
+ *idp = ent->mid0 = body->nd_mid;
body = ent->method = body->nd_head;
}
else {
*classp = ent->origin = origin;
- ent->mid = id;
+ ent->mid = ent->mid0 = id;
ent->method = body;
}
@@ -142,17 +122,9 @@ rb_alias(class, name, def)
if (name == def) return;
body = search_method(class, def, &origin);
- if (body == Qnil || body->nd_body == Qnil) {
- Fail("undefined method `%s' for class `%s'",
- rb_id2name(def), rb_class2name(class));
- }
-
- if (st_lookup(class->m_tbl, name, &old)) {
- Warning("redefine %s", rb_id2name(name));
- rb_clear_cache_body(body);
- }
- else {
- rb_clear_cache_entry(class, name);
+ if (!body || !body->nd_body) {
+ NameError("undefined method `%s' for class `%s'",
+ rb_id2name(def), rb_class2name(class));
}
st_insert(class->m_tbl, name,
@@ -170,9 +142,9 @@ rb_export_method(class, name, noex)
struct RClass *origin;
body = search_method(class, name, &origin);
- if (body == Qnil) {
- Fail("undefined method `%s' for class `%s'",
- rb_id2name(name), rb_class2name(class));
+ if (!body) {
+ NameError("undefined method `%s' for class `%s'",
+ rb_id2name(name), rb_class2name(class));
}
if (body->nd_noex != noex) {
if (class == origin) {
@@ -200,85 +172,56 @@ method_boundp(class, id, ex)
return FALSE;
}
-VALUE
+int
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;
-{
- struct cache_entry *ent, *end;
-
- ent = cache; end = ent + CACHE_SIZE;
- while (ent < end) {
- if (ent->method == body) {
- ent->class = Qnil;
- ent->mid = Qnil;
- }
- ent++;
- }
-}
-
-static void
-rb_clear_cache_entry(class, mid)
- struct RClass *class;
- ID mid;
-{
- struct cache_entry *ent;
-
- /* is it in the method cache? */
- ent = cache + EXPR1(class, mid);
- if (ent->mid == mid && ent->class == class) {
- ent->class = Qnil;
- ent->mid = Qnil;
- }
+ if (method_boundp(class, id, NOEX_PRIVATE))
+ return TRUE;
+ return FALSE;
}
void
-rb_clear_cache(class)
- struct RClass *class;
+rb_clear_cache()
{
struct cache_entry *ent, *end;
ent = cache; end = ent + CACHE_SIZE;
while (ent < end) {
- if (ent->origin == class) {
- ent->class = Qnil;
- ent->mid = Qnil;
- }
+ ent->mid = 0;
ent++;
}
}
-static ID match, each, aref, aset;
-VALUE errstr, errat;
+static ID init, eqq, each, aref, aset;
+VALUE errinfo = Qnil, errat = Qnil;
extern NODE *eval_tree;
extern int nerrs;
-extern VALUE TopSelf;
-VALUE Qself;
-
-#define PUSH_SELF(s) { \
- VALUE __saved_self__ = Qself; \
- Qself = s; \
+extern VALUE cKernel;
+extern VALUE eGlobalExit;
+extern VALUE eInterrupt;
+extern VALUE eSystemExit;
+extern VALUE eException;
+extern VALUE eRuntimeError;
+extern VALUE eSyntaxError;
+static VALUE eLocalJumpError;
-#define POP_SELF() Qself = __saved_self__; }
+extern VALUE TopSelf;
struct FRAME *the_frame;
struct SCOPE *the_scope;
static struct FRAME *top_frame;
-static struct SCOPE *top_scope;
+static struct SCOPE *top_scope;
#define PUSH_FRAME() { \
struct FRAME _frame; \
_frame.prev = the_frame; \
_frame.file = sourcefile; \
_frame.line = sourceline; \
+ _frame.iter = the_iter->iter; \
+ _frame.cbase = the_frame->cbase; \
the_frame = &_frame; \
#define POP_FRAME() the_frame = _frame.prev; }
@@ -289,6 +232,7 @@ struct BLOCK {
VALUE self;
struct FRAME frame;
struct SCOPE *scope;
+ struct RClass *class;
int level;
int iter;
struct RVarmap *d_vars;
@@ -297,24 +241,29 @@ struct BLOCK {
#define PUSH_BLOCK(v,b) { \
struct BLOCK _block; \
- _block.level = tag_level; \
+ _block.level = (int)&prot_tag; \
_block.var = v; \
_block.body = b; \
- _block.self = Qself; \
+ _block.self = self; \
_block.frame = *the_frame; \
+ _block.class = the_class; \
_block.frame.file = sourcefile; \
_block.frame.line = sourceline; \
_block.scope = the_scope; \
_block.d_vars = the_dyna_vars; \
_block.prev = the_block; \
- _block.iter = iter->iter; \
+ _block.iter = the_iter->iter; \
the_block = &_block; \
#define PUSH_BLOCK2(b) { \
- b->prev = the_block; \
- the_block = b; \
-
-#define POP_BLOCK() the_block = the_block->prev; }
+ struct BLOCK _block; \
+ _block = *b; \
+ _block.prev = the_block; \
+ the_block = &_block;
+
+#define POP_BLOCK() \
+ the_block = the_block->prev; \
+}
struct RVarmap *the_dyna_vars;
#define PUSH_VARS() { \
@@ -324,13 +273,28 @@ struct RVarmap *the_dyna_vars;
#define POP_VARS() the_dyna_vars = _old; }
VALUE
+dyna_var_defined(id)
+ ID id;
+{
+ struct RVarmap *vars = the_dyna_vars;
+
+ while (vars && vars->id) {
+ if (vars->id == id) return TRUE;
+ vars = vars->next;
+ }
+ return FALSE;
+}
+
+VALUE
dyna_var_ref(id)
ID id;
{
struct RVarmap *vars = the_dyna_vars;
- while (vars) {
- if (vars->id == id) return vars->val;
+ while (vars && vars->id) {
+ if (vars->id == id) {
+ return vars->val;
+ }
vars = vars->next;
}
return Qnil;
@@ -343,16 +307,16 @@ dyna_var_asgn(id, value)
{
struct RVarmap *vars = the_dyna_vars;
- while (vars) {
+ while (vars && vars->id) {
if (vars->id == id) {
vars->val = value;
- return;
+ return value;
}
vars = vars->next;
}
{
NEWOBJ(_vars, struct RVarmap);
- OBJSETUP(_vars, Qnil, T_VARMAP);
+ OBJSETUP(_vars, 0, T_VARMAP);
_vars->id = id;
_vars->val = value;
_vars->next = the_dyna_vars;
@@ -360,11 +324,22 @@ dyna_var_asgn(id, value)
}
return value;
}
-
+
+static VALUE
+dyna_var_mark()
+{
+ NEWOBJ(_vars, struct RVarmap);
+ OBJSETUP(_vars, 0, T_VARMAP);
+ _vars->id = 0;
+ _vars->val = Qnil;
+ _vars->next = the_dyna_vars;
+ the_dyna_vars = _vars;
+}
+
static struct iter {
int iter;
struct iter *prev;
-} *iter;
+} *the_iter;
#define ITER_NOT 0
#define ITER_PRE 1
@@ -372,20 +347,16 @@ static struct iter {
#define PUSH_ITER(i) { \
struct iter _iter; \
- _iter.prev = iter; \
+ _iter.prev = the_iter; \
_iter.iter = (i); \
- iter = &_iter; \
+ the_iter = &_iter; \
#define POP_ITER() \
- iter = _iter.prev; \
+ the_iter = _iter.prev; \
}
-static int tag_level, target_level;
static struct tag {
- int level;
jmp_buf buf;
- struct gc_list *gclist;
- VALUE self;
struct FRAME *frame;
struct iter *iter;
struct tag *prev;
@@ -393,57 +364,50 @@ static struct tag {
#define PUSH_TAG() { \
struct tag _tag; \
- _tag.level= ++tag_level; \
- _tag.self = Qself; \
_tag.frame = the_frame; \
- _tag.iter = iter; \
+ _tag.iter = the_iter; \
_tag.prev = prot_tag; \
- prot_tag = &_tag; \
+ prot_tag = &_tag;
-#define EXEC_TAG() (setjmp(prot_tag->buf))
+#define EXEC_TAG() ((NODE*)setjmp(prot_tag->buf))
-#define JUMP_TAG(val) { \
- Qself = prot_tag->self; \
+#define JUMP_TAG(st) { \
the_frame = prot_tag->frame; \
- iter = prot_tag->iter; \
- longjmp(prot_tag->buf,(val)); \
+ the_iter = prot_tag->iter; \
+ longjmp(prot_tag->buf,(int)(st)); \
}
+#define JUMP_TAG3(val,data1,data2) \
+ JUMP_TAG(newnode(NODE_TAG,(val),(data1),(data2)))
+
+#define JUMP_TAG2(val,data) JUMP_TAG3((val),(data),0)
+
#define POP_TAG() \
- tag_level--; \
prot_tag = _tag.prev; \
}
-#define TAG_RETURN 1
-#define TAG_BREAK 2
-#define TAG_CONTINUE 3
-#define TAG_RETRY 4
-#define TAG_REDO 5
-#define TAG_FAIL 6
-#define TAG_EXIT 7
+#define TAG_RETURN 0x1
+#define TAG_BREAK 0x2
+#define TAG_NEXT 0x3
+#define TAG_RETRY 0x4
+#define TAG_REDO 0x5
+#define TAG_RAISE 0x6
+#define TAG_THROW 0x7
+#define TAG_FATAL 0x8
-#define IN_BLOCK 0x08
+#define IN_BLOCK 0x10
struct RClass *the_class;
-struct class_link {
- struct RClass *class;
- struct class_link *prev;
-} *class_link;
#define PUSH_CLASS() { \
- struct class_link _link; \
- _link.class = the_class; \
- _link.prev = class_link; \
- class_link = &_link \
+ struct RClass *_class = the_class; \
-#define POP_CLASS() \
- the_class = class_link->class; \
- class_link = _link.prev; }
+#define POP_CLASS() the_class = _class; }
#define PUSH_SCOPE() { \
struct SCOPE *_old; \
NEWOBJ(_scope, struct SCOPE); \
- OBJSETUP(_scope, Qnil, T_SCOPE); \
+ OBJSETUP(_scope, 0, T_SCOPE); \
_old = the_scope; \
the_scope = _scope; \
@@ -452,54 +416,96 @@ struct class_link {
the_scope->local_vars = 0;\
the_scope->local_tbl = 0;\
}\
+ the_scope->flag |= SCOPE_NOSTACK;\
the_scope = _old;\
}
static VALUE rb_eval();
-static VALUE f_eval();
+static VALUE eval();
+static NODE *compile();
static VALUE rb_call();
VALUE rb_apply();
-VALUE rb_xstring();
-void rb_fail();
-
-VALUE rb_rescue();
static void module_setup();
static VALUE masign();
static void asign();
-static VALUE last_val;
-
extern VALUE rb_stderr;
extern int sourceline;
extern char *sourcefile;
-static ID last_func;
static void
-error_print(last_func)
- ID last_func;
+error_pos()
{
- if (errat) {
- fwrite(RSTRING(errat)->ptr, 1, RSTRING(errat)->len, stderr);
- if (last_func) {
- fprintf(stderr, ":in `%s': ", rb_id2name(last_func));
+ if (sourcefile) {
+ if (the_frame->last_func) {
+ fprintf(stderr, "%s:%d:in `%s': ", sourcefile, sourceline,
+ rb_id2name(the_frame->last_func));
}
else {
- fprintf(stderr, ": ");
+ fprintf(stderr, "%s:%d: ", sourcefile, sourceline);
}
}
+}
- if (errstr) {
- fwrite(RSTRING(errstr)->ptr, 1, RSTRING(errstr)->len, stderr);
- if (RSTRING(errstr)->ptr[RSTRING(errstr)->len - 1] != '\n') {
- putc('\n', stderr);
+static void
+error_print(state)
+ NODE *state;
+{
+ struct FRAME *frame = the_frame;
+ VALUE etype;
+
+ if (!NIL_P(errat)) {
+ VALUE mesg;
+
+ switch (TYPE(errat)) {
+ case T_STRING:
+ mesg = errat;
+ errat = Qnil;
+ break;
+ case T_ARRAY:
+ mesg = RARRAY(errat)->ptr[0];
+ break;
}
+ fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, stderr);
+ fprintf(stderr, ": ");
+ }
+
+ etype = rb_class_path(CLASS_OF(errinfo));
+
+ if (verbose) {
+ fwrite(RSTRING(etype)->ptr, 1, RSTRING(etype)->len, stderr);
+ putc('|', stderr);
+ }
+ if (RSTRING(errinfo)->len == 0) {
+ fprintf(stderr, "unhandled exception.\n");
}
else {
- fprintf(stderr, "unhandled failure.\n");
+ fwrite(RSTRING(errinfo)->ptr, 1, RSTRING(errinfo)->len, stderr);
+ if (RSTRING(errinfo)->ptr[RSTRING(errinfo)->len - 1] != '\n') {
+ putc('\n', stderr);
+ }
+ }
+
+ if (!NIL_P(errat)) {
+ int i;
+ struct RArray *ep = RARRAY(errat);
+
+#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
+#define TRACE_HEAD 8
+#define TRACE_TAIL 5
+
+ for (i=1; i<ep->len; i++) {
+ fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
+ if (i == TRACE_HEAD && ep->len > TRACE_MAX) {
+ fprintf(stderr, "\t ... %d levels...\n",
+ ep->len - TRACE_HEAD - TRACE_TAIL);
+ i = ep->len - TRACE_TAIL;
+ }
+ }
}
}
@@ -511,9 +517,12 @@ ruby_init(argc, argv, envp)
int argc;
char **argv, **envp;
{
- int state;
static struct FRAME frame;
+ static struct iter iter;
+ NODE *state;
+
the_frame = top_frame = &frame;
+ the_iter = &iter;
origenviron = environ;
#ifdef NT
@@ -526,144 +535,171 @@ ruby_init(argc, argv, envp)
the_scope->local_tbl = 0;
top_scope = the_scope;
- PUSH_TAG();
- PUSH_ITER(ITER_NOT);
+ PUSH_TAG()
if ((state = EXEC_TAG()) == 0) {
rb_call_inits();
the_class = (struct RClass*)cObject;
+ the_frame->cbase = (VALUE)newnode(NODE_CREF,cObject,0,0);
ruby_options(argc, argv, envp);
}
- POP_ITER();
POP_TAG();
+ if (state) error_print(state);
POP_SCOPE();
the_scope = top_scope;
-
- if (state == TAG_EXIT) {
- rb_trap_exit();
- exit(FIX2UINT(last_val));
- }
- if (state) {
- error_print(last_func);
- }
}
static VALUE
-Eval()
+eval_node(self)
+ VALUE self;
{
VALUE result = Qnil;
NODE *tree;
- int state;
- if (!eval_tree) return Qnil;
+ if (!eval_tree) return FALSE;
tree = eval_tree;
eval_tree = 0;
- result = rb_eval(tree);
+ result = rb_eval(self, tree);
return result;
}
+int rb_in_eval;
+
+#ifdef THREAD
+static void thread_cleanup();
+static void thread_wait_other_threads();
+#endif
+
+static int exit_status;
+
void
ruby_run()
{
- int state;
+ NODE *state;
+ static NODE *ex;
if (nerrs > 0) exit(nerrs);
init_stack();
- rb_define_variable("$!", &errstr);
errat = Qnil; /* clear for execution */
PUSH_TAG();
PUSH_ITER(ITER_NOT);
if ((state = EXEC_TAG()) == 0) {
- Eval();
+ eval_node(TopSelf);
+ }
+ POP_ITER();
+ POP_TAG();
+
+ if (state && !ex) ex = state;
+ PUSH_TAG();
+ PUSH_ITER(ITER_NOT);
+ if ((state = EXEC_TAG()) == 0) {
rb_trap_exit();
+#ifdef THREAD
+ thread_cleanup();
+ thread_wait_other_threads();
+#endif
+ }
+ else {
+ ex = state;
}
POP_ITER();
POP_TAG();
- switch (state) {
- case 0:
- break;
+ if (!ex) {
+ exit(0);
+ }
+
+ switch (ex->nd_tag) {
case TAG_RETURN:
- Fatal("unexpected return");
+ error_pos();
+ fprintf(stderr, "unexpected return\n");
+ exit(1);
break;
- case TAG_CONTINUE:
- Fatal("unexpected continue");
+ case TAG_NEXT:
+ error_pos();
+ fprintf(stderr, "unexpected next\n");
+ exit(1);
break;
case TAG_BREAK:
- Fatal("unexpected break");
+ error_pos();
+ fprintf(stderr, "unexpected break\n");
+ exit(1);
break;
case TAG_REDO:
- Fatal("unexpected redo");
+ error_pos();
+ fprintf(stderr, "unexpected redo\n");
+ exit(1);
break;
case TAG_RETRY:
- Fatal("retry outside of protect clause");
+ error_pos();
+ fprintf(stderr, "retry outside of protect clause\n");
+ exit(1);
break;
- case TAG_FAIL:
- error_print(last_func);
+ case TAG_RAISE:
+ case TAG_FATAL:
+ if (obj_is_kind_of(errinfo, eSystemExit)) {
+ exit(exit_status);
+ }
+ error_print(ex);
exit(1);
break;
- case TAG_EXIT:
- exit(FIX2UINT(last_val));
+ case TAG_THROW:
+ error_pos();
+ fprintf(stderr, "uncaught throw `%s'\n", rb_id2name(ex->nd_tlev));
+ exit(1);
break;
default:
- Bug("Unknown longjmp status %d", state);
+ Bug("Unknown longjmp status %d", ex->nd_tag);
break;
}
- exit(0);
}
static void
-syntax_error()
+compile_error(at)
+ char *at;
{
VALUE mesg;
- mesg = errstr;
+ mesg = errinfo;
nerrs = 0;
- errstr = str_new2("syntax error in eval():\n");
- str_cat(errstr, RSTRING(mesg)->ptr, RSTRING(mesg)->len);
- rb_fail(errstr);
+ errinfo = exc_new(eSyntaxError, "compile error in ");
+ str_cat(errinfo, at, strlen(at));
+ str_cat(errinfo, ":\n", 2);
+ str_cat(errinfo, RSTRING(mesg)->ptr, RSTRING(mesg)->len);
+ rb_raise(errinfo);
}
VALUE
rb_eval_string(str)
char *str;
{
+ VALUE v;
char *oldsrc = sourcefile;
- lex_setsrc("(eval)", str, strlen(str));
- eval_tree = 0;
- PUSH_VARS();
- yyparse();
- POP_VARS();
+ sourcefile = "(eval)";
+ v = eval(TopSelf, str_new2(str), Qnil);
sourcefile = oldsrc;
- if (nerrs == 0) {
- return Eval();
- }
- else {
- syntax_error();
- }
- return Qnil; /* not reached */
+
+ return v;
}
void
rb_eval_cmd(cmd, arg)
VALUE cmd, arg;
{
- int state;
+ NODE *state;
struct SCOPE *saved_scope;
if (TYPE(cmd) != T_STRING) {
- if (TYPE(cmd) == T_OBJECT
- && obj_is_kind_of(cmd, cProc)) {
+ if (obj_is_kind_of(cmd, cProc)) {
proc_call(cmd, arg);
return;
}
}
- PUSH_SELF(TopSelf);
PUSH_CLASS();
PUSH_TAG();
saved_scope = the_scope;
@@ -672,31 +708,29 @@ rb_eval_cmd(cmd, arg)
the_class = (struct RClass*)cObject;
if ((state = EXEC_TAG()) == 0) {
- f_eval(Qself, cmd);
+ eval(TopSelf, cmd, Qnil);
}
the_scope = saved_scope;
POP_TAG();
POP_CLASS();
- POP_SELF();
- switch (state) {
- case 0:
- break;
+ if (state == 0) return;
+ switch (state->nd_tag) {
case TAG_RETURN:
- Fatal("unexpected return");
+ Raise(eLocalJumpError, "unexpected return");
break;
- case TAG_CONTINUE:
- Fatal("unexpected continue");
+ case TAG_NEXT:
+ Raise(eLocalJumpError, "unexpected next");
break;
case TAG_BREAK:
- Fatal("unexpected break");
+ Raise(eLocalJumpError, "unexpected break");
break;
case TAG_REDO:
- Fatal("unexpected redo");
+ Raise(eLocalJumpError, "unexpected redo");
break;
case TAG_RETRY:
- Fatal("retry outside of protect clause");
+ Raise(eLocalJumpError, "retry outside of protect clause");
break;
default:
JUMP_TAG(state);
@@ -709,8 +743,7 @@ rb_trap_eval(cmd, sig)
VALUE cmd;
int sig;
{
-#ifdef SAFE_SIGHANDLE
- int state;
+ NODE *state;
PUSH_TAG();
if ((state = EXEC_TAG()) == 0) {
@@ -721,9 +754,74 @@ rb_trap_eval(cmd, sig)
trap_immediate = 0;
JUMP_TAG(state);
}
-#else
- rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig)));
-#endif
+}
+
+static VALUE
+superclass(self, node)
+ VALUE self;
+ NODE *node;
+{
+ VALUE val;
+ NODE *state;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node);
+ }
+ POP_TAG();
+ if ((state && state->nd_tag == TAG_RAISE) || !val || TYPE(val) != T_CLASS){
+ switch (nd_type(node)) {
+ case NODE_COLON2:
+ TypeError("undefined superclass `%s'", rb_id2name(node->nd_mid));
+ case NODE_CVAR:
+ case NODE_CONST:
+ TypeError("undefined superclass `%s'", rb_id2name(node->nd_vid));
+ }
+ if (state) JUMP_TAG(state);
+ TypeError("superclass undefined");
+ }
+ if (state) JUMP_TAG(state);
+
+ return val;
+}
+
+static VALUE
+ev_const_defined(cref, id)
+ NODE *cref;
+ ID id;
+{
+ NODE *cbase = cref;
+
+ while (cbase && cbase->nd_clss != cObject) {
+ struct RClass *class = RCLASS(cbase->nd_clss);
+
+ if (class->iv_tbl &&
+ st_lookup(class->iv_tbl, id, 0)) {
+ return TRUE;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_defined(cref->nd_clss, id);
+}
+
+static VALUE
+ev_const_get(cref, id)
+ NODE *cref;
+ ID id;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_clss != cObject) {
+ struct RClass *class = RCLASS(cbase->nd_clss);
+
+ if (class->iv_tbl &&
+ st_lookup(class->iv_tbl, id, &result)) {
+ return result;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_get(cref->nd_clss, id);
}
#define SETUP_ARGS {\
@@ -739,9 +837,11 @@ rb_trap_eval(cmd, sig)
n = node->nd_args;\
argv = ALLOCA_N(VALUE,argc);\
for (i=0;i<argc;i++) {\
- argv[i] = rb_eval(n->nd_head);\
+ argv[i] = rb_eval(self,n->nd_head);\
n=n->nd_next;\
}\
+ sourceline = nd_line(node);\
+ sourcefile = node->file;\
}\
else {\
argc = 0;\
@@ -749,73 +849,111 @@ rb_trap_eval(cmd, sig)
}\
}\
else {\
- VALUE args = rb_eval(n);\
+ VALUE args = rb_eval(self,n);\
if (TYPE(args) != T_ARRAY)\
args = rb_to_a(args);\
argc = RARRAY(args)->len;\
argv = ALLOCA_N(VALUE, argc);\
MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\
+ sourceline = nd_line(node);\
+ sourcefile = node->file;\
}\
}
-#define RETURN(v) do { result = (v); goto finish; } while (0)
+int
+rb_test_false_or_nil(v)
+ VALUE v;
+{
+ return (v != Qnil) && (v != FALSE);
+}
+
+static int handle_rescue();
+VALUE rb_yield_0();
static VALUE
-rb_eval(node)
+rb_eval(self, node)
+ VALUE self;
register NODE *node;
{
- int state;
+ NODE *state;
VALUE result = Qnil;
+#define RETURN(v) { result = (v); goto finish; }
+
again:
- if (node == Qnil) RETURN(Qnil);
+ if (!node) RETURN(Qnil);
- sourceline = node->line;
+ sourceline = nd_line(node);
sourcefile = node->file;
switch (nd_type(node)) {
case NODE_BLOCK:
while (node) {
- result = rb_eval(node->nd_head);
+ result = rb_eval(self, node->nd_head);
node = node->nd_next;
}
break;
+ /* begin .. end without clauses */
+ case NODE_BEGIN:
+ node = node->nd_body;
+ goto again;
+
+ /* nodes for speed-up(default match) */
+ case NODE_MATCH:
+ result = reg_match2(node->nd_head->nd_lit);
+ break;
+
+ /* nodes for speed-up(top-level loop for -n/-p) */
+ case NODE_OPT_N:
+ while (!NIL_P(f_gets())) {
+ rb_eval(self, node->nd_body);
+ }
+ RETURN(Qnil);
+
case NODE_SELF:
- RETURN(Qself);
+ RETURN(self);
case NODE_NIL:
RETURN(Qnil);
case NODE_IF:
- if (rb_eval(node->nd_cond)) {
+ if (RTEST(rb_eval(self, node->nd_cond))) {
node = node->nd_body;
}
else {
node = node->nd_else;
}
- if (node) goto again;
- RETURN(Qnil);
+ goto again;
+
+ case NODE_UNLESS:
+ if (!RTEST(rb_eval(self, node->nd_cond))) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ goto again;
case NODE_CASE:
{
VALUE val;
- val = rb_eval(node->nd_head);
+ val = rb_eval(self, node->nd_head);
node = node->nd_body;
while (node) {
- if (nd_type(node) == NODE_WHEN) {
- NODE *tag = node->nd_head;
+ NODE *tag;
- while (tag) {
- if (rb_funcall(rb_eval(tag->nd_head), match, 1, val)){
- RETURN(rb_eval(node->nd_body));
- }
- tag = tag->nd_next;
- }
+ if (nd_type(node) != NODE_WHEN) {
+
+ RETURN(rb_eval(self, node));
}
- else {
- RETURN(rb_eval(node));
+ tag = node->nd_head;
+ while (tag) {
+ if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head),eqq,1,&val))){
+ RETURN(rb_eval(self, node->nd_body));
+ }
+ tag = tag->nd_next;
}
node = node->nd_next;
}
@@ -824,60 +962,62 @@ rb_eval(node)
case NODE_WHILE:
PUSH_TAG();
- switch (state = EXEC_TAG()) {
- case 0:
- while_cont:
- while (rb_eval(node->nd_cond)) {
+ if ((state = EXEC_TAG()) == 0) {
+ while_next:
+ for (;;) {
+ if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond)))
+ break;
while_redo:
- rb_eval(node->nd_body);
+ rb_eval(self, node->nd_body);
+ if (!node->nd_state && !RTEST(rb_eval(self, node->nd_cond)))
+ break;
+ }
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ state = 0;
+ goto while_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto while_next;
+ default:
+ break;
}
- break;
- case TAG_REDO:
- state = 0;
- goto while_redo;
- case TAG_CONTINUE:
- state = 0;
- goto while_cont;
- default:
- break;
}
POP_TAG();
- switch (state) {
- case 0:
- case TAG_BREAK:
- break;
- default:
+ if (state && state->nd_tag != TAG_BREAK) {
JUMP_TAG(state);
- break;
}
RETURN(Qnil);
- case NODE_WHILE2:
+ case NODE_UNTIL:
PUSH_TAG();
- switch (state = EXEC_TAG()) {
- case 0:
- while2_cont:
- do {
- while2_redo:
- rb_eval(node->nd_body);
- } while (rb_eval(node->nd_cond));
- break;
- case TAG_REDO:
- state = 0;
- goto while2_redo;
- case TAG_CONTINUE:
- state = 0;
- goto while2_cont;
- default:
- case TAG_BREAK:
- break;
+ if ((state = EXEC_TAG()) == 0) {
+ until_next:
+ for (;;) {
+ if (node->nd_state && RTEST(rb_eval(self, node->nd_cond)))
+ break;
+ until_redo:
+ rb_eval(self, node->nd_body);
+ if (!node->nd_state && RTEST(rb_eval(self, node->nd_cond)))
+ break;
+ }
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ state = 0;
+ goto while_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto while_next;
+ default:
+ break;
+ }
}
POP_TAG();
- switch (state) {
- case 0:
- case TAG_BREAK:
- break;
- default:
+ if (state && state->nd_tag != TAG_BREAK) {
JUMP_TAG(state);
}
RETURN(Qnil);
@@ -885,6 +1025,8 @@ rb_eval(node)
case NODE_ITER:
case NODE_FOR:
{
+ int tag_level;
+
iter_retry:
PUSH_BLOCK(node->nd_var, node->nd_body);
PUSH_TAG();
@@ -893,36 +1035,37 @@ rb_eval(node)
if (state == 0) {
if (nd_type(node) == NODE_ITER) {
PUSH_ITER(ITER_PRE);
- result = rb_eval(node->nd_iter);
+ result = rb_eval(self, node->nd_iter);
POP_ITER();
}
else {
VALUE recv;
- recv = rb_eval(node->nd_iter);
+ recv = rb_eval(self, node->nd_iter);
PUSH_ITER(ITER_PRE);
+ sourceline = nd_line(node);
+ sourcefile = node->file;
result = rb_call(CLASS_OF(recv),recv,each,0,0,0);
POP_ITER();
}
}
POP_TAG();
+ tag_level = the_block->level;
POP_BLOCK();
- switch (state) {
- case 0:
- break;
-
+ if (state == 0) break;
+ switch (state->nd_tag) {
case TAG_RETRY:
goto iter_retry;
case IN_BLOCK|TAG_BREAK:
- if (target_level != tag_level) {
+ if (state->nd_tlev != tag_level) {
JUMP_TAG(state);
}
result = Qnil;
break;
case IN_BLOCK|TAG_RETURN:
- if (target_level == tag_level) {
- state &= ~IN_BLOCK;
+ if (state->nd_tlev == tag_level) {
+ state->nd_tag &= ~IN_BLOCK;
}
/* fall through */
default:
@@ -931,79 +1074,91 @@ rb_eval(node)
}
break;
- case NODE_FAIL:
- {
- VALUE mesg = rb_eval(node->nd_stts);
- if (mesg) Check_Type(mesg, T_STRING);
- rb_fail(mesg);
- }
- break;
-
case NODE_YIELD:
- result = rb_yield(rb_eval(node->nd_stts));
+ result = rb_yield_0(rb_eval(self, node->nd_stts), 0);
break;
- case NODE_BEGIN:
- if (node->nd_resq == Qnil && node->nd_ensr == Qnil) {
- node = node->nd_head;
- goto again;
+ case NODE_RESCUE:
+ retry_entry:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
}
- else {
- VALUE (*r_proc)();
-
- 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);
- }
- POP_TAG();
- /* ensure clause */
- rb_eval(node->nd_ensr);
- if (state) {
- JUMP_TAG(state);
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_RAISE) {
+ NODE *resq = node->nd_resq;
+ while (resq) {
+ if (handle_rescue(self, resq)) {
+ state = 0;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, resq->nd_body);
+ }
+ POP_TAG();
+ if (state == 0) {
+ errat = Qnil;
+ }
+ else if (state->nd_tag == TAG_RETRY) {
+ state = 0;
+ goto retry_entry;
+ }
+ break;
+ }
+ resq = resq->nd_head; /* next rescue */
}
}
- else {
- result = rb_rescue(rb_eval, node->nd_head, r_proc, node->nd_resq);
+ if (state) {
+ JUMP_TAG(state);
}
}
break;
+ case NODE_ENSURE:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ rb_eval(self, node->nd_ensr);
+ if (state) {
+ JUMP_TAG(state);
+ }
+ break;
+
case NODE_AND:
- if ((result = rb_eval(node->nd_1st)) == FALSE) RETURN(result);
+ result = rb_eval(self, node->nd_1st);
+ if (!RTEST(result)) break;
node = node->nd_2nd;
goto again;
case NODE_OR:
- if ((result = rb_eval(node->nd_1st)) != FALSE) RETURN(result);
+ result = rb_eval(self, node->nd_1st);
+ if (RTEST(result)) break;
node = node->nd_2nd;
goto again;
case NODE_NOT:
- if (rb_eval(node->nd_body)) result = FALSE;
+ if (RTEST(rb_eval(self, 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)));
+ RETURN(range_new(rb_eval(self, node->nd_beg), rb_eval(self, 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;
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ node->nd_state = rb_eval(self, node->nd_end)?0:1;
result = TRUE;
}
- result = FALSE;
+ else {
+ result = FALSE;
+ }
}
else {
- if (rb_eval(node->nd_end)) {
+ if (RTEST(rb_eval(self, node->nd_end))) {
node->nd_state = 0;
}
result = TRUE;
@@ -1012,39 +1167,22 @@ rb_eval(node)
case NODE_FLIP3: /* like SED */
if (node->nd_state == 0) {
- if (rb_eval(node->nd_beg)) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
node->nd_state = 1;
result = TRUE;
}
result = FALSE;
}
else {
- if (rb_eval(node->nd_end)) {
+ if (RTEST(rb_eval(self, node->nd_end))) {
node->nd_state = 0;
}
result = TRUE;
}
break;
- case NODE_BREAK:
- JUMP_TAG(TAG_BREAK);
- break;
-
- case NODE_CONTINUE:
- JUMP_TAG(TAG_CONTINUE);
- break;
-
- case NODE_REDO:
- JUMP_TAG(TAG_REDO);
- break;
-
- case NODE_RETRY:
- JUMP_TAG(TAG_RETRY);
- break;
-
case NODE_RETURN:
- if (node->nd_stts) last_val = rb_eval(node->nd_stts);
- JUMP_TAG(TAG_RETURN);
+ JUMP_TAG2(TAG_RETURN,(node->nd_stts)?rb_eval(self, node->nd_stts):Qnil);
break;
case NODE_CALL:
@@ -1053,7 +1191,7 @@ rb_eval(node)
int argc; VALUE *argv; /* used in SETUP_ARGS */
PUSH_ITER(ITER_NOT);
- recv = rb_eval(node->nd_recv);
+ recv = rb_eval(self, node->nd_recv);
SETUP_ARGS;
POP_ITER();
result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0);
@@ -1067,7 +1205,7 @@ rb_eval(node)
PUSH_ITER(ITER_NOT);
SETUP_ARGS;
POP_ITER();
- result = rb_call(CLASS_OF(Qself),Qself,node->nd_mid,argc,argv,1);
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1);
}
break;
@@ -1086,8 +1224,8 @@ rb_eval(node)
POP_ITER();
}
- PUSH_ITER(iter->iter?ITER_PRE:ITER_NOT);
- result = rb_call(the_frame->last_class->super, Qself,
+ PUSH_ITER(the_iter->iter?ITER_PRE:ITER_NOT);
+ result = rb_call(the_frame->last_class->super, self,
the_frame->last_func, argc, argv, 1);
POP_ITER();
}
@@ -1095,11 +1233,14 @@ rb_eval(node)
case NODE_SCOPE:
{
+ VALUE save = the_frame->cbase;
+
PUSH_SCOPE();
PUSH_TAG();
- if (node->nd_cnt > 0) {
- the_scope->local_vars = ALLOCA_N(VALUE, node->nd_cnt);
- MEMZERO(the_scope->local_vars, VALUE, node->nd_cnt);
+ if (node->nd_rval) the_frame->cbase = (VALUE)node->nd_rval;
+ if (node->nd_tbl) {
+ the_scope->local_vars = ALLOCA_N(VALUE, node->nd_tbl[0]);
+ memclear(the_scope->local_vars, node->nd_tbl[0]);
the_scope->local_tbl = node->nd_tbl;
}
else {
@@ -1107,11 +1248,12 @@ rb_eval(node)
the_scope->local_tbl = 0;
}
if ((state = EXEC_TAG()) == 0) {
- result = rb_eval(node->nd_body);
+ result = rb_eval(self, node->nd_body);
}
POP_TAG();
POP_SCOPE();
- if (state != 0) JUMP_TAG(state);
+ the_frame->cbase = save;
+ if (state) JUMP_TAG(state);
}
break;
@@ -1120,12 +1262,12 @@ rb_eval(node)
VALUE recv, args, val;
NODE *rval;
- recv = rb_eval(node->nd_recv);
+ recv = rb_eval(self, node->nd_recv);
rval = node->nd_args->nd_head;
- args = rb_eval(node->nd_args->nd_next);
+ args = rb_eval(self, node->nd_args->nd_next);
val = rb_apply(recv, aref, args);
- val = rb_funcall(val, node->nd_mid, 1, rb_eval(rval));
+ val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval));
ary_push(args, val);
rb_apply(recv, aset, args);
result = val;
@@ -1134,38 +1276,40 @@ rb_eval(node)
case NODE_OP_ASGN2:
{
- ID id = node->nd_aid;
+ ID id = node->nd_next->nd_vid;
VALUE recv, val;
- recv = rb_funcall(rb_eval(node->nd_recv), id, 0);
+ recv = rb_eval(self, node->nd_recv);
+ val = rb_funcall(recv, id, 0);
- id = id_attrset(id);
+ val = rb_funcall(recv, node->nd_next->nd_mid, 2, val,
+ rb_eval(self, node->nd_value));
- val = rb_eval(node->nd_value);
- rb_funcall(recv, id, 1, val);
+ rb_funcall2(recv, id_attrset(id), 1, &val);
result = val;
}
break;
case NODE_MASGN:
- result = masign(node, rb_eval(node->nd_value));
+ result = masign(self, node, rb_eval(self, node->nd_value));
break;
case NODE_LASGN:
if (the_scope->local_vars == 0)
Bug("unexpected local variable asignment");
- result = the_scope->local_vars[node->nd_cnt] = rb_eval(node->nd_value);
+ the_scope->local_vars[node->nd_cnt] = rb_eval(self, node->nd_value);
+ result = the_scope->local_vars[node->nd_cnt];
break;
case NODE_DASGN:
- result = dyna_var_asgn(node->nd_vid, rb_eval(node->nd_value));
+ result = dyna_var_asgn(node->nd_vid, rb_eval(self, node->nd_value));
break;
case NODE_GASGN:
{
VALUE val;
- val = rb_eval(node->nd_value);
+ val = rb_eval(self, node->nd_value);
rb_gvar_set(node->nd_entry, val);
result = val;
}
@@ -1175,8 +1319,8 @@ rb_eval(node)
{
VALUE val;
- val = rb_eval(node->nd_value);
- rb_ivar_set(Qself, node->nd_vid, val);
+ val = rb_eval(self, node->nd_value);
+ rb_ivar_set(self, node->nd_vid, val);
result = val;
}
break;
@@ -1185,15 +1329,21 @@ rb_eval(node)
{
VALUE val;
- val = rb_eval(node->nd_value);
+ val = rb_eval(self, node->nd_value);
+ /* check for static scope constants */
+ if (verbose && ev_const_defined(the_frame->cbase, node->nd_vid)) {
+ Warning("already initialized constnant %s",
+ rb_id2name(node->nd_vid));
+ }
rb_const_set(the_class, node->nd_vid, val);
result = val;
}
break;
case NODE_LVAR:
- if (the_scope->local_vars == 0)
+ if (the_scope->local_vars == 0) {
Bug("unexpected local variable");
+ }
result = the_scope->local_vars[node->nd_cnt];
break;
@@ -1206,18 +1356,13 @@ rb_eval(node)
break;
case NODE_IVAR:
- result = rb_ivar_get(Qself, node->nd_vid);
+ result = rb_ivar_get(self, node->nd_vid);
break;
case NODE_CVAR:
- {
- VALUE val;
-
- val = rb_const_get(node->nd_rval->nd_clss, node->nd_vid);
- nd_set_type(node, NODE_CONST);
- node->nd_cval = val;
- result = val;
- }
+ result = ev_const_get(the_frame->cbase, node->nd_vid);
+ nd_set_type(node, NODE_CONST);
+ node->nd_cval = result;
break;
case NODE_CONST:
@@ -1228,7 +1373,7 @@ rb_eval(node)
{
VALUE cls;
- cls = rb_eval(node->nd_head);
+ cls = rb_eval(self, node->nd_head);
switch (TYPE(cls)) {
case T_CLASS:
case T_MODULE:
@@ -1237,7 +1382,7 @@ rb_eval(node)
Check_Type(cls, T_CLASS);
break;
}
- result = rb_const_get(cls, node->nd_mid);
+ result = rb_const_get_at(cls, node->nd_mid);
}
break;
@@ -1273,11 +1418,11 @@ rb_eval(node)
list = node->nd_head;
while (list) {
- key = rb_eval(list->nd_head);
+ key = rb_eval(self, list->nd_head);
list = list->nd_next;
if (list == 0)
Bug("odd number list for Hash");
- val = rb_eval(list->nd_head);
+ val = rb_eval(self, list->nd_head);
list = list->nd_next;
hash_aset(hash, key, val);
}
@@ -1297,7 +1442,7 @@ rb_eval(node)
i = node->nd_alen;
ary = ary_new2(i);
for (i=0;node;node=node->nd_next) {
- RARRAY(ary)->ptr[i++] = rb_eval(node->nd_head);
+ RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head);
RARRAY(ary)->len = i;
}
@@ -1309,9 +1454,10 @@ rb_eval(node)
result = str_new3(node->nd_lit);
break;
- case NODE_STR2:
- case NODE_XSTR2:
+ case NODE_DSTR:
+ case NODE_DXSTR:
case NODE_DREGX:
+ case NODE_DREGX_ONCE:
{
VALUE str, str2;
NODE *list = node->nd_next;
@@ -1322,7 +1468,15 @@ rb_eval(node)
str2 = list->nd_head->nd_lit;
}
else {
- str2 = rb_eval(list->nd_head);
+ if (nd_type(list->nd_head) == NODE_EVSTR) {
+ rb_in_eval++;
+ list->nd_head = compile(list->nd_head->nd_lit);
+ rb_in_eval--;
+ if (!node) {
+ compile_error("string expand");
+ }
+ }
+ str2 = rb_eval(self, list->nd_head);
}
if (str2) {
str2 = obj_as_string(str2);
@@ -1330,22 +1484,29 @@ rb_eval(node)
}
list = list->nd_next;
}
- if (nd_type(node) == NODE_DREGX) {
- 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);
- }
- else {
+ switch (nd_type(node)) {
+ case NODE_DREGX:
+ result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ break;
+ case NODE_DREGX_ONCE: /* regexp expand once */
+ result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ nd_set_type(node, NODE_LIT);
+ node->nd_lit = result;
+ break;
+ case NODE_DXSTR:
+ result = rb_funcall(self, '`', 1, str);
+ break;
+ default:
result = str;
+ break;
}
}
break;
case NODE_XSTR:
- result = rb_xstring(node->nd_lit);
+ result = rb_funcall(self, '`', 1, node->nd_lit);
break;
case NODE_LIT:
@@ -1354,8 +1515,8 @@ rb_eval(node)
case NODE_ATTRSET:
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]);
+ ArgError("Wrong # of arguments(%d for 1)", the_frame->argc);
+ result = rb_ivar_set(self, node->nd_vid, the_frame->argv[0]);
break;
case NODE_DEFN:
@@ -1365,9 +1526,17 @@ rb_eval(node)
int noex;
body = search_method(the_class, node->nd_mid, &origin);
- if (body && verbose && origin != (VALUE)the_class
- && body->nd_noex != node->nd_noex) {
- Warning("change method %s's scope", rb_id2name(node->nd_mid));
+ if (body) {
+ if (origin == (VALUE)the_class) {
+ Warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ else {
+ if (body->nd_noex != node->nd_noex) {
+ Warning("change method %s's scope",
+ rb_id2name(node->nd_mid));
+ }
+ }
+ rb_clear_cache();
}
if (body) noex = body->nd_noex;
@@ -1380,23 +1549,38 @@ rb_eval(node)
case NODE_DEFS:
if (node->nd_defn) {
- VALUE recv = rb_eval(node->nd_recv);
+ VALUE recv = rb_eval(self, node->nd_recv);
+ VALUE class;
+ NODE *body;
+
+ if (NIL_P(recv)) {
+ TypeError("Can't define method \"%s\" for nil",
+ rb_id2name(node->nd_mid));
+ }
- if (recv == Qnil) {
- Fail("Can't define method \"%s\" for nil",
- rb_id2name(node->nd_mid));
+ class = rb_singleton_class(recv);
+ if (st_lookup(RCLASS(class)->m_tbl, node->nd_mid, &body)) {
+ Warning("redefine %s", rb_id2name(node->nd_mid));
}
+ rb_clear_cache();
rb_funcall(recv, rb_intern("singleton_method_added"),
1, INT2FIX(node->nd_mid));
- rb_add_method(rb_singleton_class(recv),node->nd_mid,node->nd_defn,
- NOEX_PUBLIC);
+ rb_add_method(class, node->nd_mid, node->nd_defn, NOEX_PUBLIC);
result = Qnil;
}
break;
case NODE_UNDEF:
- rb_add_method(the_class, node->nd_mid, Qnil, NOEX_PUBLIC);
- result = Qnil;
+ {
+ NODE *body;
+
+ if (st_lookup(the_class->m_tbl, node->nd_mid, &body)) {
+ Warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ rb_clear_cache();
+ rb_add_method(the_class, node->nd_mid, 0, NOEX_PUBLIC);
+ result = Qnil;
+ }
break;
case NODE_ALIAS:
@@ -1410,35 +1594,33 @@ rb_eval(node)
struct RClass *tmp;
if (node->nd_super) {
- super = rb_eval(node->nd_super);
- if (super == Qnil || TYPE(super) != T_CLASS) {
- Fail("superclass undefined");
- }
+ super = superclass(self, node->nd_super);
}
else {
- super = Qnil;
+ super = 0;
}
- if (rb_const_defined(the_class, node->nd_cname)) {
+ if (!rb_autoload_defined(node->nd_cname) &&
+ ev_const_defined(the_frame->cbase, node->nd_cname)) {
class = rb_const_get(the_class, node->nd_cname);
+ if (TYPE(class) != T_CLASS)
+ TypeError("%s is not a class", rb_id2name(node->nd_cname));
if (super) {
- if (TYPE(class) != T_CLASS)
- Fail("%s is not a class", rb_id2name(node->nd_cname));
tmp = RCLASS(class)->super;
- while (FL_TEST(tmp, FL_SINGLE)) {
+ while (FL_TEST(tmp, FL_SINGLETON)) {
tmp = RCLASS(tmp)->super;
}
while (TYPE(tmp) == T_ICLASS) {
tmp = RCLASS(tmp)->super;
}
if (tmp != RCLASS(super))
- Fail("superclass mismatch for %s",
- rb_id2name(node->nd_cname));
+ TypeError("superclass mismatch for %s",
+ rb_id2name(node->nd_cname));
}
Warning("extending class %s", rb_id2name(node->nd_cname));
}
else {
- if (super == Qnil) super = cObject;
+ if (!super) 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));
@@ -1453,10 +1635,11 @@ rb_eval(node)
{
VALUE module;
- if (rb_const_defined(the_class, node->nd_cname)) {
+ if (!rb_autoload_defined(node->nd_cname) &&
+ ev_const_defined(the_frame->cbase, 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));
+ TypeError("%s is not a module", rb_id2name(node->nd_cname));
Warning("extending module %s", rb_id2name(node->nd_cname));
}
else {
@@ -1473,8 +1656,11 @@ rb_eval(node)
case NODE_DEFINED:
{
VALUE obj;
+ char buf[20];
+ char *desc = 0;
node = node->nd_head;
+
switch (nd_type(node)) {
case NODE_SUPER:
case NODE_ZSUPER:
@@ -1486,17 +1672,16 @@ rb_eval(node)
break;
case NODE_FCALL:
- obj = CLASS_OF(Qself);
+ obj = CLASS_OF(self);
goto check_bound;
case NODE_CALL:
PUSH_TAG();
if ((state = EXEC_TAG()) == 0) {
- obj = rb_eval(node->nd_recv);
+ obj = rb_eval(self, node->nd_recv);
}
POP_TAG();
- if (state == TAG_FAIL) {
- result = FALSE;
+ if (state) {
break;
}
else {
@@ -1505,27 +1690,23 @@ rb_eval(node)
check_bound:
if (method_boundp(obj, node->nd_mid,
nd_type(node)== NODE_CALL)) {
- result = TRUE;
+ desc = "method";
}
- else result = FALSE;
}
break;
case NODE_YIELD:
- result = iterator_p();
+ if (iterator_p()) {
+ desc = "iterator";
+ }
break;
- case NODE_BREAK:
- case NODE_CONTINUE:
- case NODE_REDO:
- case NODE_RETRY:
-
case NODE_SELF:
+ desc = "self"; break;
case NODE_NIL:
- case NODE_FAIL:
- case NODE_ATTRSET:
- case NODE_DEFINED:
+ desc = "nil"; break;
+ case NODE_ATTRSET:
case NODE_OP_ASGN1:
case NODE_OP_ASGN2:
case NODE_MASGN:
@@ -1534,60 +1715,80 @@ rb_eval(node)
case NODE_GASGN:
case NODE_IASGN:
case NODE_CASGN:
+ desc = "asignment"; break;
+
case NODE_LVAR:
+ desc = "local-variable"; break;
case NODE_DVAR:
- result = TRUE;
- break;
+ desc = "dynamic-local-variable"; break;
case NODE_GVAR:
- result = rb_gvar_defined(node->nd_entry);
+ if (rb_gvar_defined(node->nd_entry)) {
+ desc = "global-variable";
+ }
break;
case NODE_IVAR:
- result = rb_ivar_defined(node->nd_vid);
+ if (rb_ivar_defined(self, node->nd_vid)) {
+ desc = "instance-variable";
+ }
break;
case NODE_CVAR:
- result = rb_const_defined(node->nd_rval->nd_clss, node->nd_vid);
- break;
-
- case NODE_CONST:
- result = TRUE;
+ if (ev_const_defined(the_frame->cbase, node->nd_vid)) {
+ case NODE_CONST: /* jump in */
+ desc = "class-constant";
+ }
break;
case NODE_COLON2:
PUSH_TAG();
if ((state = EXEC_TAG()) == 0) {
- obj = rb_eval(node->nd_head);
+ obj = rb_eval(self, node->nd_head);
}
POP_TAG();
- if (state == TAG_FAIL) result = FALSE;
+ if (state) {
+ break;
+ }
else {
if (state) JUMP_TAG(state);
- result = rb_const_defined(obj, node->nd_mid);
+ switch (TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ if (rb_const_defined_at(obj, node->nd_mid))
+ desc = "class-constant";
+ break;
+ }
}
break;
case NODE_NTH_REF:
- result = reg_nth_defined(node->nd_nth, MATCH_DATA);
+ if (reg_nth_defined(node->nd_nth, MATCH_DATA)) {
+ sprintf(buf, "$%d", node->nd_nth);
+ desc = buf;
+ }
break;
case NODE_BACK_REF:
- result = reg_nth_defined(0, MATCH_DATA);
+ if (reg_nth_defined(0, MATCH_DATA)) {
+ sprintf(buf, "$%c", node->nd_nth);
+ desc = buf;
+ }
break;
default:
PUSH_TAG();
if ((state = EXEC_TAG()) == 0) {
- rb_eval(node);
+ rb_eval(self, node);
}
POP_TAG();
- if (state == TAG_FAIL) result = FALSE;
+ if (state) break;
else {
- if (state) JUMP_TAG(state);
- result = TRUE;
+ desc = "expression";
}
}
+ if (desc) result = str_new2(desc);
+ else result = FALSE;
}
break;
@@ -1595,12 +1796,8 @@ rb_eval(node)
Bug("unknown node type %d", nd_type(node));
}
finish:
-#ifdef SAFE_SIGHANDLE
- if (trap_pending) {
- rb_trap_exec();
- }
-#endif
- return result; /* not reached */
+ CHECK_INTS;
+ return result;
}
static void
@@ -1608,7 +1805,8 @@ module_setup(module, node)
VALUE module;
NODE *node;
{
- int state;
+ NODE *state;
+ VALUE save = the_frame->cbase;
/* fill c-ref */
node->nd_clss = module;
@@ -1616,12 +1814,12 @@ module_setup(module, node)
PUSH_CLASS();
the_class = (struct RClass*)module;
- PUSH_SELF((VALUE)the_class);
PUSH_SCOPE();
- if (node->nd_cnt > 0) {
- the_scope->local_vars = ALLOCA_N(VALUE, node->nd_cnt);
- MEMZERO(the_scope->local_vars, VALUE, node->nd_cnt);
+ if (node->nd_rval) the_frame->cbase = node->nd_rval;
+ if (node->nd_tbl) {
+ the_scope->local_vars = ALLOCA_N(VALUE, node->nd_tbl[0]);
+ memclear(the_scope->local_vars, node->nd_tbl[0]);
the_scope->local_tbl = node->nd_tbl;
}
else {
@@ -1631,21 +1829,48 @@ module_setup(module, node)
PUSH_TAG();
if ((state = EXEC_TAG()) == 0) {
- rb_eval(node->nd_body);
+ rb_eval((VALUE)the_class, node->nd_body);
}
POP_TAG();
POP_SCOPE();
- POP_SELF();
POP_CLASS();
+ the_frame->cbase = save;
if (state) JUMP_TAG(state);
}
-VALUE
-rb_responds_to(obj, id)
+int
+rb_respond_to(obj, id)
VALUE obj;
ID id;
{
- if (rb_method_boundp(CLASS_OF(obj), id)) {
+ if (rb_method_boundp(CLASS_OF(obj), id, 0)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+krn_respond_to(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE mid, priv;
+ ID id;
+
+ rb_scan_args(argc, argv, "11", &mid, &priv);
+ id = rb_to_id(mid);
+ if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+mod_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ if (rb_method_boundp(mod, rb_to_id(mid), TRUE)) {
return TRUE;
}
return FALSE;
@@ -1655,11 +1880,11 @@ void
rb_exit(status)
int status;
{
- last_val = INT2FIX(status);
- if (prot_tag)
- JUMP_TAG(TAG_EXIT);
- rb_trap_exit();
- exit(FIX2UINT(last_val));
+ if (prot_tag) {
+ exit_status = status;
+ rb_raise(exc_new(eSystemExit, ""));
+ }
+ exit(status);
}
static VALUE
@@ -1671,71 +1896,141 @@ f_exit(argc, argv, obj)
VALUE status;
if (rb_scan_args(argc, argv, "01", &status) == 1) {
- Need_Fixnum(status);
+ status = NUM2INT(status);
}
else {
- status = INT2FIX(0);
+ status = 0;
}
- last_val = status;
- JUMP_TAG(TAG_EXIT);
-
- return Qnil; /* not reached */
+ rb_exit(status);
+ /* not reached */
}
void
rb_break()
{
- JUMP_TAG(TAG_BREAK);
+ JUMP_TAG2(TAG_BREAK, 0);
}
-void
-rb_redo()
+static VALUE
+f_break()
+{
+ JUMP_TAG2(TAG_BREAK, 0);
+}
+
+static VALUE
+f_next()
+{
+ JUMP_TAG2(TAG_NEXT, 0);
+}
+
+static VALUE
+f_redo()
+{
+ JUMP_TAG2(TAG_REDO, 0);
+}
+
+static VALUE
+f_retry()
+{
+ JUMP_TAG2(TAG_RETRY, 0);
+}
+
+#ifdef __GNUC__
+static volatile voidfn rb_longjmp;
+#endif
+
+static VALUE make_backtrace();
+
+static void
+rb_longjmp(tag, mesg)
+ int tag;
+ VALUE mesg;
{
- JUMP_TAG(TAG_REDO);
+ if (NIL_P(errat) && NIL_P(mesg)) {
+ errinfo = exc_new(eRuntimeError, "");
+ }
+
+ if (sourcefile && (NIL_P(errat) || !NIL_P(mesg))) {
+ errat = make_backtrace();
+ }
+
+ if (!NIL_P(mesg)) {
+ if (obj_is_kind_of(mesg, eGlobalExit)) {
+ errinfo = mesg;
+ }
+ else {
+ errinfo = exc_new2(eRuntimeError, mesg);
+ }
+ str_freeze(errinfo);
+ }
+
+ JUMP_TAG2(tag, 0);
}
void
-rb_retry()
+rb_raise(mesg)
+ VALUE mesg;
{
- JUMP_TAG(TAG_RETRY);
+ rb_longjmp(TAG_RAISE, mesg);
}
void
-rb_fail(mesg)
+rb_fatal(mesg)
VALUE mesg;
{
- char buf[BUFSIZ];
+ rb_longjmp(TAG_FATAL, mesg);
+}
- if (errat == Qnil && mesg == Qnil) {
- errstr = Qnil;
+void
+rb_interrupt()
+{
+ rb_raise(exc_new(eInterrupt, "Interrupt"));
+}
+
+static VALUE
+f_raise(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE arg1, arg2;
+ VALUE etype, mesg;
+
+ etype = eRuntimeError;
+ mesg = Qnil;
+ switch (rb_scan_args(argc, argv, "02", &arg1, &arg2)) {
+ case 1:
+ mesg = arg1;
+ break;
+ case 2:
+ etype = arg1;
+ mesg = arg2;
+ break;
}
- if (errat == Qnil && sourcefile) {
- if (the_frame->last_func) {
- last_func = the_frame->last_func;
+ if (!NIL_P(mesg)) {
+ Check_Type(mesg, T_STRING);
+ if (!obj_is_kind_of(mesg, eException)) {
+ mesg = exc_new2(etype, mesg);
}
- sprintf(buf, "%s:%d", sourcefile, sourceline);
- errat = str_new2(buf);
}
- if (mesg) {
- errstr = mesg;
- }
- if (prot_tag->level == 0) error_print(last_func);
- JUMP_TAG(TAG_FAIL);
+ PUSH_FRAME(); /* fake frame */
+ *the_frame = *_frame.prev->prev;
+ rb_raise(mesg);
+ POP_FRAME();
}
-VALUE
+int
iterator_p()
{
- if (iter->iter) return TRUE;
+ if (the_frame->iter) return TRUE;
return FALSE;
}
static VALUE
f_iterator_p()
{
- if (iter->prev && iter->prev->iter) return TRUE;
+ if (the_frame->prev && the_frame->prev->iter) return TRUE;
return FALSE;
}
@@ -1743,18 +2038,19 @@ VALUE
rb_yield_0(val, self)
VALUE val, self;
{
- struct BLOCK *block;
NODE *node;
- int state;
+ NODE *state;
VALUE result = Qnil;
+ struct BLOCK *block;
struct SCOPE *old_scope;
struct FRAME frame;
if (!iterator_p()) {
- Fail("yield called out of iterator");
+ Raise(eLocalJumpError, "yield called out of iterator");
}
PUSH_VARS();
+ PUSH_CLASS();
block = the_block;
frame = block->frame;
frame.prev = the_frame;
@@ -1763,52 +2059,53 @@ rb_yield_0(val, self)
the_scope = block->scope;
the_block = block->prev;
the_dyna_vars = block->d_vars;
+ the_class = block->class;
+ if (!self) self = block->self;
+ node = block->body;
if (block->var) {
if (nd_type(block->var) == NODE_MASGN)
- masign(block->var, val);
+ masign(self, block->var, val);
else
- asign(block->var, val);
+ asign(self, block->var, val);
}
- node = block->body;
-
PUSH_ITER(block->iter);
- PUSH_SELF(self?self:block->self);
PUSH_TAG();
- switch (state = EXEC_TAG()) {
+ if ((state = EXEC_TAG()) == 0) {
redo:
- case 0:
if (!node) {
result = Qnil;
}
else if (nd_type(node) == NODE_CFUNC) {
- result = (*node->nd_cfnc)(val,node->nd_argc);
+ result = (*node->nd_cfnc)(val,node->nd_argc,self);
}
else {
- result = rb_eval(node);
+ result = rb_eval(self, node);
+ }
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ goto redo;
+ case TAG_NEXT:
+ state = 0;
+ break;
+ case TAG_BREAK:
+ case TAG_RETURN:
+ state->nd_tlev = block->level;
+ state->nd_tag = IN_BLOCK|state->nd_tag;
+ break;
+ default:
+ break;
}
- break;
- case TAG_REDO:
- goto redo;
- case TAG_CONTINUE:
- state = 0;
- break;
- case TAG_BREAK:
- case TAG_RETURN:
- target_level = block->level;
- state = IN_BLOCK|state;
- break;
- default:
- break;
}
POP_TAG();
- POP_SELF();
POP_ITER();
+ POP_CLASS();
POP_VARS();
the_block = block;
the_frame = the_frame->prev;
the_scope = old_scope;
if (state) JUMP_TAG(state);
-
return result;
}
@@ -1823,11 +2120,10 @@ static VALUE
f_loop()
{
for (;;) { rb_yield(Qnil); }
- return Qnil;
}
static VALUE
-masign(node, val)
+masign(self, node, val)
NODE *node;
VALUE val;
{
@@ -1842,30 +2138,31 @@ masign(node, val)
}
len = RARRAY(val)->len;
for (i=0; list && i<len; i++) {
- asign(list->nd_head, RARRAY(val)->ptr[i]);
+ asign(self, list->nd_head, RARRAY(val)->ptr[i]);
list = list->nd_next;
}
if (node->nd_args) {
if (!list && i<len) {
- asign(node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i));
+ asign(self, node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i));
}
else {
- asign(node->nd_args, Qnil);
+ asign(self, node->nd_args, ary_new2(0));
}
}
}
else if (node->nd_args) {
- asign(node->nd_args, Qnil);
+ asign(self, node->nd_args, Qnil);
}
while (list) {
- asign(list->nd_head, Qnil);
+ asign(self, list->nd_head, Qnil);
list = list->nd_next;
}
return val;
}
static void
-asign(lhs, val)
+asign(self, lhs, val)
+ VALUE self;
NODE *lhs;
VALUE val;
{
@@ -1875,7 +2172,7 @@ asign(lhs, val)
break;
case NODE_IASGN:
- rb_ivar_set(Qself, lhs->nd_vid, val);
+ rb_ivar_set(self, lhs->nd_vid, val);
break;
case NODE_LASGN:
@@ -1895,16 +2192,16 @@ asign(lhs, val)
case NODE_CALL:
{
VALUE recv;
- recv = rb_eval(lhs->nd_recv);
- if (lhs->nd_args->nd_head == Qnil) {
+ recv = rb_eval(self, lhs->nd_recv);
+ if (!lhs->nd_args->nd_head) {
/* attr set */
- rb_funcall(recv, lhs->nd_mid, 1, val);
+ rb_funcall2(recv, lhs->nd_mid, 1, &val);
}
else {
/* array set */
VALUE args;
- args = rb_eval(lhs->nd_args);
+ args = rb_eval(self, lhs->nd_args);
RARRAY(args)->ptr[RARRAY(args)->len-1] = val;
rb_apply(recv, lhs->nd_mid, args);
}
@@ -1922,13 +2219,15 @@ rb_iterate(it_proc, data1, bl_proc, data2)
VALUE (*it_proc)(), (*bl_proc)();
void *data1, *data2;
{
- int state;
+ NODE *state;
VALUE retval = Qnil;
NODE *node = NEW_CFUNC(bl_proc, data2);
+ VALUE self = TopSelf;
+ int tag_level;
iter_retry:
PUSH_ITER(ITER_PRE);
- PUSH_BLOCK(Qnil, node);
+ PUSH_BLOCK(0, node);
PUSH_TAG();
state = EXEC_TAG();
@@ -1937,33 +2236,53 @@ rb_iterate(it_proc, data1, bl_proc, data2)
}
POP_TAG();
+ tag_level = the_block->level;
POP_BLOCK();
POP_ITER();
- switch (state) {
- case 0:
- break;
+ if (state) {
+ switch (state->nd_tag) {
+ case TAG_RETRY:
+ goto iter_retry;
- case TAG_RETRY:
- goto iter_retry;
+ case IN_BLOCK|TAG_BREAK:
+ if (state->nd_tlev != tag_level) {
+ JUMP_TAG(state);
+ }
+ retval = Qnil;
+ break;
- case IN_BLOCK|TAG_BREAK:
- if (target_level != tag_level) {
+ case IN_BLOCK|TAG_RETURN:
+ if (state->nd_tlev == tag_level) {
+ state->nd_tag &= ~IN_BLOCK;
+ }
+ /* fall through */
+ default:
JUMP_TAG(state);
}
- retval = Qnil;
- break;
+ }
+ return retval;
+}
- case IN_BLOCK|TAG_RETURN:
- if (target_level == tag_level) {
- state &= ~IN_BLOCK;
- }
- /* fall through */
- default:
- JUMP_TAG(state);
+static int
+handle_rescue(self, node)
+ VALUE self;
+ NODE *node;
+{
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+
+ if (!node->nd_args) {
+ return obj_is_kind_of(errinfo, eException);
}
- return retval;
+ PUSH_ITER(ITER_NOT);
+ SETUP_ARGS;
+ POP_ITER();
+ while (argc--) {
+ if (obj_is_kind_of(errinfo, argv[0])) return 1;
+ argv++;
+ }
+ return 0;
}
VALUE
@@ -1971,43 +2290,36 @@ rb_rescue(b_proc, data1, r_proc, data2)
VALUE (*b_proc)(), (*r_proc)();
void *data1, *data2;
{
- int state;
- VALUE result = Qnil;
- volatile SIGHANDLE handle;
+ NODE *state;
+ VALUE result;
PUSH_TAG();
- switch (state = EXEC_TAG()) {
- case 0:
- handle = sig_beg();
+ if ((state = EXEC_TAG()) == 0) {
retry_entry:
result = (*b_proc)(data1);
- break;
-
- case TAG_FAIL:
- sig_end(handle);
- if (r_proc) {
- PUSH_TAG();
- state = EXEC_TAG();
- if (state == 0) {
- result = (*r_proc)(data2);
+ }
+ else {
+ if (state->nd_tag == TAG_RAISE) {
+ if (r_proc) {
+ PUSH_TAG();
+ state = EXEC_TAG();
+ if (state == 0) {
+ result = (*r_proc)(data2, errinfo);
+ }
+ POP_TAG();
+ if (state && state->nd_tag == TAG_RETRY) {
+ state = 0;
+ goto retry_entry;
+ }
}
- POP_TAG();
- if (state == TAG_RETRY) {
- goto retry_entry;
+ else {
+ result = Qnil;
+ state = 0;
+ }
+ if (state == 0) {
+ errat = Qnil;
}
}
- else {
- state = 0;
- }
- if (state == 0) {
- errat = Qnil;
- last_func = 0;
- }
- break;
-
- default:
- sig_end(handle);
- break;
}
POP_TAG();
if (state) JUMP_TAG(state);
@@ -2020,7 +2332,7 @@ rb_ensure(b_proc, data1, e_proc, data2)
VALUE (*b_proc)(), (*e_proc)();
void *data1, *data2;
{
- int state;
+ NODE *state;
VALUE result = Qnil;
PUSH_TAG();
@@ -2030,7 +2342,7 @@ rb_ensure(b_proc, data1, e_proc, data2)
POP_TAG();
(*e_proc)(data2);
- if (state != 0) {
+ if (state) {
JUMP_TAG(state);
}
return result;
@@ -2044,7 +2356,7 @@ f_missing(argc, argv, obj)
VALUE *argv;
VALUE obj;
{
- VALUE desc;
+ VALUE desc = 0;
ID id;
char *format;
struct FRAME *frame;
@@ -2052,27 +2364,44 @@ f_missing(argc, argv, obj)
id = FIX2INT(argv[0]);
argc--; argv++;
- if (TYPE(obj) == T_STRING) {
- desc = krn_inspect(obj);
- }
- else {
+ switch (TYPE(obj)) {
+ case T_NIL:
+ format = "undefined method `%s' for nil";
+ break;
+ case T_TRUE:
+ format = "undefined method `%s' for TRUE";
+ break;
+ case T_FALSE:
+ format = "undefined method `%s' for FALSE";
+ break;
+ case T_OBJECT:
desc = obj_as_string(obj);
+ break;
+ default:
+ desc = rb_inspect(obj);
+ break;
+ }
+ if (desc) {
+ if (last_noex)
+ format = "private method `%s' called for %s(%s)";
+ else if (argc == 0) {
+ format = "undefined local variable or method `%s' for %s(%s)";
+ }
+ else {
+ format = "undefined method `%s' for %s(%s)";
+ }
+ if (RSTRING(desc)->len > 65) {
+ desc = krn_to_s(obj);
+ }
}
- if (last_noex)
- format = "method `%s' not available for %s(%s)";
- else
- format = "undefined method `%s' for %s(%s)";
- /* 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)));
+ PUSH_FRAME(); /* fake frame */
+ *the_frame = *_frame.prev->prev;
+
+ NameError(format,
+ rb_id2name(id),
+ desc?RSTRING(desc)->ptr:"",
+ desc?rb_class2name(CLASS_OF(obj)):"");
POP_FRAME();
}
@@ -2095,8 +2424,20 @@ 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;
+#define STACK_LEVEL_MAX 655350
+extern VALUE *gc_stack_start;
+static int
+stack_length()
+{
+ VALUE pos;
+
+#ifdef sparc
+ return gc_stack_start - &pos + 0x80;
+#else
+ return (&pos < gc_stack_start) ? gc_stack_start - &pos
+ : &pos - gc_stack_start;
+#endif
+}
static VALUE
rb_call(class, recv, mid, argc, argv, scope)
@@ -2109,38 +2450,30 @@ rb_call(class, recv, mid, argc, argv, scope)
{
NODE *body;
int noex;
- VALUE result = Qnil;
+ ID id = mid;
struct cache_entry *ent;
+ VALUE result = Qnil;
int itr;
enum node_type type;
+ static int tick;
/* is it in the method cache? */
ent = cache + EXPR1(class, mid);
if (ent->mid == mid && ent->class == class) {
class = ent->origin;
- mid = ent->mid;
- body = ent->method;
+ id = ent->mid0;
noex = ent->noex;
+ body = ent->method;
}
- else {
- ID id = mid;
-
- if ((body = rb_get_method_body(&class, &id, &noex)) == FALSE) {
- return rb_undefined(recv, mid, argc, argv, 0);
- }
- mid = id;
+ else if ((body = rb_get_method_body(&class, &id, &noex)) == 0) {
+ return rb_undefined(recv, mid, argc, argv, 0);
}
- switch (noex) {
- case NOEX_PUBLIC:
- break;
- case NOEX_PRIVATE:
- if (scope == 0) /* receiver specified */
- return rb_undefined(recv, mid, argc, argv, 1);
- break;
- }
+ /* receiver specified form for private method */
+ if (noex == NOEX_PRIVATE && scope == 0)
+ return rb_undefined(recv, mid, argc, argv, 1);
- switch (iter->iter) {
+ switch (the_iter->iter) {
case ITER_PRE:
itr = ITER_CUR;
break;
@@ -2153,16 +2486,15 @@ rb_call(class, recv, mid, argc, argv, scope)
type = nd_type(body);
if (type == NODE_ZSUPER) {
/* for re-scoped method */
- return rb_call(class->super, recv, mid, argc, argv, scope?scope:1);
+ return rb_call(class->super, recv, id, argc, argv, scope?scope:1);
}
- if (stack_level++ > STACK_LEVEL_MAX)
- Fail("stack level too deep");
+ if (++tick % 1000 == 0 && stack_length() > STACK_LEVEL_MAX)
+ Fatal("stack level too deep");
PUSH_ITER(itr);
- PUSH_SELF(recv);
PUSH_FRAME();
- the_frame->last_func = mid;
+ the_frame->last_func = id;
the_frame->last_class = class;
the_frame->argc = argc;
the_frame->argv = argv;
@@ -2173,7 +2505,7 @@ rb_call(class, recv, mid, argc, argv, scope)
int len = body->nd_argc;
if (len >= 0 && argc != len) {
- Fail("Wrong # of arguments(%d for %d)", argc, len);
+ ArgError("Wrong # of arguments(%d for %d)", argc, len);
}
switch (len) {
@@ -2273,7 +2605,7 @@ rb_call(class, recv, mid, argc, argv, scope)
len, rb_class2name(class), rb_id2name(mid));
}
else {
- Fail("too many arguments(%d)", len);
+ ArgError("too many arguments(%d)", len);
}
break;
}
@@ -2283,19 +2615,20 @@ rb_call(class, recv, mid, argc, argv, scope)
/* for attr get/set */
case NODE_ATTRSET:
case NODE_IVAR:
- result = rb_eval(body);
+ result = rb_eval(recv, body);
break;
default:
{
- int state;
+ NODE *state;
VALUE *local_vars;
PUSH_SCOPE();
- if (body->nd_cnt > 0) {
- local_vars = ALLOCA_N(VALUE, body->nd_cnt);
- MEMZERO(local_vars, VALUE, body->nd_cnt);
+ if (body->nd_rval) the_frame->cbase = body->nd_rval;
+ if (body->nd_tbl) {
+ local_vars = ALLOCA_N(VALUE, body->nd_tbl[0]);
+ memclear(local_vars, body->nd_tbl[0]);
the_scope->local_tbl = body->nd_tbl;
the_scope->local_vars = local_vars;
}
@@ -2306,6 +2639,8 @@ rb_call(class, recv, mid, argc, argv, scope)
body = body->nd_body;
PUSH_TAG();
+ PUSH_VARS();
+ dyna_var_mark();
state = EXEC_TAG();
if (state == 0) {
if (nd_type(body) == NODE_BLOCK) {
@@ -2321,7 +2656,7 @@ rb_call(class, recv, mid, argc, argv, scope)
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);
+ ArgError("Wrong # of arguments(%d for %d)", argc, i);
}
if (local_vars) {
@@ -2333,56 +2668,54 @@ rb_call(class, recv, mid, argc, argv, scope)
NODE *opt = node->nd_opt;
while (opt && argc) {
- asign(opt->nd_head, *argv);
+ asign(recv, opt->nd_head, *argv);
argv++; argc--;
opt = opt->nd_next;
}
- rb_eval(opt);
+ rb_eval(recv, 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);
+ local_vars[node->nd_rest]=ary_new2(0);
}
}
}
else if (nd_type(body) == NODE_ARGS) {
body = 0;
}
- result = rb_eval(body);
+ result = rb_eval(recv, body);
}
+ POP_VARS();
POP_TAG();
POP_SCOPE();
- switch (state) {
- case 0:
- break;
- case TAG_CONTINUE:
- Fatal("unexpected continue");
- break;
- case TAG_BREAK:
- Fatal("unexpected break");
- break;
- case TAG_REDO:
- Fatal("unexpected redo");
- break;
- case TAG_RETURN:
- result = last_val;
- break;
- case TAG_RETRY:
- if (!iterator_p()) {
- Fatal("retry outside of rescue clause");
+ if (state) {
+ switch (state->nd_tag) {
+ case TAG_NEXT:
+ Raise(eLocalJumpError, "unexpected next");
+ break;
+ case TAG_BREAK:
+ Raise(eLocalJumpError, "unexpected break");
+ break;
+ case TAG_REDO:
+ Raise(eLocalJumpError, "unexpected redo");
+ break;
+ case TAG_RETURN:
+ result = state->nd_tval;
+ break;
+ case TAG_RETRY:
+ if (!iterator_p()) {
+ Raise(eLocalJumpError, "retry outside of rescue clause");
+ }
+ default:
+ JUMP_TAG(state);
}
- default:
- stack_level--;
- JUMP_TAG(state);
}
}
}
POP_FRAME();
- POP_SELF();
POP_ITER();
- stack_level--;
return result;
}
@@ -2410,7 +2743,7 @@ f_send(argc, argv, recv)
VALUE vid;
ID mid;
- if (argc == 0) Fail("no method name given");
+ if (argc == 0) ArgError("no method name given");
vid = argv[0]; argc--; argv++;
if (TYPE(vid) == T_STRING) {
@@ -2463,26 +2796,31 @@ rb_funcall2(recv, mid, argc, argv)
}
static VALUE
-f_caller(argc, argv)
- int argc;
- VALUE *argv;
+backtrace(lev)
+ int lev;
{
- VALUE level;
struct FRAME *frame = the_frame;
- int lev, n;
char buf[BUFSIZ];
+ VALUE ary;
- 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);
+ ary = ary_new();
+ if (lev < 0) {
+ if (frame->last_func) {
+ sprintf(buf, "%s:%d:in `%s'", sourcefile, sourceline,
+ rb_id2name(frame->last_func));
+ }
+ else {
+ sprintf(buf, "%s:%d", sourcefile, sourceline);
+ }
+ ary_push(ary, str_new2(buf));
+ }
else {
- while (n-- > 0) {
+ while (lev-- > 0) {
frame = frame->prev;
if (!frame) return Qnil;
}
- if (!frame->file) return Qnil;
+ }
+ while (frame && frame->file) {
if (frame->prev && frame->prev->last_func) {
sprintf(buf, "%s:%d:in `%s'",
frame->file, frame->line,
@@ -2491,73 +2829,169 @@ f_caller(argc, argv)
else {
sprintf(buf, "%s:%d", frame->file, frame->line);
}
+ ary_push(ary, str_new2(buf));
+ frame = frame->prev;
}
- return str_new2(buf);
+ return ary;
+}
+
+static VALUE
+f_caller(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE level;
+ struct FRAME *frame = the_frame;
+ int lev;
+
+ rb_scan_args(argc, argv, "01", &level);
+
+ if (NIL_P(level)) lev = 1;
+ else lev = NUM2INT(level);
+ if (lev < 0) ArgError("negative level(%d)", lev);
+
+ return backtrace(lev);
}
void
rb_backtrace()
{
- VALUE c, lev;
- int n = 0;
+ int i, lev;
+ VALUE ary, c;
- lev = INT2FIX(n);
- while (c = f_caller(1, &lev)) {
- printf("%s\n", RSTRING(c)->ptr);
- n++;
- lev = INT2FIX(n);
+ lev = INT2FIX(0);
+ ary = backtrace(-1);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr)->ptr);
}
}
+static VALUE
+make_backtrace()
+{
+ VALUE lev;
+
+ lev = INT2FIX(0);
+ return backtrace(-1);
+}
+
ID
rb_frame_last_func()
{
return the_frame->last_func;
}
-int rb_in_eval = 0;
+static NODE*
+compile(src)
+ struct RString *src;
+{
+ NODE *node;
+
+ Check_Type(src, T_STRING);
+
+ errinfo = Qnil;
+ node = compile_string(sourcefile, src->ptr, src->len);
+
+ if (nerrs == 0) return node;
+ return 0;
+}
+
+static void blk_free();
static VALUE
-f_eval(obj, src)
- VALUE obj;
+eval(self, src, scope)
+ VALUE self;
struct RString *src;
+ struct RData *scope;
{
+ struct BLOCK *data;
VALUE result = Qnil;
- int state;
NODE *node;
+ NODE *state;
+ struct BLOCK *old_block;
+ struct SCOPE *old_scope;
+ struct FRAME frame;
+ char *file = sourcefile;
+ int line = sourceline;
- Check_Type(src, T_STRING);
PUSH_TAG();
- rb_in_eval = 1;
- node = eval_tree;
-
PUSH_CLASS();
+ if (!NIL_P(scope)) {
+ if (TYPE(scope) != T_DATA || scope->dfree != blk_free) {
+ TypeError("wrong argument type %s (expected Proc/Binding)",
+ rb_class2name(CLASS_OF(scope)));
+ }
+
+ Get_Data_Struct(scope, struct BLOCK, data);
+
+ /* PUSH BLOCK from data */
+ frame = data->frame;
+ frame.prev = the_frame;
+ the_frame = &(frame);
+ old_scope = the_scope;
+ the_scope = data->scope;
+ old_block = the_block;
+ the_block = data->prev;
+ the_dyna_vars = data->d_vars;
+ the_class = data->class;
+ self = data->self;
+ }
+
+ rb_in_eval++;
if (TYPE(the_class) == T_ICLASS) {
the_class = (struct RClass*)RBASIC(the_class)->class;
}
-
if ((state = EXEC_TAG()) == 0) {
- lex_setsrc("(eval)", src->ptr, src->len);
- eval_tree = 0;
- PUSH_VARS();
- yyparse();
- POP_VARS();
- if (nerrs == 0) {
- result = Eval();
+ if (!compile(src)) {
+ rb_in_eval--;
+ compile_error("eval()");
}
+ result = eval_node(self);
+ }
+ if (!NIL_P(scope)) {
+ the_frame = the_frame->prev;
+ the_scope = old_scope;
+ the_block = old_block;
}
- eval_tree = node;
POP_CLASS();
POP_TAG();
- if (state) JUMP_TAG(state);
-
- if (nerrs > 0) {
- syntax_error();
+ rb_in_eval--;
+ if (state) {
+ VALUE err ;
+
+ switch (state->nd_tag) {
+ case TAG_RAISE:
+ sourcefile = file;
+ sourceline = line;
+ if (strcmp(sourcefile, "(eval)") == 0) {
+ err = errat;
+ if (sourceline != 1) {
+ str_cat(err, ": ", 2);
+ str_cat(err, RSTRING(errinfo)->ptr, RSTRING(errinfo)->len);
+ }
+ errat = Qnil;
+ rb_raise(exc_new2(CLASS_OF(errinfo), err));
+ }
+ rb_raise(Qnil);
+ }
+ JUMP_TAG(state);
}
return result;
}
+static VALUE
+f_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE src, scope;
+
+ rb_scan_args(argc, argv, "11", &src, &scope);
+ return eval(self, src, scope);
+}
+
VALUE rb_load_path;
char *dln_find_file();
@@ -2567,20 +3001,18 @@ find_file(file)
char *file;
{
extern VALUE rb_load_path;
- VALUE sep, vpath;
+ VALUE vpath;
char *path;
if (file[0] == '/') return file;
if (rb_load_path) {
Check_Type(rb_load_path, T_ARRAY);
- sep = str_new2(":");
- vpath = ary_join(rb_load_path, sep);
+ vpath = ary_join(rb_load_path, str_new2(":"));
path = RSTRING(vpath)->ptr;
- sep = Qnil;
}
else {
- path = Qnil;
+ path = 0;
}
return dln_find_file(file, path);
@@ -2591,36 +3023,39 @@ f_load(obj, fname)
VALUE obj;
struct RString *fname;
{
- int state, in_eval = rb_in_eval;
+ NODE *state;
char *file, *src;
+ volatile ID last_func;
Check_Type(fname, T_STRING);
file = find_file(fname->ptr);
- if (!file) Fail("No such file to load -- %s", fname->ptr);
+ if (!file) LoadError("No such file to load -- %s", fname->ptr);
- PUSH_SELF(TopSelf);
PUSH_TAG();
PUSH_CLASS();
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;
+
state = EXEC_TAG();
+ last_func = the_frame->last_func;
+ the_frame->last_func = 0;
if (state == 0) {
+ rb_in_eval++;
rb_load_file(file);
+ rb_in_eval--;
if (nerrs == 0) {
- Eval();
+ eval_node(TopSelf);
}
}
+ the_frame->last_func = last_func;
top_scope->flag = the_scope->flag;
POP_SCOPE();
POP_CLASS();
POP_TAG();
- POP_SELF();
- rb_in_eval = in_eval;
if (nerrs > 0) {
- rb_fail(errstr);
+ rb_raise(errinfo);
}
if (state) JUMP_TAG(state);
@@ -2629,16 +3064,17 @@ f_load(obj, fname)
static VALUE rb_features;
-static VALUE
+static int
rb_provided(feature)
char *feature;
{
+ struct RArray *features = RARRAY(rb_features);
VALUE *p, *pend;
char *f;
int len;
- p = RARRAY(rb_features)->ptr;
- pend = p + RARRAY(rb_features)->len;
+ p = features->ptr;
+ pend = p + features->len;
while (p < pend) {
Check_Type(*p, T_STRING);
f = RSTRING(*p)->ptr;
@@ -2653,6 +3089,11 @@ rb_provided(feature)
return FALSE;
}
+#ifdef THREAD
+static int thread_loading();
+static void thread_loading_done();
+#endif
+
void
rb_provide(feature)
char *feature;
@@ -2670,7 +3111,8 @@ f_require(obj, fname)
VALUE load;
Check_Type(fname, T_STRING);
- if (rb_provided(fname->ptr)) return FALSE;
+ if (rb_provided(fname->ptr))
+ return FALSE;
ext = strrchr(fname->ptr, '.');
if (ext) {
@@ -2680,9 +3122,9 @@ f_require(obj, fname)
if (file) goto rb_load;
}
else if (strcmp(".o", ext) == 0) {
- feature = fname->ptr;
+ file = feature = fname->ptr;
if (strcmp(".o", DLEXT) != 0) {
- buf = ALLOCA_N(char, strlen(fname->ptr) + 3);
+ buf = ALLOCA_N(char, strlen(fname->ptr)+sizeof(DLEXT)+1);
strcpy(buf, feature);
ext = strrchr(buf, '.');
strcpy(ext, DLEXT);
@@ -2696,7 +3138,7 @@ f_require(obj, fname)
if (file) goto dyna_load;
}
}
- buf = ALLOCA_N(char, strlen(fname->ptr) + 4);
+ buf = ALLOCA_N(char, strlen(fname->ptr) + 5);
sprintf(buf, "%s.rb", fname->ptr);
file = find_file(buf);
if (file) {
@@ -2710,58 +3152,80 @@ f_require(obj, fname)
feature = buf;
goto dyna_load;
}
- Fail("No such file to load -- %s", fname->ptr);
+ LoadError("No such file to load -- %s", fname->ptr);
dyna_load:
- load = str_new2(file);
- file = RSTRING(load)->ptr;
- dln_load(file);
- rb_provide(feature);
+#ifdef THREAD
+ if (thread_loading(feature)) return FALSE;
+ else {
+ NODE *state;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+#endif
+ load = str_new2(file);
+ file = RSTRING(load)->ptr;
+ dln_load(file);
+ rb_provide(feature);
+#ifdef THREAD
+ }
+ POP_TAG();
+ thread_loading_done();
+ if (state) JUMP_TAG(state);
+ }
+#endif
return TRUE;
rb_load:
- f_load(obj, fname);
- rb_provide(feature);
+#ifdef THREAD
+ if (thread_loading(feature)) return FALSE;
+ else {
+ NODE *state;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+#endif
+ f_load(obj, fname);
+ rb_provide(feature);
+#ifdef THREAD
+ }
+ POP_TAG();
+ thread_loading_done();
+ if (state) JUMP_TAG(state);
+ }
+#endif
return TRUE;
}
static void
-set_method_visibility(argc, argv, ex)
+set_method_visibility(self, argc, argv, ex)
+ VALUE self;
int argc;
VALUE *argv;
int ex;
{
- 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);
+ rb_export_method(self, rb_to_id(argv[i]), ex);
}
}
static VALUE
-mod_public(argc, argv)
+mod_public(argc, argv, module)
int argc;
VALUE *argv;
+ VALUE module;
{
- set_method_visibility(argc, argv, NOEX_PUBLIC);
+ set_method_visibility(module, argc, argv, NOEX_PUBLIC);
return Qnil;
}
static VALUE
-mod_private(argc, argv)
+mod_private(argc, argv, module)
int argc;
VALUE *argv;
+ VALUE module;
{
- set_method_visibility(argc, argv, NOEX_PRIVATE);
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
return Qnil;
}
@@ -2775,19 +3239,13 @@ mod_modfunc(argc, argv, module)
ID id;
NODE *body, *old;
- set_method_visibility(argc, argv, NOEX_PRIVATE);
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
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);
- }
+ id = rb_to_id(argv[i]);
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));
+ NameError("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);
}
@@ -2806,7 +3264,21 @@ mod_include(argc, argv, module)
Check_Type(argv[i], T_MODULE);
rb_include_module(module, argv[i]);
}
- return (VALUE)module;
+ return Qnil;
+}
+
+VALUE /* moved from object.c for push_iter */
+class_s_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ VALUE obj = obj_alloc(class);
+
+ PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT);
+ rb_funcall2(obj, init, argc, argv);
+ POP_ITER();
+ return obj;
}
static VALUE
@@ -2823,7 +3295,13 @@ obj_extend(argc, argv, obj)
VALUE *argv;
VALUE obj;
{
- return mod_include(argc, argv, rb_singleton_class(obj));
+ int i;
+
+ mod_include(argc, argv, rb_singleton_class(obj));
+ for (i=0; i<argc; i++) {
+ rb_funcall(argv[i], rb_intern("object_extended"), 1, obj);
+ }
+ return Qnil;
}
void
@@ -2833,16 +3311,66 @@ rb_extend_object(obj, module)
rb_include_module(rb_singleton_class(obj), module);
}
-extern VALUE cKernel;
extern VALUE cModule;
VALUE f_trace_var();
VALUE f_untrace_var();
+extern VALUE rb_str_setter();
+
+static VALUE
+errat_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (!NIL_P(val) && TYPE(val) != T_ARRAY) {
+ TypeError("value of $@ must be Array of String");
+ }
+ return *var = val;
+}
+
+static VALUE
+f_catch(dmy, tag)
+{
+ NODE *state;
+ ID t;
+ VALUE val;
+
+ t = rb_to_id(tag);
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_yield(tag);
+ }
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_THROW && state->nd_tlev == t) {
+ return state->nd_tval;
+ }
+ JUMP_TAG(state);
+ }
+ return val;
+}
+
+static VALUE
+f_throw(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tag, value;
+ ID t;
+
+ rb_scan_args(argc, argv, "11", &tag, &value);
+ t = rb_to_id(tag);
+ JUMP_TAG3(TAG_THROW, value, t);
+ /* not reached */
+}
+
void
Init_eval()
{
- match = rb_intern("=~");
+ init = rb_intern("initialize");
+ eqq = rb_intern("===");
each = rb_intern("each");
aref = rb_intern("[]");
@@ -2851,25 +3379,49 @@ Init_eval()
rb_global_variable(&top_scope);
rb_global_variable(&eval_tree);
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_hooked_variable("$@", &errat, 0, errat_setter);
+ rb_define_hooked_variable("$!", &errinfo, 0, rb_str_setter);
+
+ 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_method(cKernel, "respond_to?", krn_respond_to, -1);
+
+ rb_define_private_method(cKernel, "break", f_break, 0);
+ rb_define_alias(cKernel, "break!", "break");
+ rb_define_private_method(cKernel, "next", f_next, 0);
+ rb_define_alias(cKernel, "next!", "next");
+ rb_define_alias(cKernel, "continue", "next");
+ rb_define_private_method(cKernel, "redo", f_redo, 0);
+ rb_define_alias(cKernel, "redo!", "redo");
+ rb_define_private_method(cKernel, "retry", f_retry, 0);
+ rb_define_alias(cKernel, "retry!", "retry");
+ rb_define_private_method(cKernel, "raise", f_raise, -1);
+ rb_define_alias(cKernel, "fail", "raise");
+
rb_define_private_method(cKernel, "caller", f_caller, -1);
+ rb_define_private_method(cKernel, "exit", f_exit, -1);
+
+ rb_define_private_method(cKernel, "catch", f_catch, 1);
+ rb_define_private_method(cKernel, "throw", f_throw, -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(cModule, "method_defined?", mod_method_defined, 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);
+ rb_define_private_method(cKernel, "untrace_var", f_untrace_var, -1);
}
VALUE f_autoload();
@@ -2909,8 +3461,6 @@ scope_dup(scope)
}
}
-static ID blkdata;
-
static void
blk_mark(data)
struct BLOCK *data;
@@ -2931,6 +3481,29 @@ blk_free(data)
}
static VALUE
+f_binding(self)
+ VALUE self;
+{
+ extern VALUE cData;
+ struct BLOCK *data;
+ VALUE bind;
+
+ PUSH_BLOCK(0,0);
+ bind = Make_Data_Struct(cData, struct BLOCK, blk_mark, blk_free, data);
+ MEMCPY(data, the_block, struct BLOCK, 1);
+
+ data->iter = ITER_NOT;
+ data->frame.last_func = 0;
+ 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);
+ POP_BLOCK();
+
+ return bind;
+}
+
+static VALUE
proc_s_new(class)
VALUE class;
{
@@ -2938,15 +3511,13 @@ proc_s_new(class)
struct BLOCK *data;
if (!iterator_p() && !f_iterator_p()) {
- Fail("tryed to create Procedure-Object out of iterator");
+ ArgError("tryed to create Procedure-Object out of iterator");
}
- proc = obj_alloc(class);
-
- if (!blkdata) blkdata = rb_intern("blk");
- Make_Data_Struct(proc, blkdata, struct BLOCK, blk_mark, blk_free, data);
- MEMCPY(data, the_block, struct BLOCK, 1);
+ proc = Make_Data_Struct(class, struct BLOCK, blk_mark, blk_free, data);
+ *data = *the_block;
+ data->iter = ITER_NOT;
data->frame.argv = ALLOC_N(VALUE, data->frame.argc);
MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc);
@@ -2967,7 +3538,8 @@ proc_call(proc, args)
{
struct BLOCK *data;
VALUE result = Qnil;
- int state;
+ NODE *state;
+ int tag_level;
if (TYPE(args) == T_ARRAY) {
switch (RARRAY(args)->len) {
@@ -2980,11 +3552,12 @@ proc_call(proc, args)
}
}
- Get_Data_Struct(proc, blkdata, struct BLOCK, data);
+ Get_Data_Struct(proc, struct BLOCK, data);
/* PUSH BLOCK from data */
PUSH_BLOCK2(data);
PUSH_ITER(ITER_CUR);
+ the_frame->iter = ITER_CUR;
PUSH_TAG();
state = EXEC_TAG();
@@ -2994,34 +3567,1047 @@ proc_call(proc, args)
POP_TAG();
POP_ITER();
+ tag_level = the_block->level;
POP_BLOCK();
- switch (state) {
- case 0:
- break;
- case TAG_BREAK:
- case IN_BLOCK|TAG_BREAK:
- Fail("break from block-closure");
- break;
- case TAG_RETURN:
- case IN_BLOCK|TAG_RETURN:
- Fail("return from block-closure");
- break;
- default:
+ if (state) {
+ if (data->scope && (data->scope->flag & SCOPE_NOSTACK)) {
+ /* orphan procedure */
+ switch (state->nd_tag) {
+ case TAG_BREAK: /* never happen */
+ break;
+ case IN_BLOCK|TAG_BREAK:
+ if (state->nd_tlev != tag_level)
+ Raise(eLocalJumpError, "break from proc-closure");
+ break;
+ case TAG_RETRY:
+ Raise(eLocalJumpError, "retry from proc-closure");
+ break;
+ case TAG_RETURN: /* never happen */
+ case IN_BLOCK|TAG_RETURN:
+ Raise(eLocalJumpError, "return from proc-closure");
+ break;
+ }
+ }
+ else {
+ state->nd_tag &= ~IN_BLOCK;
+ }
JUMP_TAG(state);
}
-
return result;
}
void
Init_Proc()
{
- cProc = rb_define_class("Proc", cObject);
+ eLocalJumpError = rb_define_class("LocalJumpError", eException);
+ cProc = rb_define_class("Proc", cObject);
rb_define_singleton_method(cProc, "new", proc_s_new, 0);
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);
+ rb_define_private_method(cKernel, "lambda", f_lambda, 0);
+ rb_define_private_method(cKernel, "binding", f_binding, 0);
}
+
+#ifdef THREAD
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+int thread_pending = 0;
+
+static VALUE cThread;
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+
+extern VALUE last_status;
+
+enum thread_status {
+ THREAD_RUNNABLE,
+ THREAD_STOPPED,
+ THREAD_TO_KILL,
+ THREAD_KILLED,
+};
+
+#define WAIT_FD (1<<0)
+#define WAIT_TIME (1<<1)
+#define WAIT_JOIN (1<<2)
+
+/* +infty, for this purpose */
+#define DELAY_INFTY 1E30
+
+typedef struct thread * thread_t;
+
+struct thread {
+ struct thread *next, *prev;
+ jmp_buf context;
+ VALUE (*func)();
+ void *arg;
+
+ VALUE result;
+
+ int stk_len;
+ int stk_max;
+ VALUE*stk_ptr;
+ VALUE*stk_pos;
+
+ struct FRAME *frame;
+ struct SCOPE *scope;
+ struct RClass *class;
+ struct RVarmap *dyna_vars;
+ struct BLOCK *block;
+ struct iter *iter;
+ struct tag *tag;
+
+ char *file;
+ int line;
+
+ VALUE errat, errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ enum thread_status status;
+ int wait_for;
+ int fd;
+ double delay;
+ thread_t join;
+ VALUE thread;
+};
+
+static thread_t curr_thread;
+static int num_waiting_on_fd;
+static int num_waiting_on_timer;
+static int num_waiting_on_join;
+
+thread_curr() {return (int)curr_thread;}
+
+#define FOREACH_THREAD(x) x = curr_thread; do { x = x->next;
+#define END_FOREACH(x) } while (x != curr_thread)
+
+/* Return the current time as a floating-point number */
+static double
+timeofday()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+}
+
+static thread_t main_thread;
+
+#define ADJ(addr) (void*)(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr)
+#define STACK(addr) (th->stk_pos<(addr) && (addr)<th->stk_pos+th->stk_len)
+
+static void
+thread_mark(th)
+ thread_t th;
+{
+ struct FRAME *frame;
+ struct BLOCK *block;
+
+ gc_mark(th->result);
+ gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len);
+#ifdef THINK_C
+ gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2);
+#endif
+ gc_mark(th->thread);
+ if (th->join) gc_mark(th->join->thread);
+
+ gc_mark(th->scope);
+ gc_mark(th->dyna_vars);
+ gc_mark(th->errat);
+ gc_mark(th->errinfo);
+ gc_mark(th->last_line);
+ gc_mark(th->last_match);
+
+ /* mark data in copied stack */
+ frame = th->frame;
+ while (frame && frame != top_frame) {
+ frame = ADJ(frame);
+ if (frame->argv && !STACK(frame->argv)) {
+ gc_mark_frame(frame);
+ }
+ frame = frame->prev;
+ }
+ block = th->block;
+ while (block) {
+ block = ADJ(block);
+ if (block->frame.argv && !STACK(block->frame.argv)) {
+ gc_mark_frame(&block->frame);
+ }
+ block = block->prev;
+ }
+}
+
+void
+gc_mark_threads()
+{
+ thread_t th;
+
+ FOREACH_THREAD(th) {
+ thread_mark(th);
+ } END_FOREACH(th);
+}
+
+static void
+thread_free(th)
+ thread_t th;
+{
+ if (th->stk_ptr) free(th->stk_ptr);
+ th->stk_ptr = 0;
+}
+
+static thread_t
+thread_check(data)
+ struct RData *data;
+{
+ if (TYPE(data) != T_DATA || data->dfree != thread_free) {
+ TypeError("wrong argument type %s (expected Thread)",
+ rb_class2name(CLASS_OF(data)));
+ }
+ return (thread_t)data->data;
+}
+
+VALUE lastline_get();
+void lastline_set();
+VALUE backref_get();
+void backref_set();
+
+static int
+thread_save_context(th)
+ thread_t th;
+{
+ VALUE v;
+
+ th->stk_len = stack_length();
+ th->stk_pos = (gc_stack_start<(VALUE*)&v)?gc_stack_start
+ :gc_stack_start - th->stk_len;
+ if (th->stk_len > th->stk_max) {
+ th->stk_max = th->stk_len;
+ REALLOC_N(th->stk_ptr, VALUE, th->stk_max);
+ }
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len);
+
+ th->frame = the_frame;
+ th->scope = the_scope;
+ th->class = the_class;
+ th->dyna_vars = the_dyna_vars;
+ th->block = the_block;
+ th->iter = the_iter;
+ th->tag = prot_tag;
+ th->errat = errat;
+ th->errinfo = errinfo;
+ th->last_status = last_status;
+ th->last_line = lastline_get();
+ th->last_match = backref_get();
+
+ th->file = sourcefile;
+ th->line = sourceline;
+}
+
+static void thread_restore_context();
+
+static void
+stack_extend(th, exit)
+ thread_t th;
+ int exit;
+{
+ VALUE space[1024];
+
+ memset(space, 0, 1); /* prevent array from optimization */
+ thread_restore_context(th, exit);
+}
+
+static void
+thread_restore_context(th, exit)
+ thread_t th;
+ int exit;
+{
+ VALUE v;
+ static thread_t tmp;
+ static int ex;
+
+ if (!th->stk_ptr) Bug("unsaved context");
+
+ if (&v < gc_stack_start) {
+ /* Stack grows downward */
+ if (&v > th->stk_pos) stack_extend(th, exit);
+ }
+ else {
+ /* Stack grows upward */
+ if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
+ }
+
+ the_frame = th->frame;
+ the_scope = th->scope;
+ the_class = th->class;
+ the_dyna_vars = th->dyna_vars;
+ the_block = th->block;
+ the_iter = th->iter;
+ prot_tag = th->tag;
+ the_class = th->class;
+ errat = th->errat;
+ errinfo = th->errinfo;
+ last_status = th->last_status;
+
+ lastline_set(th->last_line);
+ backref_set(th->last_match);
+
+ sourcefile = th->file;
+ sourceline = th->line;
+
+ tmp = th;
+ ex = exit;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len);
+
+ switch (ex) {
+ case 1:
+ JUMP_TAG2(TAG_FATAL, INT2FIX(0));
+ break;
+
+ case 2:
+ rb_interrupt();
+ break;
+
+ default:
+ longjmp(tmp->context, 1);
+ }
+}
+
+static void
+thread_ready(th)
+ thread_t th;
+{
+ /* The thread is no longer waiting on anything */
+ if (th->wait_for & WAIT_FD) {
+ num_waiting_on_fd--;
+ }
+ if (th->wait_for & WAIT_TIME) {
+ num_waiting_on_timer--;
+ }
+ if (th->wait_for & WAIT_JOIN) {
+ num_waiting_on_join--;
+ }
+ th->wait_for = 0;
+ th->status = THREAD_RUNNABLE;
+}
+
+static void
+thread_remove()
+{
+ thread_ready(curr_thread);
+ curr_thread->status = THREAD_KILLED;
+ curr_thread->prev->next = curr_thread->next;
+ curr_thread->next->prev = curr_thread->prev;
+ thread_schedule();
+}
+
+static int
+thread_dead(th)
+ thread_t th;
+{
+ return th->status == THREAD_KILLED;
+}
+
+void
+thread_schedule()
+{
+ thread_t next;
+ thread_t th;
+ thread_t curr;
+
+ thread_pending = 0;
+ if (curr_thread == curr_thread->next) return;
+
+ next = 0;
+ curr = curr_thread; /* real current thread */
+
+ if (curr_thread->status == THREAD_KILLED) {
+ curr_thread = curr_thread->prev;
+ }
+
+ again:
+ FOREACH_THREAD(th) {
+ if (th->status != THREAD_STOPPED && th->status != THREAD_KILLED) {
+ next = th;
+ break;
+ }
+ }
+ END_FOREACH(th);
+
+ if (num_waiting_on_join) {
+ FOREACH_THREAD(th) {
+ if ((th->wait_for & WAIT_JOIN) && thread_dead(th->join)) {
+ th->join = 0;
+ th->wait_for &= ~WAIT_JOIN;
+ th->status = THREAD_RUNNABLE;
+ num_waiting_on_join--;
+ if (!next) next = th;
+ }
+ }
+ END_FOREACH(th);
+ }
+
+ if (num_waiting_on_fd > 0 || num_waiting_on_timer > 0) {
+ fd_set readfds;
+ struct timeval delay_tv, *delay_ptr;
+ double delay, now;
+
+ int n, max;
+
+ do {
+ select_err:
+ max = 0;
+ FD_ZERO(&readfds);
+ if (num_waiting_on_fd > 0) {
+ FOREACH_THREAD(th) {
+ if (th->wait_for & WAIT_FD) {
+ FD_SET(th->fd, &readfds);
+ if (th->fd > max) max = th->fd;
+ }
+ }
+ END_FOREACH(th);
+ }
+
+ delay = DELAY_INFTY;
+ if (num_waiting_on_timer > 0) {
+ now = timeofday();
+ FOREACH_THREAD(th) {
+ if (th->wait_for & WAIT_TIME) {
+ if (th->delay <= now) {
+ th->delay = 0.0;
+ th->wait_for &= ~WAIT_TIME;
+ th->status = THREAD_RUNNABLE;
+ num_waiting_on_timer--;
+ next = th;
+ } else if (th->delay < delay) {
+ delay = th->delay;
+ }
+ }
+ }
+ END_FOREACH(th);
+ }
+ /* Do the select if needed */
+ if (num_waiting_on_fd > 0 || !next) {
+ /* Convert delay to a timeval */
+ /* If a thread is runnable, just poll */
+ if (next) {
+ delay_tv.tv_sec = 0;
+ delay_tv.tv_usec = 0;
+ delay_ptr = &delay_tv;
+ }
+ else if (delay == DELAY_INFTY) {
+ delay_ptr = 0;
+ }
+ else {
+ delay -= now;
+ delay_tv.tv_sec = (unsigned int)delay;
+ delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec) * 1e6;
+ delay_ptr = &delay_tv;
+ }
+ n = select(max+1, &readfds, 0, 0, delay_ptr);
+ if (n > 0) {
+ /* Some descriptors are ready.
+ Make the corresponding threads runnable. */
+ FOREACH_THREAD(th)
+ if ((th->wait_for&WAIT_FD)
+ && FD_ISSET(th->fd, &readfds)) {
+ /* Wake up only one thread per fd. */
+ FD_CLR(th->fd, &readfds);
+ th->status = THREAD_RUNNABLE;
+ th->fd = 0;
+ th->wait_for &= ~WAIT_FD;
+ num_waiting_on_fd--;
+ if (!next) next = th; /* Found one. */
+ }
+ END_FOREACH(th);
+ }
+ if (n < 0 && !next) goto select_err;
+ }
+ /* The delays for some of the threads should have expired.
+ Go through the loop once more, to check the delays. */
+ } while (!next && delay != DELAY_INFTY);
+ }
+
+ if (!next) {
+ FOREACH_THREAD(th) {
+ fprintf(stderr, "%s:%d:deadlock 0x%x: %d:%d %s\n",
+ th->file, th->line, th->thread, th->status,
+ th->wait_for, th==main_thread?"(main)":"");
+ }
+ END_FOREACH(th);
+ Fatal("Thread: deadlock");
+ }
+ if (next == curr) {
+ return;
+ }
+
+ /* context switch */
+ if (curr == curr_thread) {
+ thread_save_context(curr);
+ if (setjmp(curr->context)) {
+ return;
+ }
+ }
+
+ curr_thread = next;
+ if (next->status == THREAD_TO_KILL) {
+ /* execute ensure-clause if any */
+ thread_restore_context(next, 1);
+ }
+ thread_restore_context(next, 0);
+}
+
+void
+thread_wait_fd(fd)
+ int fd;
+{
+ if (curr_thread == curr_thread->next) return;
+
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->fd = fd;
+ num_waiting_on_fd++;
+ curr_thread->wait_for |= WAIT_FD;
+ thread_schedule();
+}
+
+void
+thread_fd_writable(fd)
+ int fd;
+{
+ struct timeval zero;
+ fd_set fds;
+
+ zero.tv_sec = zero.tv_usec = 0;
+ if (curr_thread == curr_thread->next) return;
+
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ if (select(fd+1, 0, &fds, 0, &zero) == 1) break;
+ thread_schedule();
+ }
+}
+
+void
+thread_wait_for(time)
+ struct timeval time;
+{
+ double date;
+
+ if (curr_thread == curr_thread->next) {
+ int n;
+#ifndef linux
+ double d, limit;
+ limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6;
+#endif
+ for (;;) {
+ TRAP_BEG;
+ n = select(0, 0, 0, 0, &time);
+ TRAP_END;
+ if (n == 0) return;
+
+#ifndef linux
+ d = limit - timeofday();
+
+ time.tv_sec = (int)d;
+ time.tv_usec = (int)((d - (int)d)*1e6);
+ if (time.tv_usec < 0) {
+ time.tv_usec += 1e6;
+ time.tv_sec -= 1;
+ }
+ if (time.tv_sec < 0) return;
+#endif
+ }
+ }
+
+ date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = date;
+ num_waiting_on_timer++;
+ curr_thread->wait_for |= WAIT_TIME;
+ thread_schedule();
+}
+
+void thread_sleep();
+
+int
+thread_select(max, read, write, except, timeout)
+ int max;
+ fd_set *read, *write, *except;
+ struct timeval *timeout;
+{
+ double limit;
+ struct timeval zero;
+ fd_set r, *rp, w, *wp, x, *xp;
+ int n;
+
+ if (!read && !write && !except) {
+ if (!timeout) {
+ thread_sleep();
+ return;
+ }
+ thread_wait_for(*timeout);
+ return 0;
+ }
+
+ if (timeout) {
+ limit = timeofday()+
+ (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
+ }
+
+ if (curr_thread == curr_thread->next) { /* no other thread */
+#ifndef linux
+ struct timeval tv, *tvp = timeout;
+
+ if (timeout) {
+ tv = *timeout;
+ tvp = &tv;
+ }
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, tvp);
+ TRAP_END;
+ if (n < 0 && errno == EINTR) {
+ if (timeout) {
+ double d = timeofday() - limit;
+
+ tv.tv_sec = (unsigned int)d;
+ tv.tv_usec = (d - (double)tv.tv_sec) * 1e6;
+ }
+ continue;
+ }
+ return n;
+ }
+#else
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, timeout);
+ TRAP_END;
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
+ return n;
+ }
+#endif
+
+ }
+
+ for (;;) {
+ zero.tv_sec = zero.tv_usec = 0;
+ if (read) {rp = &r; r = *read;} else {rp = 0;}
+ if (write) {wp = &w; w = *write;} else {wp = 0;}
+ if (except) {xp = &x; x = *except;} else {xp = 0;}
+ n = select(max, rp, wp, xp, &zero);
+ if (n > 0) {
+ /* write back fds */
+ if (read) {*read = r;}
+ if (write) {*write = w;}
+ if (except) {*except = x;}
+ return n;
+ }
+ if (n < 0 && errno != EINTR) {
+ return n;
+ }
+ if (timeout) {
+ if (timeout->tv_sec == 0 && timeout->tv_usec == 0) return 0;
+ if (limit <= timeofday()) return 0;
+ }
+
+ thread_schedule();
+ CHECK_INTS;
+ }
+}
+
+static VALUE
+thread_join(dmy, data)
+ VALUE dmy;
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ if (thread_dead(th)) return Qnil;
+ if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread)
+ Fatal("Thread.join: deadlock");
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->join = th;
+ num_waiting_on_join++;
+ curr_thread->wait_for |= WAIT_JOIN;
+ thread_schedule();
+
+ return Qnil;
+}
+
+static VALUE
+thread_current()
+{
+ return curr_thread->thread;
+}
+
+int
+th_cur()
+{
+ return (int)curr_thread;
+}
+
+static VALUE
+thread_run(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ if (th->status == THREAD_KILLED) Fail("killed thread");
+ thread_ready(th);
+ thread_schedule();
+
+ return (VALUE)data;
+}
+
+static VALUE
+thread_kill(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ if (th->status == THREAD_TO_KILL) return Qnil;
+ if (th->status == THREAD_KILLED) return Qnil;
+ if (th == th->next || th == main_thread) rb_exit(0);
+
+ thread_ready(th);
+ th->status = THREAD_TO_KILL;
+ thread_schedule();
+ /* not reached */
+}
+
+static VALUE
+thread_s_kill(obj, th)
+ VALUE obj, th;
+{
+ return thread_kill(th);
+}
+
+static VALUE
+thread_exit()
+{
+ return thread_kill(curr_thread->thread);
+}
+
+static VALUE
+thread_stop_method(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ th->status = THREAD_STOPPED;
+ thread_schedule();
+
+ return Qnil;
+}
+
+static void
+thread_stop()
+{
+ thread_stop_method(curr_thread->thread);
+}
+
+void
+thread_sleep()
+{
+ if (curr_thread == curr_thread->next) {
+ TRAP_BEG;
+ sleep((32767<<16)+32767);
+ TRAP_END;
+ return;
+ }
+ thread_stop_method(curr_thread->thread);
+}
+
+static thread_t
+thread_alloc()
+{
+ thread_t th;
+
+ th = ALLOC(struct thread);
+ th->status = THREAD_RUNNABLE;
+ th->func = 0;
+ th->arg = 0;
+
+ th->status = 0;
+ th->result = 0;
+ th->errinfo = Qnil;
+ th->errat = Qnil;
+
+ th->stk_ptr = 0;
+ th->stk_len = 0;
+ th->stk_max = 0;
+ th->wait_for = 0;
+ th->fd = 0;
+ th->delay = 0.0;
+ th->join = 0;
+
+ th->frame = 0;
+ th->scope = 0;
+ th->class = 0;
+ th->dyna_vars = 0;
+ th->block = 0;
+ th->iter = 0;
+ th->tag = 0;
+
+ th->thread = data_object_alloc(cThread, th, 0, thread_free);
+
+ if (curr_thread) {
+ th->prev = curr_thread;
+ curr_thread->next->prev = th;
+ th->next = curr_thread->next;
+ curr_thread->next = th;
+ }
+ else {
+ curr_thread = th->prev = th->next = th;
+ th->status = THREAD_RUNNABLE;
+ }
+
+ return th;
+}
+
+VALUE
+thread_create(fn, arg)
+ VALUE (*fn)();
+ void *arg;
+{
+ thread_t th = thread_alloc();
+ NODE *state;
+
+ thread_save_context(curr_thread);
+ if (setjmp(curr_thread->context)) {
+ return th->thread;
+ }
+
+ th->func = fn;
+ th->arg = arg;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ thread_save_context(th);
+ if (setjmp(th->context) == 0) {
+ curr_thread = th;
+ th->result = (*th->func)(th->arg, th);
+ }
+ }
+ POP_TAG();
+ if (state && th->status != THREAD_TO_KILL) {
+ /* global exit within this thread */
+ main_thread->errat = errat;
+ main_thread->errinfo = errinfo;
+ thread_cleanup();
+ }
+ thread_remove();
+}
+
+static void
+thread_yield(arg, th)
+ thread_t th;
+{
+ scope_dup(the_block->scope);
+ rb_yield(th->thread);
+}
+
+static VALUE
+thread_start()
+{
+ if (!iterator_p()) {
+ Raise(eLocalJumpError, "must be called as iterator");
+ }
+ return thread_create(thread_yield, 0);
+}
+
+static VALUE
+thread_value(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ thread_join(0, data);
+ return th->result;
+}
+
+static VALUE
+thread_status(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ return thread_dead(th)?FALSE:TRUE;
+}
+
+static VALUE
+thread_stopped(data)
+ struct RData *data;
+{
+ thread_t th = thread_check(data);
+
+ if (thread_dead(th)) return TRUE;
+ if (th->status == THREAD_STOPPED) return TRUE;
+ return FALSE;
+}
+
+static void
+thread_wait_other_threads()
+{
+ /* wait other threads to terminate */
+ while (curr_thread != curr_thread->next) {
+ thread_schedule();
+ }
+}
+
+static void
+thread_cleanup()
+{
+ thread_t th;
+
+ FOREACH_THREAD(th) {
+ if (th != curr_thread && th->status != THREAD_KILLED) {
+ th->status = THREAD_TO_KILL;
+ th->wait_for = 0;
+ }
+ }
+ END_FOREACH(th);
+}
+
+int thread_critical;
+
+static VALUE
+thread_exclusive()
+{
+ NODE *state;
+
+ thread_critical++;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_yield(Qnil);
+ }
+ POP_TAG();
+ thread_critical--;
+
+ if (state) JUMP_TAG(state);
+ thread_schedule();
+ return Qnil;
+}
+
+void
+thread_interrupt()
+{
+ thread_t th = main_thread;
+
+ thread_ready(main_thread);
+ if (th == curr_thread) {
+ rb_interrupt();
+ }
+ curr_thread = main_thread;
+ thread_restore_context(curr_thread, 2);
+}
+
+static thread_t loading_thread;
+static int loading_nest;
+
+static int
+thread_loading(feature)
+ char *feature;
+{
+ if (curr_thread != curr_thread->next && loading_thread) {
+ while (loading_thread != curr_thread) {
+ thread_schedule();
+ CHECK_INTS;
+ }
+ if (rb_provided(feature)) return TRUE; /* no need to load */
+ }
+
+ loading_thread = curr_thread;
+ loading_nest++;
+
+ return FALSE;
+}
+
+static void
+thread_loading_done()
+{
+ if (--loading_nest == 0) {
+ loading_thread = 0;
+ }
+}
+
+#if defined(HAVE_SETITIMER) && !defined(__BOW__)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ if (!thread_critical) {
+ if (trap_immediate) {
+ trap_immediate = 0;
+ thread_schedule();
+ }
+ else thread_pending = 1;
+ }
+}
+#else
+int thread_tick = THREAD_TICK;
+#endif
+
+void
+Init_Thread()
+{
+ cThread = rb_define_class("Thread", cObject);
+
+ rb_define_singleton_method(cThread, "new", thread_start, 0);
+ rb_define_singleton_method(cThread, "start", thread_start, 0);
+ rb_define_singleton_method(cThread, "fork", thread_start, 0);
+
+ rb_define_singleton_method(cThread, "stop", thread_stop, 0);
+ rb_define_singleton_method(cThread, "kill", thread_s_kill, 1);
+ rb_define_singleton_method(cThread, "exit", thread_exit, 0);
+ rb_define_singleton_method(cThread, "pass", thread_schedule, 0);
+ rb_define_singleton_method(cThread, "join", thread_join, 1);
+ rb_define_singleton_method(cThread, "current", thread_current, 0);
+ rb_define_singleton_method(cThread, "exclusive", thread_exclusive, 0);
+
+ rb_define_method(cThread, "run", thread_run, 0);
+ rb_define_method(cThread, "stop", thread_stop_method, 0);
+ rb_define_method(cThread, "exit", thread_kill, 0);
+ rb_define_method(cThread, "value", thread_value, 0);
+ rb_define_method(cThread, "status", thread_status, 0);
+ rb_define_method(cThread, "stop?", thread_stopped, 0);
+ rb_define_method(cThread, "stopped?", thread_stopped, 0);
+
+ /* allocate main thread */
+ main_thread = thread_alloc();
+
+#if defined(HAVE_SETITIMER) && !defined(__BOW__)
+ {
+ struct itimerval tval;
+
+#ifdef POSIX_SIGNAL
+ posix_signal(SIGVTALRM, catch_timer);
+#else
+ signal(SIGVTALRM, catch_timer);
+#endif
+
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 50000;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+ }
+#endif
+}
+#endif
diff --git a/ext/Setup b/ext/Setup
index 93586ea0e6..4ea6aea10a 100644
--- a/ext/Setup
+++ b/ext/Setup
@@ -2,6 +2,8 @@
#dbm
#etc
+#kconv
#marshal
+#md5
#socket
-tkutil
+#tkutil
diff --git a/ext/Setup.dj b/ext/Setup.dj
new file mode 100644
index 0000000000..eb60525de0
--- /dev/null
+++ b/ext/Setup.dj
@@ -0,0 +1,8 @@
+option nodynamic
+
+dbm
+#etc
+marshal
+md5
+#socket
+#tkutil
diff --git a/ext/dbm/MANIFEST b/ext/dbm/MANIFEST
index 141b8dd601..8beec6783d 100644
--- a/ext/dbm/MANIFEST
+++ b/ext/dbm/MANIFEST
@@ -1,5 +1,4 @@
MANIFEST
dbm.c
-dbm.doc
depend
extconf.rb
diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c
index dbdd99c0ca..5d8a12e3f9 100644
--- a/ext/dbm/dbm.c
+++ b/ext/dbm/dbm.c
@@ -17,10 +17,14 @@
#include <errno.h>
VALUE cDBM;
-static ID id_dbm;
extern VALUE mEnumerable;
+struct dbmdata {
+ int di_size;
+ DBM *di_dbm;
+};
+
static void
closeddbm()
{
@@ -28,24 +32,15 @@ closeddbm()
}
#define GetDBM(obj, dbmp) {\
- DBM **_dbm;\
- Get_Data_Struct(obj, id_dbm, DBM*, _dbm);\
- dbmp = *_dbm;\
- if (dbmp == Qnil) closeddbm();\
+ Get_Data_Struct(obj, struct dbmdata, dbmp);\
+ if (dbmp->di_dbm == 0) closeddbm();\
}
static void
free_dbm(dbmp)
- DBM **dbmp;
+ struct dbmdata *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;\
+ if (dbmp->di_dbm) dbm_close(dbmp->di_dbm);
}
static VALUE
@@ -55,7 +50,8 @@ fdbm_s_open(argc, argv, class)
VALUE class;
{
VALUE file, vmode;
- DBM *dbm, **dbm2;
+ DBM *dbm;
+ struct dbmdata *dbmp;
int mode;
VALUE obj;
@@ -70,7 +66,7 @@ fdbm_s_open(argc, argv, class)
}
Check_Type(file, T_STRING);
- dbm = Qnil;
+ dbm = 0;
if (mode >= 0)
dbm = dbm_open(RSTRING(file)->ptr, O_RDWR|O_CREAT, mode);
if (!dbm)
@@ -83,8 +79,9 @@ fdbm_s_open(argc, argv, class)
rb_sys_fail(RSTRING(file)->ptr);
}
- obj = obj_alloc(class);
- MakeDBM(obj, dbm);
+ obj = Make_Data_Struct(class,struct dbmdata,0,free_dbm,dbmp);
+ dbmp->di_dbm = dbm;
+ dbmp->di_size = -1;
return obj;
}
@@ -93,12 +90,12 @@ static VALUE
fdbm_close(obj)
VALUE obj;
{
- DBM **dbmp;
+ struct dbmdata *dbmp;
- Get_Data_Struct(obj, id_dbm, DBM*, dbmp);
- if (*dbmp == Qnil) Fail("already closed DBM file");
- dbm_close(*dbmp);
- *dbmp = Qnil;
+ Get_Data_Struct(obj, struct dbmdata, dbmp);
+ if (dbmp->di_dbm == 0) closeddbm();
+ dbm_close(dbmp->di_dbm);
+ dbmp->di_dbm = 0;
return Qnil;
}
@@ -108,15 +105,17 @@ fdbm_fetch(obj, keystr)
VALUE obj, keystr;
{
datum key, value;
+ struct dbmdata *dbmp;
DBM *dbm;
Check_Type(keystr, T_STRING);
key.dptr = RSTRING(keystr)->ptr;
key.dsize = RSTRING(keystr)->len;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
value = dbm_fetch(dbm, key);
- if (value.dptr == Qnil) {
+ if (value.dptr == 0) {
return Qnil;
}
return str_new(value.dptr, value.dsize);
@@ -128,38 +127,47 @@ fdbm_indexes(obj, args)
struct RArray *args;
{
VALUE *p, *pend;
- struct RArray *new;
+ VALUE 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);
+ args = (struct RArray*)rb_to_a(args);
+ new = ary_new2(args->len);
p = args->ptr; pend = p + args->len;
while (p < pend) {
- new->ptr[i++] = fdbm_fetch(obj, *p++);
- new->len = i;
+ ary_push(new, fdbm_fetch(obj, *p++));
}
- return (VALUE)new;
+ return new;
}
static VALUE
fdbm_delete(obj, keystr)
VALUE obj, keystr;
{
- datum key;
+ datum key, value;
+ struct dbmdata *dbmp;
DBM *dbm;
Check_Type(keystr, T_STRING);
key.dptr = RSTRING(keystr)->ptr;
key.dsize = RSTRING(keystr)->len;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
+
+ value = dbm_fetch(dbm, key);
+ if (value.dptr == 0) {
+ if (iterator_p()) rb_yield(Qnil);
+ return Qnil;
+ }
+
if (dbm_delete(dbm, key)) {
+ dbmp->di_size = -1;
Fail("dbm_delete failed");
}
+ else if (dbmp->di_size >= 0) {
+ dbmp->di_size--;
+ }
return obj;
}
@@ -168,10 +176,12 @@ fdbm_shift(obj)
VALUE obj;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
VALUE keystr, valstr;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
key = dbm_firstkey(dbm);
if (!key.dptr) return Qnil;
@@ -188,17 +198,19 @@ fdbm_delete_if(obj)
VALUE obj;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
VALUE keystr, valstr;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
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");
+ if (RTEST(rb_yield(assoc_new(keystr, valstr)))) {
+ if (dbm_delete(dbm, key)) {
+ Fail("dbm_delete failed");
+ }
}
}
return obj;
@@ -209,9 +221,12 @@ fdbm_clear(obj)
VALUE obj;
{
datum key;
+ struct dbmdata *dbmp;
DBM *dbm;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
+ dbmp->di_size = -1;
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
if (dbm_delete(dbm, key)) {
Fail("dbm_delete failed");
@@ -225,6 +240,7 @@ fdbm_store(obj, keystr, valstr)
VALUE obj, keystr, valstr;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
if (valstr == Qnil) {
@@ -232,19 +248,26 @@ fdbm_store(obj, keystr, valstr)
return Qnil;
}
- Check_Type(keystr, T_STRING);
+ keystr = obj_as_string(keystr);
+
key.dptr = RSTRING(keystr)->ptr;
key.dsize = RSTRING(keystr)->len;
- Check_Type(valstr, T_STRING);
+
+ if (NIL_P(valstr)) return fdbm_delete(obj, keystr);
+
+ valstr = obj_as_string(valstr);
val.dptr = RSTRING(valstr)->ptr;
val.dsize = RSTRING(valstr)->len;
- GetDBM(obj, dbm);
+ Get_Data_Struct(obj, struct dbmdata, dbmp);
+ dbmp->di_size = -1;
+ dbm = dbmp->di_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;
}
@@ -253,24 +276,56 @@ fdbm_length(obj)
VALUE obj;
{
datum key;
+ struct dbmdata *dbmp;
DBM *dbm;
int i = 0;
- GetDBM(obj, dbm);
+ Get_Data_Struct(obj, struct dbmdata, dbmp);
+ if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
+ dbm = dbmp->di_dbm;
+
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
i++;
}
+ dbmp->di_size = i;
+
return INT2FIX(i);
}
static VALUE
+fdbm_empty(obj)
+ VALUE obj;
+{
+ datum key;
+ struct dbmdata *dbmp;
+ DBM *dbm;
+ int i = 0;
+
+ Get_Data_Struct(obj, struct dbmdata, dbmp);
+ if (dbmp->di_size < 0) {
+ dbm = dbmp->di_dbm;
+
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ i++;
+ }
+ }
+ else {
+ i = dbmp->di_size;
+ }
+ if (i == 0) return TRUE;
+ return FALSE;
+}
+
+static VALUE
fdbm_each_value(obj)
VALUE obj;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_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));
@@ -283,9 +338,11 @@ fdbm_each_key(obj)
VALUE obj;
{
datum key;
+ struct dbmdata *dbmp;
DBM *dbm;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
rb_yield(str_new(key.dptr, key.dsize));
}
@@ -298,9 +355,11 @@ fdbm_each_pair(obj)
{
datum key, val;
DBM *dbm;
+ struct dbmdata *dbmp;
VALUE keystr, valstr;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
val = dbm_fetch(dbm, key);
@@ -317,11 +376,14 @@ fdbm_keys(obj)
VALUE obj;
{
datum key;
+ struct dbmdata *dbmp;
DBM *dbm;
VALUE ary;
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
+
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));
}
@@ -334,11 +396,14 @@ fdbm_values(obj)
VALUE obj;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
VALUE ary;
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
+
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));
@@ -352,13 +417,15 @@ fdbm_has_key(obj, keystr)
VALUE obj, keystr;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
Check_Type(keystr, T_STRING);
key.dptr = RSTRING(keystr)->ptr;
key.dsize = RSTRING(keystr)->len;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
val = dbm_fetch(dbm, key);
if (val.dptr) return TRUE;
return FALSE;
@@ -369,13 +436,15 @@ fdbm_has_value(obj, valstr)
VALUE obj, valstr;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
Check_Type(valstr, T_STRING);
val.dptr = RSTRING(valstr)->ptr;
val.dsize = RSTRING(valstr)->len;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
val = dbm_fetch(dbm, key);
if (val.dsize == RSTRING(valstr)->len &&
@@ -390,10 +459,12 @@ fdbm_to_a(obj)
VALUE obj;
{
datum key, val;
+ struct dbmdata *dbmp;
DBM *dbm;
VALUE ary;
- GetDBM(obj, dbm);
+ GetDBM(obj, dbmp);
+ dbm = dbmp->di_dbm;
ary = ary_new();
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
@@ -417,6 +488,7 @@ Init_dbm()
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, "empty?", fdbm_empty, 0);
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);
@@ -427,9 +499,11 @@ Init_dbm()
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, "include?", 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, "key?", fdbm_has_key, 1);
+ rb_define_method(cDBM, "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
deleted file mode 100644
index 45f174b7aa..0000000000
--- a/ext/dbm/dbm.doc
+++ /dev/null
@@ -1,107 +0,0 @@
-.\" 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/extconf.rb b/ext/dbm/extconf.rb
index 5105cd662f..2302ee2d5d 100644
--- a/ext/dbm/extconf.rb
+++ b/ext/dbm/extconf.rb
@@ -1,4 +1,4 @@
-have_library("dbm", "dbm_open")
+have_library("gdbm", "dbm_open") or have_library("dbm", "dbm_open")
if have_func("dbm_open")
create_makefile("dbm")
end
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index e4e4098f8a..524800bd03 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -24,12 +24,16 @@ static VALUE
etc_getlogin(obj)
VALUE obj;
{
+ char *getenv();
+ char *login;
+
#ifdef HAVE_GETLOGIN
char *getlogin();
- char *login = getlogin();
+
+ login = getlogin();
+ if (!login) login = getenv("USER");
#else
- char *getenv();
- char *login = getenv("USER");
+ login = getenv("USER");
#endif
if (login)
@@ -42,7 +46,7 @@ static VALUE
setup_passwd(pwd)
struct passwd *pwd;
{
- if (pwd == Qnil) rb_sys_fail("/etc/passwd");
+ if (pwd == 0) rb_sys_fail("/etc/passwd");
return struct_new(sPasswd,
str_new2(pwd->pw_name),
str_new2(pwd->pw_passwd),
@@ -69,7 +73,8 @@ setup_passwd(pwd)
#ifdef PW_EXPIRE
INT2FIX(pwd->pw_expire),
#endif
- Qnil);
+ 0 /*dummy*/
+ );
}
#endif
@@ -91,7 +96,7 @@ etc_getpwuid(argc, argv, obj)
uid = getuid();
}
pwd = getpwuid(uid);
- if (pwd == Qnil) Fail("can't find user for %d", uid);
+ if (pwd == 0) Fail("can't find user for %d", uid);
return setup_passwd(pwd);
#else
return Qnil;
@@ -107,7 +112,7 @@ etc_getpwnam(obj, nam)
Check_Type(nam, T_STRING);
pwd = getpwnam(RSTRING(nam)->ptr);
- if (pwd == Qnil) Fail("can't find user for %s", RSTRING(nam)->ptr);
+ if (pwd == 0) Fail("can't find user for %s", RSTRING(nam)->ptr);
return setup_passwd(pwd);
#else
return Qnil;
@@ -130,7 +135,7 @@ etc_passwd(obj)
return obj;
}
pw = getpwent();
- if (pw == Qnil) Fail("can't fetch next -- /etc/passwd");
+ if (pw == 0) Fail("can't fetch next -- /etc/passwd");
return setup_passwd(pw);
#else
return Qnil;
@@ -155,8 +160,7 @@ setup_group(grp)
str_new2(grp->gr_name),
str_new2(grp->gr_passwd),
INT2FIX(grp->gr_gid),
- mem,
- Qnil);
+ mem);
}
#endif
@@ -170,7 +174,7 @@ etc_getgrgid(obj, id)
gid = NUM2INT(id);
grp = getgrgid(gid);
- if (grp == Qnil) Fail("can't find group for %d", gid);
+ if (grp == 0) Fail("can't find group for %d", gid);
return setup_group(grp);
#else
return Qnil;
@@ -186,7 +190,7 @@ etc_getgrnam(obj, nam)
Check_Type(nam, T_STRING);
grp = getgrnam(RSTRING(nam)->ptr);
- if (grp == Qnil) Fail("can't find group for %s", RSTRING(nam)->ptr);
+ if (grp == 0) Fail("can't find group for %s", RSTRING(nam)->ptr);
return setup_group(grp);
#else
return Qnil;
@@ -214,7 +218,7 @@ etc_group(obj)
#endif
}
-VALUE mEtc;
+static VALUE mEtc;
void
Init_etc()
@@ -252,9 +256,11 @@ Init_etc()
#ifdef PW_EXPIRE
"expire",
#endif
- Qnil);
+ 0);
+ rb_global_variable(&sPasswd);
#ifdef HAVE_GETGRENT
- sGroup = struct_define("Group", "name", "passwd", "gid", "mem", Qnil);
+ sGroup = struct_define("Group", "name", "passwd", "gid", "mem", 0);
+ rb_global_variable(&sGroup);
#endif
}
diff --git a/ext/extmk.rb.in b/ext/extmk.rb.in
index b61ccd222f..bd4eed306b 100644
--- a/ext/extmk.rb.in
+++ b/ext/extmk.rb.in
@@ -1,21 +1,24 @@
#! /usr/local/bin/ruby
-if $ARGV[0] == 'install'
+if ARGV[0] == 'static'
+ $force_static = TRUE
+ ARGV.shift
+elsif ARGV[0] == 'install'
$install = TRUE
- $ARGV.shift
-end
-
-if $ARGV[0] == 'clean'
+ ARGV.shift
+elsif ARGV[0] == 'clean'
$clean = TRUE
- $ARGV.shift
+ ARGV.shift
end
+$extlist = []
+
$cache_mod = FALSE;
$lib_cache = {}
$func_cache = {}
$hdr_cache = {}
-if File.exists?("config.cache") then
+if File.exist?("config.cache") then
f = open("config.cache", "r")
while f.gets
case $_
@@ -31,10 +34,10 @@ if File.exists?("config.cache") then
end
def older(file1, file2)
- if !File.exists?(file1) then
+ if !File.exist?(file1) then
return TRUE
end
- if !File.exists?(file2) then
+ if !File.exist?(file2) then
return FALSE
end
if File.mtime(file1) < File.mtime(file2)
@@ -61,7 +64,7 @@ def have_library(lib, func)
end
cfile = open("conftest.c", "w")
- printf cfile, "\
+ cfile.printf "\
int main() { return 0; }
int t() { %s(); return 0; }
", func
@@ -99,7 +102,7 @@ def have_func(func)
end
cfile = open("conftest.c", "w")
- printf cfile, "\
+ cfile.printf "\
char %s();
int main() { return 0; }
int t() { %s(); return 0; }
@@ -136,7 +139,7 @@ def have_header(header)
end
cfile = open("conftest.c", "w")
- printf cfile, "\
+ cfile.printf "\
#include <%s>
", header
cfile.close
@@ -162,7 +165,7 @@ def create_header()
hfile = open("extconf.h", "w")
for line in $defs
line =~ /^-D(.*)/
- printf hfile, "#define %s 1\n", $1
+ hfile.printf "#define %s 1\n", $1
end
hfile.close
end
@@ -180,7 +183,7 @@ def create_makefile(target)
$libs = "" if not $libs
mfile = open("Makefile", "w")
- printf mfile, "\
+ mfile.printf "\
SHELL = /bin/sh
#### Start of system configuration section. ####
@@ -191,36 +194,37 @@ VPATH = @srcdir@
CC = @CC@
CFLAGS = %s #$CFLAGS %s
+LDFLAGS = @LDFLAGS@
+DLDFLAGS = @DLDFLAGS@
LDSHARED = @LDSHARED@
", if $static then "" else "@CCDLFLAGS@" end, $defs.join(" ")
- printf mfile, "\
+ mfile.printf "\
prefix = @prefix@
-binprefix =
exec_prefix = @exec_prefix@
bindir = $(exec_prefix)/bin
-libdir = @prefix@/lib/ruby
+libdir = @archlib@
@SET_MAKE@
#### End of system configuration section. ####
"
- printf mfile, "LIBS = %s\n", $libs
- printf mfile, "OBJS = "
+ mfile.printf "LIBS = %s\n", $libs
+ mfile.printf "OBJS = "
if !$objs then
$objs = Dir["*.c"]
for f in $objs
f.sub!(/\.c$/, ".o")
end
end
- printf mfile, $objs.join(" ")
- printf mfile, "\n"
+ mfile.printf $objs.join(" ")
+ mfile.printf "\n"
- printf mfile, "\
+ dots = if "@INSTALL@" =~ /^\// then "" else "../" end
+ mfile.printf "\
TARGET = %s.%s
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
+INSTALL = %s@INSTALL@
all: $(TARGET)
@@ -229,56 +233,53 @@ clean:; @rm -f *.o *.so *.sl
@rm -f core ruby *~
realclean: clean
-", target, if $static then "o" else "@DLEXT@" end
+", target,
+ if $static then "o" else "@DLEXT@" end, dots
if !$static
- printf mfile, "\
+ mfile.printf "\
install: $(libdir)/$(TARGET)
$(libdir)/$(TARGET): $(TARGET)
@test -d $(libdir) || mkdir $(libdir)
- $(INSTALL_DATA) $(TARGET) $(libdir)/$(TARGET)
+ $(INSTALL) $(TARGET) $(libdir)/$(TARGET)
"
else
- printf mfile, "\
+ mfile.printf "\
install:;
"
end
if !$static && "@DLEXT@" != "o"
- printf mfile, "\
+ mfile.printf "\
$(TARGET): $(OBJS)
- $(LDSHARED) -o $(TARGET) $(OBJS) $(LIBS)
+ $(LDSHARED) $(DLDFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
"
- elsif !File.exists?(target + ".c")
- printf mfile, "\
+ elsif !File.exist?(target + ".c")
+ mfile.printf "\
$(TARGET): $(OBJS)
- ld $(LDDLFLAGS) -r $(TARGET) $(OBJS)
+ ld $(LDFLAGS) -r -o $(TARGET) $(OBJS)
"
end
- if File.exists?("depend")
+ if File.exist?("depend")
dfile = open("depend", "r")
- printf mfile, "###\n"
+ mfile.printf "###\n"
while line = dfile.gets()
- printf mfile, "%s", line
+ mfile.printf "%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)
+ $extlist.push [$static,target]
end
end
def extmake(target)
- if $static_ext[target]
+ if $force_static or $static_ext[target]
$static = target
else
$static = FALSE
@@ -294,19 +295,19 @@ def extmake(target)
begin
Dir.chdir target
if $static_ext.size > 0 ||
- !File.exists?("./Makefile") ||
+ !File.exist?("./Makefile") ||
older("./Makefile", "../Setup") ||
older("./Makefile", "../extmk.rb") ||
older("./Makefile", "./extconf.rb")
then
$defs = []
- if File.exists?("extconf.rb")
+ if File.exist?("extconf.rb")
load "extconf.rb"
else
create_makefile(target);
end
end
- if File.exists?("./Makefile")
+ if File.exist?("./Makefile")
if $install
system "make install"
elsif $clean
@@ -328,10 +329,10 @@ if File.file? "./Setup"
while f.gets()
$_.chop!
sub!(/#.*$/, '')
- continue if /^\s*$/
+ next if /^\s*$/
if /^option +nodynamic/
$nodynamic = TRUE
- continue
+ next
end
$static_ext[$_.split[0]] = TRUE
end
@@ -339,36 +340,56 @@ if File.file? "./Setup"
end
for d in Dir["*"]
- File.directory?(d) || continue
- File.file?(d + "/MANIFEST") || continue
+ File.directory?(d) || next
+ File.file?(d + "/MANIFEST") || next
d = $1 if d =~ /\/([\/]*)$/
- print "compiling ", d, "\n"
-
+ if $install
+ print "installing ", d, "\n"
+ elsif $clean
+ print "cleaning ", d, "\n"
+ else
+ print "compiling ", d, "\n"
+ end
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
+ f.printf "lib: %s %s\n", k, v
end
for k,v in $func_cache
- printf f, "func: %s %s\n", k, v
+ f.printf "func: %s %s\n", k, v
end
for k,v in $hdr_cache
- printf f, "hdr: %s %s\n", k, v
+ f.printf "hdr: %s %s\n", k, v
end
f.close
end
-exit if $install
-if $extobjs
+exit if $install or $clean
+if $extlist.size > 0
+ for s,t in $extlist
+ f = format("%s/%s.o", s, t)
+ if File.exist?(f)
+ $extinit += format("\
+\tInit_%s();\n\
+\trb_provide(\"%s.o\");\n\
+", t, t)
+ $extobjs += "ext/"
+ $extobjs += f
+ $extobjs += " "
+ else
+ FALSE
+ end
+ end
+
if older("extinit.c", "Setup")
f = open("extinit.c", "w")
- printf f, "void Init_ext() {\n"
- printf f, $extinit
- printf f, "}\n"
+ f.printf "void Init_ext() {\n"
+ f.printf $extinit
+ f.printf "}\n"
f.close
end
if older("extinit.o", "extinit.c")
@@ -378,16 +399,19 @@ if $extobjs
end
Dir.chdir ".."
- $extobjs = "ext/extinit.o " + $extobjs
if older("ruby", "ext/Setup") or older("ruby", "miniruby")
`rm -f ruby`
end
+
+ $extobjs = "ext/extinit.o " + $extobjs
system format('make ruby PROGRAM=ruby EXTOBJS="%s" EXTLIBS="%s"', $extobjs, $extlibs)
else
Dir.chdir ".."
- `rm -f ruby`
- `cp miniruby ruby`
+ if older("ruby", "miniruby")
+ `rm -f ruby`
+ `cp miniruby ruby`
+ end
end
#Local variables:
diff --git a/ext/kconv/MANIFEST b/ext/kconv/MANIFEST
new file mode 100644
index 0000000000..8f37a9e166
--- /dev/null
+++ b/ext/kconv/MANIFEST
@@ -0,0 +1,2 @@
+MANIFEST
+kconv.c
diff --git a/ext/kconv/kconv.c b/ext/kconv/kconv.c
new file mode 100644
index 0000000000..fd6c3bed0a
--- /dev/null
+++ b/ext/kconv/kconv.c
@@ -0,0 +1,1934 @@
+/** Network Kanji Filter. (PDS Version)
+************************************************************************
+** Copyright (C) 1987, Fujitsu LTD. (Itaru ICHIKAWA)
+** 連絡先: (株)富士通研究所 ソフト3研 市川 至
+** (E-Mail Address: ichikawa@flab.fujitsu.co.jp)
+** Copyright (C) 1996
+** 連絡先: 琉球大学情報工学科 河野 真治 mine/X0208 support
+** (E-Mail Address: kono@ie.u-ryukyu.ac.jp)
+** 連絡先: COW for DOS & Win16 & Win32 & OS/2
+** (E-Mail Address: GHG00637@niftyserve.or.jp)
+** 営利を目的としない限り、このソースのいかなる
+** 複写,改変,修正も許諾します。その際には、この部分を残すこと。
+** このプログラムについては特に何の保証もしない、悪しからず。
+** Everyone is permitted to do anything on this program
+** including copying, modifying, improving
+** as long as you don't try to make money off it,
+** or pretend that you wrote it.
+** i.e., the above copyright notice has to appear in all copies.
+** THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
+***********************************************************************/
+
+/***********************************************************************
+** 1996/03/10 modified for Kconv - by Ikuo Nakagawa
+***********************************************************************/
+/***********************************************************************
+** 1996/12/18 modified for kconv(ruby) - by matz@ruby.club.or.jp
+***********************************************************************/
+
+static char *CopyRight =
+ "Copyright (C) 1987, FUJITSU LTD. (I.Ichikawa),1996 S. Kono, COW";
+static char *Version =
+ "1.62";
+static char *Patchlevel =
+ "5/9612/Shinji Kono, COW matz";
+
+/*
+**
+**
+**
+** USAGE: nkf [flags] [file]
+**
+** Flags:
+** b Output is bufferred (DEFAULT)
+** u Output is unbufferred
+**
+** t no operation
+**
+** j Outout code is JIS 7 bit (DEFAULT SELECT)
+** s Output code is MS Kanji (DEFAULT SELECT)
+** e Output code is AT&T JIS (DEFAULT SELECT)
+** l Output code is JIS 7bit and ISO8859-1 Latin-1
+**
+** m MIME conversion for ISO-2022-JP
+** i_ Output sequence to designate JIS-kanji (DEFAULT_J)
+** o_ Output sequence to designate single-byte roman characters (DEFAULT_R)
+**
+** r {de/en}crypt ROT13/47
+**
+** v display Version
+**
+** T Text mode output (for MS-DOS)
+**
+** x Do not convert X0201 kana into X0208
+** Z Convert X0208 alphabet to ASCII
+**
+** f60 fold option
+**
+** m MIME decode
+** B try to fix broken JIS, missing Escape
+** B[1-9] broken level
+**
+** O Output to 'nkf.out' file
+** d Delete \r in line feed
+** c Add \r in line feed
+**/
+/******************************/
+/* デフォルトの出力コード選択 */
+/* Select DEFAULT_CODE */
+#define DEFAULT_CODE_JIS
+/* #define DEFAULT_CODE_SJIS */
+/* #define DEFAULT_CODE_EUC */
+/******************************/
+/* プロトタイプの選択 */
+#define ANSI_C_PROTOTYPE
+/******************************/
+
+/* for Kconv: _AUTO, _EUC, _SJIS, _JIS */
+#define _AUTO 0
+#define _JIS 1
+#define _EUC 2
+#define _SJIS 3
+
+#ifdef __STDC__
+#define ANSI_C_PROTOTYPE
+#endif
+
+#if (defined(__TURBOC__) || defined(LSI_C)) && !defined(MSDOS)
+#define MSDOS
+#endif
+
+#include <stdio.h>
+
+#if defined(MSDOS) || defined(__OS2__)
+#include <stdlib.h>
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+#ifdef MSDOS
+#ifdef LSI_C
+#define setbinmode(fp) fsetbin(fp)
+#else /* Microsoft C, Turbo C */
+#define setbinmode(fp) setmode(fileno(fp), O_BINARY)
+#endif
+#else /* UNIX,OS/2 */
+#define setbinmode(fp)
+#endif
+
+#ifdef _IOFBF /* SysV and MSDOS */
+#define setvbuffer(fp, buf, size) setvbuf(fp, buf, _IOFBF, size)
+#else /* BSD */
+#define setvbuffer(fp, buf, size) setbuffer(fp, buf, size)
+#endif
+
+/*Borland C++ 4.5 EasyWin*/
+#if defined(__TURBOC__) && defined(_Windows) && !defined(__WIN32__) /*Easy Win */
+#define EASYWIN
+#include <windows.h>
+#endif
+
+#define FALSE 0
+#define TRUE 1
+
+/* state of output_mode and input_mode */
+
+#define ASCII 0
+#define X0208 1
+#define X0201 2
+#define NO_X0201 3
+#define JIS_INPUT 4
+#define SJIS_INPUT 5
+#define LATIN1_INPUT 6
+#define FIXED_MIME 7
+#define DOUBLE_SPACE -2
+
+#define NL 0x0a
+#define ESC 0x1b
+#define SP 0x20
+#define AT 0x40
+#define SSP 0xa0
+#define DEL 0x7f
+#define SI 0x0f
+#define SO 0x0e
+#define SSO 0x8e
+
+#define HOLD_SIZE 32
+#define IOBUF_SIZE 16384
+
+#define DEFAULT_J 'B'
+#define DEFAULT_R 'B'
+
+#define SJ0162 0x00e1 /* 01 - 62 ku offset */
+#define SJ6394 0x0161 /* 63 - 94 ku offset */
+
+
+/* MIME preprocessor */
+
+#define _GETC() (*inptr ? (int)(*inptr++) : EOF)
+#define _UNGETC(c) (*--inptr = (c))
+#define PUTCHAR(c) (outlen + 1 < outsiz ? \
+ ((outptr[outlen++] = (c)), (outptr[outlen] = '\0')) : EOF)
+#define GETC() ((!mime_mode)?_GETC():mime_getc())
+#define UNGETC(c) ((!mime_mode)?_UNGETC(c):mime_ungetc(c))
+
+#ifdef EASYWIN /*Easy Win */
+extern POINT _BufferSize;
+#endif
+
+/* buffers */
+
+static unsigned char hold_buf[HOLD_SIZE*2];
+static int hold_count;
+static unsigned char *inptr;
+static char *outptr;
+static int outsiz;
+static int outlen;
+
+/* MIME preprocessor fifo */
+
+#define MIME_BUF_SIZE (1024) /* 2^n ring buffer */
+#define MIME_BUF_MASK (MIME_BUF_SIZE-1)
+#define Fifo(n) mime_buf[(n)&MIME_BUF_MASK]
+static unsigned char mime_buf[MIME_BUF_SIZE];
+static unsigned int mime_top = 0;
+static unsigned int mime_last = 0; /* decoded */
+static unsigned int mime_input = 0; /* undecoded */
+
+/* flags */
+static int unbuf_f = FALSE;
+static int estab_f = FALSE;
+#ifdef notdef
+static int nop_f = FALSE;
+static int binmode_f = TRUE; /* binary mode */
+#endif
+static int rot_f = FALSE; /* rot14/43 mode */
+static int input_f = FALSE; /* non fixed input code */
+static int alpha_f = FALSE; /* convert JIx0208 alphbet to ASCII */
+static int mime_f = FALSE; /* convert MIME B base64 or Q */
+static int mimebuf_f = FALSE; /* MIME buffered input */
+static int broken_f = FALSE; /* convert ESC-less broken JIS */
+static int iso8859_f = FALSE; /* ISO8859 through */
+#if defined(MSDOS) || defined(__OS2__)
+static int x0201_f = TRUE; /* Assume JISX0201 kana */
+#else
+static int x0201_f = NO_X0201; /* Assume NO JISX0201 */
+#endif
+
+/* X0208 -> ASCII converter */
+
+static int c1_return;
+
+/* fold parameter */
+static int line = 0; /* chars in line */
+static int prev = 0;
+static int fold_f = FALSE;
+static int fold_len = 0;
+
+/* options */
+static char kanji_intro = DEFAULT_J,
+ ascii_intro = DEFAULT_R;
+
+/* Folding */
+
+static int fold();
+#define FOLD_MARGIN 10
+#define DEFAULT_FOLD 60
+
+/* Global states */
+static int output_mode = ASCII, /* output kanji mode */
+ input_mode = ASCII, /* input kanji mode */
+ shift_mode = FALSE; /* TRUE shift out, or X0201 */
+static int mime_mode = FALSE; /* MIME mode B base64, Q hex */
+
+/* X0208 -> ASCII translation table */
+
+static unsigned char cv[],dv[],ev[],fv[];
+
+#ifdef notdef
+static int file_out = FALSE;
+static int end_check;
+#endif
+static int add_cr = FALSE;
+static int del_cr = FALSE;
+
+/* function prototype */
+#ifdef ANSI_C_PROTOTYPE
+static void (*iconv) (register int c2,register int c1); /* s_iconv or oconv */
+static void (*oconv) (register int c2,register int c1); /* [ejs]_oconv */
+static int do_kconv(char *i, char *o, int siz, int out_code, int in_code);
+static void h_conv(register int c2,register int c1);
+static int push_hold_buf(int c2,int c1);
+static void s_iconv(register int c2,register int c1);
+static void e_oconv(register int c2,register int c1);
+static void s_oconv(register int c2,register int c1);
+static void j_oconv(register int c2,register int c1);
+static int fold(register int c2,register int c1);
+static int pre_convert(register int c1,register int c2);
+static int mime_begin();
+static int mime_getc();
+static int mime_ungetc(unsigned int c);
+static int mime_integrity(unsigned char *p);
+static int base64decode(int c);
+#else
+static void (*iconv) (); /* s_iconv or oconv */
+static void (*oconv) (); /* [ejs]_oconv */
+static int s_iconv ();
+static int e_oconv ();
+static int j_oconv ();
+static int s_oconv ();
+static int noconvert ();
+static int do_kconv();
+static void h_conv ();
+static int push_hold_buf ();
+#endif
+
+#ifdef notdef
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register FILE *fin;
+ register char *cp;
+
+#ifdef EASYWIN /*Easy Win */
+ _BufferSize.y = 400;/*Set Scroll Buffer Size*/
+#endif
+#ifdef DEFAULT_CODE_JIS
+ oconv = j_oconv; /* DEFAULT Code is JIS */
+#endif
+#ifdef DEFAULT_CODE_SJIS
+ oconv = s_oconv; /* DEFAULT Code is S-JIS */
+#endif
+#ifdef DEFAULT_CODE_EUC
+ oconv = e_oconv; /* DEFAULT Code is EUC */
+#endif
+
+ for (argc--,argv++; (argc > 0) && **argv == '-'; argc--, argv++) {
+ cp = *argv;
+ while (*cp) {
+ switch (*cp++) {
+ case 'b': /* buffered mode */
+ unbuf_f = FALSE;
+ continue;
+ case 'u': /* non bufferd mode */
+ unbuf_f = TRUE;
+ continue;
+ case 't': /* transparent mode */
+ nop_f = TRUE;
+ continue;
+ case 'j': /* JIS output */
+ case 'n':
+ oconv = j_oconv;
+ continue;
+ case 'e': /* AT&T EUC output */
+ oconv = e_oconv;
+ continue;
+ case 's': /* SJIS output */
+ oconv = s_oconv;
+ continue;
+ case 'l': /* ISO8859 Latin-1 support, no conversion */
+ iso8859_f = TRUE; /* Only compatible with ISO-2022-JP */
+ input_f = LATIN1_INPUT;
+ continue;
+ case 'i': /* Kanji IN ESC-$-@/B */
+ if(*cp=='@'||*cp=='B')
+ kanji_intro = *cp++;
+ continue;
+ case 'o': /* ASCII IN ESC-(-J/B */
+ if(*cp=='J'||*cp=='B'||*cp=='H')
+ ascii_intro = *cp++;
+ continue;
+ case 'r':
+ rot_f = TRUE;
+ continue;
+#if defined(MSDOS) || defined(__OS2__)
+ case 'T':
+ binmode_f = FALSE;
+ continue;
+#endif
+ case 'v':
+ usage();
+ exit(1);
+ break;
+ /* Input code assumption */
+ case 'J': /* JIS input */
+ case 'E': /* AT&T EUC input */
+ input_f = JIS_INPUT;
+ continue;
+ case 'S': /* MS Kanji input */
+ input_f = SJIS_INPUT;
+ if(x0201_f==NO_X0201) x0201_f=TRUE;
+ continue;
+ case 'Z': /* Convert X0208 alphabet to asii */
+ /* bit:0 Convert X0208
+ bit:1 Convert Kankaku to one space
+ bit:2 Convert Kankaku to two spaces
+ */
+ if('9'>= *cp && *cp>='0')
+ alpha_f |= 1<<(*cp++ -'0');
+ else
+ alpha_f |= TRUE;
+ continue;
+ case 'x': /* Convert X0201 kana to X0208 or X0201 Conversion */
+ x0201_f = FALSE; /* No X0201->X0208 conversion */
+ /* accept X0201
+ ESC-(-I in JIS, EUC, MS Kanji
+ SI/SO in JIS, EUC, MS Kanji
+ SSO in EUC, JIS, not in MS Kanji
+ MS Kanji (0xa0-0xdf)
+ output X0201
+ ESC-(-I in JIS (0x20-0x5f)
+ SSO in EUC (0xa0-0xdf)
+ 0xa0-0xd in MS Kanji (0xa0-0xdf)
+ */
+ continue;
+ case 'X': /* Assume X0201 kana */
+ /* Default value is NO_X0201 for EUC/MS-Kanji mix */
+ x0201_f = TRUE;
+ continue;
+ case 'f': /* folding -f60 or -f */
+ fold_f = TRUE;
+ fold_len = atoi(cp);
+ if(!(0<fold_len && fold_len<BUFSIZ))
+ fold_len = DEFAULT_FOLD;
+ while('0'<= *cp && *cp <='9') cp++;
+ continue;
+ case 'm': /* MIME support */
+ mime_f = TRUE;
+ if(*cp=='B'||*cp=='Q') {
+ mime_mode = *cp++;
+ mimebuf_f = FIXED_MIME;
+ }
+ continue;
+ case 'B': /* Broken JIS support */
+ /* bit:0 no ESC JIS
+ bit:1 allow any x on ESC-(-x or ESC-$-x
+ bit:2 reset to ascii on NL
+ */
+ if('9'>= *cp && *cp>='0')
+ broken_f |= 1<<(*cp++ -'0');
+ else
+ broken_f |= TRUE;
+ continue;
+ case 'O':/* for Output file */
+ file_out = TRUE;
+ continue;
+ case 'c':/* add cr code */
+ add_cr = TRUE;
+ continue;
+ case 'd':/* delete cr code */
+ del_cr = TRUE;
+ continue;
+ default:
+ /* bogus option but ignored */
+ continue;
+ }
+ }
+ }
+
+ if(iso8859_f && (oconv != j_oconv || !x0201_f )) {
+ fprintf(stderr,"Mixed ISO8859/JISX0201/SJIS/EUC output is not allowed.\n");
+ exit(1);
+ }
+
+ if(binmode_f == TRUE)
+#ifdef __OS2__
+ if(freopen("","wb",stdout) == NULL)
+ return (-1);
+#else
+ setbinmode(stdout);
+#endif
+
+ if(unbuf_f)
+ setbuf (stdout, (char *) NULL);
+ else
+ setvbuffer (stdout, stdobuf, IOBUF_SIZE);
+
+ if(argc == 0) {
+ if(binmode_f == TRUE)
+#ifdef __OS2__
+ if(freopen("","rb",stdin) == NULL) return (-1);
+#else
+ setbinmode(stdin);
+#endif
+ setvbuffer (stdin, stdibuf, IOBUF_SIZE);
+ if(nop_f)
+ noconvert (stdin);
+ else
+ convert (stdin);
+ } else {
+ while (argc--) {
+ if((fin = fopen (*argv++, "r")) == NULL) {
+ perror (*--argv);
+ return (-1);
+ } else {
+/* reopen file for stdout */
+ if(file_out == TRUE){
+ if(argc == 1 ) {
+ if(freopen(*argv++, "w", stdout) == NULL) {
+ perror (*--argv);
+ return (-1);
+ }
+ argc--;
+ } else {
+ if(freopen("nkf.out", "w", stdout) == NULL) {
+ perror (*--argv);
+ return (-1);
+ }
+ }
+ if(binmode_f == TRUE) {
+#ifdef __OS2__
+ if(freopen("","wb",stdout) == NULL)
+ return (-1);
+#else
+ setbinmode(stdout);
+#endif
+ }
+ }
+ if(binmode_f == TRUE)
+#ifdef __OS2__
+ if(freopen("","rb",fin) == NULL)
+ return (-1);
+#else
+ setbinmode(fin);
+#endif
+ setvbuffer (fin, stdibuf, IOBUF_SIZE);
+ if(nop_f)
+ noconvert (fin);
+ else
+ convert (fin);
+ fclose (fin);
+ }
+ }
+ }
+#ifdef EASYWIN /*Easy Win */
+ if(file_out == FALSE)
+ scanf("%d",&end_check);
+ else
+ fclose(stdout);
+#else /* for Other OS */
+ if(file_out == TRUE)
+ fclose(stdout);
+#endif
+ return (0);
+}
+
+int
+noconvert (f)
+ register FILE *f;
+{
+ register int c;
+
+ while ((c = getc (f)) != EOF)
+ putchar (c);
+ return 1;
+}
+
+#endif /* notdef */
+
+static int
+do_kconv(i, o, siz, out_code, in_code)
+ char *i;
+ char *o;
+ int siz, out_code, in_code;
+{
+ register int c1,
+ c2;
+
+ c2 = 0;
+
+ if (siz <= 0) {
+ return 0;
+ }
+ *o = '\0';
+
+ inptr = (unsigned char *)i; /* input buffer */
+ outptr = o; /* output buffer */
+ outsiz = siz; /* output buffer size */
+ outlen = 0; /* current length of output string */
+ x0201_f = FALSE; /* don't assume JISX0201 kana */
+ rot_f = FALSE; /* rot14/43 mode */
+ input_f = FALSE; /* non fixed input code */
+ alpha_f = FALSE; /* convert JISX0208 alphbet to ASCII */
+ mime_f = TRUE; /* convert MIME base64 */
+ broken_f = FALSE; /* convert ESC-less broken JIS */
+
+ switch (out_code) {
+ case _SJIS:
+ oconv = s_oconv;
+ break;
+ case _EUC:
+ oconv = e_oconv;
+ break;
+ default:
+ oconv = j_oconv;
+ break;
+ }
+
+ switch (in_code) {
+ case _SJIS:
+ input_f = SJIS_INPUT;
+ x0201_f = TRUE;
+ break;
+ case _EUC:
+ case _JIS:
+ input_f = JIS_INPUT;
+ break;
+ default:
+ input_f = FALSE;
+ break;
+ }
+
+ if(input_f == JIS_INPUT || input_f == LATIN1_INPUT) {
+ estab_f = TRUE; iconv = oconv;
+ } else if(input_f == SJIS_INPUT) {
+ estab_f = TRUE; iconv = s_iconv;
+ } else {
+ estab_f = FALSE; iconv = oconv;
+ }
+ input_mode = ASCII;
+ output_mode = ASCII;
+ shift_mode = FALSE;
+ mime_mode = FALSE;
+
+#define NEXT continue /* no output, get next */
+#define SEND ; /* output c1 and c2, get next */
+#define LAST break /* end of loop, go closing */
+
+ while ((c1 = GETC()) != EOF) {
+ if(!c2 && !input_mode && c1<DEL && !mime_mode && !output_mode
+ && !shift_mode && !fold_f && !rot_f) {
+ /* plain ASCII tight loop, no conversion and no fold */
+ while(c1!='=' && c1!=SO && c1!=EOF &&
+ c1!=ESC && c1!='$' && c1<DEL && c1!='\r' && c1!='\n') {
+ PUTCHAR(c1);
+ c1 = _GETC();
+ }
+ if(c1==EOF) LAST;
+ }
+ if(c2) {
+ /* second byte */
+ if(c2 > DEL) {
+ /* in case of 8th bit is on */
+ if(!estab_f) {
+ /* in case of not established yet */
+ if(c1 > SSP) {
+ /* It is still ambiguious */
+ h_conv (c2, c1);
+ c2 = 0;
+ NEXT;
+ } else if(c1 < AT) {
+ /* ignore bogus code */
+ c2 = 0;
+ NEXT;
+ } else {
+ /* established */
+ /* it seems to be MS Kanji */
+ estab_f = TRUE;
+ iconv = s_iconv;
+ SEND;
+ }
+ } else
+ /* in case of already established */
+ if(c1 < AT) {
+ /* ignore bogus code */
+ c2 = 0;
+ NEXT;
+ } else
+ SEND;
+ } else
+ /* 7 bit code */
+ /* it might be kanji shitfted */
+ if((c1 == DEL) || (c1 <= SP)) {
+ /* ignore bogus first code */
+ c2 = 0;
+ NEXT;
+ } else
+ SEND;
+ } else {
+ /* first byte */
+ if(c1 > DEL) {
+ /* 8 bit code */
+ if(!estab_f && !iso8859_f) {
+ /* not established yet */
+ if(c1 < SSP) {
+ /* it seems to be MS Kanji */
+ estab_f = TRUE;
+ iconv = s_iconv;
+ } else if(c1 < 0xe0) {
+ /* it seems to be EUC */
+ estab_f = TRUE;
+ iconv = oconv;
+ } else {
+ /* still ambiguious */
+ }
+ c2 = c1;
+ NEXT;
+ } else { /* estab_f==TRUE */
+ if(iso8859_f) {
+ SEND;
+ } else if(SSP<=c1 && c1<0xe0 && iconv == s_iconv) {
+ /* SJIS X0201 Case... */
+ /* This is too arrogant, but ... */
+ if(x0201_f==NO_X0201) {
+ iconv = oconv;
+ c2 = c1;
+ NEXT;
+ } else
+ if(x0201_f) {
+ if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) {
+ /* look ahead for X0201/X0208conversion */
+ if((c2 = GETC()) == EOF) {
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ LAST;
+ } else if(c2==(0xde)) { /* 濁点 */
+ (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ } else if(c2==(0xdf)&&ev[(c1-SSP)*2]) {
+ /* 半濁点 */
+ (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ }
+ UNGETC(c2); c2 = 0;
+ }
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ NEXT;
+ } else
+ SEND;
+ } else if(c1==SSO && iconv != s_iconv) {
+ /* EUC X0201 Case */
+ /* This is too arrogant
+ if(x0201_f == NO_X0201) {
+ estab_f = FALSE;
+ c2 = 0;
+ NEXT;
+ } */
+ c1 = GETC(); /* skip SSO */
+ euc_1byte_check:
+ if(x0201_f && SSP<=c1 && c1<0xe0) {
+ if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) {
+ if((c2 = GETC()) == EOF) {
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ LAST;
+ }
+ /* forward lookup 濁点/半濁点 */
+ if(c2 != SSO) {
+ UNGETC(c2); c2 = 0;
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ NEXT;
+ } else if((c2 = GETC()) == EOF) {
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ (*oconv)(0,SSO);
+ LAST;
+ } else if(c2==(0xde)) { /* 濁点 */
+ (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ } else if(c2==(0xdf)&&ev[(c1-SSP)*2]) {
+ /* 半濁点 */
+ (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ } else {
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ /* we have to check this c2 */
+ /* and no way to push back SSO */
+ c1 = c2; c2 = 0;
+ goto euc_1byte_check;
+ }
+ }
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ NEXT;
+ } else
+ SEND;
+ } else if(c1 < SSP && iconv != s_iconv) {
+ /* strange code in EUC */
+ iconv = s_iconv; /* try SJIS */
+ c2 = c1;
+ NEXT;
+ } else {
+ /* already established */
+ c2 = c1;
+ NEXT;
+ }
+ }
+ } else if((c1 > SP) && (c1 != DEL)) {
+ /* in case of Roman characters */
+ if(shift_mode) {
+ c1 |= 0x80;
+ /* output 1 shifted byte */
+ if(x0201_f && (!iso8859_f||input_mode==X0201) &&
+ SSP<=c1 && c1<0xe0 ) {
+ if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) {
+ if((c2 = GETC()) == EOF) {
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ LAST;
+ } else if(c2==(0xde&0x7f)) { /* 濁点 */
+ (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ } else if(c2==(0xdf&0x7f)&&ev[(c1-SSP)*2]) {
+ /* 半濁点 */
+ (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]);
+ c2=0;
+ NEXT;
+ }
+ UNGETC(c2); c2 = 0;
+ }
+ (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]);
+ NEXT;
+ } else
+ SEND;
+ } else if(c1 == '(' && broken_f && input_mode == X0208
+ && !mime_mode ) {
+ /* Try to recover missing escape */
+ if((c1 = GETC()) == EOF) {
+ (*oconv) (0, '(');
+ LAST;
+ } else {
+ if(c1 == 'B' || c1 == 'J' || c1 == 'H') {
+ input_mode = ASCII; shift_mode = FALSE;
+ NEXT;
+ } else {
+ (*oconv) (0, '(');
+ /* do not modify various input_mode */
+ /* It can be vt100 sequence */
+ SEND;
+ }
+ }
+ } else if(input_mode == X0208) {
+ /* in case of Kanji shifted */
+ c2 = c1;
+ NEXT;
+ /* goto next_byte */
+ } else if(c1 == '=' && mime_f && !mime_mode ) {
+ if((c1 = _GETC()) == EOF) {
+ (*oconv) (0, '=');
+ LAST;
+ } else if(c1 == '?') {
+ /* =? is mime conversion start sequence */
+ if(mime_begin() == EOF) /* check in detail */
+ LAST;
+ else
+ NEXT;
+ } else {
+ (*oconv) (0, '=');
+ _UNGETC(c1);
+ NEXT;
+ }
+ } else if(c1 == '$' && broken_f && !mime_mode) {
+ /* try to recover missing escape */
+ if((c1 = GETC()) == EOF) {
+ (*oconv) (0, '$');
+ LAST;
+ } else if(c1 == '@'|| c1 == 'B') {
+ /* in case of Kanji in ESC sequence */
+ input_mode = X0208;
+ shift_mode = FALSE;
+ NEXT;
+ } else {
+ /* sorry */
+ (*oconv) (0, '$');
+ (*oconv) (0, c1);
+ NEXT;
+ }
+ } else
+ SEND;
+ } else if(c1 == SI) {
+ shift_mode = FALSE;
+ NEXT;
+ } else if(c1 == SO) {
+ shift_mode = TRUE;
+ NEXT;
+ } else if(c1 == ESC ) {
+ if((c1 = GETC()) == EOF) {
+ (*oconv) (0, ESC);
+ LAST;
+ } else if(c1 == '$') {
+ if((c1 = GETC()) == EOF) {
+ (*oconv) (0, ESC);
+ (*oconv) (0, '$');
+ LAST;
+ } else if(c1 == '@'|| c1 == 'B') {
+ /* This is kanji introduction */
+ input_mode = X0208;
+ shift_mode = FALSE;
+ NEXT;
+ } else if(broken_f&0x2) {
+ input_mode = X0208;
+ shift_mode = FALSE;
+ NEXT;
+ } else {
+ (*oconv) (0, ESC);
+ (*oconv) (0, '$');
+ (*oconv) (0, c1);
+ NEXT;
+ }
+ } else if(c1 == '(') {
+ if((c1 = GETC()) == EOF) {
+ (*oconv) (0, ESC);
+ (*oconv) (0, '(');
+ LAST;
+ } else {
+ if(c1 == 'I') {
+ /* This is X0201 kana introduction */
+ input_mode = X0201; shift_mode = X0201;
+ NEXT;
+ } else if(c1 == 'B' || c1 == 'J' || c1 == 'H') {
+ /* This is X0208 kanji introduction */
+ input_mode = ASCII; shift_mode = FALSE;
+ NEXT;
+ } else if(broken_f&0x2) {
+ input_mode = ASCII; shift_mode = FALSE;
+ NEXT;
+ } else {
+ (*oconv) (0, ESC);
+ (*oconv) (0, '(');
+ /* maintain various input_mode here */
+ SEND;
+ }
+ }
+ } else {
+ /* lonely ESC */
+ (*oconv) (0, ESC);
+ SEND;
+ }
+ } else if(c1 == NL && broken_f&4) {
+ input_mode = ASCII;
+ SEND;
+ } else
+ SEND;
+ }
+ /* send: */
+ if(input_mode == X0208)
+ (*oconv) (c2, c1); /* this is JIS, not SJIS/EUC case */
+ else
+ (*iconv) (c2, c1); /* can be EUC/SJIS */
+ c2 = 0;
+ continue;
+ /* goto next_word */
+ }
+
+ /* epilogue */
+ (*iconv) (EOF, 0);
+ return outlen;
+}
+
+
+static void
+h_conv (c2, c1)
+ register int c1,
+ c2;
+{
+ register int wc;
+
+
+ /** it must NOT be in the kanji shifte sequence */
+ /** it must NOT be written in JIS7 */
+ /** and it must be after 2 byte 8bit code */
+
+ hold_count = 0;
+ push_hold_buf (c2, c1);
+ c2 = 0;
+
+ while ((c1 = GETC()) != EOF) {
+ if(c2) {
+ /* second byte */
+ if(!estab_f) {
+ /* not established */
+ if(c1 > SSP) {
+ /* it is still ambiguious yet */
+ SEND;
+ } else if(c1 < AT) {
+ /* ignore bogus first byte */
+ c2 = 0;
+ SEND;
+ } else {
+ /* now established */
+ /* it seems to be MS Kanji */
+ estab_f = TRUE;
+ iconv = s_iconv;
+ SEND;
+ }
+ } else
+ SEND;
+ } else {
+ /* First byte */
+ if(c1 > DEL) {
+ /* 8th bit is on */
+ if(c1 < SSP) {
+ /* it seems to be MS Kanji */
+ estab_f = TRUE;
+ iconv = s_iconv;
+ } else if(c1 < 0xe0) {
+ /* it seems to be EUC */
+ estab_f = TRUE;
+ iconv = oconv;
+ } else {
+ /* still ambiguious */
+ }
+ c2 = c1;
+ NEXT;
+ } else
+ /* 7 bit code , then send without any process */
+ SEND;
+ }
+ /* send: */
+ if((push_hold_buf (c2, c1) == EOF) || estab_f)
+ break;
+ c2 = 0;
+ continue;
+ }
+
+ /** now,
+ ** 1) EOF is detected, or
+ ** 2) Code is established, or
+ ** 3) Buffer is FULL (but last word is pushed)
+ **
+ ** in 1) and 3) cases, we continue to use
+ ** Kanji codes by oconv and leave estab_f unchanged.
+ **/
+
+ for (wc = 0; wc < hold_count; wc += 2) {
+ c2 = hold_buf[wc];
+ c1 = hold_buf[wc+1];
+ (*iconv) (c2, c1);
+ }
+ return;
+}
+
+
+
+int
+push_hold_buf (c2, c1)
+ int c2,
+ c1;
+{
+ if(hold_count >= HOLD_SIZE*2)
+ return (EOF);
+ hold_buf[hold_count++] = c2;
+ hold_buf[hold_count++] = c1;
+ return ((hold_count >= HOLD_SIZE*2) ? EOF : hold_count);
+}
+
+
+static void
+s_iconv (c2, c1)
+ register int c2,
+ c1;
+{
+ if((c2 == EOF) || (c2 == 0)) {
+ /* NOP */
+ } else {
+ c2 = c2 + c2 - ((c2 <= 0x9f) ? SJ0162 : SJ6394);
+ if(c1 < 0x9f)
+ c1 = c1 - ((c1 > DEL) ? SP : 0x1f);
+ else {
+ c1 = c1 - 0x7e;
+ c2++;
+ }
+ }
+ (*oconv) (c2, c1);
+}
+
+
+static void
+e_oconv (c2, c1)
+ register int c2,
+ c1;
+{
+ c2 = pre_convert(c1,c2); c1 = c1_return;
+ if(fold_f) {
+ switch(fold(c2,c1)) {
+ case '\n':
+ if(add_cr == TRUE) {
+ PUTCHAR('\r');
+ c1 = '\n';
+ }
+ PUTCHAR('\n');
+ break;
+ case 0: return;
+ case '\r':
+ c1 = '\n'; c2 = 0;
+ break;
+ case '\t':
+ case ' ':
+ c1 = ' '; c2 = 0;
+ break;
+ }
+ }
+ if(c2==DOUBLE_SPACE) {
+ PUTCHAR(' '); PUTCHAR(' ');
+ return;
+ }
+ if(c2 == EOF)
+ return;
+ else if(c2 == 0 && (c1&0x80)) {
+ PUTCHAR(SSO); PUTCHAR(c1);
+ } else if(c2 == 0) {
+ if(c1 == '\n' && add_cr == TRUE)
+ PUTCHAR('\r');
+ if(c1 != '\r')
+ PUTCHAR(c1);
+ else if(del_cr == FALSE)
+ PUTCHAR(c1);
+ } else {
+ if((c1<0x20 || 0x7e<c1) ||
+ (c2<0x20 || 0x7e<c2)) {
+ estab_f = FALSE;
+ return; /* too late to rescue this char */
+ }
+ PUTCHAR(c2 | 0x080);
+ PUTCHAR(c1 | 0x080);
+ }
+}
+
+
+static void
+s_oconv (c2, c1)
+ register int c2,
+ c1;
+{
+ c2 = pre_convert(c1,c2); c1 = c1_return;
+ if(fold_f) {
+ switch(fold(c2,c1)) {
+ case '\n':
+ if(add_cr == TRUE) {
+ PUTCHAR('\r');
+ c1 = '\n';
+ }
+ PUTCHAR('\n');
+ break;
+ case '\r':
+ c1 = '\n'; c2 = 0;
+ break;
+ case 0: return;
+ case '\t':
+ case ' ':
+ c1 = ' '; c2 = 0;
+ break;
+ }
+ }
+ if(c2==DOUBLE_SPACE) {
+ PUTCHAR(' '); PUTCHAR(' ');
+ return;
+ }
+ if(c2 == EOF)
+ return;
+ else if(c2 == 0) {
+ if(c1 == '\n' && add_cr == TRUE)
+ PUTCHAR('\r');
+ if(c1 != '\r')
+ PUTCHAR(c1);
+ else if(del_cr == FALSE)
+ PUTCHAR(c1);
+ } else {
+ if((c1<0x20 || 0x7e<c1) ||
+ (c2<0x20 || 0x7e<c2)) {
+ estab_f = FALSE;
+ return; /* too late to rescue this char */
+ }
+ PUTCHAR((((c2 - 1) >> 1) + ((c2 <= 0x5e) ? 0x71 : 0xb1)));
+ PUTCHAR((c1 + ((c2 & 1) ? ((c1 < 0x60) ? 0x1f : 0x20) : 0x7e)));
+ }
+}
+
+static void
+j_oconv (c2, c1)
+ register int c2,
+ c1;
+{
+ c2 = pre_convert(c1,c2); c1 = c1_return;
+ if(fold_f) {
+ switch(fold(c2,c1)) {
+ case '\n':
+ if(output_mode) {
+ PUTCHAR(ESC);
+ PUTCHAR('(');
+ PUTCHAR(ascii_intro);
+ }
+ if(add_cr == TRUE) {
+ PUTCHAR('\r');
+ c1 = '\n';
+ }
+ PUTCHAR('\n');
+ output_mode = ASCII;
+ break;
+ case '\r':
+ c1 = '\n'; c2 = 0;
+ break;
+ case '\t':
+ case ' ':
+ c1 = ' '; c2 = 0;
+ break;
+ case 0: return;
+ }
+ }
+ if(c2 == EOF) {
+ if(output_mode) {
+ PUTCHAR(ESC);
+ PUTCHAR('(');
+ PUTCHAR(ascii_intro);
+ }
+ } else if(c2 == 0 && (c1 & 0x80)) {
+ if(input_mode==X0201 || !iso8859_f) {
+ if(output_mode!=X0201) {
+ PUTCHAR(ESC);
+ PUTCHAR('(');
+ PUTCHAR('I');
+ output_mode = X0201;
+ }
+ c1 &= 0x7f;
+ } else {
+ /* iso8859 introduction, or 8th bit on */
+ /* Can we convert in 7bit form using ESC-'-'-A ?
+ Is this popular? */
+ }
+ PUTCHAR(c1);
+ } else if(c2 == 0) {
+ if(output_mode) {
+ PUTCHAR(ESC);
+ PUTCHAR('(');
+ PUTCHAR(ascii_intro);
+ output_mode = ASCII;
+ }
+ if(c1 == '\n' && add_cr == TRUE)
+ PUTCHAR('\r');
+ if(c1 != '\r')
+ PUTCHAR(c1);
+ else if(del_cr == FALSE)
+ PUTCHAR(c1);
+ } else if(c2 == DOUBLE_SPACE) {
+ if(output_mode) {
+ PUTCHAR(ESC);
+ PUTCHAR('(');
+ PUTCHAR(ascii_intro);
+ output_mode = ASCII;
+ }
+ PUTCHAR(' ');
+ if(c1 == '\n' && add_cr == TRUE)
+ PUTCHAR('\r');
+ if(c1 != '\r')
+ PUTCHAR(c1);
+ else if(del_cr == FALSE)
+ PUTCHAR(c1);
+ } else {
+ if(output_mode != X0208) {
+ PUTCHAR(ESC);
+ PUTCHAR('$');
+ PUTCHAR(kanji_intro);
+ output_mode = X0208;
+ }
+ if(c1<0x20 || 0x7e<c1)
+ return;
+ if(c2<0x20 || 0x7e<c2)
+ return;
+ PUTCHAR(c2);
+ if(c1 == '\n' && add_cr == TRUE)
+ PUTCHAR('\r');
+ if(c1 != '\r')
+ PUTCHAR(c1);
+ else if(del_cr == FALSE)
+ PUTCHAR(c1);
+ }
+}
+
+
+#define rot13(c) ( \
+ ( c < 'A' ) ? c: \
+ (c <= 'M') ? (c + 13): \
+ (c <= 'Z') ? (c - 13): \
+ (c < 'a') ? (c): \
+ (c <= 'm') ? (c + 13): \
+ (c <= 'z') ? (c - 13): \
+ (c) \
+)
+
+#define rot47(c) ( \
+ ( c < '!' ) ? c: \
+ ( c <= 'O' ) ? (c + 47) : \
+ ( c <= '~' ) ? (c - 47) : \
+ c \
+)
+
+/*
+ Return value of fold()
+
+ \n add newline and output char
+ \r add newline and output nothing
+ ' ' space
+ 0 skip
+ 1 (or else) normal output
+
+ fold state in prev (previous character)
+
+ >0x80 Japanese (X0208/X0201)
+ <0x80 ASCII
+ \n new line
+ ' ' space
+
+ This fold algorthm does not preserve heading space in a line.
+ This is the main difference from fmt.
+*/
+
+int
+fold(c2,c1)
+register int c2,c1;
+{
+ int prev0;
+ if(c1=='\r')
+ return 0; /* ignore cr */
+ if(c1== 8) {
+ if(line>0) line--;
+ return 1;
+ }
+ if(c2==EOF && line != 0) /* close open last line */
+ return '\n';
+ /* new line */
+ if(c1=='\n') {
+ if(prev == c1) { /* duplicate newline */
+ if(line) {
+ line = 0;
+ return '\n'; /* output two newline */
+ } else {
+ line = 0;
+ return 1;
+ }
+ } else {
+ if(prev&0x80) { /* Japanese? */
+ prev = c1;
+ return 0; /* ignore given single newline */
+ } else if(prev==' ') {
+ return 0;
+ } else {
+ prev = c1;
+ if(++line<=fold_len)
+ return ' ';
+ else {
+ line = 0;
+ return '\r'; /* fold and output nothing */
+ }
+ }
+ }
+ }
+ if(c1=='\f') {
+ prev = '\n';
+ if(line==0)
+ return 1;
+ line = 0;
+ return '\n'; /* output newline and clear */
+ }
+ /* X0208 kankaku or ascii space */
+ if( (c2==0&&c1==' ')||
+ (c2==0&&c1=='\t')||
+ (c2==DOUBLE_SPACE)||
+ (c2=='!'&& c1=='!')) {
+ if(prev == ' ') {
+ return 0; /* remove duplicate spaces */
+ }
+ prev = ' ';
+ if(++line<=fold_len)
+ return ' '; /* output ASCII space only */
+ else {
+ prev = ' '; line = 0;
+ return '\r'; /* fold and output nothing */
+ }
+ }
+ prev0 = prev; /* we still need this one... , but almost done */
+ prev = c1;
+ if(c2 || (SSP<=c1 && c1<=0xdf))
+ prev |= 0x80; /* this is Japanese */
+ line += (c2==0)?1:2;
+ if(line<=fold_len) { /* normal case */
+ return 1;
+ }
+ if(line>=fold_len+FOLD_MARGIN) { /* too many kinsou suspension */
+ line = (c2==0)?1:2;
+ return '\n'; /* We can't wait, do fold now */
+ }
+ /* simple kinsoku rules return 1 means no folding */
+ if(c2==0) {
+ if(c1==0xde) return 1; /* ゛*/
+ if(c1==0xdf) return 1; /* ゜*/
+ if(c1==0xa4) return 1; /* 。*/
+ if(c1==0xa3) return 1; /* ,*/
+ if(c1==0xa1) return 1; /* 」*/
+ if(c1==0xb0) return 1; /* - */
+ if(SSP<=c1 && c1<=0xdf) { /* X0201 */
+ line = 1;
+ return '\n';/* add one new line before this character */
+ }
+ /* fold point in ASCII { [ ( */
+ if(( c1!=')'&&
+ c1!=']'&&
+ c1!='}'&&
+ c1!='.'&&
+ c1!=','&&
+ c1!='!'&&
+ c1!='?'&&
+ c1!='/'&&
+ c1!=':'&&
+ c1!=';')&&
+ ((prev0=='\n')|| (prev0==' ')|| /* ignored new line */
+ (prev0&0x80)) /* X0208 - ASCII */
+ ) {
+ line = 1;
+ return '\n';/* add one new line before this character */
+ }
+ return 1; /* default no fold in ASCII */
+ } else {
+ if(c2=='!') {
+ if(c1=='"') return 1; /* 、 */
+ if(c1=='#') return 1; /* 。 */
+ if(c1=='$') return 1; /* , */
+ if(c1=='%') return 1; /* . */
+ if(c1=='\'') return 1; /* + */
+ if(c1=='(') return 1; /* ; */
+ if(c1==')') return 1; /* ? */
+ if(c1=='*') return 1; /* ! */
+ if(c1=='+') return 1; /* ゛ */
+ if(c1==',') return 1; /* ゜ */
+ }
+ line = 2;
+ return '\n'; /* add one new line before this character */
+ }
+}
+
+int
+pre_convert(c1,c2)
+register int c1,c2;
+{
+ if(c2) c1 &= 0x7f;
+ c1_return = c1;
+ if(c2==EOF) return c2;
+ c2 &= 0x7f;
+ if(rot_f) {
+ if(c2) {
+ c1 = rot47(c1);
+ c2 = rot47(c2);
+ } else {
+ if(!(c1 & 0x80))
+ c1 = rot13(c1);
+ }
+ c1_return = c1;
+ }
+ /* JISX0208 Alphabet */
+ if(alpha_f && c2 == 0x23 ) return 0;
+ /* JISX0208 Kigou */
+ if(alpha_f && c2 == 0x21 ) {
+ if(0x21==c1) {
+ if(alpha_f&0x2) {
+ c1_return = ' ';
+ return 0;
+ } else if(alpha_f&0x4) {
+ c1_return = ' ';
+ return DOUBLE_SPACE;
+ } else {
+ return c2;
+ }
+ } else if(0x20<c1 && c1<0x7f && fv[c1-0x20]) {
+ c1_return = fv[c1-0x20];
+ return 0;
+ }
+ }
+ return c2;
+}
+
+
+/* X0201 / X0208 conversion tables */
+
+/* X0201 kana conversion table */
+/* 90-9F A0-DF */
+unsigned char cv[]= {
+0x21,0x21,0x21,0x23,0x21,0x56,0x21,0x57,
+0x21,0x22,0x21,0x26,0x25,0x72,0x25,0x21,
+0x25,0x23,0x25,0x25,0x25,0x27,0x25,0x29,
+0x25,0x63,0x25,0x65,0x25,0x67,0x25,0x43,
+0x21,0x3c,0x25,0x22,0x25,0x24,0x25,0x26,
+0x25,0x28,0x25,0x2a,0x25,0x2b,0x25,0x2d,
+0x25,0x2f,0x25,0x31,0x25,0x33,0x25,0x35,
+0x25,0x37,0x25,0x39,0x25,0x3b,0x25,0x3d,
+0x25,0x3f,0x25,0x41,0x25,0x44,0x25,0x46,
+0x25,0x48,0x25,0x4a,0x25,0x4b,0x25,0x4c,
+0x25,0x4d,0x25,0x4e,0x25,0x4f,0x25,0x52,
+0x25,0x55,0x25,0x58,0x25,0x5b,0x25,0x5e,
+0x25,0x5f,0x25,0x60,0x25,0x61,0x25,0x62,
+0x25,0x64,0x25,0x66,0x25,0x68,0x25,0x69,
+0x25,0x6a,0x25,0x6b,0x25,0x6c,0x25,0x6d,
+0x25,0x6f,0x25,0x73,0x21,0x2b,0x21,0x2c,
+0x00,0x00};
+
+
+/* X0201 kana conversion table for daguten */
+/* 90-9F A0-DF */
+unsigned char dv[]= {
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x25,0x2c,0x25,0x2e,
+0x25,0x30,0x25,0x32,0x25,0x34,0x25,0x36,
+0x25,0x38,0x25,0x3a,0x25,0x3c,0x25,0x3e,
+0x25,0x40,0x25,0x42,0x25,0x45,0x25,0x47,
+0x25,0x49,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x25,0x50,0x25,0x53,
+0x25,0x56,0x25,0x59,0x25,0x5c,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00};
+
+/* X0201 kana conversion table for han-daguten */
+/* 90-9F A0-DF */
+unsigned char ev[]= {
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x25,0x51,0x25,0x54,
+0x25,0x57,0x25,0x5a,0x25,0x5d,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00};
+
+
+/* X0208 kigou conversion table */
+/* 0x8140 - 0x819e */
+unsigned char fv[] = {
+
+0x00,0x00,0x00,0x00,0x2c,0x2e,0x00,0x3a,
+0x3b,0x3f,0x21,0x00,0x00,0x27,0x60,0x00,
+0x5e,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x2d,0x00,0x2f,
+0x5c,0x00,0x00,0x7c,0x00,0x00,0x60,0x27,
+0x22,0x22,0x28,0x29,0x00,0x00,0x5b,0x5d,
+0x7b,0x7d,0x3c,0x3e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x2b,0x2d,0x00,0x00,
+0x00,0x3d,0x00,0x3c,0x3e,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x24,0x00,0x00,0x25,0x23,0x26,0x2a,0x40,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+} ;
+
+
+/* This converts =?ISO-2022-JP?B?HOGE HOGE?= */
+
+unsigned char *mime_pattern[] = {
+ (unsigned char *)"\075?ISO-8859-1?Q?",
+ (unsigned char *)"\075?ISO-2022-JP?B?",
+ (unsigned char *)"\075?ISO-2022-JP?Q?",
+ NULL
+};
+
+int mime_encode[] = {
+ 'Q', 'B', 'Q',
+ 0
+};
+
+int iso8859_f_save;
+
+#define nkf_toupper(c) (('a'<=c && c<='z')?(c-('a'-'A')):c)
+/* I don't trust portablity of toupper */
+
+int
+mime_begin()
+{
+ int c1;
+ int i,j,k;
+ unsigned char *p,*q;
+ int r[20]; /* recovery buffer, max mime pattern lenght */
+
+ mime_mode = FALSE;
+ /* =? has been checked */
+ j = 0;
+ p = mime_pattern[j];
+ r[0]='='; r[1]='?';
+
+ for(i=2;p[i]>' ';i++) { /* start at =? */
+ if( ((r[i] = c1 = _GETC())==EOF) || nkf_toupper(c1) != p[i] ) {
+ /* pattern fails, try next one */
+ q = p;
+ while (p = mime_pattern[++j]) {
+ for(k=2;k<i;k++) /* assume length(p) > i */
+ if(p[k]!=q[k]) break;
+ if(k==i && nkf_toupper(c1)==p[k]) break;
+ }
+ if(p) continue; /* found next one, continue */
+ /* all fails, output from recovery buffer */
+ _UNGETC(c1);
+ for(j=0;j<i;j++) {
+ (*oconv)(0,r[j]);
+ }
+ return c1;
+ }
+ }
+ iso8859_f_save = iso8859_f;
+ if(j==0) {
+ iso8859_f = TRUE;
+ }
+ mime_mode = mime_encode[j];
+ if(mime_mode=='B') {
+ mimebuf_f = unbuf_f;
+ if(!unbuf_f) {
+ /* do MIME integrity check */
+ return mime_integrity(mime_pattern[j]);
+ }
+ }
+ mimebuf_f = TRUE;
+ return c1;
+}
+
+#define mime_getc0() (mimebuf_f?_GETC():Fifo(mime_input++))
+#define mime_ungetc0(c) (mimebuf_f?_UNGETC(c):mime_input--)
+
+int
+mime_getc()
+{
+ int c1, c2, c3, c4, cc;
+ int t1, t2, t3, t4, mode, exit_mode;
+
+ if(mime_top != mime_last) { /* Something is in FIFO */
+ return Fifo(mime_top++);
+ }
+
+ if(mimebuf_f == FIXED_MIME)
+ exit_mode = mime_mode;
+ else
+ exit_mode = FALSE;
+ if(mime_mode == 'Q') {
+ if((c1 = mime_getc0()) == EOF) return (EOF);
+ if(c1=='_') return ' ';
+ if(c1!='=' && c1!='?')
+ return c1;
+ mime_mode = exit_mode; /* prepare for quit */
+ if(c1<=' ') return c1;
+ if((c2 = mime_getc0()) == EOF) return (EOF);
+ if(c2<=' ') return c2;
+ if(c1=='?'&&c2=='=') {
+ /* end Q encoding */
+ input_mode = exit_mode;
+ iso8859_f = iso8859_f_save;
+ return _GETC();
+ }
+ if(c1=='?') {
+ mime_mode = 'Q'; /* still in MIME */
+ mime_ungetc0(c2);
+ return c1;
+ }
+ if((c3 = mime_getc0()) == EOF) return (EOF);
+ if(c2<=' ') return c2;
+ mime_mode = 'Q'; /* still in MIME */
+#define hex(c) (('0'<=c&&c<='9')?(c-'0'):\
+ ('A'<=c&&c<='F')?(c-'A'+10):('a'<=c&&c<='f')?(c-'a'+10):0)
+ return ((hex(c2)<<4) + hex(c3));
+ }
+
+ if(mime_mode != 'B') {
+ mime_mode = FALSE;
+ return _GETC();
+ }
+
+
+ /* Base64 encoding */
+ /*
+ MIME allows line break in the middle of
+ Base64, but we are very pessimistic in decoding
+ in unbuf mode because MIME encoded code may broken by
+ less or editor's control sequence (such as ESC-[-K in unbuffered
+ mode. ignore incomplete MIME.
+ */
+ mode = mime_mode;
+ mime_mode = exit_mode; /* prepare for quit */
+
+ while ((c1 = mime_getc0())<=' ') {
+ if(c1==EOF)
+ return (EOF);
+ }
+ if((c2 = mime_getc0())<=' ') {
+ if(c2==EOF)
+ return (EOF);
+ if(mimebuf_f!=FIXED_MIME) input_mode = ASCII;
+ return c2;
+ }
+ if((c1 == '?') && (c2 == '=')) {
+ input_mode = ASCII;
+ while((c1 = _GETC())==' ' /* || c1=='\n' || c1=='\r' */);
+ return c1;
+ }
+ if((c3 = mime_getc0())<=' ') {
+ if(c3==EOF)
+ return (EOF);
+ if(mimebuf_f!=FIXED_MIME) input_mode = ASCII;
+ return c3;
+ }
+ if((c4 = mime_getc0())<=' ') {
+ if(c4==EOF)
+ return (EOF);
+ if(mimebuf_f!=FIXED_MIME) input_mode = ASCII;
+ return c4;
+ }
+
+ mime_mode = mode; /* still in MIME sigh... */
+
+ /* BASE 64 decoding */
+
+ t1 = 0x3f & base64decode(c1);
+ t2 = 0x3f & base64decode(c2);
+ t3 = 0x3f & base64decode(c3);
+ t4 = 0x3f & base64decode(c4);
+ cc = ((t1 << 2) & 0x0fc) | ((t2 >> 4) & 0x03);
+ if(c2 != '=') {
+ Fifo(mime_last++) = cc;
+ cc = ((t2 << 4) & 0x0f0) | ((t3 >> 2) & 0x0f);
+ if(c3 != '=') {
+ Fifo(mime_last++) = cc;
+ cc = ((t3 << 6) & 0x0c0) | (t4 & 0x3f);
+ if(c4 != '=')
+ Fifo(mime_last++) = cc;
+ }
+ } else {
+ return c1;
+ }
+ return Fifo(mime_top++);
+}
+
+int
+mime_ungetc(c)
+unsigned int c;
+{
+ Fifo(mime_last++) = c;
+ return c;
+}
+
+
+int
+mime_integrity(p)
+unsigned char *p;
+{
+ int c,d;
+ unsigned int q;
+ /* In buffered mode, read until =? or NL or buffer full
+ */
+ mime_input = mime_top;
+ mime_last = mime_top;
+ while(*p) Fifo(mime_input++) = *p++;
+ d = 0;
+ q = mime_input;
+ while((c=_GETC())!=EOF) {
+ if(((mime_input-mime_top)&MIME_BUF_MASK)==0) break;
+ if(c=='=' && d=='?') {
+ /* checked. skip header, start decode */
+ Fifo(mime_input++) = c;
+ mime_input = q;
+ return 1;
+ }
+ if(!( (c=='+'||c=='/'|| c=='=' || c=='?' ||
+ ('a'<=c && c<='z')||('A'<= c && c<='Z')||('0'<=c && c<='9'))))
+ break;
+ /* Should we check length mod 4? */
+ Fifo(mime_input++) = c;
+ d=c;
+ }
+ /* In case of Incomplete MIME, no MIME decode */
+ Fifo(mime_input++) = c;
+ mime_last = mime_input; /* point undecoded buffer */
+ mime_mode = 1; /* no decode on Fifo last in mime_getc */
+ return 1;
+}
+
+int
+base64decode(c)
+ int c;
+{
+ int i;
+ if(c > '@')
+ if(c < '[')
+ i = c - 'A'; /* A..Z 0-25 */
+ else
+ i = c - 'G' /* - 'a' + 26 */ ; /* a..z 26-51 */
+ else if(c > '/')
+ i = c - '0' + '4' /* - '0' + 52 */ ; /* 0..9 52-61 */
+ else if(c == '+')
+ i = '>' /* 62 */ ; /* + 62 */
+ else
+ i = '?' /* 63 */ ; /* / 63 */
+ return (i);
+}
+
+#ifdef notdef
+int
+usage()
+{
+ fprintf(stderr,"USAGE: nkf(nkf32,wnkf,nkf2) -[flags] [in file] .. [out file for -O flag]\n");
+ fprintf(stderr,"Flags:\n");
+ fprintf(stderr,"b,u Output is bufferred (DEFAULT),Output is unbufferred\n");
+#ifdef DEFAULT_CODE_SJIS
+ fprintf(stderr,"j,s,e Outout code is JIS 7 bit, Shift JIS (DEFAULT), AT&T JIS (EUC)\n");
+#endif
+#ifdef DEFAULT_CODE_JIS
+ fprintf(stderr,"j,s,e Outout code is JIS 7 bit (DEFAULT), Shift JIS, AT&T JIS (EUC)\n");
+#endif
+#ifdef DEFAULT_CODE_EUC
+ fprintf(stderr,"j,s,e Outout code is JIS 7 bit, Shift JIS, AT&T JIS (EUC) (DEFAULT)\n");
+#endif
+ fprintf(stderr,"J,S,E Input assumption is JIS 7 bit , Shift JIS, AT&T JIS (EUC)\n");
+ fprintf(stderr,"t no conversion\n");
+ fprintf(stderr,"i_ Output sequence to designate JIS-kanji (DEFAULT B)\n");
+ fprintf(stderr,"o_ Output sequence to designate ASCII (DEFAULT B)\n");
+ fprintf(stderr,"r {de/en}crypt ROT13/47\n");
+ fprintf(stderr,"v Show this usage\n");
+ fprintf(stderr,"m[BQ] MIME decode [B:base64 stream,Q:quoted stream]\n");
+ fprintf(stderr,"l ISO8859-1 (Latin-1) support\n");
+ fprintf(stderr,"f Folding: -f60 or -f\n");
+ fprintf(stderr,"Z[0-2] Convert X0208 alphabet to ASCII 1: Kankaku to space,2: 2 spaces\n");
+ fprintf(stderr,"X,x Assume X0201 kana in MS-Kanji, -x preserves X0201\n");
+ fprintf(stderr,"B[0-2] Broken input 0: missing ESC,1: any X on ESC-[($]-X,2: ASCII on NL\n");
+#ifdef MSDOS
+ fprintf(stderr,"T Text mode output\n");
+#endif
+ fprintf(stderr,"O Output to File (DEFAULT 'nkf.out')\n");
+ fprintf(stderr,"d,c Delete \\r in line feed, Add \\r in line feed\n");
+ fprintf(stderr,"Network Kanji Filter Version %s (%s) "
+#if defined(MSDOS) && !defined(_Windows)
+ "for DOS"
+#endif
+#if !defined(__WIN32__) && defined(_Windows)
+ "for Win16"
+#endif
+#if defined(__WIN32__) && defined(_Windows)
+ "for Win32"
+#endif
+#ifdef __OS2__
+ "for OS/2"
+#endif
+ ,Version,Patchlevel);
+ fprintf(stderr,"\n%s\n",CopyRight);
+ return 0;
+}
+#endif /* notdef */
+
+#include "ruby.h"
+
+static VALUE
+kconv_kconv(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ struct RString *src, *dst;
+ VALUE in, out;
+ int in_code, out_code;
+
+ rb_scan_args(argc, argv, "12", &src, &out, &in);
+ Check_Type(src, T_STRING);
+
+ if (NIL_P(out)) {
+ out_code = _JIS;
+ }
+ else {
+ out_code = NUM2INT(out);
+ }
+ if (NIL_P(in)) {
+ in_code = _AUTO;
+ }
+ else {
+ in_code = NUM2INT(in);
+ }
+
+ dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */
+ dst->len = do_kconv(src->ptr, dst->ptr, dst->len, out_code, in_code);
+
+ return (VALUE)dst;
+}
+
+static VALUE
+kconv_tojis(obj, src)
+ VALUE obj;
+ struct RString *src;
+{
+ struct RString *dst;
+
+ Check_Type(src, T_STRING);
+
+ dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */
+ dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _JIS, _AUTO);
+
+ return (VALUE)dst;
+}
+
+static VALUE
+kconv_toeuc(obj, src)
+ VALUE obj;
+ struct RString* src;
+{
+ struct RString *dst;
+
+ Check_Type(src, T_STRING);
+
+ dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */
+ dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _EUC, _AUTO);
+
+ return (VALUE)dst;
+}
+
+static VALUE
+kconv_tosjis(obj, src)
+ VALUE obj;
+ struct RString* src;
+{
+ struct RString *dst;
+
+ Check_Type(src, T_STRING);
+
+ dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */
+ dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _SJIS, _AUTO);
+
+ return (VALUE)dst;
+}
+
+void
+Init_kconv()
+{
+ VALUE mKconv = rb_define_module("Kconv");
+
+ rb_define_module_function(mKconv, "kconv", kconv_kconv, -1);
+ rb_define_module_function(mKconv, "tojis", kconv_tojis, 1);
+ rb_define_module_function(mKconv, "toeuc", kconv_toeuc, 1);
+ rb_define_module_function(mKconv, "tosjis", kconv_tosjis, 1);
+
+ rb_define_const(mKconv, "AUTO", INT2FIX(_AUTO));
+ rb_define_const(mKconv, "JIS", INT2FIX(_JIS));
+ rb_define_const(mKconv, "EUC", INT2FIX(_EUC));
+ rb_define_const(mKconv, "SJIS", INT2FIX(_SJIS));
+}
+
+/**
+ ** パッチ制作者
+ ** void@merope.pleiades.or.jp (Kusakabe Youichi)
+ ** NIDE Naoyuki <nide@ics.nara-wu.ac.jp>
+ ** ohta@src.ricoh.co.jp (Junn Ohta)
+ ** inouet@strl.nhk.or.jp (Tomoyuki Inoue)
+ ** kiri@pulser.win.or.jp (Tetsuaki Kiriyama)
+ ** Kimihiko Sato <sato@sail.t.u-tokyo.ac.jp>
+ ** a_kuroe@kuroe.aoba.yokohama.jp (Akihiko Kuroe)
+ ** kono@ie.u-ryukyu.ac.jp (Shinji Kono)
+ ** GHG00637@nifty-serve.or.jp (COW)
+ ** j_kuro@pluto.ai.kyutech.ac.jp (Jun Kuroda)
+ **
+ ** 最終更新日
+ ** 1996.12.18
+ **/
+
+/* end */
diff --git a/ext/marshal/MANIFEST b/ext/marshal/MANIFEST
index 53b0849484..54870ec71f 100644
--- a/ext/marshal/MANIFEST
+++ b/ext/marshal/MANIFEST
@@ -1,4 +1,5 @@
MANIFEST
depend
+extconf.rb
marshal.c
marshal.doc
diff --git a/ext/marshal/extconf.rb b/ext/marshal/extconf.rb
new file mode 100644
index 0000000000..65923049e5
--- /dev/null
+++ b/ext/marshal/extconf.rb
@@ -0,0 +1,2 @@
+have_func("tmpnam")
+create_makefile("marshal")
diff --git a/ext/marshal/marshal.c b/ext/marshal/marshal.c
index 0b29ad5ab8..eae8d7fe09 100644
--- a/ext/marshal/marshal.c
+++ b/ext/marshal/marshal.c
@@ -13,24 +13,52 @@
#include "io.h"
#include "st.h"
+#define MARSHAL_MAJOR 2
+#define MARSHAL_MINOR 1
+
#define TYPE_NIL '0'
+#define TYPE_TRUE 'T'
+#define TYPE_FALSE 'F'
#define TYPE_FIXNUM 'i'
#define TYPE_OBJECT 'o'
-#define TYPE_LINK '@'
+#define TYPE_USERDEF 'u'
#define TYPE_FLOAT 'f'
#define TYPE_BIGNUM 'l'
#define TYPE_STRING '"'
+#define TYPE_STRING2 '\''
#define TYPE_REGEXP '/'
#define TYPE_ARRAY '['
+#define TYPE_ARRAY2 ']'
#define TYPE_HASH '{'
+#define TYPE_HASH2 '}'
#define TYPE_STRUCT 'S'
+#define TYPE_SYMBOL ':'
+#define TYPE_SYMLINK ';'
+
+VALUE cString;
+VALUE cArray;
+VALUE cHash;
+
char *rb_class2path();
VALUE rb_path2class();
static ID s_dump, s_load;
+#if (defined(linux) && defined(USE_DLN_A_OUT)) || !defined(HAVE_TMPNAM)
+#define tmpnam(s) ltmpnam(s)
+static char *
+tmpnam(s)
+ char *s;
+{
+ static int n = 0;
+
+ sprintf(s, "/tmp/rb-mrsr-%x%x", getpid(), n++);
+ return s;
+}
+#endif
+
#define w_byte(c, fp) putc((c), fp)
#define w_bytes(s, n, fp) (w_long((n), fp),fwrite(s, 1, n, fp))
@@ -66,70 +94,119 @@ w_float(d, fp)
}
static void
-w_symbol(id, fp)
+w_symbol(id, fp, table)
ID id;
FILE *fp;
+ st_table *table;
{
char *sym = rb_id2name(id);
+ int num;
- w_bytes(sym, strlen(sym), fp);
+ if (st_lookup(table, id, &num)) {
+ w_byte(TYPE_SYMLINK, fp);
+ w_long(num, fp);
+ }
+ else {
+ w_byte(TYPE_SYMBOL, fp);
+ w_bytes(sym, strlen(sym), fp);
+ st_insert(table, id, table->num_entries);
+ }
+}
+
+static void
+w_unique(s, fp, table)
+ char *s;
+ FILE *fp;
+ st_table *table;
+{
+ w_symbol(rb_intern(s), fp, table);
}
static void w_object();
extern VALUE cBignum, cStruct;
+struct each_arg {
+ FILE *fp;
+ VALUE limit;
+ st_table *table;
+};
+
static int
-hash_each(key, value, fp)
+hash_each(key, value, arg)
VALUE key, value;
- FILE *fp;
+ struct each_arg *arg;
{
- w_object(key, fp);
- w_object(value, fp);
+ w_object(key, arg->fp, arg->limit, arg->table);
+ w_object(value, arg->fp, arg->limit, arg->table);
return ST_CONTINUE;
}
static int
-obj_each(id, value, fp)
+obj_each(id, value, arg)
ID id;
VALUE value;
- FILE *fp;
+ struct each_arg *arg;
{
- w_symbol(id, fp);
- w_object(value, fp);
+ w_symbol(id, arg->fp, arg->table);
+ w_object(value, arg->fp, arg->limit, arg->table);
return ST_CONTINUE;
}
-struct st_table *new_idhash();
-
static void
-w_object(obj, fp, port, table)
- VALUE obj, port;
+w_object(obj, fp, limit, table)
+ VALUE obj;
FILE *fp;
+ int limit;
st_table *table;
{
+ struct each_arg arg;
+ int n;
+
+ if (limit == 0) {
+ Fail("exceed depth limit");
+ }
+ limit--;
+
+ arg.fp = fp;
+ arg.limit = limit;
+ arg.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 (obj == TRUE) {
+ w_byte(TYPE_TRUE, fp);
+ }
+ else if (obj == FALSE) {
+ w_byte(TYPE_FALSE, fp);
}
- else if (st_lookup(table, obj, 0)) {
- w_byte(TYPE_LINK, fp);
- w_long(obj, fp);
+ else if (FIXNUM_P(obj)) {
+ if (sizeof(long) == 4) {
+ w_byte(TYPE_FIXNUM, fp);
+ w_long(FIX2INT(obj), fp);
+ }
}
else {
- st_insert(table, obj, 0);
+ if (rb_respond_to(obj, s_dump)) {
+ VALUE v;
+
+ w_byte(TYPE_USERDEF, fp);
+ w_unique(rb_class2path(CLASS_OF(obj)), fp, table);
+ v = rb_funcall(obj, s_dump, 1, limit);
+ if (TYPE(v) != T_STRING) {
+ TypeError("_dump_to must return String");
+ }
+ w_bytes(RSTRING(v)->ptr, RSTRING(v)->len, fp);
+ return;
+ }
switch (BUILTIN_TYPE(obj)) {
case T_FLOAT:
w_byte(TYPE_FLOAT, fp);
- w_long(obj, fp);
w_float(RFLOAT(obj)->value, fp);
- break;
+ return;
case T_BIGNUM:
w_byte(TYPE_BIGNUM, fp);
- w_long(obj, fp);
{
char sign = RBIGNUM(obj)->sign?'+':'-';
int len = RBIGNUM(obj)->len;
@@ -142,80 +219,92 @@ w_object(obj, fp, port, table)
d++;
}
}
- break;
+ return;
+ }
+ switch (BUILTIN_TYPE(obj)) {
case T_STRING:
- w_byte(TYPE_STRING, fp);
- w_long(obj, fp);
- w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp);
- break;
+ if (CLASS_OF(obj) == cString) {
+ w_byte(TYPE_STRING, fp);
+ w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp);
+ }
+ else {
+ w_byte(TYPE_STRING2, fp);
+ w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp);
+ w_unique(rb_class2path(CLASS_OF(obj)), fp, table);
+ }
+ return;
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;
+ return;
case T_ARRAY:
- w_byte(TYPE_ARRAY, fp);
- w_long(obj, fp);
+ if (CLASS_OF(obj) == cArray) w_byte(TYPE_ARRAY, fp);
+ else w_byte(TYPE_ARRAY2, fp);
{
int len = RARRAY(obj)->len;
VALUE *ptr = RARRAY(obj)->ptr;
w_long(len, fp);
while (len--) {
- w_object(*ptr, fp, port, table);
+ w_object(*ptr, fp, limit, table);
ptr++;
}
}
+ if (CLASS_OF(obj) != cArray) {
+ w_unique(rb_class2path(CLASS_OF(obj)), fp, table);
+ }
break;
case T_HASH:
+ if (CLASS_OF(obj) == cHash) w_byte(TYPE_HASH, fp);
+ else w_byte(TYPE_HASH2, fp);
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);
+ st_foreach(RHASH(obj)->tbl, hash_each, &arg);
+ if (CLASS_OF(obj) != cHash) {
+ w_unique(rb_class2path(CLASS_OF(obj)), fp, table);
+ }
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_unique(path, fp, table);
w_long(len, fp);
mem = rb_ivar_get(CLASS_OF(obj), rb_intern("__member__"));
if (mem == Qnil) {
- Fail("non-initialized struct");
+ Fatal("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);
+ w_symbol(FIX2INT(RARRAY(mem)->ptr[i]), fp, table);
+ w_object(RSTRUCT(obj)->ptr[i], fp, limit, table);
}
}
break;
case T_OBJECT:
w_byte(TYPE_OBJECT, fp);
- w_long(obj, fp);
{
VALUE class = CLASS_OF(obj);
- char *path = rb_class2path(class);
+ char *path;
- w_bytes(path, strlen(path), fp);
- if (rb_responds_to(obj, s_dump)) {
- w_long(-1, fp);
- rb_funcall(obj, s_dump, 1, port);
+ if (FL_TEST(class, FL_SINGLETON)) {
+ TypeError("singleton can't be dumped");
}
- else if (ROBJECT(obj)->iv_tbl) {
+ path = rb_class2path(class);
+ w_unique(path, fp, table);
+ if (ROBJECT(obj)->iv_tbl) {
w_long(ROBJECT(obj)->iv_tbl->num_entries, fp);
- st_foreach(ROBJECT(obj)->iv_tbl, obj_each, fp);
+ st_foreach(ROBJECT(obj)->iv_tbl, obj_each, &arg);
}
else {
w_long(0, fp);
@@ -224,59 +313,108 @@ w_object(obj, fp, port, table)
break;
default:
- Fail("can't dump %s", rb_class2name(CLASS_OF(obj)));
+ TypeError("can't dump %s", rb_class2name(CLASS_OF(obj)));
break;
}
}
}
+struct dump_arg {
+ VALUE obj;
+ FILE *fp;
+ int limit;
+ st_table *table;
+};
+
static VALUE
-marshal_dump(self, obj, port)
- VALUE self, obj, port;
+dump(arg)
+ struct dump_arg *arg;
+{
+ w_object(arg->obj, arg->fp, arg->limit, arg->table);
+}
+
+static VALUE
+dump_ensure(arg)
+ struct dump_arg *arg;
+{
+ st_free_table(arg->table);
+}
+
+static VALUE
+dump_on(obj, port, limit)
+ VALUE obj, port;
+ int limit;
{
extern VALUE cIO;
FILE *fp;
OpenFile *fptr;
- st_table *table;
+ struct dump_arg arg;
if (obj_is_kind_of(port, cIO)) {
GetOpenFile(port, fptr);
- if (!(fptr->mode & FMODE_WRITABLE)) {
- Fail("not opened for writing");
- }
+ io_writable(fptr);
fp = (fptr->f2) ? fptr->f2 : fptr->f;
}
else {
- Fail("instance of IO needed");
+ TypeError("instance of IO needed");
}
- table = new_idhash();
+ w_byte(MARSHAL_MAJOR, fp);
+ w_byte(MARSHAL_MINOR, fp);
- w_object(obj, fp, port, table);
+ arg.obj = obj;
+ arg.fp = fp;
+ arg.limit = limit;
+ arg.table = st_init_numtable();
+ rb_ensure(dump, &arg, dump_ensure, &arg);
- st_free_table(table);
return Qnil;
}
static VALUE
-marshal_dumps(self, obj)
- VALUE self, obj;
+marshal_dump(argc, argv)
+ int argc;
+ VALUE argv;
+{
+ VALUE obj, port, lim;
+ int limit;
+
+ rb_scan_args(argc, argv, "21", &obj, &port, &lim);
+ if (NIL_P(lim)) limit = 100;
+ else limit = NUM2INT(lim);
+
+ dump_on(obj, port, limit);
+}
+
+static VALUE
+marshal_dumps(argc, argv)
+ int argc;
+ VALUE argv;
{
+ VALUE obj, lim;
+ int limit;
VALUE str = str_new(0, 0);
VALUE port;
- FILE *fp = Qnil;
+ FILE *fp = 0;
char buf[BUFSIZ];
int n;
- sprintf(buf, "/tmp/rb-mrsr-%x", getpid()^(int)buf);
+ rb_scan_args(argc, argv, "11", &obj, &lim);
+ if (NIL_P(lim)) limit = 100;
+ else limit = NUM2INT(lim);
+
+ tmpnam(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)");
+ fp = rb_fopen(buf, "r");
+#if !defined(MSDOS) && !defined(__BOW__)
unlink(buf);
+#endif
- marshal_dump(self, obj, port);
+ dump_on(obj, port, limit);
io_close(port);
+#if defined(MSDOS) || defined(__BOW__)
+ unlink(buf);
+#endif
while (n = fread(buf, 1, BUFSIZ, fp)) {
str_cat(str, buf, n);
@@ -310,67 +448,96 @@ r_long(fp)
/* 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;
+
+#define r_bytes(s, fp) \
+ (s = (char*)r_long(fp), r_bytes0(&s,ALLOCA_N(char,(int)s),(int)s,fp))
+
+static char
+r_bytes0(sp, s, len, fp)
+ char **sp, *s;
+ int len;
FILE *fp;
{
- int len = r_long(fp);
- *s = ALLOC_N(char, len+1);
+ fread(s, 1, len, fp);
+ (s)[len] = '\0';
+ *sp = s;
- fread(*s, 1, len, fp);
- (*s)[len] = '\0';
return len;
}
static ID
-r_symbol(fp)
+r_symbol(fp, table)
FILE *fp;
+ st_table *table;
{
char *buf;
ID id;
+ char type;
+ if (r_byte(fp) == TYPE_SYMLINK) {
+ int num = r_long(fp);
+
+ if (st_lookup(table, num, &id)) {
+ return id;
+ }
+ TypeError("bad symbol");
+ }
r_bytes(buf, fp);
id = rb_intern(buf);
- free(buf);
+ st_insert(table, table->num_entries, id);
+
return id;
}
+static char*
+r_unique(fp, table)
+ FILE *fp;
+ st_table *table;
+{
+ return rb_id2name(r_symbol(fp, table));
+}
+
static VALUE
-r_object(fp, port, table)
+r_string(fp)
+ FILE *fp;
+{
+ char *buf;
+ int len = r_bytes(buf, fp);
+ VALUE v;
+
+ v = str_new(buf, len);
+
+ return v;
+}
+
+static VALUE
+r_object(fp, 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");
+ eof_error("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_TRUE:
+ return TRUE;
+
+ case TYPE_FALSE:
+ return FALSE;
case TYPE_FIXNUM:
{
int i = r_long(fp);
return INT2FIX(i);
}
- }
- id = r_long(fp);
- switch (type) {
case TYPE_FLOAT:
{
double atof();
@@ -378,9 +545,8 @@ r_object(fp, port, table)
r_bytes(buf, fp);
v = float_new(atof(buf));
- free(buf);
+ return v;
}
- break;
case TYPE_BIGNUM:
{
@@ -395,18 +561,16 @@ r_object(fp, port, table)
while (len--) {
*digits++ = r_short(fp);
}
- v = (VALUE)big;
+ return (VALUE)big;
}
- break;
case TYPE_STRING:
- {
- char *buf;
- int len = r_bytes(buf, fp);
- v = str_new(buf, len);
- free(buf);
- }
- break;
+ return r_string(fp);
+
+ case TYPE_STRING2:
+ v = r_string(fp);
+ RBASIC(v)->class = rb_path2class(r_unique(fp, table));
+ return v;
case TYPE_REGEXP:
{
@@ -414,19 +578,18 @@ r_object(fp, port, table)
int len = r_bytes(buf, fp);
int ci = r_byte(fp);
v = reg_new(buf, len, ci);
- free(buf);
+ return v;
}
- break;
case TYPE_ARRAY:
{
int len = r_long(fp);
v = ary_new2(len);
while (len--) {
- ary_push(v, r_object(fp, port, table));
+ ary_push(v, r_object(fp, table));
}
+ return v;
}
- break;
case TYPE_HASH:
{
@@ -434,120 +597,150 @@ r_object(fp, port, table)
v = hash_new();
while (len--) {
- VALUE key = r_object(fp, port, table);
- VALUE value = r_object(fp, port, table);
+ VALUE key = r_object(fp, table);
+ VALUE value = r_object(fp, table);
hash_aset(v, key, value);
}
+ return v;
}
- break;
case TYPE_STRUCT:
{
VALUE class, mem, values;
- char *path;
int i, len;
- r_bytes(path, fp);
- class = rb_path2class(path);
- free(path);
+ class = rb_path2class(r_unique(fp, table));
mem = rb_ivar_get(class, rb_intern("__member__"));
if (mem == Qnil) {
- Fail("non-initialized struct");
+ Fatal("non-initialized struct");
}
len = r_long(fp);
- values = ary_new();
+ values = ary_new2(len);
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));
+ for (i=0; i<len; i++) {
+ ID slot = r_symbol(fp, table);
+ if (RARRAY(mem)->ptr[i] != INT2FIX(slot))
+ TypeError("struct not compatible");
+ ary_push(values, r_object(fp, table));
}
v = struct_alloc(class, values);
}
break;
+ case TYPE_USERDEF:
+ {
+ VALUE class;
+ int len;
+
+ class = rb_path2class(r_unique(fp, table));
+ if (rb_respond_to(class, s_load)) {
+ v = rb_funcall(class, s_load, 1, r_string(fp));
+ }
+ else {
+ TypeError("class %s needs to have method `_load_from'",
+ rb_class2name(class));
+ }
+ }
+ break;
case TYPE_OBJECT:
{
VALUE class;
int len;
- char *path;
- r_bytes(path, fp);
- class = rb_path2class(path);
- free(path);
+ class = rb_path2class(r_unique(fp, table));
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);
- }
+ v = obj_alloc(class);
+ if (len > 0) {
+ while (len--) {
+ ID id = r_symbol(fp, table);
+ VALUE val = r_object(fp, table);
+ rb_ivar_set(v, id, val);
}
}
}
break;
default:
- Fail("dump format error(0x%x)", type);
+ ArgError("dump format error(0x%x)", type);
break;
}
- st_insert(table, id, v);
return v;
}
+struct load_arg {
+ FILE *fp;
+ st_table *table;
+};
+
+static VALUE
+load(arg)
+ struct load_arg *arg;
+{
+ return r_object(arg->fp, arg->table);
+}
+
+static VALUE
+load_ensure(arg)
+ struct load_arg *arg;
+{
+ st_free_table(arg->table);
+}
+
static VALUE
marshal_load(self, port)
VALUE self, port;
{
extern VALUE cIO;
- void *fp;
+ FILE *fp;
+ int major;
VALUE v;
OpenFile *fptr;
- st_table *table;
+ char buf[32];
+#if defined(MSDOS) || defined(__BOW__)
+ int need_unlink_tmp = 0;
+#endif
+ struct load_arg arg;
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");
+ tmpnam(buf);
+ fp = rb_fopen(buf, "w");
v = file_open(buf, "r");
- if (!v) rb_sys_fail("tmp file(read)");
+#if defined(MSDOS) || defined(__BOW__)
+ need_unlink_tmp = 0;
+#else
unlink(buf);
+#endif
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");
- }
+ io_readable(fptr);
fp = fptr->f;
}
else {
- Fail("instance of IO needed");
+ TypeError("instance of IO needed");
}
- table = new_idhash();
-
- v = r_object(fp, port, table);
-
- st_free_table(table);
+ major = r_byte(fp);
+ if (major == MARSHAL_MAJOR) {
+ if (r_byte(fp) != MARSHAL_MINOR) {
+ Warning("Old marshal file format (can be read)");
+ }
+ arg.fp = fp;
+ arg.table = st_init_numtable();
+ v = rb_ensure(load, &arg, load_ensure, &arg);
+ }
+#if defined(MSDOS) || defined(__BOW__)
+ if (need_unlink_tmp) unlink(buf);
+#endif
+ if (major != MARSHAL_MAJOR) {
+ TypeError("Old marshal file format (can't read)");
+ }
return v;
}
@@ -558,8 +751,8 @@ Init_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, "dump", marshal_dump, -1);
+ 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
index 8c3b63072e..7529e7942f 100644
--- a/ext/marshal/marshal.doc
+++ b/ext/marshal/marshal.doc
@@ -10,7 +10,7 @@ rubyオブジェクトをファイルに書き出したり,読みも度したりする機能を提供
Methods:
Single Methods:
- dump(obj, port)
+ dump(obj, port[, limit])
objを再帰的にファイルに書き出す.ファイルに書き出せないクラスのイ
ンスタンスをファイルに書き出そうとすると例外を発生させる.ファイル
@@ -28,6 +28,9 @@ Single Methods:
`_dump_to'を持つクラスは必ず同じフォーマットを読み戻す特異メソッド
`_load_from'を定義する必要がある.
+ limitを指定した場合,limit段以上深くリンクしたオブジェクトをダンプ
+ できない(デフォルトは100レベル)。負のlimitを指定すると深さチェック
+ を行わない。
dumps(obj)
diff --git a/ext/md5/MANIFEST b/ext/md5/MANIFEST
new file mode 100644
index 0000000000..e4f0004b4a
--- /dev/null
+++ b/ext/md5/MANIFEST
@@ -0,0 +1,6 @@
+MANIFEST
+depend
+md5.doc
+md5.h
+md5c.c
+md5init.c
diff --git a/ext/md5/depend b/ext/md5/depend
new file mode 100644
index 0000000000..be56da89b9
--- /dev/null
+++ b/ext/md5/depend
@@ -0,0 +1,2 @@
+md5c.o: md5c.c md5.h
+md5init.o: md5init.c ../../ruby.h ../../config.h ../../defines.h md5.h
diff --git a/ext/md5/md5.doc b/ext/md5/md5.doc
new file mode 100644
index 0000000000..2203404602
--- /dev/null
+++ b/ext/md5/md5.doc
@@ -0,0 +1,36 @@
+.\" md5.doc - -*- Indented-Text -*- created at: Fri Aug 2 12:01:27 JST 1996
+
+** MD5(クラス)
+
+RFC1321に記述されているRSA Data Security, Inc. の MD5 Message-Digest
+Algorithmを実装するクラス.
+
+SuperClass: Object
+
+Class Methods:
+
+ new([str])
+ md5([str])
+
+ 新しいMD5オブジェクトを生成する.文字列引数が与えられるとそれ
+ を追加する(see update).
+
+Methods:
+
+ clone
+
+ MD5オブジェクトの複製を作る
+
+ digest
+
+ 今までに追加した文字列に対するハッシュ値を16バイト長の文字列で
+ 返す.
+
+ update(str)
+
+ keyをキーとする値を返す.
+
+-------------------------------------------------------
+Local variables:
+fill-column: 70
+end:
diff --git a/ext/md5/md5.h b/ext/md5/md5.h
new file mode 100644
index 0000000000..81a6d7ff36
--- /dev/null
+++ b/ext/md5/md5.h
@@ -0,0 +1,86 @@
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* ========== include global.h ========== */
+/* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+ function argument prototyping.
+The following makes PROTOTYPES default to 0 if it has not already
+ been defined with C compiler flags.
+ */
+#ifdef HAVE_PROTOTYPES
+#define PROTOTYPES 1
+#endif
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+/* Wild guess */
+#define LONG_MAX 2147483647L
+#endif
+
+/* UINT4 defines a four byte word */
+#if defined(INT_MAX) && INT_MAX == 2147483647
+typedef unsigned int UINT4;
+#else
+#if defined(LONG_MAX) && LONG_MAX == 2147483647L
+typedef unsigned long int UINT4;
+#endif
+/* Too bad if neither is */
+#endif
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+ returns an empty list.
+ */
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+/* ========== End global.h; continue md5.h ========== */
+
+/* MD5 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST
+ ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
diff --git a/ext/md5/md5c.c b/ext/md5/md5c.c
new file mode 100644
index 0000000000..d7c7e4fb27
--- /dev/null
+++ b/ext/md5/md5c.c
@@ -0,0 +1,337 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
+static void Encode PROTO_LIST
+ ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+ ((UINT4 *, unsigned char *, unsigned int));
+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
+static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init (context)
+MD5_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void MD5Update (context, input, inputLen)
+MD5_CTX *context; /* context */
+unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+*/
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void MD5Final (digest, context)
+unsigned char digest[16]; /* message digest */
+MD5_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+POINTER input;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
diff --git a/ext/md5/md5init.c b/ext/md5/md5init.c
new file mode 100644
index 0000000000..85f0847e71
--- /dev/null
+++ b/ext/md5/md5init.c
@@ -0,0 +1,90 @@
+/************************************************
+
+ md5init.c -
+
+ $Author: matz $
+ created at: Fri Aug 2 09:24:12 JST 1996
+
+ Copyright (C) 1995 Yukihiro Matsumoto
+
+************************************************/
+/* This module provides an interface to the RSA Data Security,
+ Inc. MD5 Message-Digest Algorithm, described in RFC 1321.
+ It requires the files md5c.c and md5.h (which are slightly changed
+ from the versions in the RFC to avoid the "global.h" file.) */
+
+#include "ruby.h"
+#include "md5.h"
+
+static VALUE cMD5;
+
+static VALUE
+md5_update(obj, str)
+ VALUE obj;
+ struct RString *str;
+{
+ MD5_CTX *md5;
+
+ Check_Type(str, T_STRING);
+ Get_Data_Struct(obj, MD5_CTX, md5);
+ MD5Update(md5, str->ptr, str->len);
+
+ return Qnil;
+}
+static VALUE
+md5_digest(obj)
+ VALUE obj;
+{
+ MD5_CTX *md5, ctx;
+ unsigned char digest[16];
+
+ Get_Data_Struct(obj, MD5_CTX, md5);
+ ctx = *md5;
+ MD5Final(digest, &ctx);
+
+ return str_new(digest, 16);
+}
+
+static VALUE
+md5_clone(obj)
+ VALUE obj;
+{
+ VALUE clone;
+ MD5_CTX *md5, *md5_new;
+
+ Get_Data_Struct(obj, MD5_CTX, md5);
+ obj = Make_Data_Struct(CLASS_OF(obj), MD5_CTX, 0, 0, md5_new);
+ *md5_new = *md5;
+
+ return obj;
+}
+
+static VALUE
+md5_new(argc, argv, class)
+{
+ int i;
+ VALUE arg, obj;
+ MD5_CTX *md5;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ if (!NIL_P(arg)) Check_Type(arg, T_STRING);
+
+ obj = Make_Data_Struct(class, MD5_CTX, 0, 0, md5);
+ MD5Init(md5);
+ if (!NIL_P(arg)) {
+ md5_update(obj, arg);
+ }
+
+ return obj;
+}
+
+Init_md5()
+{
+ cMD5 = rb_define_class("MD5", cObject);
+
+ rb_define_singleton_method(cMD5, "new", md5_new, -1);
+
+ rb_define_method(cMD5, "update", md5_update, 1);
+ rb_define_method(cMD5, "digest", md5_digest, 0);
+ rb_define_method(cMD5, "clone", md5_clone, 0);
+}
diff --git a/ext/socket/MANIFEST b/ext/socket/MANIFEST
index 836caada41..d41d9e0b69 100644
--- a/ext/socket/MANIFEST
+++ b/ext/socket/MANIFEST
@@ -2,4 +2,3 @@ MANIFEST
depend
extconf.rb
socket.c
-socket.doc
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 60d6deeb84..f2ec0578d5 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -1,6 +1,11 @@
-have_library("inet", "gethostbyname")
have_library("socket", "socket")
+have_library("inet", "gethostbyname")
+have_library("nsl", "gethostbyname")
have_header("sys/un.h")
if have_func("socket")
+ have_func("hsterror")
+ unless have_func("gethostname")
+ have_func("uname")
+ end
create_makefile("socket")
end
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 5671b2762c..6b4f0f5559 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -18,11 +18,11 @@
#include <errno.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
-#else
-#undef AF_UNIX
#endif
extern VALUE cIO;
+extern VALUE cInteger;
+
VALUE cBasicSocket;
VALUE cTCPsocket;
VALUE cTCPserver;
@@ -32,6 +32,9 @@ VALUE cUNIXserver;
#endif
VALUE cSocket;
+extern VALUE eException;
+static VALUE eSocket;
+
FILE *rb_fdopen();
char *strdup();
@@ -52,8 +55,9 @@ sock_new(class, fd)
VALUE class;
int fd;
{
- VALUE sock = obj_alloc(class);
OpenFile *fp;
+ NEWOBJ(sock, struct RFile);
+ OBJSETUP(sock, class, T_FILE);
MakeOpenFile(sock, fp);
#ifdef NT
@@ -64,7 +68,7 @@ sock_new(class, fd)
fp->f2 = rb_fdopen(fd, "w");
fp->mode = FMODE_READWRITE|FMODE_SYNC;
- return sock;
+ return (VALUE)sock;
}
static VALUE
@@ -86,34 +90,53 @@ bsock_shutdown(argc, argv, sock)
}
GetOpenFile(sock, fptr);
if (shutdown(fileno(fptr->f), how) == -1)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
return INT2FIX(0);
}
static VALUE
-bsock_setopt(sock, lev, optname, val)
+bsock_setsockopt(sock, lev, optname, val)
VALUE sock, lev, optname;
struct RString *val;
{
int level, option;
OpenFile *fptr;
+ int i;
+ char *v;
+ int vlen;
level = NUM2INT(lev);
option = NUM2INT(optname);
- Check_Type(val, T_STRING);
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ i = FIX2INT(val);
+ goto numval;
+ case T_FALSE:
+ i = 0;
+ goto numval;
+ case T_TRUE:
+ i = 1;
+ numval:
+ v = (char*)&i; vlen = sizeof(i);
+ break;
+ default:
+ Check_Type(val, T_STRING);
+ v = val->ptr; vlen = val->len;
+ }
GetOpenFile(sock, fptr);
- if (setsockopt(fileno(fptr->f), level, option, val->ptr, val->len) < 0)
+ if (setsockopt(fileno(fptr->f), level, option, v, vlen) < 0)
rb_sys_fail(fptr->path);
return INT2FIX(0);
}
static VALUE
-bsock_getopt(sock, lev, optname)
+bsock_getsockopt(sock, lev, optname)
VALUE sock, lev, optname;
{
+#if !defined(__CYGWIN32__)
int level, option, len;
struct RString *val;
OpenFile *fptr;
@@ -128,7 +151,11 @@ bsock_getopt(sock, lev, optname)
if (getsockopt(fileno(fptr->f), level, option, val->ptr, &len) < 0)
rb_sys_fail(fptr->path);
val->len = len;
+
return (VALUE)val;
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -184,8 +211,14 @@ open_inet(class, h, serv, server)
if (hostaddr == -1) {
if (server && !strlen(host))
hostaddr = INADDR_ANY;
- else
- rb_sys_fail(host);
+ else {
+#ifdef HAVE_HSTRERROR
+ extern int h_errno;
+ Raise(eSocket, (char *)hstrerror(h_errno));
+#else
+ Raise(eSocket, "host not found");
+#endif
+ }
}
_hostent.h_addr_list = (char **)hostaddrPtr;
_hostent.h_addr_list[0] = (char *)&hostaddr;
@@ -203,27 +236,31 @@ open_inet(class, h, serv, server)
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);
+ servport = strtoul(RSTRING(serv)->ptr, 0, 0);
+ if (servport == -1) {
+ Raise(eSocket, "no such servce %s", RSTRING(serv)->ptr);
+ }
setup_servent:
- _servent.s_port = servport;
- _servent.s_proto = "tcp";
+ _servent.s_port = htons(servport);
+ _servent.s_proto = "tcp";
servent = &_servent;
}
protoent = getprotobyname(servent->s_proto);
- if (protoent == NULL) Fail("no such proto %s", servent->s_proto);
+ if (protoent == NULL) {
+ Raise(eSocket, "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 {
+ if (h) {
memcpy((char *)&(sockaddr.sin_addr.s_addr),
(char *) hostent->h_addr_list[0],
(size_t) hostent->h_length);
}
+ else {
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ }
sockaddr.sin_port = servent->s_port;
if (server) {
@@ -248,7 +285,7 @@ open_inet(class, h, serv, server)
}
static VALUE
-tcp_s_sock_open(class, host, serv)
+tcp_s_open(class, host, serv)
VALUE class, host, serv;
{
Check_Type(host, T_STRING);
@@ -266,7 +303,7 @@ tcp_svr_s_open(argc, argv, class)
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);
+ return open_inet(class, 0, arg1, 1);
}
static VALUE
@@ -279,10 +316,15 @@ s_accept(class, fd, sockaddr, len)
int fd2;
retry:
+#ifdef THREAD
+ thread_wait_fd(fd);
+#endif
+ TRAP_BEG;
fd2 = accept(fd, sockaddr, len);
+ TRAP_END;
if (fd2 < 0) {
if (errno == EINTR) goto retry;
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
return sock_new(class, fd2);
}
@@ -301,7 +343,7 @@ tcp_accept(sock)
(struct sockaddr*)&from, &fromlen);
}
-#ifdef AF_UNIX
+#ifdef HAVE_SYS_UN_H
static VALUE
open_unix(class, path, server)
VALUE class;
@@ -346,11 +388,63 @@ open_unix(class, path, server)
}
#endif
+static void
+setipaddr(name, addr)
+ char *name;
+ struct sockaddr_in *addr;
+{
+ int d1, d2, d3, d4;
+ char ch;
+ struct hostent *hp;
+ long x;
+ unsigned char *a;
+ char buf[16];
+
+ if (name[0] == 0) {
+ addr->sin_addr.s_addr = INADDR_ANY;
+ }
+ else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
+ addr->sin_addr.s_addr = INADDR_BROADCAST;
+ }
+ else if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
+ 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
+ 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
+ addr->sin_addr.s_addr = htonl(
+ ((long) d1 << 24) | ((long) d2 << 16) |
+ ((long) d3 << 8) | ((long) d4 << 0));
+ }
+ else {
+ hp = gethostbyname(name);
+ if (!hp) {
+#ifdef HAVE_HSTRERROR
+ extern int h_errno;
+ Raise(eSocket, (char *)hstrerror(h_errno));
+#else
+ Raise(eSocket, "host not found");
+#endif
+ }
+ memcpy((char *) &addr->sin_addr, hp->h_addr, hp->h_length);
+ }
+}
+
+static VALUE
+mkipaddr(x)
+ unsigned long x;
+{
+ char buf[16];
+
+ x = ntohl(x);
+ sprintf(buf, "%d.%d.%d.%d",
+ (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
+ (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
+ return str_new2(buf);
+}
+
static VALUE
tcpaddr(sockaddr)
struct sockaddr_in *sockaddr;
{
- VALUE family, port, addr;
+ VALUE family, port, addr1, addr2;
VALUE ary;
struct hostent *hostent;
@@ -358,17 +452,15 @@ tcpaddr(sockaddr)
hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr,
sizeof(sockaddr->sin_addr),
AF_INET);
+ addr1 = 0;
if (hostent) {
- addr = str_new2(hostent->h_name);
+ addr1 = 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);
+ addr2 = mkipaddr(sockaddr->sin_addr.s_addr);
+ if (!addr1) addr1 = addr2;
+
+ port = INT2FIX(ntohs(sockaddr->sin_port));
+ ary = ary_new3(4, family, port, addr1, addr2);
return ary;
}
@@ -399,11 +491,30 @@ tcp_peeraddr(sock)
GetOpenFile(sock, fptr);
if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
- rb_sys_fail("getsockname(2)");
+ rb_sys_fail("getpeername(2)");
return tcpaddr(&addr);
}
-#ifdef AF_UNIX
+static VALUE
+tcp_s_getaddress(obj, host)
+ VALUE obj, host;
+{
+ struct sockaddr_in addr;
+ struct hostent *h;
+
+ if (obj_is_kind_of(host, cInteger)) {
+ int i = NUM2INT(host);
+ addr.sin_addr.s_addr = htonl(i);
+ }
+ else {
+ Check_Type(host, T_STRING);
+ setipaddr(RSTRING(host)->ptr, &addr);
+ }
+
+ return mkipaddr(addr.sin_addr.s_addr);
+}
+
+#ifdef HAVE_SYS_UN_H
static VALUE
unix_s_sock_open(sock, path)
VALUE sock, path;
@@ -418,11 +529,11 @@ unix_path(sock)
OpenFile *fptr;
GetOpenFile(sock, fptr);
- if (fptr->path == Qnil) {
+ if (fptr->path == 0) {
struct sockaddr_un addr;
int len = sizeof(addr);
if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
fptr->path = strdup(addr.sun_path);
}
return str_new2(fptr->path);
@@ -515,7 +626,7 @@ setup_domain_and_type(domain, dv, type, tv)
*dv = PF_IPX;
#endif
else
- Fail("Unknown socket domain %s", ptr);
+ Raise(eSocket, "Unknown socket domain %s", ptr);
}
else {
*dv = NUM2INT(domain);
@@ -543,7 +654,7 @@ setup_domain_and_type(domain, dv, type, tv)
*tv = SOCK_PACKET;
#endif
else
- Fail("Unknown socket type %s", ptr);
+ Raise(eSocket, "Unknown socket type %s", ptr);
}
else {
*tv = NUM2INT(type);
@@ -559,7 +670,7 @@ sock_s_open(class, domain, type, protocol)
setup_domain_and_type(domain, &d, type, &t);
fd = socket(d, t, NUM2INT(protocol));
- if (fd < 0) rb_sys_fail("socke(2)");
+ if (fd < 0) rb_sys_fail("socket(2)");
return sock_new(class, fd);
}
@@ -574,6 +685,7 @@ static VALUE
sock_s_socketpair(class, domain, type, protocol)
VALUE class, domain, type, protocol;
{
+#if !defined(__CYGWIN32__)
int fd;
int d, t, sp[2];
@@ -582,6 +694,9 @@ sock_s_socketpair(class, domain, type, protocol)
rb_sys_fail("socketpair(2)");
return assoc_new(sock_new(class, sp[0]), sock_new(class, sp[1]));
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -665,6 +780,9 @@ sock_send(argc, argv, sock)
GetOpenFile(sock, fptr);
f = fptr->f2?fptr->f2:fptr->f;
fd = fileno(f);
+#ifdef THREAD
+ thread_fd_writable(fd);
+#endif
if (to) {
Check_Type(to, T_STRING);
n = sendto(fd, msg->ptr, msg->len, NUM2INT(flags),
@@ -703,8 +821,15 @@ s_recv(sock, argc, argv, from)
GetOpenFile(sock, fptr);
fd = fileno(fptr->f);
- if ((str->len = recvfrom(fd, str->ptr, str->len, flags,
- (struct sockaddr*)buf, &alen)) < 0) {
+#ifdef THREAD
+ thread_wait_fd(fd);
+#endif
+ TRAP_BEG;
+ str->len = recvfrom(fd, str->ptr, str->len, flags,
+ (struct sockaddr*)buf, &alen);
+ TRAP_END;
+
+ if (str->len < 0) {
rb_sys_fail("recvfrom(2)");
}
@@ -732,28 +857,173 @@ sock_recvfrom(argc, argv, sock)
return s_recv(sock, argc, argv, 1);
}
+#ifdef HAVE_GETHOSTNAME
+static VALUE
+sock_gethostname(obj)
+ VALUE obj;
+{
+ char buf[1024];
+
+ if (gethostname(buf, (int)sizeof buf - 1) < 0)
+ rb_sys_fail("gethostname");
+
+ buf[sizeof buf - 1] = '\0';
+ return str_new2(buf);
+}
+#else
+#ifdef HAVE_UNAME
+
+#include <sys/utsname.h>
+
+static VALUE
+sock_gethostname(obj)
+ VALUE obj;
+{
+ struct utsname un;
+
+ uname(&un);
+ return str_new2(un.nodename);
+}
+#else
+static VALUE
+sock_gethostname(obj)
+ VALUE obj;
+{
+ rb_notimplement();
+}
+#endif
+#endif
+
+static VALUE
+mkhostent(h)
+ struct hostent *h;
+{
+ struct sockaddr_in addr;
+ char **pch;
+ VALUE ary, names;
+
+ if (h == NULL) {
+#ifdef HAVE_HSTRERROR
+ extern int h_errno;
+ Raise(eSocket, (char *)hstrerror(h_errno));
+#else
+ Raise(eSocket, "host not found");
+#endif
+ }
+ ary = ary_new();
+ ary_push(ary, str_new2(h->h_name));
+ names = ary_new();
+ ary_push(ary, names);
+ for (pch = h->h_aliases; *pch; pch++) {
+ ary_push(names, str_new2(*pch));
+ }
+ ary_push(ary, INT2FIX(h->h_length));
+#ifdef h_addr
+ for (pch = h->h_addr_list; *pch; pch++) {
+ ary_push(ary, str_new(*pch, h->h_length));
+ }
+#else
+ ary_push(ary, str_new(h->h_addr, h->h_length));
+#endif
+
+ return ary;
+}
+
+static VALUE
+sock_s_gethostbyname(obj, host)
+ VALUE obj, host;
+{
+ struct sockaddr_in addr;
+ struct hostent *h;
+
+ if (obj_is_kind_of(host, cInteger)) {
+ int i = NUM2INT(host);
+ addr.sin_addr.s_addr = htonl(i);
+ }
+ else {
+ Check_Type(host, T_STRING);
+ setipaddr(RSTRING(host)->ptr, &addr);
+ }
+ h = gethostbyaddr((char *)&addr.sin_addr,
+ sizeof(addr.sin_addr),
+ AF_INET);
+
+ return mkhostent(h);
+}
+
+sock_s_gethostbyaddr(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vaddr, vtype;
+ int type;
+
+ struct sockaddr_in *addr;
+ struct hostent *h;
+
+ rb_scan_args(argc, argv, "11", &addr, &type);
+ Check_Type(addr, T_STRING);
+ if (!NIL_P(type)) {
+ type = NUM2INT(vtype);
+ }
+ else {
+ type = AF_INET;
+ }
+
+ h = gethostbyaddr(RSTRING(addr)->ptr, RSTRING(addr)->len, type);
+
+ return mkhostent(h);
+}
+
+static VALUE
+sock_s_getservbyaname(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE service, protocol;
+ char *name, *proto;
+ struct servent *sp;
+ int port;
+
+ rb_scan_args(argc, argv, "11", &service, &protocol);
+ Check_Type(service, T_STRING);
+ if (NIL_P(protocol)) proto = "tcp";
+ else proto = RSTRING(protocol)->ptr;
+
+ sp = getservbyname(RSTRING(service)->ptr, proto);
+ if (!sp) {
+ Raise(eSocket, "service/proto not found");
+ }
+ port = ntohs(sp->s_port);
+
+ return INT2FIX(port);
+}
+
Init_socket ()
{
+ eSocket = rb_define_class("SocketError", eException);
+
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, "setsockopt", bsock_setsockopt, 3);
+ rb_define_method(cBasicSocket, "getsockopt", bsock_getsockopt, 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_singleton_method(cTCPsocket, "open", tcp_s_open, 2);
+ rb_define_singleton_method(cTCPsocket, "new", tcp_s_open, 2);
rb_define_method(cTCPsocket, "addr", tcp_addr, 0);
rb_define_method(cTCPsocket, "peeraddr", tcp_peeraddr, 0);
+ rb_define_singleton_method(cTCPsocket, "getaddress", tcp_s_getaddress, 1);
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
+#ifdef HAVE_SYS_UN_H
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);
@@ -782,4 +1052,64 @@ Init_socket ()
rb_define_method(cSocket, "recvfrom", sock_recv, -1);
rb_define_singleton_method(cSocket, "socketpair", sock_s_socketpair, 3);
+ rb_define_singleton_method(cSocket, "pair", sock_s_socketpair, 3);
+ rb_define_singleton_method(cSocket, "gethostname", sock_gethostname, 0);
+ rb_define_singleton_method(cSocket, "gethostbyname", sock_s_gethostbyname, 1);
+ rb_define_singleton_method(cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1);
+ rb_define_singleton_method(cSocket, "getservbyname", sock_s_getservbyaname, -1);
+
+ /* constants */
+ rb_define_const(cSocket, "AF_INET", INT2FIX(AF_INET));
+ rb_define_const(cSocket, "PF_INET", INT2FIX(PF_INET));
+#ifdef AF_UNIX
+ rb_define_const(cSocket, "AF_UNIX", INT2FIX(AF_UNIX));
+ rb_define_const(cSocket, "PF_UNIX", INT2FIX(PF_UNIX));
+#endif
+#ifdef AF_IPX
+ rb_define_const(cSocket, "AF_IPX", INT2FIX(AF_IPX));
+ rb_define_const(cSocket, "PF_IPX", INT2FIX(PF_IPX));
+#endif
+#ifdef AF_APPLETALK
+ rb_define_const(cSocket, "AF_APPLETALK", INT2FIX(AF_APPLETALK));
+ rb_define_const(cSocket, "PF_APPLETALK", INT2FIX(PF_APPLETALK));
+#endif
+
+ rb_define_const(cSocket, "MSG_OOB", INT2FIX(MSG_OOB));
+ rb_define_const(cSocket, "MSG_PEEK", INT2FIX(MSG_PEEK));
+ rb_define_const(cSocket, "MSG_DONTROUTE", INT2FIX(MSG_DONTROUTE));
+
+ rb_define_const(cSocket, "SOCK_STREAM", INT2FIX(SOCK_STREAM));
+ rb_define_const(cSocket, "SOCK_DGRAM", INT2FIX(SOCK_DGRAM));
+ rb_define_const(cSocket, "SOCK_RAW", INT2FIX(SOCK_RAW));
+#ifdef SOCK_RDM
+ rb_define_const(cSocket, "SOCK_RDM", INT2FIX(SOCK_RDM));
+#endif
+#ifdef SOCK_SEQPACKET
+ rb_define_const(cSocket, "SOCK_SEQPACKET", INT2FIX(SOCK_SEQPACKET));
+#endif
+#ifdef SOCK_PACKET
+ rb_define_const(cSocket, "SOCK_PACKET", INT2FIX(SOCK_PACKET));
+#endif
+
+ rb_define_const(cSocket, "SOL_SOCKET", INT2FIX(SOL_SOCKET));
+#ifdef SOL_IP
+ rb_define_const(cSocket, "SOL_IP", INT2FIX(SOL_IP));
+#endif
+#ifdef SOL_IPX
+ rb_define_const(cSocket, "SOL_IPX", INT2FIX(SOL_IPX));
+#endif
+#ifdef SOL_ATALK
+ rb_define_const(cSocket, "SOL_ATALK", INT2FIX(SOL_ATALK));
+#endif
+#ifdef SOL_TCP
+ rb_define_const(cSocket, "SOL_TCP", INT2FIX(SOL_TCP));
+#endif
+#ifdef SOL_UDP
+ rb_define_const(cSocket, "SOL_UDP", INT2FIX(SOL_UDP));
+#endif
+
+ rb_define_const(cSocket, "SO_DEBUG", INT2FIX(SO_DEBUG));
+ rb_define_const(cSocket, "SO_REUSEADDR", INT2FIX(SO_REUSEADDR));
+ rb_define_const(cSocket, "SO_KEEPALIVE", INT2FIX(SO_KEEPALIVE));
+ rb_define_const(cSocket, "SO_LINGER", INT2FIX(SO_LINGER));
}
diff --git a/ext/socket/socket.doc b/ext/socket/socket.doc
deleted file mode 100644
index aa5bfedbff..0000000000
--- a/ext/socket/socket.doc
+++ /dev/null
@@ -1,227 +0,0 @@
-.\" 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
index 98df4663b3..870e04b586 100644
--- a/ext/tkutil/MANIFEST
+++ b/ext/tkutil/MANIFEST
@@ -1,3 +1,3 @@
MANIFEST
-extconf.rb
tkutil.c
+depend
diff --git a/ext/tkutil/depend b/ext/tkutil/depend
new file mode 100644
index 0000000000..ead83eda57
--- /dev/null
+++ b/ext/tkutil/depend
@@ -0,0 +1 @@
+tkutil.o: tkutil.c ../../ruby.h ../../config.h ../../defines.h
diff --git a/ext/tkutil/extconf.rb b/ext/tkutil/extconf.rb
deleted file mode 100644
index b61a7ac01c..0000000000
--- a/ext/tkutil/extconf.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-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
index 2b74b254c2..51e3412ab5 100644
--- a/ext/tkutil/tkutil.c
+++ b/ext/tkutil/tkutil.c
@@ -47,7 +47,6 @@ 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);
diff --git a/file.c b/file.c
index 0691545c6a..447cba5b52 100644
--- a/file.c
+++ b/file.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:36 $
created at: Mon Nov 15 12:24:34 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -26,7 +26,7 @@
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#else
-struct timeval {
+stuct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
@@ -60,28 +60,37 @@ VALUE
file_open(fname, mode)
char *fname, *mode;
{
- VALUE port;
OpenFile *fptr;
-
- port = obj_alloc(cFile);
-
+ NEWOBJ(port, struct RFile);
+ OBJSETUP(port, cFile, T_FILE);
MakeOpenFile(port, fptr);
- fptr->mode = io_mode_flags(mode);
- fptr->f = fopen(fname, mode);
- if (fptr->f == NULL) {
- if (errno == EMFILE) {
- gc();
- fptr->f = fopen(fname, mode);
- }
- if (fptr->f == NULL) {
- rb_sys_fail(fname);
- }
- }
+ fptr->mode = io_mode_flags(mode);
+ fptr->f = rb_fopen(fname, mode);
fptr->path = strdup(fname);
- return port;
+ return (VALUE)port;
+}
+
+static VALUE
+file_s_open(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, vmode;
+ char *mode;
+
+ rb_scan_args(argc, argv, "11", &fname, &mode);
+ Check_Type(fname, T_STRING);
+ if (!NIL_P(mode)) {
+ Check_Type(mode, T_STRING);
+ mode = RSTRING(mode)->ptr;
+ }
+ else {
+ mode = "r";
+ }
+ return file_open(RSTRING(fname)->ptr, mode);
}
static int
@@ -113,7 +122,7 @@ file_tell(obj)
GetOpenFile(obj, fptr);
pos = ftell(fptr->f);
- if (ferror(fptr->f) != 0) rb_sys_fail(Qnil);
+ if (ferror(fptr->f) != 0) rb_sys_fail(0);
return int2inum(pos);
}
@@ -128,7 +137,7 @@ file_seek(obj, offset, ptrname)
GetOpenFile(obj, fptr);
pos = fseek(fptr->f, NUM2INT(offset), NUM2INT(ptrname));
- if (pos != 0) rb_sys_fail(Qnil);
+ if (pos != 0) rb_sys_fail(0);
clearerr(fptr->f);
return obj;
@@ -143,7 +152,7 @@ file_set_pos(obj, offset)
GetOpenFile(obj, fptr);
pos = fseek(fptr->f, NUM2INT(offset), 0);
- if (pos != 0) rb_sys_fail(Qnil);
+ if (pos != 0) rb_sys_fail(0);
clearerr(fptr->f);
return obj;
@@ -156,7 +165,7 @@ file_rewind(obj)
OpenFile *fptr;
GetOpenFile(obj, fptr);
- if (fseek(fptr->f, 0L, 0) != 0) rb_sys_fail(Qnil);
+ if (fseek(fptr->f, 0L, 0) != 0) rb_sys_fail(0);
clearerr(fptr->f);
return obj;
@@ -191,14 +200,14 @@ file_isatty(obj)
}
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/file.h>
+#include <sys/stat.h>
static VALUE
stat_new(st)
struct stat *st;
{
- if (st == Qnil) Bug("stat_new() called with nil");
+ if (!st) Bug("stat_new() called with bad value");
return struct_new(sStat,
INT2FIX((int)st->st_dev),
INT2FIX((int)st->st_ino),
@@ -224,26 +233,7 @@ stat_new(st)
#endif
time_new(st->st_atime, 0),
time_new(st->st_mtime, 0),
- time_new(st->st_ctime, 0),
- Qnil);
-}
-
-static struct stat laststat;
-
-int
-cache_stat(path, st)
- char *path;
- struct stat *st;
-{
- if (strcmp("&", path) == 0) {
- *st = laststat;
- return 0;
- }
- if (stat(path, st) == -1)
- return -1;
-
- laststat = *st;
- return 0;
+ time_new(st->st_ctime, 0));
}
static VALUE
@@ -254,7 +244,7 @@ file_s_stat(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) == -1) {
+ if (stat(fname->ptr, &st) == -1) {
rb_sys_fail(fname->ptr);
}
return stat_new(&st);
@@ -265,12 +255,13 @@ file_stat(obj)
VALUE obj;
{
OpenFile *fptr;
+ struct stat st;
GetOpenFile(obj, fptr);
- if (fstat(fileno(fptr->f), &laststat) == -1) {
+ if (fstat(fileno(fptr->f), &st) == -1) {
rb_sys_fail(fptr->path);
}
- return stat_new(&laststat);
+ return stat_new(&st);
}
static VALUE
@@ -278,6 +269,7 @@ file_s_lstat(obj, fname)
VALUE obj;
struct RString *fname;
{
+#if !defined(MSDOS)
struct stat st;
Check_Type(fname, T_STRING);
@@ -285,12 +277,16 @@ file_s_lstat(obj, fname)
rb_sys_fail(fname->ptr);
}
return stat_new(&st);
+#else
+ rb_notimplement();
+#endif
}
static VALUE
file_lstat(obj)
VALUE obj;
{
+#if !defined(MSDOS)
OpenFile *fptr;
struct stat st;
@@ -299,6 +295,9 @@ file_lstat(obj)
rb_sys_fail(fptr->path);
}
return stat_new(&st);
+#else
+ rb_notimplement();
+#endif
}
static int
@@ -339,7 +338,7 @@ eaccess(path, mode)
struct stat st;
static int euid = -1;
- if (cache_stat(path, &st) < 0) return (-1);
+ if (stat(path, &st) < 0) return (-1);
if (euid == -1)
euid = geteuid ();
@@ -378,7 +377,7 @@ test_d(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISDIR(st.st_mode)) return TRUE;
return FALSE;
}
@@ -396,7 +395,7 @@ test_p(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISFIFO(st.st_mode)) return TRUE;
#endif
@@ -426,7 +425,7 @@ test_l(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (lstat(fname->ptr, &st) < 0) return FALSE;
if (S_ISLNK(st.st_mode)) return TRUE;
#endif
@@ -456,7 +455,7 @@ test_S(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISSOCK(st.st_mode)) return TRUE;
#endif
@@ -478,7 +477,7 @@ test_b(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISBLK(st.st_mode)) return TRUE;
#endif
@@ -497,7 +496,7 @@ test_c(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISBLK(st.st_mode)) return TRUE;
return FALSE;
@@ -511,7 +510,7 @@ test_e(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
return TRUE;
}
@@ -583,7 +582,7 @@ test_f(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (S_ISREG(st.st_mode)) return TRUE;
return FALSE;
}
@@ -596,7 +595,7 @@ test_z(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (st.st_size == 0) return TRUE;
return FALSE;
}
@@ -609,7 +608,7 @@ test_s(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (st.st_size == 0) return FALSE;
return int2inum(st.st_size);
}
@@ -622,7 +621,7 @@ test_owned(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (st.st_uid == geteuid()) return TRUE;
return FALSE;
}
@@ -635,7 +634,7 @@ test_rowned(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (st.st_uid == getuid()) return TRUE;
return FALSE;
}
@@ -649,7 +648,7 @@ test_grpowned(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (stat(fname->ptr, &st) < 0) return FALSE;
if (st.st_gid == getegid()) return TRUE;
#else
Check_Type(fname, T_STRING);
@@ -665,7 +664,7 @@ check3rdbyte(file, mode)
{
struct stat st;
- if (cache_stat(file, &st) < 0) return FALSE;
+ if (stat(file, &st) < 0) return FALSE;
if (st.st_mode & mode) return TRUE;
return FALSE;
}
@@ -719,7 +718,7 @@ file_s_type(obj, fname)
char *t;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
if (S_ISREG(st.st_mode)) {
t = "file";
@@ -763,7 +762,7 @@ file_s_atime(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
return time_new(st.st_atime, 0);
}
@@ -789,7 +788,7 @@ file_s_mtime(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
return time_new(st.st_mtime, 0);
}
@@ -815,7 +814,7 @@ file_s_ctime(obj, fname)
struct stat st;
Check_Type(fname, T_STRING);
- if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
return time_new(st.st_ctime, 0);
}
@@ -868,8 +867,13 @@ file_chmod(obj, vmode)
mode = NUM2INT(vmode);
GetOpenFile(obj, fptr);
+#if defined(DJGPP) || defined(__CYGWIN32__)
+ if (chmod(fptr->path, mode) == -1)
+ rb_sys_fail(fptr->path);
+#else
if (fchmod(fileno(fptr->f), mode) == -1)
rb_sys_fail(fptr->path);
+#endif
return INT2FIX(0);
}
@@ -897,13 +901,13 @@ file_s_chown(argc, argv)
int n;
rb_scan_args(argc, argv, "2*", &o, &g, &rest);
- if (o == Qnil) {
+ if (NIL_P(o)) {
arg.owner = -1;
}
else {
arg.owner = NUM2INT(o);
}
- if (g == Qnil) {
+ if (NIL_P(g)) {
arg.group = -1;
}
else {
@@ -921,13 +925,18 @@ file_chown(obj, owner, group)
OpenFile *fptr;
GetOpenFile(obj, fptr);
+#if defined(DJGPP) || defined(__CYGWIN32__)
+ if (chown(fptr->path, NUM2INT(owner), NUM2INT(group)) == -1)
+ rb_sys_fail(fptr->path);
+#else
if (fchown(fileno(fptr->f), NUM2INT(owner), NUM2INT(group)) == -1)
rb_sys_fail(fptr->path);
+#endif
return INT2FIX(0);
}
-struct timeval *time_timeval();
+struct timeval time_timeval();
#ifdef HAVE_UTIMES
@@ -951,8 +960,8 @@ file_s_utime(argc, argv)
rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
- tvp[0] = *time_timeval(atime);
- tvp[1] = *time_timeval(mtime);
+ tvp[0] = time_timeval(atime);
+ tvp[1] = time_timeval(mtime);
n = apply2files(utime_internal, rest, tvp);
return INT2FIX(n);
@@ -987,15 +996,15 @@ file_s_utime(argc, argv)
{
VALUE atime, mtime, rest;
int n;
- struct timeval *tv;
+ struct timeval tv;
struct utimbuf utbuf;
rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
tv = time_timeval(atime);
- utbuf.actime = tv->tv_sec;
+ utbuf.actime = tv.tv_sec;
tv = time_timeval(mtime);
- utbuf.modtime = tv->tv_sec;
+ utbuf.modtime = tv.tv_sec;
n = apply2files(utime_internal, rest, &utbuf);
return INT2FIX(n);
@@ -1021,12 +1030,16 @@ file_s_symlink(obj, from, to)
VALUE obj;
struct RString *from, *to;
{
+#if !defined(MSDOS)
Check_Type(from, T_STRING);
Check_Type(to, T_STRING);
if (symlink(from->ptr, to->ptr) < 0)
rb_sys_fail(from->ptr);
return TRUE;
+#else
+ rb_notimplement();
+#endif
}
static VALUE
@@ -1034,6 +1047,7 @@ file_s_readlink(obj, path)
VALUE obj;
struct RString *path;
{
+#if !defined(MSDOS)
char buf[MAXPATHLEN];
int cc;
@@ -1043,6 +1057,9 @@ file_s_readlink(obj, path)
rb_sys_fail(path->ptr);
return str_new(buf, cc);
+#else
+ rb_notimplement();
+#endif
}
static void
@@ -1060,7 +1077,7 @@ file_s_unlink(obj, args)
{
int n;
- n = apply2files(unlink_internal, args, Qnil);
+ n = apply2files(unlink_internal, args, 0);
return INT2FIX(n);
}
@@ -1093,82 +1110,11 @@ file_s_umask(argc, argv)
omask = umask(NUM2INT(argv[1]));
}
else {
- Fail("wrong # of argument");
+ ArgError("wrong # of argument");
}
return INT2FIX(omask);
}
-#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
-static VALUE
-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
-file_truncate(obj, len)
- VALUE obj, len;
-{
- OpenFile *fptr;
-
- GetOpenFile(obj, fptr);
-
- 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
-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;
@@ -1218,7 +1164,7 @@ file_s_expand_path(obj, fname)
#ifdef HAVE_GETCWD
getcwd(buf, MAXPATHLEN);
#else
- getwd(buf)l
+ getwd(buf);
#endif
p = &buf[strlen(buf)];
}
@@ -1292,17 +1238,17 @@ file_s_basename(argc, argv)
rb_scan_args(argc, argv, "11", &fname, &ext);
Check_Type(fname, T_STRING);
- if (ext) Check_Type(ext, T_STRING);
+ if (!NIL_P(ext)) Check_Type(ext, T_STRING);
p = strrchr(fname->ptr, '/');
- if (p == Qnil) {
- if (ext) {
+ if (!p) {
+ if (!NIL_P(ext)) {
f = rmext(fname->ptr, ext->ptr);
if (f) return str_new(fname->ptr, f);
}
return (VALUE)fname;
}
p++; /* skip last `/' */
- if (ext) {
+ if (!NIL_P(ext)) {
f = rmext(p, ext->ptr);
if (f) return str_new(p, f);
}
@@ -1315,12 +1261,119 @@ file_s_dirname(obj, fname)
struct RString *fname;
{
char *p;
+
Check_Type(fname, T_STRING);
p = strrchr(fname->ptr, '/');
- if (p == Qnil) return (VALUE)fname;
+ if (!p) {
+ return str_new(0,0);
+ }
return str_new(fname->ptr, p - fname->ptr);
}
+static VALUE separator;
+
+static VALUE
+file_s_split(obj, path)
+ VALUE obj, path;
+{
+ return assoc_new(file_s_dirname(Qnil, path), file_s_basename(1,&path));
+}
+
+static VALUE
+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);
+ }
+# else
+ rb_notimplement();
+# endif
+#endif
+ return TRUE;
+}
+
+static VALUE
+file_truncate(obj, len)
+ VALUE obj, len;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+
+ 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);
+# else
+ rb_notimplement();
+# endif
+#endif
+ return TRUE;
+}
+
+static VALUE
+file_fcntl(obj, req, arg)
+ VALUE obj, req;
+ struct RString *arg;
+{
+#ifdef HAVE_FCNTL
+ io_ctl(obj, req, arg, 0);
+#else
+ rb_notimplement();
+#endif
+ return obj;
+}
+
+static VALUE
+file_flock(obj, operation)
+ VALUE obj;
+ VALUE operation;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+
+ if (flock(fileno(fptr->f), NUM2INT(operation)) < 0) {
+#ifdef EWOULDBLOCK
+ if (errno = EWOULDBLOCK) {
+ return FALSE;
+ }
+#endif
+ rb_sys_fail(fptr->path);
+ }
+ return obj;
+}
+
static void
test_check(n, argc, argv)
int n, argc;
@@ -1329,7 +1382,7 @@ test_check(n, argc, argv)
int i;
n+=1;
- if (n < argc) Fail("Wrong # of arguments(%d for %d)", argc, n);
+ if (n < argc) ArgError("Wrong # of arguments(%d for %d)", argc, n);
for (i=1; i<n; i++) {
Check_Type(argv[i], T_STRING);
}
@@ -1344,7 +1397,7 @@ f_test(argc, argv)
{
int cmd;
- if (argc == 0) Fail("Wrong # of arguments");
+ if (argc == 0) ArgError("Wrong # of arguments");
Need_Fixnum(argv[0]);
cmd = FIX2INT(argv[0]);
if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
@@ -1423,7 +1476,7 @@ f_test(argc, argv)
struct stat st;
CHECK(1);
- if (cache_stat(RSTRING(argv[1])->ptr, &st) == -1) {
+ if (stat(RSTRING(argv[1])->ptr, &st) == -1) {
rb_sys_fail(RSTRING(argv[1])->ptr);
}
@@ -1474,7 +1527,8 @@ Init_File()
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, "exist?", test_e, 1);
+ rb_define_module_function(mFileTest, "exists?", test_e, 1); /* temporary */
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);
@@ -1501,9 +1555,11 @@ Init_File()
cFile = rb_define_class("File", cIO);
rb_extend_object(cFile, CLASS_OF(mFileTest));
+ rb_define_singleton_method(cFile, "open", file_s_open, -1);
+
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, "ftype", file_s_type, 1);
rb_define_singleton_method(cFile, "atime", file_s_atime, 1);
rb_define_singleton_method(cFile, "mtime", file_s_mtime, 1);
@@ -1521,13 +1577,15 @@ Init_File()
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);
+ separator = INT2FIX('/');
+ rb_define_const(cFile, "Separator", separator);
+ rb_define_singleton_method(cFile, "split", file_s_split, 1);
+
rb_define_method(cFile, "stat", file_stat, 0);
rb_define_method(cFile, "lstat", file_lstat, 0);
@@ -1537,9 +1595,7 @@ Init_File()
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);
@@ -1553,9 +1609,26 @@ Init_File()
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, "flock", file_flock, 1);
+
+# ifndef LOCK_SH
+# define LOCK_SH 1
+# endif
+# ifndef LOCK_EX
+# define LOCK_EX 2
+# endif
+# ifndef LOCK_NB
+# define LOCK_NB 4
+# endif
+# ifndef LOCK_UN
+# define LOCK_UN 8
+# endif
+
+ rb_define_const(cFile, "LOCK_SH", INT2FIX(LOCK_SH));
+ rb_define_const(cFile, "LOCK_EX", INT2FIX(LOCK_EX));
+ rb_define_const(cFile, "LOCK_UN", INT2FIX(LOCK_UN));
+ rb_define_const(cFile, "LOCK_NB", INT2FIX(LOCK_NB));
rb_define_method(cFile, "path", file_path, 0);
@@ -1564,5 +1637,5 @@ Init_File()
sStat = struct_define("Stat", "dev", "ino", "mode",
"nlink", "uid", "gid", "rdev",
"size", "blksize", "blocks",
- "atime", "mtime", "ctime", Qnil);
+ "atime", "mtime", "ctime", 0);
}
diff --git a/gc.c b/gc.c
index e3c809cded..c1817b7cd0 100644
--- a/gc.c
+++ b/gc.c
@@ -6,18 +6,23 @@
$Date: 1995/01/12 08:54:47 $
created at: Tue Oct 5 09:44:46 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#include "ruby.h"
-#include "env.h"
+#include "sig.h"
#include "st.h"
#include "node.h"
+#include "env.h"
#include "re.h"
#include <stdio.h>
#include <setjmp.h>
+#ifdef _AIX
+#pragma alloca
+#endif
+
void *malloc();
void *calloc();
void *realloc();
@@ -36,10 +41,10 @@ xmalloc(size)
if (size == 0) size = 1;
mem = malloc(size);
- if (mem == Qnil) {
+ if (!mem) {
gc();
mem = malloc(size);
- if (mem == Qnil)
+ if (!mem)
Fatal("failed to allocate memory");
}
@@ -65,12 +70,12 @@ xrealloc(ptr, size)
{
void *mem;
- if (ptr == Qnil) return xmalloc(size);
+ if (!ptr) return xmalloc(size);
mem = realloc(ptr, size);
- if (mem == Qnil) {
+ if (!mem) {
gc();
mem = realloc(ptr, size);
- if (mem == Qnil)
+ if (!mem)
Fatal("failed to allocate memory(realloc)");
}
@@ -107,12 +112,6 @@ Paradigm Associates Inc Phone: 617-492-6079
Cambridge, MA 02138
*/
-#ifdef sparc
-#define FLUSH_REGISTER_WINDOWS asm("ta 3")
-#else
-#define FLUSH_REGISTER_WINDOWS /* empty */
-#endif
-
static int dont_gc;
VALUE
@@ -136,10 +135,9 @@ gc_s_disable()
VALUE mGC;
static struct gc_list {
- int n;
VALUE *varptr;
struct gc_list *next;
-} *Global_List = Qnil;
+} *Global_List = 0;
void
rb_global_variable(var)
@@ -150,7 +148,6 @@ rb_global_variable(var)
tmp = ALLOC(struct gc_list);
tmp->next = Global_List;
tmp->varptr = var;
- tmp->n = 1;
Global_List = tmp;
}
@@ -171,6 +168,7 @@ typedef struct RVALUE {
struct RData data;
struct RStruct rstruct;
struct RBignum bignum;
+ struct RFile file;
struct RNode node;
struct RMatch match;
struct RVarmap varmap;
@@ -236,15 +234,15 @@ newobj()
}
VALUE
-data_new(datap, dmark, dfree)
+data_object_alloc(class, datap, dmark, dfree)
+ VALUE class;
void *datap;
void (*dfree)();
void (*dmark)();
{
- extern VALUE cData;
struct RData *data = (struct RData*)newobj();
- OBJSETUP(data, cData, T_DATA);
+ OBJSETUP(data, class, T_DATA);
data->data = datap;
data->dfree = dfree;
data->dmark = dmark;
@@ -253,9 +251,9 @@ data_new(datap, dmark, dfree)
}
extern st_table *rb_class_tbl;
-static VALUE *stack_start_ptr;
+VALUE *gc_stack_start;
-static long
+static int
looks_pointerp(p)
register RVALUE *p;
{
@@ -288,8 +286,8 @@ mark_locations_array(x, n)
}
}
-static void
-mark_locations(start, end)
+void
+gc_mark_locations(start, end)
VALUE *start, *end;
{
VALUE *tmp;
@@ -351,8 +349,8 @@ gc_mark(obj)
register RVALUE *obj;
{
Top:
- if (obj == Qnil) return; /* nil not marked */
if (FIXNUM_P(obj)) return; /* fixnum not marked */
+ if (rb_special_const_p(obj)) return; /* special const not marked */
if (obj->as.basic.flags == 0) return; /* free cell */
if (obj->as.basic.flags & FL_MARK) return; /* marked */
@@ -408,7 +406,10 @@ gc_mark(obj)
break;
case T_STRING:
- if (obj->as.string.orig) gc_mark(obj->as.string.orig);
+ if (obj->as.string.orig) {
+ obj = (RVALUE*)obj->as.string.orig;
+ goto Top;
+ }
break;
case T_DATA:
@@ -419,6 +420,7 @@ gc_mark(obj)
if (obj->as.object.iv_tbl) mark_tbl(obj->as.object.iv_tbl);
break;
+ case T_FILE:
case T_MATCH:
case T_REGEXP:
case T_FLOAT:
@@ -426,7 +428,9 @@ gc_mark(obj)
break;
case T_VARMAP:
- gc_mark(obj->as.varmap.next);
+ gc_mark(obj->as.varmap.val);
+ obj = (RVALUE*)obj->as.varmap.next;
+ goto Top;
break;
case T_SCOPE:
@@ -435,7 +439,7 @@ gc_mark(obj)
VALUE *tbl = obj->as.scope.local_vars;
while (n--) {
- gc_mark(*tbl);
+ gc_mark_maybe(*tbl);
tbl++;
}
}
@@ -512,12 +516,12 @@ obj_free(obj)
break;
case T_MODULE:
case T_CLASS:
- rb_clear_cache(obj);
+ rb_clear_cache();
st_free_table(obj->as.class.m_tbl);
if (obj->as.object.iv_tbl) st_free_table(obj->as.object.iv_tbl);
break;
case T_STRING:
- if (obj->as.string.orig == Qnil) free(obj->as.string.ptr);
+ if (!obj->as.string.orig) free(obj->as.string.ptr);
break;
case T_ARRAY:
free(obj->as.array.ptr);
@@ -538,17 +542,26 @@ obj_free(obj)
free(obj->as.match.regs);
if (obj->as.match.ptr) free(obj->as.match.ptr);
break;
+ case T_FILE:
+ io_fptr_finalize(obj->as.file.fptr);
+ free(obj->as.file.fptr);
+ break;
case T_ICLASS:
/* iClass shares table with the module */
+ break;
+
case T_FLOAT:
case T_VARMAP:
+ case T_TRUE:
+ case T_FALSE:
break;
+
case T_BIGNUM:
- free(obj->as.bignum.digits);
+ if (obj->as.bignum.digits) free(obj->as.bignum.digits);
break;
case T_NODE:
- if (nd_type(obj) == NODE_SCOPE && obj->as.node.nd_tbl) {
- free(obj->as.node.nd_tbl);
+ if (nd_type(obj) == NODE_SCOPE && obj->as.node.u1.tbl) {
+ free(obj->as.node.u1.tbl);
}
return; /* no need to free iv_tbl */
@@ -576,7 +589,7 @@ gc_mark_frame(frame)
VALUE *tbl = frame->argv;
while (n--) {
- gc_mark(*tbl);
+ gc_mark_maybe(*tbl);
tbl++;
}
}
@@ -601,18 +614,23 @@ gc()
gc_mark_frame(frame);
}
gc_mark(the_scope);
+ gc_mark(the_dyna_vars);
FLUSH_REGISTER_WINDOWS;
/* This assumes that all registers are saved into the jmp_buf */
setjmp(save_regs_gc_mark);
- mark_locations((VALUE*)save_regs_gc_mark,
- (VALUE*)(((char*)save_regs_gc_mark)+sizeof(save_regs_gc_mark)));
- mark_locations(stack_start_ptr, (VALUE*) &stack_end);
-#if defined(THINK_C)
- mark_locations((VALUE*)((char*)stack_start_ptr + 2),
+ gc_mark_locations((VALUE*)save_regs_gc_mark,
+ (VALUE*)(((char*)save_regs_gc_mark)+sizeof(save_regs_gc_mark)));
+ gc_mark_locations(gc_stack_start, (VALUE*) &stack_end);
+#ifdef THINK_C
+ gc_mark_locations((VALUE*)((char*)gc_stack_start + 2),
(VALUE*)((char*)&stack_end + 2));
#endif
+#ifdef THREAD
+ gc_mark_threads();
+#endif
+
/* mark protected global variables */
for (list = Global_List; list; list = list->next) {
gc_mark(*list->varptr);
@@ -631,7 +649,7 @@ init_stack()
{
VALUE start;
- stack_start_ptr = &start;
+ gc_stack_start = &start;
}
void
@@ -641,12 +659,87 @@ init_heap()
add_heap();
}
+static VALUE
+os_live_obj(obj)
+ VALUE obj;
+{
+ int i;
+ int n = 0;
+
+ for (i = 0; i < heaps_used; i++) {
+ RVALUE *p, *pend;
+
+ p = heaps[i]; pend = p + HEAP_SLOTS;
+ for (;p < pend; p++) {
+ if (p->as.basic.flags) {
+ switch (TYPE(p)) {
+ case T_ICLASS:
+ case T_VARMAP:
+ case T_SCOPE:
+ case T_NODE:
+ continue;
+ case T_CLASS:
+ if (FL_TEST(p, FL_SINGLETON)) continue;
+ default:
+ rb_yield(p);
+ n++;
+ }
+ }
+ }
+ }
+
+ return INT2FIX(n);
+}
+
+static VALUE
+os_obj_of(obj, of)
+ VALUE obj, of;
+{
+ int i;
+ int n = 0;
+
+ for (i = 0; i < heaps_used; i++) {
+ RVALUE *p, *pend;
+
+ p = heaps[i]; pend = p + HEAP_SLOTS;
+ for (;p < pend; p++) {
+ if (p->as.basic.flags) {
+ switch (TYPE(p)) {
+ case T_ICLASS:
+ case T_VARMAP:
+ case T_SCOPE:
+ case T_NODE:
+ continue;
+ case T_CLASS:
+ if (FL_TEST(p, FL_SINGLETON)) continue;
+ default:
+ if (obj_is_kind_of(p, of)) {
+ rb_yield(p);
+ n++;
+ }
+ }
+ }
+ }
+ }
+
+ return INT2FIX(n);
+}
+
+extern VALUE cModule;
+
void
Init_GC()
{
+ VALUE mObSpace;
+
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);
+
+ mObSpace = rb_define_module("ObjectSpace");
+ rb_define_module_function(mObSpace, "each_live_object", os_live_obj, 0);
+ rb_define_module_function(mObSpace, "each_object_of", os_obj_of, 1);
+ rb_define_module_function(mObSpace, "garbage_collect", gc, 0);
}
diff --git a/glob.c b/glob.c
index b0c750f243..7599c1ca72 100644
--- a/glob.c
+++ b/glob.c
@@ -48,7 +48,7 @@
# endif /* !USG */
#endif /* !HAVE_DIRENT_H */
-#if defined (_POSIX_SOURCE)
+#if defined (_POSIX_SOURCE) || defined(DJGPP)
/* Posix does not require that the d_ino field be present, and some
systems do not provide it. */
# define REAL_DIR_ENTRY(dp) 1
@@ -70,11 +70,15 @@
# define bcopy(s, d, n) (memcpy ((d), (s), (n)))
+#ifdef _AIX
+#pragma alloca
+#else
#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
#include <alloca.h>
#else
char *alloca ();
#endif
+#endif
#include "fnmatch.h"
@@ -376,7 +380,9 @@ char **
glob_filename (pathname)
char *pathname;
{
+#ifndef strrchr
char *strrchr();
+#endif
char **result;
unsigned int result_size;
diff --git a/hash.c b/hash.c
index da4671409f..d2ee598869 100644
--- a/hash.c
+++ b/hash.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:26 $
created at: Mon Nov 22 18:51:18 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -27,38 +27,85 @@ static VALUE envtbl;
static ID hash;
VALUE f_getenv(), f_setenv();
-static VALUE
+static int
rb_cmp(a, b)
VALUE a, b;
{
- return rb_equal(a, b)?0:1;
+ if (FIXNUM_P(a)) {
+ if (FIXNUM_P(b)) return a != b;
+ }
+
+ if (TYPE(a) == T_STRING) {
+ if (TYPE(b) == T_STRING) return str_cmp(a, b);
+ }
+
+ return !rb_eql(a, b);
}
-static VALUE
+static int
rb_hash(a, mod)
VALUE a;
int mod;
{
- return rb_funcall(a, hash, 0) % mod;
+ unsigned int hval;
+
+ switch (TYPE(a)) {
+ case T_FIXNUM:
+ hval = a;
+ break;
+
+ case T_STRING:
+ hval = str_hash(a);
+ break;
+
+ default:
+ hval = rb_funcall(a, hash, 0);
+ hval = FIX2INT(hval);
+ }
+ return hval % mod;
}
-#define ASSOC_KEY(a) RASSOC(a)->car
-#define ASSOC_VAL(a) RASSOC(a)->cdr
+static struct st_hash_type objhash = {
+ rb_cmp,
+ rb_hash,
+};
static VALUE
-hash_s_new(class)
+hash_s_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
VALUE class;
{
+ VALUE sz;
+ int size;
+
NEWOBJ(hash, struct RHash);
OBJSETUP(hash, class, T_HASH);
- hash->tbl = st_init_table(rb_cmp, rb_hash);
+ rb_scan_args(argc, argv, "01", &sz);
+ if (NIL_P(sz)) size = 0;
+ else size = NUM2INT(sz);
+
+ hash->tbl = st_init_table_with_size(&objhash, size);
return (VALUE)hash;
}
static VALUE hash_clone();
+VALUE
+hash_new2(class)
+ VALUE class;
+{
+ return hash_s_new(0, 0, class);
+}
+
+VALUE
+hash_new()
+{
+ return hash_new2(cHash);
+}
+
static VALUE
hash_s_create(argc, argv, class)
int argc;
@@ -80,9 +127,9 @@ hash_s_create(argc, argv, class)
}
if (argc % 2 != 0) {
- Fail("odd number args for Hash");
+ ArgError("odd number args for Hash");
}
- hash = (struct RHash*)hash_s_new(class);
+ hash = (struct RHash*)hash_new2(class);
for (i=0; i<argc; i+=2) {
st_insert(hash->tbl, argv[i], argv[i+1]);
@@ -91,12 +138,6 @@ hash_s_create(argc, argv, class)
return (VALUE)hash;
}
-VALUE
-hash_new()
-{
- return hash_s_new(cHash);
-}
-
static VALUE
hash_clone(hash)
struct RHash *hash;
@@ -109,7 +150,7 @@ hash_clone(hash)
return (VALUE)hash2;
}
-static VALUE
+VALUE
hash_aref(hash, key)
struct RHash *hash;
VALUE key;
@@ -128,29 +169,21 @@ hash_indexes(hash, args)
struct RArray *args;
{
VALUE *p, *pend;
- struct RArray *new_hash;
+ struct RArray *indexes;
int i = 0;
- if (!args || args->len == 0) {
- Fail("wrong # of argment");
- }
- else if (args->len == 1) {
- if (TYPE(args->ptr[0])) {
- args = (struct RArray*)rb_to_a(args->ptr[0]);
- }
- else {
- args = (struct RArray*)args->ptr[0];
- }
+ if (!args || NIL_P(args)) {
+ return ary_new2(0);
}
- new_hash = (struct RArray*)ary_new2(args->len);
+ indexes = (struct RArray*)ary_new2(args->len);
p = args->ptr; pend = p + args->len;
while (p < pend) {
- new_hash->ptr[i++] = hash_aref(hash, *p++);
+ indexes->ptr[i++] = hash_aref(hash, *p++);
}
- new_hash->len = i;
- return (VALUE)new_hash;
+ indexes->len = i;
+ return (VALUE)indexes;
}
static VALUE
@@ -162,6 +195,7 @@ hash_delete(hash, key)
if (st_delete(hash->tbl, &key, &val))
return val;
+ if (iterator_p()) rb_yield(Qnil);
return Qnil;
}
@@ -209,7 +243,7 @@ static VALUE
hash_delete_if(hash)
struct RHash *hash;
{
- st_foreach(hash->tbl, delete_if_i, Qnil);
+ st_foreach(hash->tbl, delete_if_i, 0);
return (VALUE)hash;
}
@@ -235,7 +269,7 @@ hash_aset(hash, key, val)
struct RHash *hash;
VALUE key, val;
{
- if (val == Qnil) {
+ if (NIL_P(val)) {
hash_delete(hash, key);
return Qnil;
}
@@ -253,6 +287,15 @@ hash_length(hash)
return INT2FIX(hash->tbl->num_entries);
}
+VALUE
+hash_empty_p(hash)
+ struct RHash *hash;
+{
+ if (hash->tbl->num_entries == 0)
+ return TRUE;
+ return FALSE;
+}
+
static int
each_value_i(key, value)
VALUE key, value;
@@ -327,15 +370,14 @@ inspect_i(key, value, str)
struct RString *str;
{
VALUE str2;
- ID inspect = rb_intern("inspect");
if (str->len > 1) {
str_cat(str, ", ", 2);
}
- str2 = rb_funcall(key, inspect, 0, 0);
+ str2 = rb_inspect(key);
str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
str_cat(str, "=>", 2);
- str2 = rb_funcall(value, inspect, 0, 0);
+ str2 = rb_inspect(value);
str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
return ST_CONTINUE;
@@ -554,7 +596,7 @@ f_getenv(obj, name)
Check_Type(name, T_STRING);
if (strlen(name->ptr) != name->len)
- Fail("Bad environment name");
+ ArgError("Bad environment name");
env = getenv(name->ptr);
if (env) {
@@ -569,7 +611,7 @@ f_setenv(obj, name, value)
struct RString *name, *value;
{
Check_Type(name, T_STRING);
- if (value == Qnil) {
+ if (NIL_P(value)) {
env_delete(obj, name);
return Qnil;
}
@@ -577,9 +619,9 @@ f_setenv(obj, name, value)
Check_Type(value, T_STRING);
if (strlen(name->ptr) != name->len)
- Fail("Bad environment name");
+ ArgError("Bad environment name");
if (strlen(value->ptr) != value->len)
- Fail("Bad environment value");
+ ArgError("Bad environment value");
setenv(name->ptr, value->ptr, 1);
return TRUE;
@@ -588,7 +630,7 @@ f_setenv(obj, name, value)
static VALUE
env_to_s()
{
- return str_new2("$ENV");
+ return str_new2("ENV");
}
void
@@ -603,7 +645,7 @@ Init_Hash()
rb_include_module(cHash, mEnumerable);
- rb_define_singleton_method(cHash, "new", hash_s_new, 0);
+ rb_define_singleton_method(cHash, "new", hash_s_new, -1);
rb_define_singleton_method(cHash, "[]", hash_s_create, -1);
rb_define_method(cHash,"clone", hash_clone, 0);
@@ -619,6 +661,8 @@ Init_Hash()
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,"empty?", hash_empty_p, 0);
+
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);
@@ -632,8 +676,11 @@ Init_Hash()
rb_define_method(cHash,"delete_if", hash_delete_if, 0);
rb_define_method(cHash,"clear", hash_clear, 0);
+ rb_define_method(cHash,"include?", hash_has_key, 1);
rb_define_method(cHash,"has_key?", hash_has_key, 1);
rb_define_method(cHash,"has_value?", hash_has_value, 1);
+ rb_define_method(cHash,"key?", hash_has_key, 1);
+ rb_define_method(cHash,"value?", hash_has_value, 1);
envtbl = obj_alloc(cObject);
rb_extend_object(envtbl, mEnumerable);
@@ -645,5 +692,5 @@ Init_Hash()
rb_define_singleton_method(envtbl,"to_s", env_to_s, 0);
rb_define_readonly_variable("$ENV", &envtbl);
- rb_define_const(cKernel, "ENV", envtbl);
+ rb_define_global_const("ENV", envtbl);
}
diff --git a/inits.c b/inits.c
index 673b6eed4b..a6eb7f5150 100644
--- a/inits.c
+++ b/inits.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:38 $
created at: Tue Dec 28 16:01:58 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -18,16 +18,20 @@ rb_call_inits()
Init_sym();
Init_var_tables();
Init_Object();
+#ifdef THREAD
+ Init_Thread();
+#endif
Init_GC();
Init_eval();
Init_Comparable();
Init_Enumerable();
+ Init_String();
+ Init_Exception();
Init_Numeric();
Init_Bignum();
Init_Array();
Init_Hash();
Init_Struct();
- Init_String();
Init_Regexp();
Init_pack();
Init_Range();
diff --git a/io.c b/io.c
index 47bd4b0fb0..43e2458668 100644
--- a/io.c
+++ b/io.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:39 $
created at: Fri Oct 15 18:08:59 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -16,8 +16,9 @@
#include <errno.h>
#include <sys/types.h>
-#include <sys/stat.h>
+#ifndef DJGPP
#include <sys/ioctl.h>
+#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
@@ -31,26 +32,94 @@ struct timeval {
#include <vfork.h>
#endif
+#include <sys/stat.h>
+
+#ifdef DJGPP
+#include <fcntl.h>
+#endif
+
VALUE rb_ad_string();
VALUE cIO;
extern VALUE cFile;
+VALUE eEOFError;
+VALUE eIOError;
VALUE rb_stdin, rb_stdout, rb_stderr, rb_defout;
VALUE FS, OFS;
VALUE RS, ORS;
+VALUE RS_default;
static VALUE argf;
ID id_write, id_fd, id_print_on;
+VALUE lastline_get();
+void lastline_set();
+
extern char *inplace;
+struct timeval time_timeval();
+
+#ifdef _STDIO_USES_IOSTREAM /* GNU libc */
+# 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
+# ifdef FILE_COUNT
+# define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0)
+# else
+/* requires systems own version of the ReadDataPending() */
+extern int ReadDataPending();
+# define READ_DATA_PENDING(fp) ReadDataPending(fp)
+# endif
+#endif
+
+#ifndef THREAD
+# define READ_CHECK(fp) 0
+#else
+# define READ_CHECK(fp) do {\
+ if (!READ_DATA_PENDING(fp)) thread_wait_fd(fileno(fp));\
+} while(0)
+#endif
+
+void
+eof_error()
+{
+ Raise(eEOFError, "End of file reached");
+}
+
+void
+io_writable(fptr)
+ OpenFile *fptr;
+{
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ Raise(eIOError, "not opened for writing");
+ }
+}
+
+void
+io_readable(fptr)
+ OpenFile *fptr;
+{
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Raise(eIOError, "not opened for reading");
+ }
+}
+
+static void
+closed()
+{
+ Raise(eIOError, "closed stream");
+}
+
/* writing functions */
VALUE
-io_write(obj, str)
- VALUE obj;
+io_write(io, str)
+ VALUE io;
struct RString *str;
{
OpenFile *fptr;
@@ -58,19 +127,17 @@ io_write(obj, str)
VALUE out;
int n;
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_WRITABLE)) {
- Fail("not opened for writing");
- }
+ GetOpenFile(io, fptr);
+ io_writable(fptr);
f = (fptr->f2) ? fptr->f2 : fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
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);
+ n = fwrite(str->ptr, 1, str->len, f);
if (n == 0 || ferror(f)) {
rb_sys_fail(fptr->path);
}
@@ -82,44 +149,43 @@ io_write(obj, str)
}
static VALUE
-io_puts(obj, str)
- VALUE obj, str;
+io_puts(io, str)
+ VALUE io, str;
{
- io_write(obj, str);
- return obj;
+ io_write(io, str);
+ return io;
}
static VALUE
-io_flush(obj)
- VALUE obj;
+io_flush(io)
+ VALUE io;
{
OpenFile *fptr;
FILE *f;
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_WRITABLE)) {
- Fail("not opend for writing");
- }
+ GetOpenFile(io, fptr);
+ io_writable(fptr);
f = (fptr->f2) ? fptr->f2 : fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
- if (fflush(f) == EOF) rb_sys_fail(Qnil);
+ if (fflush(f) == EOF) rb_sys_fail(0);
- return obj;
+ return io;
}
static VALUE
-io_eof(obj)
- VALUE obj;
+io_eof(io)
+ VALUE io;
{
OpenFile *fptr;
int ch;
- GetOpenFile(obj, fptr);
-#ifdef STDSTDIO /* (the code works without this) */
- if (fptr->f->_cnt > 0) /* cheat a little, since */
- return FALSE; /* this is the most usual case */
-#endif
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
+ if (fptr->f == NULL) closed();
+
+ if (READ_DATA_PENDING(fptr->f)) return FALSE;
+ if (feof(fptr->f)) return TRUE;
TRAP_BEG;
ch = getc(fptr->f);
@@ -129,31 +195,27 @@ io_eof(obj)
(void)ungetc(ch, fptr->f);
return FALSE;
}
-#ifdef STDSTDIO
- if (fptr->f->_cnt < -1)
- fptr->f->_cnt = -1;
-#endif
return TRUE;
}
static VALUE
-io_sync(obj)
- VALUE obj;
+io_sync(io)
+ VALUE io;
{
OpenFile *fptr;
- GetOpenFile(obj, fptr);
+ GetOpenFile(io, fptr);
return (fptr->mode & FMODE_SYNC) ? TRUE : FALSE;
}
static VALUE
-io_set_sync(obj, mode)
- VALUE obj, mode;
+io_set_sync(io, mode)
+ VALUE io, mode;
{
OpenFile *fptr;
- GetOpenFile(obj, fptr);
- if (mode) {
+ GetOpenFile(io, fptr);
+ if (RTEST(mode)) {
fptr->mode |= FMODE_SYNC;
}
else {
@@ -163,13 +225,13 @@ io_set_sync(obj, mode)
}
static VALUE
-io_fileno(obj)
- VALUE obj;
+io_fileno(io)
+ VALUE io;
{
OpenFile *fptr;
int fd;
- GetOpenFile(obj, fptr);
+ GetOpenFile(io, fptr);
fd = fileno(fptr->f);
return INT2FIX(fd);
}
@@ -185,54 +247,50 @@ read_all(port)
int n;
GetOpenFile(port, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
- }
- if (fptr->f == NULL) Fail("closed stream");
+ io_readable(fptr);
+ if (fptr->f == NULL) closed();
str = str_new(0, 0);
for (;;) {
+ READ_CHECK(fptr->f);
TRAP_BEG;
n = fread(buf, 1, BUFSIZ, fptr->f);
TRAP_END;
- if (n == 0) {
- if (feof(fptr->f)) break;
- rb_sys_fail(Qnil);
- }
+ if (n == 0) break;
+ if (n < 0) rb_sys_fail(0);
str_cat(str, buf, n);
}
return str;
}
static VALUE
-io_read(argc, argv, obj)
+io_read(argc, argv, io)
int argc;
VALUE *argv;
- VALUE obj;
+ VALUE io;
{
OpenFile *fptr;
int n, lgt;
VALUE len, str;
if (rb_scan_args(argc, argv, "01", &len) == 0) {
- return read_all(obj);
+ return read_all(io);
}
lgt = NUM2INT(len);
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
- }
- if (fptr->f == NULL) Fail("closed stream");
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
+ if (fptr->f == NULL) closed();
str = str_new(0, lgt);
+ READ_CHECK(fptr->f);
TRAP_BEG;
n = fread(RSTRING(str)->ptr, 1, RSTRING(str)->len, fptr->f);
TRAP_END;
if (n == 0) {
if (feof(fptr->f)) return Qnil;
- rb_sys_fail(Qnil);
+ rb_sys_fail(fptr->path);
}
RSTRING(str)->len = n;
@@ -241,42 +299,54 @@ io_read(argc, argv, obj)
return str;
}
-VALUE rb_lastline;
static VALUE lineno;
-static VALUE
-io_gets(obj)
- VALUE obj;
+VALUE
+io_gets_method(argc, argv, io)
+ int argc;
+ VALUE argv;
+ VALUE io;
{
OpenFile *fptr;
FILE *f;
struct RString *str;
int c, newline;
- int rslen;
+ char *rsptr;
+ int rslen, rspara = 0;
+ VALUE rs;
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
+ if (rb_scan_args(argc, argv, "01", &rs) == 1) {
+ if (!NIL_P(rs)) Check_Type(rs, T_STRING);
}
+ else {
+ rs = RS;
+ }
+
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
f = fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
- if (RS) {
- rslen = RSTRING(RS)->len;
+ if (!NIL_P(rs)) {
+ rslen = RSTRING(rs)->len;
if (rslen == 0) {
- newline = '\n';
+ rsptr = "\n\n";
+ rslen = 2;
+ rspara = 1;
}
else {
- newline = RSTRING(RS)->ptr[rslen-1];
+ rsptr = RSTRING(rs)->ptr;
}
}
else {
- newline = 0777; /* non matching char */
- rslen = 1;
+ rsptr = 0;
+ rslen = 0;
}
+ newline = rslen ? rsptr[rslen - 1] : 0777;
- if (rslen == 0) {
+ if (rspara) {
do {
+ READ_CHECK(f);
TRAP_BEG;
c = getc(f);
TRAP_END;
@@ -290,51 +360,54 @@ io_gets(obj)
{
char buf[8192];
char *bp, *bpe = buf + sizeof buf - 3;
+ int cnt;
int append = 0;
again:
bp = buf;
- retry:
- TRAP_BEG;
- while ((c = getc(f)) != EOF && (*bp++ = c) != newline && bp < bpe)
- ;
- TRAP_END;
+ if (rslen) {
+ for (;;) {
+ READ_CHECK(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+ if (c == EOF) break;
+ if ((*bp++ = c) == newline) break;
+ if (bp == bpe) break;
+ }
+ cnt = bp - buf;
+ }
+ else {
+ cnt = fread(buf, 1, sizeof(buf), f);
+ c = cnt ? buf[cnt - 1]: EOF;
+ }
if (c == EOF) {
- if (!feof(f)) goto retry;
- if (!append && bp == buf) {
- str = Qnil;
+ if (!append && cnt == 0) {
+ str = RSTRING(Qnil);
goto return_gets;
}
}
if (append)
- str_cat(str, buf, bp - buf);
+ str_cat(str, buf, cnt);
else
- str = (struct RString*)str_new(buf, bp - buf);
-
- if (c != EOF
- &&
- (c != newline
- ||
- (rslen > 1
- &&
- (str->len < rslen
- ||
- memcmp(str->ptr+str->len-rslen, RSTRING(RS)->ptr, rslen)
- )
- )
- )
- ) {
+ str = (struct RString*)str_new(buf, cnt);
+
+ if (c != EOF &&
+ (!rslen ||
+ str->len < rslen ||
+ memcmp(str->ptr+str->len-rslen, rsptr, rslen))) {
append = 1;
goto again;
}
}
return_gets:
- if (rslen == 0) {
+ if (rspara) {
while (c != EOF) {
+ READ_CHECK(f);
TRAP_BEG;
c = getc(f);
TRAP_END;
@@ -349,82 +422,132 @@ io_gets(obj)
fptr->lineno++;
lineno = INT2FIX(fptr->lineno);
}
- return rb_lastline = (VALUE)str;
+ lastline_set(str);
+
+ return (VALUE)str;
+}
+
+VALUE
+io_gets(io)
+ VALUE io;
+{
+ return io_gets_method(0, 0, io);
}
static VALUE
-io_each_line(obj)
- VALUE obj;
+io_readline(argc, argv, io)
+ int argc;
+ VALUE argv;
+ VALUE io;
+{
+ VALUE line = io_gets_method(argc, argv, io);
+
+ if (NIL_P(line)) {
+ eof_error();
+ }
+ return line;
+}
+
+static VALUE
+io_each_line(argc, argv, io)
+ int argc;
+ VALUE argv;
+ VALUE io;
{
VALUE str;
- while (str = io_gets(obj)) {
+ while (!NIL_P(str = io_gets_method(argc, argv, io))) {
rb_yield(str);
}
return Qnil;
}
static VALUE
-io_each_byte(obj)
- VALUE obj;
+io_each_byte(io)
+ VALUE io;
{
OpenFile *fptr;
FILE *f;
int c;
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
- }
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
f = fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
for (;;) {
+ READ_CHECK(f);
TRAP_BEG;
c = getc(f);
TRAP_END;
if (c == EOF) break;
rb_yield(INT2FIX(c & 0xff));
}
- if (ferror(f) != 0) rb_sys_fail(Qnil);
- return obj;
+ if (ferror(f) != 0) rb_sys_fail(fptr->path);
+ return Qnil;
}
-static VALUE
-io_getc(obj)
- VALUE obj;
+VALUE
+io_getc(io)
+ VALUE io;
{
OpenFile *fptr;
FILE *f;
int c;
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
- }
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
f = fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
+ READ_CHECK(f);
TRAP_BEG;
c = getc(f);
TRAP_END;
if (c == EOF) {
- if (ferror(f) != 0) rb_sys_fail(Qnil);
+ if (ferror(f) != 0) rb_sys_fail(fptr->path);
return Qnil;
}
return INT2FIX(c & 0xff);
}
static VALUE
-io_isatty(obj)
- VALUE obj;
+io_readchar(io)
+ VALUE io;
+{
+ VALUE c = io_getc(io);
+
+ if (NIL_P(c)) {
+ eof_error();
+ }
+ return c;
+}
+
+VALUE
+io_ungetc(io, c)
+ VALUE io, c;
+{
+ OpenFile *fptr;
+
+ Check_Type(c, T_FIXNUM);
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
+ if (fptr->f == NULL) closed();
+
+ if (ungetc(FIX2INT(c), fptr->f) == EOF)
+ rb_sys_fail(fptr->path);
+}
+
+static VALUE
+io_isatty(io)
+ VALUE io;
{
OpenFile *fptr;
#ifndef NT
- GetOpenFile(obj, fptr);
- if (fptr->f == NULL) Fail("closed stream");
+ GetOpenFile(io, fptr);
+ if (fptr->f == NULL) closed();
if (isatty(fileno(fptr->f)) == 0)
return FALSE;
#endif
@@ -466,30 +589,30 @@ io_fptr_finalize(fptr)
}
VALUE
-io_close(obj)
- VALUE obj;
+io_close(io)
+ VALUE io;
{
OpenFile *fptr;
- GetOpenFile(obj, fptr);
+ GetOpenFile(io, fptr);
io_fptr_finalize(fptr);
return Qnil;
}
static VALUE
-io_closed(obj)
- VALUE obj;
+io_closed(io)
+ VALUE io;
{
OpenFile *fptr;
- GetOpenFile(obj, fptr);
+ GetOpenFile(io, fptr);
return fptr->f?FALSE:TRUE;
}
static VALUE
-io_syswrite(obj, str)
- VALUE obj, str;
+io_syswrite(io, str)
+ VALUE io, str;
{
OpenFile *fptr;
FILE *f;
@@ -498,43 +621,45 @@ io_syswrite(obj, str)
if (TYPE(str) != T_STRING)
str = obj_as_string(str);
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_WRITABLE)) {
- Fail("not opend for writing");
- }
+ GetOpenFile(io, fptr);
+ io_writable(fptr);
f = (fptr->f2) ? fptr->f2 : fptr->f;
- if (f == NULL) Fail("closed stream");
+ if (f == NULL) closed();
+#ifdef THREAD
+ thread_fd_writable(fileno(f));
+#endif
n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len);
- if (n == -1) rb_sys_fail(Qnil);
+ if (n == -1) rb_sys_fail(fptr->path);
return INT2FIX(n);
}
static VALUE
-io_sysread(obj, len)
- VALUE obj, len;
+io_sysread(io, len)
+ VALUE io, len;
{
OpenFile *fptr;
int n, ilen;
VALUE str;
ilen = NUM2INT(len);
- GetOpenFile(obj, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- Fail("not opend for reading");
- }
- if (fptr->f == NULL) Fail("closed stream");
+ GetOpenFile(io, fptr);
+ io_readable(fptr);
+ if (fptr->f == NULL) closed();
str = str_new(0, ilen);
+#ifdef THREAD
+ thread_wait_fd(fileno(fptr->f));
+#endif
TRAP_BEG;
n = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len);
TRAP_END;
- if (n == -1) rb_sys_fail(Qnil);
- if (n == 0) return Qnil; /* EOF */
+ if (n == -1) rb_sys_fail(fptr->path);
+ if (n == 0) eof_error();
RSTRING(str)->len = n;
RSTRING(str)->ptr[n] = '\0';
@@ -542,21 +667,21 @@ io_sysread(obj, len)
}
static VALUE
-io_binmode(obj)
- VALUE obj;
+io_binmode(io)
+ VALUE io;
{
-#ifdef NT
+#if defined(NT) || defined(DJGPP)
OpenFile *fptr;
- GetOpenFile(obj, fptr);
- if (setmode(fileno(fptr), O_BINARY) == -1)
- rb_sys_fail(Qnil);
+ GetOpenFile(io, fptr);
+ if (fptr->f && setmode(fileno(fptr->f), O_BINARY) == -1)
+ rb_sys_fail(fptr->path);
+ if (fptr->f2 && setmode(fileno(fptr->f2), O_BINARY) == -1)
+ rb_sys_fail(fptr->path);
#endif
- return obj;
+ return io;
}
-VALUE obj_alloc();
-
int
io_mode_flags(mode)
char *mode;
@@ -574,7 +699,7 @@ io_mode_flags(mode)
flags |= FMODE_WRITABLE;
break;
default:
- Fail("illegal access mode");
+ ArgError("illegal access mode");
}
if (mode[1] == '+') {
flags |= FMODE_READABLE | FMODE_WRITABLE;
@@ -584,6 +709,26 @@ io_mode_flags(mode)
}
FILE *
+rb_fopen(fname, mode)
+ char *fname;
+ char *mode;
+{
+ FILE *f;
+
+ f = fopen(fname, mode);
+ if (f == NULL) {
+ if (errno == EMFILE || errno == ENFILE) {
+ gc();
+ f = fopen(fname, mode);
+ }
+ if (f == NULL) {
+ rb_sys_fail(fname);
+ }
+ }
+ return f;
+}
+
+FILE *
rb_fdopen(fd, mode)
int fd;
char *mode;
@@ -596,13 +741,13 @@ rb_fdopen(fd, mode)
f = fdopen(fd, mode);
}
if (f == NULL) {
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
}
return f;
}
-#ifdef NT
+#if defined (NT) || defined(DJGPP)
static void
pipe_finalize(fptr)
OpenFile *fptr;
@@ -619,29 +764,30 @@ pipe_open(pname, mode)
char *pname, *mode;
{
int modef = io_mode_flags(mode);
- VALUE port;
OpenFile *fptr;
-#ifdef NT
+#if defined(NT) || defined(DJGPP)
FILE *f = popen(pname, mode);
if (f == NULL) rb_sys_fail(pname);
+ else {
+ NEWOBJ(port, struct RFile);
+ OBJSETUP(port, cIO, T_FILE);
+ MakeOpenFile(port, fptr);
+ fptr->finalize = pipe_finalize;
- port = obj_alloc(cIO);
- MakeOpenFile(port, fptr);
- 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;
+ if (modef & FMODE_READABLE) fptr->f = f;
+ if (modef & FMODE_WRITABLE) fptr->f2 = f;
+ fptr->mode = modef | FMODE_SYNC;
+ return (VALUE)port;
+ }
#else
int pid, pr[2], pw[2];
volatile int doexec;
if (((modef & FMODE_READABLE) && pipe(pr) == -1) ||
((modef & FMODE_WRITABLE) && pipe(pw) == -1))
- rb_sys_fail(Qnil);
+ rb_sys_fail(pname);
doexec = (strcmp("-", pname) != 0);
if (!doexec) {
@@ -688,28 +834,52 @@ pipe_open(pname, mode)
break;
default: /* parent */
- 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;
+ {
+ NEWOBJ(port, struct RFile);
+ OBJSETUP(port, cIO, T_FILE);
+ 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 (modef & FMODE_READABLE) fptr->f = rb_fdopen(pr[0], "r");
+ if (modef & FMODE_WRITABLE) fptr->f2 = rb_fdopen(pw[1], "w");
+
+ return (VALUE)port;
+ }
}
+#endif
+}
- fptr->pid = pid;
- if (modef & FMODE_READABLE) fptr->f = rb_fdopen(pr[0], "r");
- if (modef & FMODE_WRITABLE) fptr->f2 = rb_fdopen(pw[1], "w");
+static VALUE
+io_popen(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ char *mode;
+ VALUE pname, pmode;
- return port;
-#endif
+ rb_scan_args(argc, argv, "11", &pname, &pmode);
+ Check_Type(pname, T_STRING);
+ if (NIL_P(pmode)) {
+ mode = "r";
+ }
+ else {
+ Check_Type(pmode, T_STRING);
+ if (RSTRING(pmode)->len == 0 || RSTRING(pmode)->len > 2)
+ ArgError("illegal access mode");
+ mode = RSTRING(pmode)->ptr;
+ }
+ return pipe_open(RSTRING(pname)->ptr, mode);
}
static VALUE
io_open(fname, mode)
char *fname, *mode;
{
- int pipe = 0;
-
if (fname[0] == '|') {
return pipe_open(fname+1, mode);
}
@@ -725,19 +895,17 @@ f_open(argc, argv, self)
VALUE self;
{
char *mode;
- VALUE port;
- int pipe = 0;
VALUE pname, pmode;
rb_scan_args(argc, argv, "11", &pname, &pmode);
Check_Type(pname, T_STRING);
- if (pmode == Qnil) {
+ if (NIL_P(pmode)) {
mode = "r";
}
else {
Check_Type(pmode, T_STRING);
if (RSTRING(pmode)->len == 0 || RSTRING(pmode)->len > 2)
- Fail("illegal access mode");
+ ArgError("illegal access mode");
mode = RSTRING(pmode)->ptr;
}
return io_open(RSTRING(pname)->ptr, mode);
@@ -765,13 +933,13 @@ f_printf(argc, argv)
if (TYPE(argv[0]) == T_STRING) {
out = rb_defout;
}
- else if (rb_responds_to(argv[0], id_write)) {
+ else if (rb_respond_to(argv[0], id_write)) {
out = argv[0];
argv++;
argc--;
}
else {
- Fail("output must responds to `write'");
+ NameError("output must responds to `write'");
}
rb_funcall(out, id_write, 1, f_sprintf(argc, argv));
@@ -790,30 +958,26 @@ io_print(argc, argv, out)
/* if no argument given, print `$_' */
if (argc == 0) {
argc = 1;
- if (rb_lastline)
- argv = &rb_lastline;
- else {
- line = str_new(0,0);
- argv = &line;
- }
+ line = lastline_get();
+ argv = &line;
}
for (i=0; i<argc; i++) {
- if (OFS && i>0) {
+ if (!NIL_P(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);
+ case T_NIL:
+ io_write(out, str_new2("nil"));
break;
default:
rb_funcall(argv[i], id_print_on, 1, out);
break;
}
}
- if (ORS) {
+ if (!NIL_P(ORS)) {
io_write(out, ORS);
}
@@ -829,6 +993,17 @@ f_print(argc, argv)
return Qnil;
}
+VALUE
+f_p(obj, val)
+{
+ VALUE str = rb_inspect(val);
+
+ Check_Type(str, T_STRING);
+ io_write(rb_defout, str);
+ io_write(rb_defout, str_new2("\n"));
+ return Qnil;
+}
+
static VALUE
io_defset(val, id)
VALUE val;
@@ -838,7 +1013,7 @@ io_defset(val, id)
val = io_open(RSTRING(val)->ptr, "w");
}
if (!obj_is_kind_of(val, cIO)) {
- Fail("$< must be a file, %s given", rb_class2name(CLASS_OF(val)));
+ TypeError("$< must be a file, %s given", rb_class2name(CLASS_OF(val)));
}
return rb_defout = val;
}
@@ -855,14 +1030,15 @@ prep_stdio(f, mode)
FILE *f;
int mode;
{
- VALUE obj = obj_alloc(cIO);
OpenFile *fp;
+ NEWOBJ(obj, struct RFile);
+ OBJSETUP(obj, cIO, T_FILE);
MakeOpenFile(obj, fp);
fp->f = f;
fp->mode = mode;
- return obj;
+ return (VALUE)obj;
}
static VALUE filename, file;
@@ -900,9 +1076,8 @@ next_argv()
}
}
else {
- FILE *fr = fopen(fn, "r");
+ FILE *fr = rb_fopen(fn, "r");
- if (!fr) rb_sys_fail(fn);
if (inplace) {
struct stat st, st2;
VALUE str;
@@ -919,25 +1094,40 @@ next_argv()
#else
str_cat(str, inplace, strlen(inplace));
#endif
+#if defined(MSDOS) || defined(__BOW__)
+ (void)fclose(fr);
+ (void)unlink(RSTRING(str)->ptr);
+ (void)rename(fn, RSTRING(str)->ptr);
+ fr = rb_fopen(RSTRING(str)->ptr, "r");
+#else
if (rename(fn, RSTRING(str)->ptr) < 0) {
Warning("Can't rename %s to %s: %s, skipping file",
fn, RSTRING(str)->ptr, strerror(errno));
fclose(fr);
goto retry;
}
+#endif
}
- else if (unlink(fn) < 0) {
- Warning("Can't remove %s: %s, skipping file",
+ else {
+#if !defined(MSDOS) && !defined(__BOW__)
+ if (unlink(fn) < 0) {
+ Warning("Can't remove %s: %s, skipping file",
fn, strerror(errno));
- fclose(fr);
- goto retry;
+ fclose(fr);
+ goto retry;
+ }
+#else
+ Fatal("Can't do inplace edit without backup");
+#endif
}
- fw = fopen(fn, "w");
+ fw = rb_fopen(fn, "w");
+#if !defined(DJGPP) && !defined(__CYGWIN32__)
fstat(fileno(fw), &st2);
fchmod(fileno(fw), st.st_mode);
if (st.st_uid!=st2.st_uid || st.st_gid!=st2.st_gid) {
fchown(fileno(fw), st.st_uid, st.st_gid);
}
+#endif
rb_defout = prep_stdio(fw, FMODE_WRITABLE);
}
file = prep_stdio(fr, FMODE_READABLE);
@@ -952,25 +1142,48 @@ next_argv()
}
static VALUE
-f_gets()
+f_gets_method(argc, argv)
+ int argc;
+ VALUE argv;
{
VALUE line;
retry:
if (!next_argv()) return Qnil;
- line = io_gets(file);
- if (line == Qnil && next_p != -1) {
+ line = io_gets_method(argc, argv, file);
+ if (NIL_P(line) && next_p != -1) {
io_close(file);
next_p = 1;
goto retry;
}
-
gets_lineno++;
lineno = INT2FIX(gets_lineno);
return line;
}
+VALUE
+f_gets(argc, argv)
+ int argc;
+ VALUE argv;
+{
+ return f_gets_method(0,0);
+}
+
+static VALUE
+f_readline(argc, argv)
+ int argc;
+ VALUE argv;
+{
+ VALUE line = f_gets_method(argc, argv);
+
+ if (NIL_P(line)) {
+ eof_error();
+ }
+
+ return line;
+}
+
static VALUE
f_eof()
{
@@ -990,13 +1203,36 @@ f_getc()
}
static VALUE
-f_readlines(obj)
- VALUE obj;
+f_ungetc(obj, c)
+ VALUE obj, c;
+{
+ if (!next_argv()) {
+ ArgError("no stream to ungetc");
+ }
+
+ return io_ungetc(file, c);
+}
+
+static VALUE
+f_readchar()
+{
+ VALUE c = f_getc();
+
+ if (NIL_P(c)) {
+ eof_error();
+ }
+ return c;
+}
+
+static VALUE
+f_readlines(argc, argv)
+ int argc;
+ VALUE argv;
{
VALUE line, ary;
ary = ary_new();
- while (line = f_gets(obj)) {
+ while (RTEST(line = f_gets_method(argc, argv))) {
ary_push(ary, line);
}
@@ -1009,14 +1245,15 @@ rb_str_setter(val, id, var)
ID id;
VALUE *var;
{
- if (val && TYPE(val) != T_STRING) {
- Fail("value of %s must be String", rb_id2name(id));
+ if (!NIL_P(val) && TYPE(val) != T_STRING) {
+ TypeError("value of %s must be String", rb_id2name(id));
}
return *var = val;
}
-VALUE
-rb_xstring(str)
+static VALUE
+f_backquote(obj, str)
+ VALUE obj;
struct RString *str;
{
VALUE port, result;
@@ -1031,21 +1268,8 @@ rb_xstring(str)
return result;
}
-struct timeval *time_timeval();
-
-#ifdef _STDIO_USES_IOSTREAM /* GNU libc */
-# 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
-# ifdef FILE_COUNT
-# define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0)
-# else
-extern int ReadDataPending();
-# define READ_DATA_PENDING(fp) ReadDataPending(fp)
-# endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
#endif
static VALUE
@@ -1063,28 +1287,31 @@ f_select(argc, argv, obj)
int interrupt = 0;
rb_scan_args(argc, argv, "13", &read, &write, &except, &timeout);
- if (timeout) {
- tp = time_timeval(timeout);
+ if (NIL_P(timeout)) {
+ tp = NULL;
}
else {
- tp = NULL;
+ timerec = time_timeval(timeout);
+ tp = &timerec;
}
FD_ZERO(&pset);
- if (read) {
+ if (!NIL_P(read)) {
int pending = 0;
Check_Type(read, T_ARRAY);
rp = &rset;
FD_ZERO(rp);
for (i=0; i<RARRAY(read)->len; i++) {
+ Check_Type(RARRAY(read)->ptr[i], T_FILE);
GetOpenFile(RARRAY(read)->ptr[i], fptr);
+ if (fptr->f == NULL) closed();
FD_SET(fileno(fptr->f), rp);
if (READ_DATA_PENDING(fptr->f)) { /* check for buffered data */
pending++;
FD_SET(fileno(fptr->f), &pset);
}
- if (max < (int)fileno(fptr->f)) max = fileno(fptr->f);
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
}
if (pending) { /* no blocking if there's buffered data */
timerec.tv_sec = timerec.tv_usec = 0;
@@ -1094,14 +1321,16 @@ f_select(argc, argv, obj)
else
rp = NULL;
- if (write) {
+ if (!NIL_P(write)) {
Check_Type(write, T_ARRAY);
wp = &wset;
FD_ZERO(wp);
for (i=0; i<RARRAY(write)->len; i++) {
+ Check_Type(RARRAY(write)->ptr[i], T_FILE);
GetOpenFile(RARRAY(write)->ptr[i], fptr);
+ if (fptr->f == NULL) closed();
FD_SET(fileno(fptr->f), wp);
- if (max > (int)fileno(fptr->f)) max = fileno(fptr->f);
+ if (max > fileno(fptr->f)) max = fileno(fptr->f);
if (fptr->f2) {
FD_SET(fileno(fptr->f2), wp);
if (max < (int)fileno(fptr->f2)) max = fileno(fptr->f2);
@@ -1111,14 +1340,16 @@ f_select(argc, argv, obj)
else
wp = NULL;
- if (except) {
+ if (!NIL_P(except)) {
Check_Type(except, T_ARRAY);
ep = &eset;
FD_ZERO(ep);
for (i=0; i<RARRAY(except)->len; i++) {
+ Check_Type(RARRAY(except)->ptr[i], T_FILE);
GetOpenFile(RARRAY(except)->ptr[i], fptr);
+ if (fptr->f == NULL) closed();
FD_SET(fileno(fptr->f), ep);
- if (max < (int)fileno(fptr->f)) max = fileno(fptr->f);
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
if (fptr->f2) {
FD_SET(fileno(fptr->f2), ep);
if (max > (int)fileno(fptr->f2)) max = fileno(fptr->f2);
@@ -1130,17 +1361,24 @@ f_select(argc, argv, obj)
max++;
+#ifdef THREAD
+ n = thread_select(max, rp, wp, ep, tp);
+ if (n < 0) {
+ rb_sys_fail(0);
+ }
+#else
retry:
TRAP_BEG;
n = select(max, rp, wp, ep, tp);
TRAP_END;
if (n < 0) {
if (errno != EINTR) {
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
if (tp == NULL) goto retry;
interrupt = 1;
}
+#endif
res = ary_new2(3);
ary_push(res, rp?ary_new():ary_new2(0));
@@ -1191,16 +1429,17 @@ f_select(argc, argv, obj)
}
void
-io_ctl(obj, req, arg, io_p)
- VALUE obj, req;
+io_ctl(io, req, arg, io_p)
+ VALUE io, req;
struct RString *arg;
int io_p;
{
+#if !defined(MSDOS)
int cmd = NUM2INT(req);
OpenFile *fptr;
int len, fd;
- GetOpenFile(obj, fptr);
+ GetOpenFile(io, fptr);
#ifdef IOCPARM_MASK
#ifndef IOCPARM_LEN
@@ -1216,9 +1455,7 @@ io_ctl(obj, req, arg, io_p)
Check_Type(arg, T_STRING);
str_modify(arg);
- if (arg->len < len) {
- str_grow(arg, len+1);
- }
+ str_resize(arg, len+1);
arg->ptr[len] = 17;
fd = fileno(fptr->f);
#ifdef HAVE_FCNTL
@@ -1227,22 +1464,25 @@ io_ctl(obj, req, arg, io_p)
}
#else
if (!io_p) {
- Bug("fcntl() not implemented");
+ rb_notimplement();
}
if (ioctl(fd, cmd, arg->ptr)<0) rb_sys_fail(fptr->path);
#endif
if (arg->ptr[len] != 17) {
- Fail("Return value overflowed string");
+ ArgError("Return value overflowed string");
}
+#else
+ rb_notimplement();
+#endif
}
static VALUE
-io_ioctl(obj, req, arg)
- VALUE obj, req;
+io_ioctl(io, req, arg)
+ VALUE io, req;
struct RString *arg;
{
- io_ctl(obj, req, arg, 1);
- return obj;
+ io_ctl(io, req, arg, 1);
+ return io;
}
static VALUE
@@ -1279,7 +1519,7 @@ f_syscall(argc, argv)
}
switch (argc) {
case 0:
- Fail("Too few args to syscall");
+ ArgError("Too few args to syscall");
case 1:
retval = syscall(arg[0]);
break;
@@ -1335,13 +1575,57 @@ f_syscall(argc, argv)
if (retval == -1) rb_sys_fail(0);
return INT2FIX(0);
#else
- Fail("syscall() unimplemented");
+ rb_notimplement();
#endif
}
static VALUE
-arg_read(obj)
- VALUE obj;
+io_pipe()
+{
+ int pipes[2];
+ VALUE r, w, ary;
+
+ if (pipe(pipes) == -1)
+ rb_sys_fail(0);
+ r = prep_stdio(fdopen(pipes[0], "r"), FMODE_READABLE);
+ w = prep_stdio(fdopen(pipes[1], "w"), FMODE_WRITABLE);
+
+ ary = ary_new2(2);
+ ary_push(ary, r);
+ ary_push(ary, w);
+
+ return ary;
+}
+
+
+static VALUE
+io_foreach_line(io)
+ VALUE io;
+{
+ VALUE str;
+
+ while (!NIL_P(str = io_gets(io))) {
+ rb_yield(str);
+ }
+ return Qnil;
+}
+
+static VALUE
+io_foreach(io, fname)
+ VALUE io;
+ struct RString *fname;
+{
+ VALUE f, v;
+
+ Check_Type(fname, T_STRING);
+ f = io_open(fname->ptr, "r");
+ return rb_ensure(io_foreach_line, f, io_close, f);
+}
+
+static VALUE
+arg_read(argc, argv)
+ int argc;
+ VALUE argv;
{
VALUE str, str2;
@@ -1349,13 +1633,13 @@ arg_read(obj)
for (;;) {
retry:
if (!next_argv()) return Qnil;
- str2 = io_read(0, Qnil, file);
- if (str2 == Qnil && next_p != -1) {
+ str2 = io_read(argc, argv, file);
+ if (NIL_P(str2) && next_p != -1) {
io_close(file);
next_p = 1;
goto retry;
}
- if (str2 == Qnil) break;
+ if (NIL_P(str2)) break;
str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
}
@@ -1370,7 +1654,7 @@ arg_getc()
retry:
if (!next_argv()) return Qnil;
byte = io_getc(file);
- if (byte == Qnil && next_p != -1) {
+ if (NIL_P(byte) && next_p != -1) {
io_close(file);
next_p = 1;
goto retry;
@@ -1380,11 +1664,13 @@ arg_getc()
}
static VALUE
-arg_each_line()
+arg_each_line(argc, argv)
+ int argc;
+ VALUE argv;
{
VALUE str;
- while (str = f_gets()) {
+ while (RTEST(str = f_gets_method(argc, argv))) {
rb_yield(str);
}
return Qnil;
@@ -1395,7 +1681,7 @@ arg_each_byte()
{
VALUE byte;
- while (byte = arg_getc()) {
+ while (!NIL_P(byte = arg_getc())) {
rb_yield(byte);
}
return Qnil;
@@ -1419,6 +1705,9 @@ void
Init_IO()
{
extern VALUE cKernel;
+ extern VALUE eException;
+
+ eEOFError = rb_define_class("EOFError", eException);
id_write = rb_intern("write");
id_fd = rb_intern("fd");
@@ -1429,32 +1718,46 @@ Init_IO()
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, "gets", f_gets_method, -1);
+ rb_define_private_method(cKernel, "readline", f_readline, -1);
rb_define_private_method(cKernel, "eof", f_eof, 0);
rb_define_private_method(cKernel, "getc", f_getc, 0);
+ rb_define_private_method(cKernel, "readchar", f_readchar, 0);
rb_define_private_method(cKernel, "select", f_select, -1);
+ rb_define_private_method(cKernel, "ungetc", f_ungetc, 1);
- rb_define_private_method(cKernel, "readlines", f_readlines, 0);
+ rb_define_private_method(cKernel, "readlines", f_readlines, -1);
rb_define_method(cKernel, "print_on", f_print_on, 1);
+ rb_define_private_method(cKernel, "`", f_backquote, 1);
+ rb_define_private_method(cKernel, "pipe", io_pipe, 0);
+
+ rb_define_private_method(cKernel, "p", f_p, 1);
+
cIO = rb_define_class("IO", cObject);
rb_include_module(cIO, mEnumerable);
+ rb_define_singleton_method(cIO, "popen", io_popen, -1);
+ rb_define_singleton_method(cIO, "foreach", io_foreach, 1);
+ rb_define_singleton_method(cIO, "select", f_select, -1);
+
+ FS = OFS = Qnil;
rb_define_hooked_variable("$;", &FS, 0, rb_str_setter);
rb_define_hooked_variable("$,", &OFS, 0, rb_str_setter);
- RS = str_new2("\n");
+ RS = RS_default = str_new2("\n"); ORS = Qnil;
+ rb_global_variable(&RS_default);
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_virtual_variable("$_", lastline_get, lastline_set);
rb_define_method(cIO, "print", io_print, -1);
+ rb_define_method(cIO, "printf", io_printf, -1);
- rb_define_method(cIO, "each", io_each_line, 0);
+ rb_define_method(cIO, "each", io_each_line, -1);
rb_define_method(cIO, "each_line", io_each_line, 0);
rb_define_method(cIO, "each_byte", io_each_byte, 0);
@@ -1469,11 +1772,13 @@ Init_IO()
rb_define_alias(cIO, "readlines", "to_a");
- rb_define_method(cIO, "read", io_read, -2);
+ rb_define_method(cIO, "read", io_read, -1);
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, "gets", io_gets_method, -1);
+ rb_define_method(cIO, "readline", io_readline, -1);
rb_define_method(cIO, "getc", io_getc, 0);
+ rb_define_method(cIO, "readchar", io_readchar, 0);
+ rb_define_method(cIO, "ungetc",io_ungetc, 1);
rb_define_method(cIO, "puts", io_puts, 1);
rb_define_method(cIO, "<<", io_puts, 1);
rb_define_method(cIO, "flush", io_flush, 0);
@@ -1497,27 +1802,29 @@ Init_IO()
rb_defout = rb_stdout;
rb_define_hooked_variable("$>", &rb_defout, 0, io_defset);
- rb_define_const(cObject, "STDIN", rb_stdin);
- rb_define_const(cObject, "STDOUT", rb_stdout);
- rb_define_const(cObject, "STDERR", rb_stderr);
+ rb_define_global_const("STDIN", rb_stdin);
+ rb_define_global_const("STDOUT", rb_stdout);
+ rb_define_global_const("STDERR", rb_stderr);
argf = obj_alloc(cObject);
rb_extend_object(argf, mEnumerable);
rb_define_readonly_variable("$<", &argf);
rb_define_readonly_variable("$ARGF", &argf);
+ rb_define_global_const("ARGF", argf);
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_line", arg_each_line, -1);
rb_define_singleton_method(argf, "each_byte", arg_each_byte, 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, "read", arg_read, -1);
+ rb_define_singleton_method(argf, "readlines", f_readlines, -1);
+ rb_define_singleton_method(argf, "gets", f_gets_method, -1);
+ rb_define_singleton_method(argf, "readline", f_readline, -1);
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_singleton_method(argf, "ungetc", f_ungetc, 1);
rb_define_singleton_method(argf, "to_s", arg_filename, 0);
rb_define_singleton_method(argf, "filename", arg_filename, 0);
diff --git a/io.h b/io.h
index b3772c9a2f..2817835ef3 100644
--- a/io.h
+++ b/io.h
@@ -7,7 +7,7 @@
$Date: 1994/08/12 11:06:42 $
created at: Fri Nov 12 16:47:09 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -18,7 +18,7 @@
#include <stdio.h>
#include <errno.h>
-typedef struct {
+typedef struct OpenFile {
FILE *f; /* stdio ptr for read/write */
FILE *f2; /* additional ptr for rw pipes */
int mode; /* mode flags */
@@ -33,14 +33,10 @@ typedef struct {
#define FMODE_READWRITE 3
#define FMODE_SYNC 4
-extern ID id_fd;
-
-#define GetOpenFile(obj,fp) Get_Data_Struct(obj, id_fd, OpenFile, fp)
-
-void io_fptr_finalize();
+#define GetOpenFile(obj,fp) fp = RFILE(obj)->fptr
#define MakeOpenFile(obj, fp) do {\
- Make_Data_Struct(obj, id_fd, OpenFile, 0, io_fptr_finalize, fp);\
+ fp = RFILE(obj)->fptr = ALLOC(OpenFile);\
fp->f = fp->f2 = NULL;\
fp->mode = 0;\
fp->pid = 0;\
@@ -49,4 +45,6 @@ void io_fptr_finalize();
fp->finalize = 0;\
} while (0)
+FILE *rb_fopen();
+
#endif
diff --git a/lib/base64.rb b/lib/base64.rb
index a6bf1adf92..9bb6487bee 100644
--- a/lib/base64.rb
+++ b/lib/base64.rb
@@ -46,7 +46,7 @@ def j2e(str)
end
def decode_b(str)
- str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/) {
+ str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/i) {
decode64($1)
}
str.gsub!(/\n/, ' ')
diff --git a/lib/cgi-lib.rb b/lib/cgi-lib.rb
new file mode 100644
index 0000000000..afadbff3b6
--- /dev/null
+++ b/lib/cgi-lib.rb
@@ -0,0 +1,56 @@
+#!/usr/local/bin/ruby
+#
+# Get CGI String
+#
+# EXAMPLE:
+# require "cgi-lib.rb"
+# foo = CGI.new
+# foo['field'] <== value of 'field'
+# foo.keys <== array of fields
+# foo.inputs <== hash of { <field> => <value> }
+
+class CGI
+ attr("inputs")
+
+ def initialize
+ str = if ENV['REQUEST_METHOD'] == "GET"
+ ENV['QUERY_STRING']
+ elsif ENV['REQUEST_METHOD'] == "POST"
+ $stdin.read ENV['CONTENT_LENGTH'].to_i
+ else
+ ""
+ end
+ arr = str.split(/&/)
+ @inputs = {}
+ arr.each do |x|
+ x.gsub!(/\+/, ' ')
+ key, val = x.split(/=/, 2)
+ val = "" unless val
+
+ key.gsub!(/%(..)/) { [$1.hex].pack("c") }
+ val.gsub!(/%(..)/) { [$1.hex].pack("c") }
+
+ @inputs[key] += "\0" if @inputs[key]
+ @inputs[key] += val
+ end
+ end
+
+ def keys
+ @inputs.keys
+ end
+
+ def [](key)
+ @inputs[key]
+ end
+
+ def CGI.message(msg, title = "")
+ print "Content-type: text/html\n\n"
+ print "<html><head><title>"
+ print title
+ print "</title></head><body>\n"
+ print msg
+ print "</body></html>\n"
+ TRUE
+ end
+
+end
diff --git a/lib/complex.rb b/lib/complex.rb
new file mode 100644
index 0000000000..aa5d219d2f
--- /dev/null
+++ b/lib/complex.rb
@@ -0,0 +1,490 @@
+#
+# complex.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.1 $
+# $Date: 1996/11/11 04:25:19 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# --
+# Usage:
+# class Complex < Numeric
+#
+# Complex(x, y) --> x + yi
+# y.im --> 0 + yi
+#
+# Complex::polar
+#
+# Complex::+
+# Complex::-
+# Complex::*
+# Complex::/
+# Complex::**
+# Complex::%
+# Complex::divmod
+# Complex::abs
+# Complex::abs2
+# Complex::arg
+# Complex::polar
+# Complex::conjugate
+# Complex::<=>
+# Complex::==
+# Complex::to_i
+# Complex::to_f
+# Complex::to_r
+# Complex::to_s
+#
+# Complex::I
+#
+# Numeric::im
+#
+# Math.sqrt
+# Math.exp
+# Math.cos
+# Math.sin
+# Math.tan
+# Math.log
+# Math.log10
+# Math.atan2
+#
+#
+
+def Complex(a, b = 0)
+ if a.kind_of?(Complex) and b == 0
+ a
+ elsif b == 0 and defined? Complex::Unify
+ a
+ else
+ Complex.new(a, b)
+ end
+end
+
+class Complex < Numeric
+
+ def Complex.generic?(other)
+ other.kind_of?(Integer) or
+ other.kind_of?(Float) or
+ (defined?(Rational) and other.kind_of?(Rational))
+ end
+
+ def Complex.polar(r, theta)
+ Complex(r*Math.cos(theta), r*Math.sin(theta))
+ end
+
+ def initialize(a, b = 0)
+ @real = a
+ @image = b
+ end
+
+ def + (other)
+ if other.kind_of?(Complex)
+ re = @real + other.real
+ im = @image + other.image
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real + other, @image)
+ else
+ x , y = a.coerce(self)
+ x + y
+ end
+ end
+
+ def - (other)
+ if other.kind_of?(Complex)
+ re = @real - other.real
+ im = @image - other.image
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real - other, @image)
+ else
+ x , y = a.coerce(self)
+ x - y
+ end
+ end
+
+ def * (other)
+ if other.kind_of?(Complex)
+ re = @real*other.real - @image*other.image
+ im = @real*other.image + @image*other.real
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real * other, @image * other)
+ else
+ x , y = a.coerce(self)
+ x * y
+ end
+ end
+
+ def / (other)
+ if other.kind_of?(Complex)
+ self * other.conjugate / other.abs2
+ elsif Complex.generic?(other)
+ Complex(@real / other, @image / other)
+ else
+ x , y = a.coerce(self)
+ x / y
+ end
+ end
+
+ def ** (other)
+ if other == 0
+ return Complex(1)
+ end
+ if other.kind_of?(Complex)
+ r, theta = polar
+ ore = other.real
+ oim = other.image
+ nr = Math.exp!(ore*Math.log!(r) - oim * theta)
+ ntheta = theta*ore + oim*Math.log!(r)
+ Complex.polar(nr, ntheta)
+ elsif other.kind_of?(Integer)
+ if other > 0
+ x = self
+ z = x
+ n = other - 1
+ while n != 0
+ while (div, mod = n.divmod(2)
+ mod == 0)
+ x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image)
+ n = div
+ end
+ z *= x
+ n -= 1
+ end
+ z
+ else
+ if defined? Rational
+ (Rational(1) / self) ** -other
+ else
+ self ** Float(other)
+ end
+ end
+ elsif Complex.generic?(other)
+ r, theta = polar
+ Complex.polar(r.power!(other), theta * other)
+ else
+ x , y = a.coerce(self)
+ x / y
+ end
+ end
+
+ def % (other)
+ if other.kind_of?(Complex)
+ Complex(@real % other.real, @image % other.image)
+ elsif Complex.generic?(other)
+ Complex(@real % other, @image % other)
+ else
+ x , y = a.coerce(self)
+ x % y
+ end
+ end
+
+ def divmod(other)
+ if other.kind_of?(Complex)
+ rdiv, rmod = @real.divmod(other.real)
+ idiv, imod = @image.divmod(other.image)
+ return Complex(rdiv, idiv), Complex(rmod, rdiv)
+ elsif Complex.generic?(other)
+ Complex(@real.divmod(other), @image.divmod(other))
+ else
+ x , y = a.coerce(self)
+ x.divmod(y)
+ end
+ end
+
+ def abs
+ Math.sqrt!((@real*@real + @image*@image).to_f)
+ end
+
+ def abs2
+ @real*@real + @image*@image
+ end
+
+ def arg
+ Math.atan2(@image.to_f, @real.to_f)
+ end
+
+ def polar
+ return abs, arg
+ end
+
+ def conjugate
+ Complex(@real, -@image)
+ end
+
+ def <=> (other)
+ self.abs <=> other.abs
+ end
+
+ def == (other)
+ if other.kind_of?(Complex)
+ @real == other.real and @image == other.image
+ elsif Complex.generic?(other)
+ @real == other and @image == 0
+ else
+ x , y = a.coerce(self)
+ x == y
+ end
+ end
+
+ def coerce(other)
+ if Complex.generic?(other)
+ return Complex.new(other), self
+ else
+ super
+ end
+ end
+
+ def to_i
+ Complex(@real.to_i, @image.to_i)
+ end
+
+ def to_f
+ Complex(@real.to_f, @image.to_f)
+ end
+
+ def to_r
+ Complex(@real.to_r, @image.to_r)
+ end
+
+ def denominator
+ @real.denominator.lcm(@image.denominator)
+ end
+
+ def numerator
+ cd = denominator
+ Complex(@real.numerator*(cd/@real.denominator),
+ @image.numerator*(cd/@image.denominator))
+ end
+
+ def to_s
+ if @real != 0
+ if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
+ if @image >= 0
+ @real.to_s+"+("+@image.to_s+")i"
+ else
+ @real.to_s+"-("+(-@image).to_s+")i"
+ end
+ else
+ if @image >= 0
+ @real.to_s+"+"+@image.to_s+"i"
+ else
+ @real.to_s+"-"+(-@image).to_s+"i"
+ end
+ end
+ else
+ if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
+ "("+@image.to_s+")i"
+ else
+ @image.to_s+"i"
+ end
+ end
+ end
+
+ def hash
+ @real ^ @image
+ end
+
+ I = Complex(0,1)
+
+ attr :real
+ attr :image
+
+end
+
+class Numeric
+ def im
+ Complex(0, self)
+ end
+
+ def real
+ self
+ end
+
+ def image
+ 0
+ end
+
+ def arg
+ if self >= 0
+ return 0
+ else
+ return Math.atan2(1,1)*4
+ end
+ end
+
+ def polar
+ return abs, arg
+ end
+
+ def conjugate
+ self
+ end
+end
+
+class Fixnum
+ if not defined? Rational
+ alias power! **
+ end
+
+ def ** (other)
+ if self < 0
+ Complex.new(self) ** other
+ else
+ if defined? Rational
+ if other >= 0
+ self.power!(other)
+ else
+ Rational.new!(self,1)**other
+ end
+ else
+ self.power!(other)
+ end
+ end
+ end
+end
+
+class Bignum
+ if not defined? Rational
+ alias power! **
+ end
+end
+
+class Float
+ alias power! **
+end
+
+module Math
+ alias sqrt! sqrt
+ alias exp! exp
+ alias cos! cos
+ alias sin! sin
+ alias tan! tan
+ alias log! log
+ alias log10! log10
+ alias atan2! atan2
+
+ def sqrt(z)
+ if Complex.generic?(z)
+ if z >= 0
+ sqrt!(z)
+ else
+ Complex(0,sqrt!(-z))
+ end
+ else
+ z**Rational(1,2)
+ end
+ end
+
+ def exp(z)
+ if Complex.generic?(z)
+ exp!(z)
+ else
+ Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image))
+ end
+ end
+
+ def cosh!(x)
+ (exp!(x) + exp!(-x))/2.0
+ end
+
+ def sinh!(x)
+ (exp!(x) - exp!(-x))/2.0
+ end
+
+ def cos(z)
+ if Complex.generic?(z)
+ cos!(z)
+ else
+ Complex(cos!(z.real)*cosh!(z.image),
+ sin!(z.real)*sinh!(z.image))
+ end
+ end
+
+ def sin(z)
+ if Complex.generic?(z)
+ sin!(z)
+ else
+ Complex(sin!(z.real)*cosh!(z.image),
+ -cos!(z.real)*sinh!(z.image))
+ end
+ end
+
+ def tan(z)
+ if Complex.generic?(z)
+ tan!(z)
+ else
+ sin(z)/cos(z)
+ end
+ end
+
+ def log(z)
+ if Complex.generic?(z) and z >= 0
+ log!(z)
+ else
+ r, theta = z.polar
+ Complex(log!(r.abs), theta)
+ end
+ end
+
+ def log10(z)
+ if Complex.generic?(z)
+ log10!(z)
+ else
+ log(z)/log!(10)
+ end
+ end
+
+ def atan2(x, y)
+ if Complex.generic?(x) and Complex.generic?(y)
+ atan2!(x, y)
+ else
+ fail "Not yet implemented."
+ end
+ end
+
+ def atanh!(x)
+ log((1.0 + x.to_f) / ( 1.0 - x.to_f)) / 2.0
+ end
+
+ def atan(z)
+ if Complex.generic?(z)
+ atan2!(z, 1)
+ elsif z.image == 0
+ atan2(z.real,1)
+ else
+ a = z.real
+ b = z.image
+
+ c = (a*a + b*b - 1.0)
+ d = (a*a + b*b + 1.0)
+
+ Complex(atan2!((c + sqrt(c*c + 4.0*a*a)), 2.0*a),
+ atanh!((-d + sqrt(d*d - 4.0*b*b))/(2.0*b)))
+ end
+ end
+
+ module_function :sqrt
+ module_function :sqrt!
+ module_function :exp!
+ module_function :exp
+ module_function :cosh!
+ module_function :cos!
+ module_function :cos
+ module_function :sinh!
+ module_function :sin!
+ module_function :sin
+ module_function :tan!
+ module_function :tan
+ module_function :log!
+ module_function :log
+ module_function :log10!
+ module_function :log
+ module_function :atan2!
+ module_function :atan2
+# module_function :atan!
+ module_function :atan
+ module_function :atanh!
+
+end
+
+
diff --git a/lib/find.rb b/lib/find.rb
index 340461c653..5ecc54329c 100644
--- a/lib/find.rb
+++ b/lib/find.rb
@@ -8,31 +8,32 @@
#
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
+ def find(*path)
+ while file = path.shift
+ catch(:prune) {
+ yield file
+ if File.directory? file and not File.symlink? file then
+ d = Dir.open(file)
+ begin
+ for f in d
+ next if f =~ /^\.\.?$/
+ if file == "/" then
+ f = "/" + f
+ else
+ f = file + "/" + f
+ end
+ path.unshift f
+ end
+ ensure
+ d.close
+ end
+ end
+ }
end
end
- private :findpath
- def find(*path)
- ary = []
- for p in path
- findpath(p, ary)
- for f in ary
- yield f
- end
- end
+ def prune
+ throw :prune
end
- module_function :find
+ module_function :find, :prune
end
diff --git a/lib/getopts.rb b/lib/getopts.rb
index 37fd3dc69d..d25437515d 100644
--- a/lib/getopts.rb
+++ b/lib/getopts.rb
@@ -1,117 +1,142 @@
+#!/usr/local/bin/ruby
#
-# getopts.rb - get options
+# getopts.rb -
# $Release Version: $
-# $Revision: 1.2 $
-# $Date: 1994/02/15 05:17:15 $
-# by Yasuo OHBA(STAFS Development Room)
+# $Revision: 1.1 $
+# $Date: 1996/11/10 05:01:15 $
+# by Yasuo OHBA(SHL Japan Inc. Technology Dept.)
#
# --
-# IvV, $OPT_?? lZbg.
-# wIvVw nil .
-# I, ZbgIvV.
#
-# getopts(single_opts, *opts)
-#
-# ex. sample [options] filename
-# options ...
-# -f -x --version --geometry 100x200 -d unix:0.0
-#
-# getopts("fx", "version", "geometry:", "d:")
-#
-# :
-# -f -x (= -fx) lIvVw.
-# nil wKv.
-# ~:
-# Ol[IvV, IvVw.
-# --version , --geometry 300x400 , -d host:0.0 .
-# w ":" Kt.
-#
-# IvVw, $OPT_?? non-nil , I
-# vVZbg.
-# -f -> $OPT_f = TRUE
-# --geometry 300x400 -> $OPT_geometry = 300x400
-#
-# - -- , ~, SIvV.
+#
#
-$RCS_ID="$Header: /var/ohba/RCS/getopts.rb,v 1.2 1994/02/15 05:17:15 ohba Exp ohba $"
+$RCS_ID="$Header: /home/jammy/current/ruby/RCS/getopts.rb,v 1.1 1996/11/10 05:01:15 jammy Exp $"
+
+def isSingle(lopt)
+ if lopt.index(":")
+ if lopt.split(":")[0].length == 1
+ return TRUE
+ end
+ end
+ return nil
+end
+
+def getOptionName(lopt)
+ return lopt.split(":")[0]
+end
+
+def getDefaultOption(lopt)
+ od = lopt.split(":")[1]
+ if od
+ return od
+ end
+ return nil
+end
+
+def setOption(name, value)
+ eval("$OPT_" + name + " = " + 'value')
+end
+
+def setDefaultOption(lopt)
+ d = getDefaultOption(lopt)
+ if d
+ setOption(getOptionName(lopt), d)
+ end
+end
+
+def setNewArgv(newargv)
+ ARGV.clear
+ for na in newargv
+ ARGV << na
+ end
+end
-def getopts(single_opts, *opts)
- if (opts)
+
+def getopts(single_opts, *options)
+ if options
single_colon = ""
long_opts = []
sc = 0
- for option in opts
- if (option.length <= 2)
- single_colon[sc, 0] = option[0, 1]
+ for o in options
+ setDefaultOption(o)
+ if isSingle(o)
+ single_colon[sc, 0] = getOptionName(o)
sc += 1
else
- long_opts.push(option)
+ long_opts.push(o)
end
end
end
-
+
opts = {}
count = 0
- while ($ARGV.length != 0)
+ newargv = []
+ while ARGV.length != 0
compare = nil
- case $ARGV[0]
+ case ARGV[0]
when /^--?$/
- $ARGV.shift
+ ARGV.shift
+ newargv += ARGV
break
when /^--.*/
- compare = $ARGV[0][2, ($ARGV[0].length - 2)]
- if (long_opts != "")
- for option in long_opts
- if (option[(option.length - 1), 1] == ":" &&
- option[0, (option.length - 1)] == compare)
- if ($ARGV.length <= 1)
+ compare = ARGV[0][2, (ARGV[0].length - 2)]
+ if long_opts != ""
+ for lo in long_opts
+ if lo.index(":") && getOptionName(lo) == compare
+ if ARGV.length <= 1
return nil
- end
- eval("$OPT_" + compare + " = " + '$ARGV[1]')
- opts[compare] = TRUE
- $ARGV.shift
+ end
+ setOption(compare, ARGV[1])
+ opts[compare] = TRUE
+ ARGV.shift
count += 1
break
- elsif (option == compare)
- eval("$OPT_" + compare + " = TRUE")
- opts[compare] = TRUE
+ elsif lo == compare
+ setOption(compare, TRUE)
+ opts[compare] = TRUE
count += 1
- break
- end
- end
+ break
+ end
+ end
+ end
+ if compare.length <= 1
+ return nil
end
when /^-.*/
- for index in 1..($ARGV[0].length - 1)
- compare = $ARGV[0][index, 1]
- if (single_opts && compare =~ "[" + single_opts + "]")
- eval("$OPT_" + compare + " = TRUE")
- opts[compare] = TRUE
+ for idx in 1..(ARGV[0].length - 1)
+ compare = ARGV[0][idx, 1]
+ if single_opts && compare =~ "[" + single_opts + "]"
+ setOption(compare, TRUE)
+ opts[compare] = TRUE
count += 1
- elsif (single_colon != "" && compare =~ "[" + single_colon + "]")
- if ($ARGV[0][index..-1].length > 1)
- eval("$OPT_" + compare + " = " + '$ARGV[0][(index + 1)..-1]')
- opts[compare] = TRUE
+ elsif single_colon != "" && compare =~ "[" + single_colon + "]"
+ if ARGV[0][idx..-1].length > 1
+ setOption(compare, ARGV[0][(idx + 1)..-1])
+ opts[compare] = TRUE
count += 1
- elsif ($ARGV.length <= 1)
+ elsif ARGV.length <= 1
return nil
else
- eval("$OPT_" + compare + " = " + '$ARGV[1]')
- opts[compare] = TRUE
- $ARGV.shift
- count = count + 1
+ setOption(compare, ARGV[1])
+ opts[compare] = TRUE
+ ARGV.shift
+ count += 1
end
break
end
end
else
- break
+ compare = ARGV[0]
+ opts[compare] = TRUE
+ newargv << ARGV[0]
end
-
- $ARGV.shift
- if (!opts.includes(compare))
+
+ ARGV.shift
+ if !opts.has_key?(compare)
return nil
end
end
+ setNewArgv(newargv)
return count
end
diff --git a/lib/jcode.rb b/lib/jcode.rb
new file mode 100644
index 0000000000..5b2289932f
--- /dev/null
+++ b/lib/jcode.rb
@@ -0,0 +1,174 @@
+# jcode.rb - ruby code to handle japanese (EUC/SJIS) string
+
+class String
+ printf STDERR, "feel free for some warnings:\n" if $VERBOSE
+
+ alias original_succ succ
+ private :original_succ
+
+ def succ
+ if self[-2] && self[-2] & 0x80 != 0
+ s = self.dup
+ s[-1] += 1
+ return s
+ else
+ original_succ
+ end
+ end
+
+ def upto(to)
+ return if self > to
+
+ curr = self
+ tail = self[-2..-1]
+ if tail.length == 2 and tail =~ /^.$/ then
+ if self[0..-2] == to[0..-2]
+ for c in self[-1] .. to[-1]
+ yield self[0..-2]+c.chr
+ end
+ end
+ else
+ loop do
+ yield curr
+ return if curr == to
+ curr = curr.succ
+ return if curr.length > to.length
+ end
+ end
+ return nil
+ end
+
+ def _expand_ch
+ a = []
+ self.scan(/(.|\n)-(.|\n)|(.|\n)/) do |r|
+ if $3
+ a.push $3
+ elsif $1.length != $2.length
+ next
+ elsif $1.length == 1
+ $1[0].upto($2[0]) { |c| a.push c.chr }
+ else
+ $1.upto($2) { |c| a.push c }
+ end
+ end
+ a
+ end
+
+ def tr!(from, to)
+ return self.delete!(from) if to.length == 0
+
+ if from =~ /^\^/
+ comp=TRUE
+ from = $'
+ end
+ afrom = from._expand_ch
+ ato = to._expand_ch
+ i = 0
+ if comp
+ self.gsub!(/(.|\n)/) do |c|
+ unless afrom.include?(c)
+ ato[-1]
+ else
+ c
+ end
+ end
+ else
+ self.gsub!(/(.|\n)/) do |c|
+ if i = afrom.index(c)
+ if i < ato.size then ato[i] else ato[-1] end
+ else
+ c
+ end
+ end
+ end
+ end
+
+ def tr(from, to)
+ self.dup.tr!(from, to)
+ end
+
+ def delete!(del)
+ if del =~ /^\^/
+ comp=TRUE
+ del = $'
+ end
+ adel = del._expand_ch
+ if comp
+ self.gsub!(/(.|\n)/) do |c|
+ next unless adel.include?(c)
+ c
+ end
+ else
+ self.gsub!(/(.|\n)/) do |c|
+ next if adel.include?(c)
+ c
+ end
+ end
+ end
+
+ def delete(del)
+ self.dup.delete!(del)
+ end
+
+ def squeeze!(del=nil)
+ if del
+ if del =~ /^\^/
+ comp=TRUE
+ del = $'
+ end
+ adel = del._expand_ch
+ if comp
+ self.gsub!(/(.|\n)\1+/) do
+ next unless adel.include?($1)
+ $&
+ end
+ else
+ for c in adel
+ cq = Regexp.quote(c)
+ self.gsub!(/#{cq}(#{cq})+/, cq)
+ end
+ end
+ self
+ else
+ self.gsub!(/(.|\n)\1+/, '\1')
+ end
+ end
+
+ def squeeze(del=nil)
+ self.dup.squeeze!(del)
+ end
+
+ def tr_s!(from, to)
+ return self.delete!(from) if to.length == 0
+ if from =~ /^\^/
+ comp=TRUE
+ from = $'
+ end
+ afrom = from._expand_ch
+ ato = to._expand_ch
+ i = 0
+ c = nil
+ last = nil
+ self.gsub!(/(.|\n)/) do |c|
+ if comp
+ unless afrom.include?(c)
+ ato[-1]
+ else
+ c
+ end
+ elsif i = afrom.index(c)
+ c = if i < ato.size then ato[i] else ato[-1] end
+ next if c == last
+ last = c
+ else
+ last = nil
+ c
+ end
+ end
+ end
+
+ def tr_s(from, to)
+ self.dup.tr_s!(from,to)
+ end
+
+end
diff --git a/lib/mailread.rb b/lib/mailread.rb
index 4b04445beb..d9feffbb7a 100644
--- a/lib/mailread.rb
+++ b/lib/mailread.rb
@@ -1,9 +1,8 @@
class Mail
-
def Mail.new(f)
- if !f.is_kind_of?(IO)
+ unless f.kind_of?(IO)
f = open(f, "r")
- me = super
+ me = super(f)
f.close
else
me = super
@@ -16,17 +15,18 @@ class Mail
@body = []
while f.gets()
$_.chop!
- continue if /^From / # skip From-line
+ next if /^From / # skip From-line
break if /^$/ # end of header
+
if /^(\S+):\s*(.*)/
- @header[attr = $1.capitalize] = $2
+ @header[attr = $1.capitalize!] = $2
elsif attr
- sub(/^\s*/, '')
+ sub!(/^\s*/, '')
@header[attr] += "\n" + $_
end
end
-
- return if ! $_
+
+ return unless $_
while f.gets()
break if /^From /
@@ -42,4 +42,7 @@ class Mail
return @body
end
+ def [](field)
+ @header[field]
+ end
end
diff --git a/lib/mathn.rb b/lib/mathn.rb
new file mode 100644
index 0000000000..359cb45769
--- /dev/null
+++ b/lib/mathn.rb
@@ -0,0 +1,307 @@
+#
+# mathn.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.1 $
+# $Date: 1996/11/11 04:25:24 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# --
+#
+#
+#
+
+require "rational.rb"
+require "complex.rb"
+
+class Integer
+
+ def gcd2(int)
+ a = self.abs
+ b = int.abs
+ a, b = b, a if a < b
+
+ pd_a = a.prime_division
+ pd_b = b.prime_division
+
+ gcd = 1
+ for pair in pd_a
+ as = pd_b.assoc(pair[0])
+ if as
+ gcd *= as[0] ** [as[1], pair[1]].min
+ end
+ end
+ return gcd
+ end
+
+ def Integer.from_prime_division(pd)
+ value = 1
+ for prime, index in pd
+ value *= prime**index
+ end
+ value
+ end
+
+ def prime_division
+ ps = Prime.new
+ value = self
+ pv = []
+ for prime in ps
+ count = 0
+ while (value1, mod = value.divmod(prime)
+ mod) == 0
+ value = value1
+ count += 1
+ end
+ if count != 0
+ pv.push [prime, count]
+ end
+ break if prime * prime >= value
+ end
+ if value > 1
+ pv.push [value, 1]
+ end
+ return pv
+ end
+end
+
+class Prime
+ include Enumerable
+
+ def initialize
+ @seed = 1
+ @primes = []
+ @counts = []
+ end
+
+ def succ
+ i = -1
+ size = @primes.size
+ while i < size
+ if i == -1
+ @seed += 1
+ i += 1
+ else
+ while @seed > @counts[i]
+ @counts[i] += @primes[i]
+ end
+ if @seed != @counts[i]
+ i += 1
+ else
+ i = -1
+ end
+ end
+ end
+ @primes.push @seed
+ @counts.push @seed + @seed
+ return @seed
+ end
+
+ def each
+ loop do
+ yield succ
+ end
+ end
+end
+
+class Fixnum
+ alias divmod! divmod
+ alias / rdiv
+ def divmod(other)
+ a = self.div(other)
+ b = self % other
+ return a,b
+ end
+end
+
+class Bignum
+ alias divmod! divmod
+ alias / rdiv
+end
+
+class Rational
+ Unify = TRUE
+
+ alias power! **
+
+ def ** (other)
+ if other.kind_of?(Rational)
+ if self < 0
+ return Complex(self, 0) ** other
+ elsif other == 0
+ return Rational(1,1)
+ elsif self == 0
+ return Rational(0,1)
+ elsif self == 1
+ return Rational(1,1)
+ end
+
+ npd = @numerator.prime_division
+ dpd = @denominator.prime_division
+ if other < 0
+ other = -other
+ npd, dpd = dpd, npd
+ end
+
+ for elm in npd
+ elm[1] = elm[1] * other
+ if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
+ return Float(self) ** other
+ end
+ elm[1] = elm[1].to_i
+ end
+
+ for elm in dpd
+ elm[1] = elm[1] * other
+ if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
+ return Float(self) ** other
+ end
+ elm[1] = elm[1].to_i
+ end
+
+ num = Integer.from_prime_division(npd)
+ den = Integer.from_prime_division(dpd)
+
+ Rational(num,den)
+
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = @numerator ** other
+ den = @denominator ** other
+ elsif other < 0
+ num = @denominator ** -other
+ den = @numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x , y = other.coerce(self)
+ x ** y
+ end
+ end
+
+ def power2(other)
+ if other.kind_of?(Rational)
+ if self < 0
+ return Complex(self, 0) ** other
+ elsif other == 0
+ return Rational(1,1)
+ elsif self == 0
+ return Rational(0,1)
+ elsif self == 1
+ return Rational(1,1)
+ end
+
+ dem = nil
+ x = self.denominator.to_f.to_i
+ neard = self.denominator.to_f ** (1.0/other.denominator.to_f)
+ loop do
+ if (neard**other.denominator == self.denominator)
+ dem = neaed
+ break
+ end
+ end
+ nearn = self.numerator.to_f ** (1.0/other.denominator.to_f)
+ Rational(num,den)
+
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = @numerator ** other
+ den = @denominator ** other
+ elsif other < 0
+ num = @denominator ** -other
+ den = @numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x , y = other.coerce(self)
+ x ** y
+ end
+ end
+end
+
+module Math
+ def sqrt(a)
+ if a.kind_of?(Complex)
+ abs = sqrt(a.real*a.real + a.image*a.image)
+# if not abs.kind_of?(Rational)
+# return a**Rational(1,2)
+# end
+ x = sqrt((a.real + abs)/Rational(2))
+ y = sqrt((-a.real + abs)/Rational(2))
+# if !(x.kind_of?(Rational) and y.kind_of?(Rational))
+# return a**Rational(1,2)
+# end
+ if a.image >= 0
+ Complex(x, y)
+ else
+ Complex(x, -y)
+ end
+ elsif a >= 0
+ rsqrt(a)
+ else
+ Complex(0,rsqrt(-a))
+ end
+ end
+
+ def rsqrt(a)
+ if a.kind_of?(Float)
+ sqrt!(a)
+ elsif a.kind_of?(Rational)
+ rsqrt(a.numerator)/rsqrt(a.denominator)
+ else
+ src = a
+ max = 2 ** 32
+ byte_a = [src & 0xffffffff]
+ # ruby's bug
+ while (src >= max) and (src >>= 32)
+ byte_a.unshift src & 0xffffffff
+ end
+
+ answer = 0
+ main = 0
+ side = 0
+ for elm in byte_a
+ main = (main << 32) + elm
+ side <<= 16
+ if answer != 0
+ if main * 4 < side * side
+ applo = main.div(side)
+ else
+ applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1
+ end
+ else
+ applo = sqrt!(main).to_i + 1
+ end
+
+ while (x = (side + applo) * applo) > main
+ applo -= 1
+ end
+ main -= x
+ answer = (answer << 16) + applo
+ side += applo * 2
+ end
+ if main == 0
+ answer
+ else
+ sqrt!(a)
+ end
+ end
+ end
+
+ module_function :sqrt
+ module_function :rsqrt
+end
+
+class Complex
+ Unify = TRUE
+end
+
diff --git a/lib/observer.rb b/lib/observer.rb
new file mode 100644
index 0000000000..9a753939a2
--- /dev/null
+++ b/lib/observer.rb
@@ -0,0 +1,40 @@
+# Observable Mixin
+#
+# Observers must respond to update
+
+module Observable
+ def add_observer(observer)
+ @observer_peers = [] unless @observer_peers
+ unless defined? observer.update
+ raise NameError, "observer needs to respond to `update'"
+ end
+ @observer_peers.push observer
+ end
+ def delete_observer(observer)
+ @observer_peers.delete observer if @observer_peers
+ end
+ def delete_observers
+ @observer_peers.clear if @observer_peers
+ end
+ def count_observers
+ if @observer_peers
+ @observer_peers.size
+ else
+ 0
+ end
+ end
+ def changed(state=TRUE)
+ @observer_state = state
+ end
+ def changed?
+ @observer_state
+ end
+ def notify_observers(*arg)
+ if @observer_state
+ for i in @observer_peers
+ i.update(*arg)
+ end
+ @observer_state = FALSE
+ end
+ end
+end
diff --git a/lib/parsearg.rb b/lib/parsearg.rb
index e7e2b7a7f3..569ed260f7 100644
--- a/lib/parsearg.rb
+++ b/lib/parsearg.rb
@@ -1,68 +1,83 @@
+#!/usr/local/bin/ruby
#
-# parseargs.rb - parse arguments
+# parsearg.rb - parse arguments
# $Release Version: $
# $Revision: 1.3 $
-# $Date: 1994/02/15 05:16:21 $
-# by Yasuo OHBA(STAFS Development Room)
+# $Date: 1996/11/12 06:48:51 $
+# by Yasuo OHBA(SHL Japan Inc. Technology Dept.)
#
# --
-# , $OPT_?? lZbg.
-# I, ZbgIvV.
#
-# parseArgs(argc, single_opts, *opts)
-#
-# ex. sample [options] filename
-# options ...
-# -f -x --version --geometry 100x200 -d unix:0.0
-#
-# parseArgs(1, nil, "fx", "version", "geometry:", "d:")
-#
-# :
-# IvVO
-# :
-# IvVKvcKKv %TRUE %FALSE.
-# O:
-# -f -x (= -fx) lIvVw.
-# nil wKv.
-# l~:
-# Ol[IvV, IvVw.
-# --version , --geometry 300x400 , -d host:0.0 .
-# w ":" Kt.
-#
-# IvVw, $OPT_?? non-nil , I
-# vVZbg.
-# -f -> $OPT_f = %TRUE
-# --geometry 300x400 -> $OPT_geometry = 300x400
-#
-# usage g, $USAGE usage() w.
-# def usage()
-# c
-# end
-# $USAGE = 'usage'
-# usage , --help w, w\ヲ.
-#
-# - -- , ~, SIvV.
+#
#
-$RCS_ID="$Header: /var/ohba/RCS/parseargs.rb,v 1.3 1994/02/15 05:16:21 ohba Exp ohba $"
+$RCS_ID="$Header: /home/jammy/current/ruby/RCS/parsearg.rb,v 1.3 1996/11/12 06:48:51 jammy Exp $"
load("getopts.rb")
def printUsageAndExit()
if $USAGE
- apply($USAGE)
+ eval($USAGE)
end
exit()
end
+def setParenthesis(ex, opt, c)
+ if opt != ""
+ ex = sprintf("%s$OPT_%s%s", ex, opt, c)
+ else
+ ex = sprintf("%s%s", ex, c)
+ end
+ return ex
+end
+
+def setOrAnd(ex, opt, c)
+ if opt != ""
+ ex = sprintf("%s$OPT_%s %s%s ", ex, opt, c, c)
+ else
+ ex = sprintf("%s %s%s ", ex, c, c)
+ end
+ return ex
+end
+
+def setExpression(ex, opt, op)
+ if !op
+ ex = sprintf("%s$OPT_%s", ex, opt)
+ return ex
+ end
+ case op.chr
+ when "(", ")"
+ ex = setParenthesis(ex, opt, op.chr)
+ when "|", "&"
+ ex = setOrAnd(ex, opt, op.chr)
+ else
+ return nil
+ end
+ return ex
+end
+
def parseArgs(argc, nopt, single_opts, *opts)
- if ((noOptions = getopts(single_opts, *opts)) == nil)
+ if (noOptions = getopts(single_opts, *opts)) == nil
printUsageAndExit()
end
- if (nopt && noOptions == 0)
- printUsageAndExit()
+ if nopt
+ ex = nil
+ pos = 0
+ for o in nopt.split(/[()|&]/)
+ pos += o.length
+ ex = setExpression(ex, o, nopt[pos])
+ pos += 1
+ end
+ begin
+ if !eval(ex)
+ printUsageAndExit()
+ end
+ rescue
+ print "Format Error!! : \"" + nopt + "\"\t[parseArgs]\n"
+ exit! -1
+ end
end
- if ($ARGV.length < argc)
+ if ARGV.length < argc
printUsageAndExit()
end
return noOptions
diff --git a/lib/rational.rb b/lib/rational.rb
new file mode 100644
index 0000000000..d4112c2956
--- /dev/null
+++ b/lib/rational.rb
@@ -0,0 +1,361 @@
+#
+# rational.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.1 $
+# $Date: 1996/11/11 04:25:14 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# --
+# Usage:
+# class Rational < Numeric
+# (include Compareable)
+#
+# Rational(a, b) --> a/b
+#
+# Rational::+
+# Rational::-
+# Rational::*
+# Rational::/
+# Rational::**
+# Rational::%
+# Rational::divmod
+# Rational::abs
+# Rational::<=>
+# Rational::to_i
+# Rational::to_f
+# Rational::to_s
+#
+# Integer::gcd
+# Integer::lcm
+# Integer::gcdlcm
+# Integer::to_r
+#
+# Fixnum::**
+# Bignum::**
+#
+#
+
+def Rational(a, b = 1)
+ if a.kind_of?(Rational) && b == 1
+ a
+ else
+ Rational.reduce(a, b)
+ end
+end
+
+class Rational < Numeric
+ def Rational.reduce(num, den = 1)
+ if den < 0
+ num = -num
+ den = -den
+ end
+ gcd = num.gcd(den)
+ num = num.div(gcd)
+ den = den.div(gcd)
+ if den == 1 && defined?(Unify)
+ num
+ else
+ new!(num, den)
+ end
+ end
+
+ def Rational.new!(num, den = 1)
+ new(num, den)
+ end
+
+ def initialize(num, den)
+ if den < 0
+ num = -num
+ den = -den
+ end
+ if num.kind_of?(Integer) and den.kind_of?(Integer)
+ @numerator = num
+ @denominator = den
+ else
+ @numerator = num.to_i
+ @denoninator = den.to_i
+ end
+ end
+
+ def + (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ num_a = a.numerator * @denominator
+ Rational(num + num_a, @denominator * a.denominator)
+ elsif a.kind_of?(Integer)
+ self + Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) + a
+ else
+ x , y = a.coerce(self)
+ x + y
+ end
+ end
+
+ def - (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ num_a = a.numerator * @denominator
+ Rational(num - num_a, @denominator*a.denominator)
+ elsif a.kind_of?(Integer)
+ self - Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) - a
+ else
+ x , y = a.coerce(self)
+ x - y
+ end
+ end
+
+ def * (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.numerator
+ den = @denominator * a.denominator
+ Rational(num, den)
+ elsif a.kind_of?(Integer)
+ self * Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) * a
+ else
+ x , y = a.coerce(self)
+ x * y
+ end
+ end
+
+ def / (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ den = @denominator * a.numerator
+ Rational(num, den)
+ elsif a.kind_of?(Integer)
+ self / Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) / a
+ else
+ x , y = a.coerce(self)
+ x / y
+ end
+ end
+
+ def ** (other)
+ if other.kind_of?(Rational)
+ Float(self) ** other
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = @numerator ** other
+ den = @denominator ** other
+ elsif other < 0
+ num = @denominator ** -other
+ den = @numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x , y = other.coerce(self)
+ x ** y
+ end
+ end
+
+ def % (other)
+ value = (self / other).to_i
+ return self - other * value
+ end
+
+ def divmod(other)
+ value = (self / other).to_i
+ return value, self - other * value
+ end
+
+ def abs
+ if @numerator > 0
+ Rational.new!(@numerator, @denominator)
+ else
+ Rational.new!(-@numerator, @denominator)
+ end
+ end
+
+ def <=> (other)
+ if other.kind_of?(Rational)
+ num = @numerator * other.denominator
+ num_a = other.numerator * @denominator
+ v = num - num_a
+ if v > 0
+ return 1
+ elsif v < 0
+ return -1
+ else
+ return 0
+ end
+ elsif other.kind_of?(Integer)
+ return self <=> Rational.new!(other, 1)
+ elsif other.kind_of?(Float)
+ return Float(self) <=> other
+ else
+ x , y = other.coerce(self)
+ return x <=> y
+ end
+ end
+
+ def coerce(other)
+ if other.kind_of?(Float)
+ return other, self.to_f
+ elsif other.kind_of?(Integer)
+ return Rational.new!(other, 1), self
+ else
+ super
+ end
+ end
+
+ def to_i
+ Integer(@numerator.div(@denominator))
+ end
+
+ def to_f
+ @numerator.to_f/@denominator.to_f
+ end
+
+ def to_s
+ if @denominator == 1
+ @numerator.to_s
+ else
+ @numerator.to_s+"/"+@denominator.to_s
+ end
+ end
+
+ def to_r
+ self
+ end
+
+ def hash
+ @numerator ^ @denominator
+ end
+
+ attr :numerator
+ attr :denominator
+
+ private :initialize
+end
+
+class Integer
+ def numerator
+ self
+ end
+
+ def denomerator
+ 1
+ end
+
+ def to_r
+ Rational(self, 1)
+ end
+
+ def gcd(int)
+ a = self.abs
+ b = int.abs
+
+ a, b = b, a if a < b
+
+ while b != 0
+ void, a = a.divmod(b)
+ a, b = b, a
+ end
+ return a
+ end
+
+ def lcm(int)
+ a = self.abs
+ b = int.abs
+ gcd = a.gcd(b)
+ (a.div(gcd)) * b
+ end
+
+ def gcdlcm(int)
+ a = self.abs
+ b = int.abs
+ gcd = a.gcd(b)
+ return gcd, (a.div(gcd)) * b
+ end
+
+end
+
+class Fixnum
+ alias div! /;
+ def div(other)
+ if other.kind_of?(Fixnum)
+ self.div!(other)
+ elsif other.kind_of?(Bignum)
+ x, y = other.coerce(self)
+ x.div!(y)
+ else
+ x, y = other.coerce(self)
+ x / y
+ end
+ end
+
+# alias divmod! divmod
+
+ if not defined? Complex
+ alias power! **;
+ end
+
+# def rdiv(other)
+# if other.kind_of?(Fixnum)
+# Rational(self, other)
+# elsif
+# x, y = other.coerce(self)
+# if defined?(x.div())
+# x.div(y)
+# else
+# x / y
+# end
+# end
+ # end
+
+ def rdiv(other)
+ Rational.new!(self,1) / other
+ end
+
+ def rpower (other)
+ if other >= 0
+ self.power!(other)
+ else
+ Rational.new!(self,1)**other
+ end
+ end
+
+ if not defined? Complex
+ alias ** rpower
+ end
+end
+
+class Bignum
+ alias div! /;
+ alias div /;
+ alias divmod! divmod
+
+ if not defined? power!
+ alias power! **
+ end
+
+ def rdiv(other)
+ Rational.new!(self,1) / other
+ end
+
+ def rpower (other)
+ if other >= 0
+ self.power!(other)
+ else
+ Rational.new!(self, 1)**other
+ end
+ end
+
+ if not defined? Complex
+ alias ** rpower
+ end
+
+end
+
diff --git a/lib/safe.rb b/lib/safe.rb
new file mode 100644
index 0000000000..7c95555495
--- /dev/null
+++ b/lib/safe.rb
@@ -0,0 +1,78 @@
+# this is a safe-mode for ruby, which is still incomplete.
+
+unless defined? SecurityError
+ class SecurityError<Exception
+ end
+end
+
+module Restricted
+
+ printf STDERR, "feel free for some warnings:\n" if $VERBOSE
+ module Bastion
+ include Restricted
+ extend Restricted
+ BINDING = binding
+ def Bastion.to_s; "main" end
+ end
+
+ class R_File<File
+ NG_FILE_OP = []
+ def R_File.open(*args)
+ raise SecurityError, "can't use File.open() in safe mode" #'
+ end
+ end
+
+ IO = nil
+ File = R_File
+ FileTest = nil
+ Dir = nil
+ ObjectSpace = nil
+
+ def eval(string)
+ begin
+ super(string, Bastion::BINDING)
+ rescue
+ $@ = caller
+ raise
+ end
+ end
+ module_function :eval
+
+ DEFAULT_SECURITY_MANAGER = Object.new
+
+ def Restricted.set_securuty_manager(sec_man)
+ if @sec_man
+ raise SecurityError, "cannot change security manager"
+ end
+ @sec_man = sec_man
+ end
+
+ def Restricted.securuty_manager
+ return @sec_man if @sec_man
+ return DEFAULT_SECURITY_MANAGER
+ end
+
+ for cmd in ["test", "require", "load", "open", "system"]
+ eval format("def DEFAULT_SECURITY_MANAGER.%s(*args)
+ raise SecurityError, \"can't use %s() in safe mode\"
+ end", cmd, cmd) #'
+ eval format("def %s(*args)
+ Restricted.securuty_manager.%s(*args)
+ end", cmd, cmd)
+ end
+
+ def `(arg) #`
+ Restricted.securuty_manager.send(:`, arg) #`)
+ end
+
+ def DEFAULT_SECURITY_MANAGER.`(arg) #`
+ raise SecurityError, "can't use backquote(``) in safe mode"
+ end
+end
+
+if $DEBUG
+ p eval("File.open('/dev/null')")
+ p Restricted.eval("self")
+ p Restricted.eval("open('/dev/null')")
+ p Restricted.eval("File.open('/dev/null')")
+end
diff --git a/lib/thread.rb b/lib/thread.rb
new file mode 100644
index 0000000000..c3347b60b4
--- /dev/null
+++ b/lib/thread.rb
@@ -0,0 +1,153 @@
+#
+# thread.rb - thread support classes
+# $Date: 1996/05/21 09:29:21 $
+# by Yukihiro Matsumoto <matz@caelum.co.jp>
+#
+
+unless defined? Thread
+ fail "Thread not available for this ruby interpreter"
+end
+
+unless defined? ThreadError
+ class ThreadError<Exception
+ end
+end
+
+class Mutex
+ def initialize
+ @waiting = []
+ @locked = FALSE;
+ end
+
+ def locked?
+ @locked
+ end
+
+ def try_lock
+ Thread.exclusive do
+ if not @locked
+ @locked=TRUE
+ return TRUE
+ end
+ end
+ FALSE
+ end
+
+ def lock
+ while not try_lock
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ end
+
+ def unlock
+ @locked = FALSE
+ if w = @waiting.shift
+ w.run
+ end
+ end
+
+ def synchronize
+ begin
+ lock
+ yield
+ ensure
+ unlock
+ end
+ end
+end
+
+class SharedMutex<Mutex
+ def initialize
+ @locking = nil
+ @num_locks = 0;
+ super
+ end
+ def try_lock
+ if @locking == Thread.current
+ @num_locks += 1
+ return TRUE
+ end
+ if super
+ @num_locks = 1
+ @locking = Thread.current
+ TRUE
+ else
+ FALSE
+ end
+ end
+ def unlock
+ unless @locking == Thread.current
+ raise ThreadError, "cannot release shared mutex"
+ end
+ @num_locks -= 1
+ if @num_locks == 0
+ @locking = nil
+ super
+ end
+ end
+end
+
+class Queue
+ def initialize
+ @que = []
+ @waiting = []
+ end
+
+ def push(obj)
+ @que.push obj
+ if t = @waiting.shift
+ t.run
+ end
+ end
+
+ def pop non_block=FALSE
+ if @que.length == 0
+ raise ThreadError, "queue empty" if non_block
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @que.shift
+ end
+
+ def empty?
+ @que.length == 0
+ end
+
+ def length
+ @que.length
+ end
+end
+
+class Condition
+ def initialize
+ @waiting = []
+ end
+
+ def wait(mut)
+ Thread.exclusive do
+ mut.unlock
+ @waiting.push Thread.current
+ end
+ Thread.sleep
+ mut.lock
+ end
+
+ def signal
+ th = nil
+ Thread.exclusive do
+ th = @waiting.pop
+ end
+ th.run
+ end
+
+ def broadcast
+ w = @waiting
+ Thread.exclusive do
+ th = []
+ end
+ for th in w
+ th.run
+ end
+ end
+end
diff --git a/lib/tk.rb b/lib/tk.rb
index 9c61269881..5a725aac11 100644
--- a/lib/tk.rb
+++ b/lib/tk.rb
@@ -3,477 +3,10 @@
# $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
+if defined? Thread and $tk_thread_safe
+ require "tkthcore"
+else
+ require "tkcore"
end
module TkSelection
@@ -550,11 +83,11 @@ module TkWinfo
def winfo_depth(window)
TkWinfo.depth self
end
- def TkWinfo.exists(window)
+ def TkWinfo.exist?(window)
bool(tk_call('winfo', 'exists', window.path))
end
- def winfo_exists(window)
- TkWinfo.exists self
+ def winfo_exist?(window)
+ TkWinfo.exist? self
end
def TkWinfo.fpixels(window, number)
number(tk_call('winfo', 'fpixels', window.path, number))
@@ -580,11 +113,11 @@ module TkWinfo
def winfo_id(window)
TkWinfo.id self
end
- def TkWinfo.ismapped(window)
+ def TkWinfo.mapped?(window)
bool(tk_call('winfo', 'ismapped', window.path))
end
- def winfo_ismapped(window)
- TkWinfo.ismapped self
+ def winfo_mapped?(window)
+ TkWinfo.mapped? self
end
def TkWinfo.parent(window)
window(tk_call('winfo', 'parent', window.path))
@@ -742,7 +275,7 @@ module TkPack
include Tk
extend Tk
def configure(win, *args)
- if args[-1].is_kind_of(Hash)
+ if args[-1].kind_of?(Hash)
keys = args.pop
end
wins = [win.epath]
@@ -780,7 +313,7 @@ module TkOption
module_function :add, :clear, :get, :readfile
end
-class TkObject:TkKernel
+class TkObject<TkKernel
include Tk
def path
@@ -822,16 +355,16 @@ class TkObject:TkKernel
configure slot, install_cmd(value)
end
- def bind(context, cmd=Proc.new)
- _bind path, context, cmd
+ def bind(context, cmd=Proc.new, args=nil)
+ _bind path, context, cmd, args
end
end
-class TkWindow:TkObject
+class TkWindow<TkObject
$tk_window_id = "w00000"
def initialize(parent=nil, keys=nil)
id = $tk_window_id
- $tk_window_id = $tk_window_id.next
+ $tk_window_id = $tk_window_id.succ
if !parent or parent == Tk.root
@path = format(".%s", id);
else
@@ -880,7 +413,7 @@ class TkWindow:TkObject
return val
end
else
- fail 'wrong # of args'
+ fail ArgumentError, 'wrong # of args'
end
end
@@ -913,7 +446,7 @@ class TkWindow:TkObject
end
end
-class TkRoot:TkWindow
+class TkRoot<TkWindow
include Wm
def TkRoot.new
return $tk_root if $tk_root
@@ -926,7 +459,7 @@ class TkRoot:TkWindow
$tk_window_list['.'] = $tk_root
end
-class TkToplevel:TkWindow
+class TkToplevel<TkWindow
include Wm
def initialize(parent=nil, screen=nil, classname=nil)
@screen = screen if screen
@@ -942,20 +475,21 @@ class TkToplevel:TkWindow
end
end
-class TkFrame:TkWindow
+class TkFrame<TkWindow
def create_self
tk_call 'frame', @path
end
end
-class TkLabel:TkWindow
+class TkLabel<TkWindow
def create_self
tk_call 'label', @path
end
def textvariable(v)
- vn = @path + v.id2name
+ v = v.id2name unless v.kind_of "String"
+ vn = @path + v
vset = format("global {%s}; set {%s}", vn, vn)
- tk_call vset, eval(v.id2name).inspect
+ tk_call vset, eval(v).inspect
trace_var v, proc{|val|
tk_call vset, val.inspect
}
@@ -963,7 +497,7 @@ class TkLabel:TkWindow
end
end
-class TkButton:TkLabel
+class TkButton<TkLabel
def create_self
tk_call 'button', @path
end
@@ -975,7 +509,7 @@ class TkButton:TkLabel
end
end
-class TkRadioButton:TkButton
+class TkRadioButton<TkButton
def create_self
tk_call 'radiobutton', @path
end
@@ -986,8 +520,13 @@ class TkRadioButton:TkButton
tk_send 'select'
end
def variable(v)
- vn = v.id2name; vn =~ /^./
- vn = 'btns_selected_' + $'
+ v = v.id2name unless v.kind_of "String"
+ if v =~ /^\$/
+ v = $'
+ else
+ fail ArgumentError, "variable must be global(%s)", v
+ end
+ vn = 'btns_selected_' + v
trace_var v, proc{|val|
tk_call 'set', vn, val
}
@@ -1004,7 +543,7 @@ class TkRadioButton:TkButton
end
end
-class TkCheckButton:TkRadioButton
+class TkCheckButton<TkRadioButton
def create_self
tk_call 'checkbutton', @path
end
@@ -1013,13 +552,13 @@ class TkCheckButton:TkRadioButton
end
end
-class TkMessage:TkLabel
+class TkMessage<TkLabel
def create_self
tk_call 'message', @path
end
end
-class TkScale:TkWindow
+class TkScale<TkWindow
def create_self
tk_call 'scale', path
end
@@ -1041,11 +580,23 @@ class TkScale:TkWindow
end
end
-class TkScrollbar:TkWindow
+class TkScrollbar<TkWindow
def create_self
tk_call 'scrollbar', path
end
+ def delta(deltax=None, deltay=None)
+ number(tk_send('delta', deltax, deltay))
+ end
+
+ def fraction(x=None, y=None)
+ number(tk_send('fraction', x, y))
+ end
+
+ def identify(x=None, y=None)
+ tk_send('fraction', x, y)
+ end
+
def get
ary1 = tk_send('get', path).split
ary2 = []
@@ -1061,7 +612,7 @@ class TkScrollbar:TkWindow
end
# abstract class for Text and Listbox
-class TkTextWin:TkWindow
+class TkTextWin<TkWindow
def bbox(index)
tk_send 'bbox', index
end
@@ -1091,7 +642,7 @@ class TkTextWin:TkWindow
end
end
-class TkListbox:TkTextWin
+class TkListbox<TkTextWin
def create_self
tk_call 'listbox', path
end
@@ -1119,7 +670,7 @@ class TkListbox:TkTextWin
end
end
-class TkMenu:TkWindow
+class TkMenu<TkWindow
def create_self
tk_call 'menu', path
end
@@ -1158,7 +709,7 @@ class TkMenu:TkWindow
end
end
-class TkMenubutton:TkLabel
+class TkMenubutton<TkLabel
def create_self
tk_call 'menubutton', path
end
@@ -1181,7 +732,7 @@ module TkComposite
def delegate(option, *wins)
@delegates = {} if not @delegates
@delegates['DEFAULT'] = @frame
- if option.is_kind_of? String
+ if option.kind_of? String
@delegates[option] = wins
else
for i in option
diff --git a/lib/tkcanvas.rb b/lib/tkcanvas.rb
index 33b28e3eff..b0ae8b1daa 100644
--- a/lib/tkcanvas.rb
+++ b/lib/tkcanvas.rb
@@ -5,12 +5,12 @@
require "tk"
-class TkCanvas:TkWindow
+class TkCanvas<TkWindow
def create_self
tk_call 'canvas', path
end
def tagid(tag)
- if tag.is_kind_of?(TkcItem)
+ if tag.kind_of?(TkcItem)
tag.id
else
tag
@@ -89,6 +89,9 @@ class TkCanvas:TkWindow
def move(tag, x, y)
tk_send 'move', tagid(tag), x, y
end
+ def itemtype(tag)
+ tk_send 'type', tagid(tag)
+ end
def postscript(keys=None)
tk_call "pack", *hash_kv(keys)
end
@@ -115,9 +118,9 @@ class TkCanvas:TkWindow
end
end
-class TkcItem:TkObject
+class TkcItem<TkObject
def initialize(parent, *args)
- if not parent.is_kind_of?(TkCanvas)
+ if not parent.kind_of?(TkCanvas)
fail format("%s need to be TkCanvas", parent.inspect)
end
@c = parent
@@ -137,7 +140,7 @@ class TkcItem:TkObject
end
def configure(slot, value)
- tk_call path, 'itemconfigure', id, "-#{slot}", value
+ tk_call path, 'itemconfigure', @id, "-#{slot}", value
end
def addtag(tag)
@@ -185,59 +188,59 @@ class TkcItem:TkObject
def scale(xorigin, yorigin, xscale, yscale)
@c.scale @id, xorigin, yorigin, xscale, yscale
end
- def type
- @c.type @id
+ def itemtype
+ @c.itemtype @id
end
def destroy
tk_call path, 'delete', @id
end
end
-class TkcArc:TkcItem
+class TkcArc<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'arc', *args)
end
end
-class TkcBitmap:TkcItem
+class TkcBitmap<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'bitmap', *args)
end
end
-class TkcImage:TkcItem
+class TkcImage<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'image', *args)
end
end
-class TkcLine:TkcItem
+class TkcLine<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'line', *args)
end
end
-class TkcOval:TkcItem
+class TkcOval<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'oval', *args)
end
end
-class TkcPolygon:TkcItem
+class TkcPolygon<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'polygon', *args)
end
end
-class TkcText:TkcItem
+class TkcText<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'text', *args)
end
end
-class TkcWindow:TkcItem
+class TkcWindow<TkcItem
def create_self(*args)
tk_call(@path, 'create', 'window', *args)
end
end
-class TkcGroup:TkcItem
+class TkcGroup<TkcItem
$tk_group_id = 'tkg00000'
def create_self(*args)
@id = $tk_group_id
- $tk_group_id = $tk_group_id.next
+ $tk_group_id = $tk_group_id.succ
end
def add(*tags)
@@ -262,20 +265,20 @@ class TkcGroup:TkcItem
end
-class TkImage:TkObject
+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_image_id = $tk_image_id.succ
tk_call 'image', @type, @path, *hash_kv(keys)
end
def height
number(tk_call('image', 'height', @path))
end
- def type
+ def itemtype
tk_call('image', 'type', @path)
end
def width
@@ -290,14 +293,14 @@ class TkImage:TkObject
end
end
-class TkBitmapImage:TkImage
+class TkBitmapImage<TkImage
def initialize(*args)
@type = 'bitmap'
super
end
end
-class TkPhotoImage:TkImage
+class TkPhotoImage<TkImage
def initialize(*args)
@type = 'bitmap'
super
diff --git a/lib/tkcore.rb b/lib/tkcore.rb
new file mode 100644
index 0000000000..df4af669ba
--- /dev/null
+++ b/lib/tkcore.rb
@@ -0,0 +1,521 @@
+#
+# tkcore.rb - Tk interface modue without thread
+# $Date: 1996/11/09 22:51:15 $
+# by Yukihiro Matsumoto <matz@caelum.co.jp>
+
+require "tkutil"
+if defined? Thread
+ require "thread"
+end
+
+module Tk
+ include TkUtil
+ extend Tk
+
+ wish_path = nil
+ ENV['PATH'].split(":").each {|path|
+ for wish in ['wish4.2', 'wish4.1', 'wish4.0', 'wish']
+ if File.exist? path+'/'+wish
+ wish_path = path+'/'+wish
+ break
+ end
+ break if wish_path
+ end
+ }
+ fail 'can\'t find wish' if not wish_path
+
+ def Tk.tk_exit
+ if not PORT.closed?
+ PORT.print "exit\n"
+ PORT.close
+ end
+ end
+
+ PORT = open(format("|%s -n %s", wish_path, File.basename($0)), "w+");
+ trap "EXIT", proc{Tk.tk_exit}
+ trap "PIPE", ""
+
+ 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 rb_ans args {
+ if [catch "$args" var] {puts "!$var"} {puts "=$var@@"}
+ 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)
+ if cmd == nil
+ READABLE.delete port
+ else
+ READABLE.push port
+ end
+ READ_CMD[port] = cmd
+ end
+
+ WRITABLE = []
+ WRITE_CMD = {}
+ def file_writable(port, cmd)
+ if cmd == nil
+ WRITABLE.delete port
+ else
+ WRITABLE.push port
+ end
+ WRITE_CMD[port] = cmd
+ end
+ module_function :file_readable, :file_writable
+
+ file_readable PORT, proc {
+ line = PORT.gets
+ exit if not line
+ Tk.dispatch(line.chop!)
+ }
+
+ 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|
+ next if s == None
+ if s.kind_of?(Hash)
+ s = hash_kv(s).join(" ")
+ else
+ if not s
+ s = "0"
+ elsif s == TRUE
+ s = "1"
+ elsif s.kind_of?(TkObject)
+ s = s.path
+ else
+ s = s.to_s
+ s.gsub!(/[{}]/, '\\\\\0')
+ end
+ "{#{s}}"
+ end
+ }
+ str = args.join(" ")
+ print str, "\n" if $DEBUG
+ tk_write 'rb_ans %s', str
+ while PORT.gets
+ print $_ if $DEBUG
+ $_.chop!
+ if /^=(.*)@@$/
+ val = $1
+ break
+ elsif /^=/
+ val = $' + "\n"
+ while TRUE
+ PORT.readline
+ if ~/@@$/
+ val += $'
+ return val
+ else
+ val += $_
+ end
+ end
+ elsif /^!/
+ $@ = error_at
+ msg = $'
+ if msg =~ /unknown option "-(.*)"/
+ fail NameError, 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 PORT.closed?
+# 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 = 0
+ def install_cmd(cmd)
+ return '' if cmd == '' # uninstall cmd
+ id = format("c%.4d", $tk_cmdid)
+ $tk_cmdid += 1
+ $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, args=nil)
+ if args
+ id = install_cmd(proc{|arg|
+ TkUtil.eval_cmd cmd, *arg
+ })
+ id + " " + args
+ else
+ id = install_cmd(proc{|arg|
+ TkUtil.eval_cmd cmd, Event.new(*arg)
+ })
+ id + " %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y"
+ end
+ end
+
+ def _bind(path, context, cmd, args=nil)
+ begin
+ id = install_bind(cmd, args)
+ 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, args=nil)
+ _bind 'all', context, cmd, args
+ end
+
+ def pack(*args)
+ TkPack.configure *args
+ end
+
+ $tk_cmdtbl = {}
+
+ def after(ms, cmd=Proc.new)
+ myid = format("c%.4d", $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
+ ensure
+ Tk.tk_exit
+ end
+ end
+
+ def root
+ $tk_root
+ end
+
+ def bell
+ tk_call 'bell'
+ end
+ module_function :after, :update, :dispatch, :mainloop, :root, :bell
+
+ 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
diff --git a/lib/tkentry.rb b/lib/tkentry.rb
index dbd848d0ca..9a03c34058 100644
--- a/lib/tkentry.rb
+++ b/lib/tkentry.rb
@@ -5,7 +5,7 @@
require 'tk.rb'
-class TkEntry:TkLabel
+class TkEntry<TkLabel
def create_self
tk_call 'entry', @path
end
diff --git a/lib/tkscrollbox.rb b/lib/tkscrollbox.rb
new file mode 100644
index 0000000000..b8dbe9b236
--- /dev/null
+++ b/lib/tkscrollbox.rb
@@ -0,0 +1,27 @@
+#
+# tkscrollbox.rb - Tk Listbox with Scrollbar
+# as an example of Composite Widget
+# $Date: 1995/12/12 18:21:01 $
+# by Yukihiro Matsumoto <matz@caelum.co.jp>
+
+require 'tk.rb'
+
+class TkScrollbox<TkListbox
+ include TkComposite
+ def initialize_composite
+ list = TkListbox.new(@frame)
+ scroll = TkScrollbar.new(@frame)
+ @path = list.path
+
+ list.configure 'yscroll', scroll.path+" set"
+ list.pack 'side'=>'left','fill'=>'both','expand'=>'yes'
+ scroll.configure 'command', list.path+" yview"
+ scroll.pack 'side'=>'right','fill'=>'y'
+
+ delegate('DEFALUT', list)
+ delegate('foreground', list, scroll)
+ delegate('background', list, scroll)
+ delegate('borderwidth', @frame)
+ delegate('relief', @frame)
+ end
+end
diff --git a/lib/tktext.rb b/lib/tktext.rb
index e7a2be950f..55e396c497 100644
--- a/lib/tktext.rb
+++ b/lib/tktext.rb
@@ -5,7 +5,7 @@
require 'tk.rb'
-class TkText:TkTextWin
+class TkText<TkTextWin
include Scrollable
def create_self
tk_call 'text', @path
@@ -77,16 +77,16 @@ class TkText:TkTextWin
end
end
-class TkTextTag:TkObject
+class TkTextTag<TkObject
$tk_text_tag = 'tag0000'
def initialize(parent)
- if not parent.is_kind_of?(TkText)
+ if not parent.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
+ $tk_text_tag = $tk_text_tag.succ
@t._addtag id, self
end
def id
@@ -116,16 +116,16 @@ class TkTextTag:TkObject
end
end
-class TkTextMark:TkObject
+class TkTextMark<TkObject
$tk_text_mark = 'mark0000'
def initialize(parent, index)
- if not parent.is_kind_of?(TkText)
+ if not parent.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_text_mark = $tk_text_mark.succ
tk_call @t, 'set', @id, index
@t._addtag id, self
end
@@ -143,9 +143,9 @@ class TkTextMark:TkObject
alias destroy unset
end
-class TkTextWindow:TkObject
+class TkTextWindow<TkObject
def initialize(parent, index, *args)
- if not parent.is_kind_of?(TkText)
+ if not parent.kind_of?(TkText)
fail format("%s need to be TkText", parent.inspect)
end
@t = parent
diff --git a/lib/tkthcore.rb b/lib/tkthcore.rb
new file mode 100644
index 0000000000..adbad775d0
--- /dev/null
+++ b/lib/tkthcore.rb
@@ -0,0 +1,546 @@
+#
+# tkthcore.rb - Tk interface modue using thread
+# $Date: 1996/11/09 22:49:15 $
+# by Yukihiro Matsumoto <matz@caelum.co.jp>
+
+require "tkutil"
+require "thread"
+
+module Tk
+ include TkUtil
+ extend Tk
+
+ def Tk.tk_exit
+ if not PORT.closed?
+ tk_write "exit"
+ PORT.close
+ end
+ end
+
+ trap "EXIT", proc{Tk.tk_exit}
+ trap "PIPE", ''
+
+ wish_path = nil
+ ENV['PATH'].split(":").each {|path|
+ for wish in ['wish4.2', 'wish4.1', 'wish4.0', 'wish']
+ if File.exist? path+'/'+wish
+ wish_path = path+'/'+wish
+ break
+ end
+ break if wish_path
+ end
+ }
+ fail 'can\'t find wish' if not wish_path
+
+ # mark for non-given arguments
+ None = Object.new
+ def None.to_s
+ 'None'
+ end
+
+ Qin = Queue.new
+ Qout = Queue.new
+ Qwish = Queue.new
+ Qcmd = Queue.new
+ PORT = open(format("|%s -n %s", wish_path, File.basename($0)), "w+");
+
+ $tk_not_init = TRUE
+
+ def Tk.init
+ $tk_not_init = FALSE
+ Thread.start do
+ loop do
+ while line = PORT.gets
+ line.chop!
+ if line =~ /^[=!]/
+ Qwish.push line
+ else
+ Qcmd.push line
+ end
+ end
+ exit
+ end
+ end
+
+ Thread.start do
+ ary = [PORT]
+ loop do
+ str = Qin.pop
+ print str, "\n" if $DEBUG
+ tk_write 'if [catch {%s} var] {puts "!$var"} {puts "=$var@@"};flush stdout', str
+ Qout.push(tk_recv)
+ end
+ end
+ end
+
+ $tk_event_queue = []
+ def tk_recv()
+ val = nil
+ $_ = Qwish.pop
+ loop do
+ if /^=(.*)@@$/
+ val = $1
+ break
+ elsif /^=/
+ val = $' + "\n"
+ while TRUE
+ PORT.readline
+ if ~/@@$/
+ val += $'
+ return val
+ else
+ v>al += $_
+ end
+ end
+ elsif /^!/
+ $@ = error_at
+ msg = $'
+ if msg =~ /unknown option "-(.*)"/
+ fail NameError, format("undefined method `%s' for %s(%s)", $1, self, self.type) #`'
+ else
+ fail format("%s - %s", self.type, msg)
+ end
+ end
+ Qcmd.push line
+ end
+
+ fail 'wish closed' if PORT.closed?
+# tk_split_list(val)
+ val
+ end
+
+ def tk_call(*args)
+ Tk.init if $tk_not_init
+ args = args.collect{|s|
+ next if s == None
+ if s.kind_of?(Hash)
+ s = hash_kv(s).join(" ")
+ else
+ if not s
+ s = "0"
+ elsif s == TRUE
+ s = "1"
+ elsif s.kind_of?(TkObject)
+ s = s.path
+ else
+ s = s.to_s
+ s.gsub!(/[{}]/, '\\\\\0')
+ end
+ "{#{s}}"
+ end
+ }
+ str = args.join(" ")
+ Qin.push str
+ return Qout.pop
+ end
+
+ def tk_write(*args)
+ PORT.printf *args; PORT.print "\n"
+ PORT.flush
+ end
+ module_function :tk_write, :tk_recv
+ 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'
+
+ READ_TH = {}
+ def file_readable(port, cmd)
+ if cmd == nil
+ if READ_TH[port].has_key?
+ READ_TH[port].exit
+ READ_TH[port] = nil
+ end
+ else
+ READ_TH[port] = Thread.start{
+ loop do
+ TkUtil.eval_cmd cmd
+ end
+ }
+ end
+ end
+
+ WRITE_TH = {}
+ def file_writable(port, cmd)
+ if cmd == nil
+ if WRITE_TH[port].has_key?
+ WRITE_TH[port].exit
+ end
+ else
+ WRITE_TH[port] = Thread.start{
+ loop do
+ TkUtil.eval_cmd cmd
+ end
+ }
+ end
+ end
+ module_function :file_readable, :file_writable
+
+ 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 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
+ module_function :dispatch
+
+ def error_at
+ n = 1
+ while c = caller(n)
+ break if c !~ /tk\.rb:/
+ n+=1
+ end
+ c
+ end
+
+ 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
+
+ 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 = 0
+ def install_cmd(cmd)
+ return '' if cmd == '' # uninstall cmd
+ id = format("c%.4d", $tk_cmdid)
+ $tk_cmdid += 1
+ $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, args=nil)
+ if args
+ id = install_cmd(proc{|arg|
+ TkUtil.eval_cmd cmd, *arg
+ })
+ id + " " + args
+ else
+ id = install_cmd(proc{|arg|
+ TkUtil.eval_cmd cmd, Event.new(*arg)
+ })
+ id + " %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y"
+ end
+ end
+
+ def _bind(path, context, cmd, args=nil)
+ begin
+ id = install_bind(cmd, args)
+ 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, args=nil)
+ _bind 'all', context, cmd, args
+ end
+
+ def pack(*args)
+ TkPack.configure *args
+ end
+
+ $tk_cmdtbl = {}
+
+ Qafter = Queue.new
+ def after(ms, cmd=Proc.new)
+ unless $tk_after_thread
+ $tk_after_thread = Thread.start{
+ loop do
+ cmd = Qafter.pop
+ TkUtil.eval_cmd cmd
+ end
+ }
+ end
+ Thread.start do
+ sleep Float(ms)/1000
+ Qafter.push cmd
+ end
+ end
+
+ def update(idle=nil)
+ if idle
+ tk_call 'update', 'idletasks'
+ else
+ tk_call 'update'
+ end
+ end
+
+ def root
+ $tk_root
+ end
+
+ def bell
+ tk_call 'bell'
+ end
+
+ def mainloop
+ begin
+ tk_call 'after', 'idle', 'wm deiconify .'
+ loop do
+ dispatch Qcmd.pop
+ end
+ ensure
+ Tk.tk_exit
+ end
+ end
+ module_function :after, :update, :dispatch, :mainloop, :root, :bell
+
+ 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
diff --git a/main.c b/main.c
index 3630f8aa7b..5bc0ecf62f 100644
--- a/main.c
+++ b/main.c
@@ -8,6 +8,10 @@
************************************************/
+#ifdef DJGPP
+unsigned int _stklen = 0x100000;
+#endif
+
void
main(argc, argv, envp)
int argc;
diff --git a/math.c b/math.c
index 41a4232083..db4e4afc7e 100644
--- a/math.c
+++ b/math.c
@@ -6,7 +6,7 @@
$Date: 1994/11/01 08:28:03 $
created at: Tue Jan 25 14:12:56 JST 1994
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -15,12 +15,13 @@
VALUE mMath;
VALUE float_new();
+VALUE f_float();
#define Need_Float(x) \
if (FIXNUM_P(x)) {\
(x) = (struct RFloat*)float_new((double)FIX2INT(x));\
} else {\
- Check_Type(x, T_FLOAT);\
+ (x) = (struct RFloat*)f_float(x, x);\
}
#define Need_Float2(x,y) {\
@@ -34,7 +35,7 @@ math_atan2(obj, x, y)
struct RFloat *x, *y;
{
Need_Float2(x, y);
- return float_new(atan2(x->value, x->value));
+ return float_new(atan2(x->value, y->value));
}
static VALUE
@@ -101,7 +102,7 @@ math_sqrt(obj, x)
{
Need_Float(x);
- if (x->value < 0.0) Fail("square root for negative number");
+ if (x->value < 0.0) ArgError("square root for negative number");
return float_new(sqrt(x->value));
}
diff --git a/missing/flock.c b/missing/flock.c
new file mode 100644
index 0000000000..a4a9544b56
--- /dev/null
+++ b/missing/flock.c
@@ -0,0 +1,90 @@
+#include "config.h"
+
+#if defined(HAVE_LOCKF)
+
+#include <unistd.h>
+#include <errno.h>
+
+/* Emulate flock() with lockf() or fcntl(). This is just to increase
+ portability of scripts. The calls might not be completely
+ interchangeable. What's really needed is a good file
+ locking module.
+*/
+
+# ifndef F_ULOCK
+# define F_ULOCK 0 /* Unlock a previously locked region */
+# endif
+# ifndef F_LOCK
+# define F_LOCK 1 /* Lock a region for exclusive use */
+# endif
+# ifndef F_TLOCK
+# define F_TLOCK 2 /* Test and lock a region for exclusive use */
+# endif
+# ifndef F_TEST
+# define F_TEST 3 /* Test a region for other processes locks */
+# endif
+
+/* These are the flock() constants. Since this sytems doesn't have
+ flock(), the values of the constants are probably not available.
+*/
+# ifndef LOCK_SH
+# define LOCK_SH 1
+# endif
+# ifndef LOCK_EX
+# define LOCK_EX 2
+# endif
+# ifndef LOCK_NB
+# define LOCK_NB 4
+# endif
+# ifndef LOCK_UN
+# define LOCK_UN 8
+# endif
+
+int
+flock(fd, operation)
+ int fd;
+ int operation;
+{
+ int i;
+ switch (operation) {
+
+ /* LOCK_SH - get a shared lock */
+ case LOCK_SH:
+ /* LOCK_EX - get an exclusive lock */
+ case LOCK_EX:
+ i = lockf (fd, F_LOCK, 0);
+ break;
+
+ /* LOCK_SH|LOCK_NB - get a non-blocking shared lock */
+ case LOCK_SH|LOCK_NB:
+ /* LOCK_EX|LOCK_NB - get a non-blocking exclusive lock */
+ case LOCK_EX|LOCK_NB:
+ i = lockf (fd, F_TLOCK, 0);
+ if (i == -1)
+ if ((errno == EAGAIN) || (errno == EACCES))
+ errno = EWOULDBLOCK;
+ break;
+
+ /* LOCK_UN - unlock */
+ case LOCK_UN:
+ i = lockf (fd, F_ULOCK, 0);
+ break;
+
+ /* Default - can't decipher operation */
+ default:
+ i = -1;
+ errno = EINVAL;
+ break;
+ }
+ return i;
+}
+#else
+int
+flock(fd, operation)
+ int fd;
+ int operation;
+{
+ rb_notimplement();
+ return -1;
+}
+#endif
diff --git a/missing/mkdir.c b/missing/mkdir.c
index 5225e586d9..b581a5e467 100644
--- a/missing/mkdir.c
+++ b/missing/mkdir.c
@@ -1,4 +1,4 @@
-/*
+*
* Written by Robert Rother, Mariah Corporation, August 1985.
*
* If you want it, it's yours. All I ask in return is that if you
@@ -96,7 +96,7 @@ rmdir (dpath)
if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
{
errno = EIO; /* We don't know why, but */
- return -1; /* /bin/mkdir failed */
+ return -1; /* /bin/rmdir failed */
}
return 0;
diff --git a/missing/setenv.c b/missing/setenv.c
index 6211bcf02b..16ecbc6090 100644
--- a/missing/setenv.c
+++ b/missing/setenv.c
@@ -19,8 +19,8 @@ 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 */
+ if (memcmp(environ[i],nam,len) == 0 && environ[i][len] == '=')
+ break; /* memcmp must come first to avoid */
} /* potential SEGV's */
return i;
}
diff --git a/missing/strftime.c b/missing/strftime.c
index 1e668ef2ae..478471c37d 100644
--- a/missing/strftime.c
+++ b/missing/strftime.c
@@ -10,9 +10,12 @@
* For extensions from SunOS, add SUNOS_EXT.
* For stuff needed to implement the P1003.2 date command, add POSIX2_DATE.
* For VMS dates, add VMS_EXT.
+ * For a an RFC822 time format, add MAILHEADER_EXT.
+ * For ISO week years, add ISO_DATE_EXT.
* For complete POSIX semantics, add POSIX_SEMANTICS.
*
- * The code for %c, %x, and %X is my best guess as to what's "appropriate".
+ * The code for %c, %x, and %X now follows the 1003.2 specification for
+ * the POSIX locale.
* This version ignores LOCALE information.
* It also doesn't worry about multi-byte characters.
* So there.
@@ -26,6 +29,9 @@
* Updated April, 1993
* Updated February, 1994
* Updated May, 1994
+ * Updated January, 1995
+ * Updated September, 1995
+ * Updated January, 1996
*
* Fixes from ado@elsie.nci.nih.gov
* February 1991, May 1992
@@ -33,36 +39,46 @@
* May, 1993
* Further fixes from ado@elsie.nci.nih.gov
* February 1994
+ * %z code from chip@chinacat.unicom.com
+ * Applied September 1995
+ * %V code fixed (again) and %G, %g added,
+ * January 1996
*/
-#include "config.h"
-
+#ifndef GAWK
#include <stdio.h>
#include <ctype.h>
-#if defined (HAVE_STRING_H)
-# include <string.h>
-#else /* !HAVE_STRING_H */
-# include <strings.h>
-#endif /* !HAVE_STRING_H */
+#include <string.h>
#include <time.h>
+#endif
+#if defined(TM_IN_SYS_TIME) || ! defined(GAWK)
#include <sys/types.h>
#include <sys/time.h>
+#endif
/* defaults: season to taste */
-#define SYSV_EXT 1 /* stuff in System V ascftime routine */
-#define SUNOS_EXT 1 /* stuff in SunOS strftime routine */
-#define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */
-#define VMS_EXT 1 /* include %v for VMS date format */
-#ifndef RUBY
-#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */
+#define SYSV_EXT 1 /* stuff in System V ascftime routine */
+#define SUNOS_EXT 1 /* stuff in SunOS strftime routine */
+#define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */
+#define VMS_EXT 1 /* include %v for VMS date format */
+#define MAILHEADER_EXT 1 /* add %z for HHMM format */
+#define ISO_DATE_EXT 1 /* %G and %g for year of ISO week */
+#ifndef GAWK
+#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */
+#endif
+
+#if defined(ISO_DATE_EXT)
+#if ! defined(POSIX2_DATE)
+#define POSIX2_DATE 1
+#endif
#endif
#if defined(POSIX2_DATE)
#if ! defined(SYSV_EXT)
-#define SYSV_EXT 1
+#define SYSV_EXT 1
#endif
#if ! defined(SUNOS_EXT)
-#define SUNOS_EXT 1
+#define SUNOS_EXT 1
#endif
#endif
@@ -103,11 +119,16 @@ adddecl(static int iso8601wknum(const struct tm *timeptr);)
#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
extern char *tzname[2];
-# ifdef HAVE_DAYLIGHT
extern int daylight;
-# endif
+#ifdef SOLARIS
+extern long timezone, altzone;
+#else
+extern int timezone, altzone;
+#endif
#endif
+#undef min /* just in case */
+
/* min --- return minimum of two numbers */
#ifndef __STDC__
@@ -119,9 +140,11 @@ static inline int
min(int a, int b)
#endif
{
- return (a < b ? a : b);
+ return (a < b ? a : b);
}
+#undef max /* also, just in case */
+
/* max --- return maximum of two numbers */
#ifndef __STDC__
@@ -133,7 +156,7 @@ static inline int
max(int a, int b)
#endif
{
- return (a > b ? a : b);
+ return (a > b ? a : b);
}
/* strftime --- produce formatted time */
@@ -150,360 +173,417 @@ size_t
strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
#endif
{
- char *endp = s + maxsize;
- char *start = s;
- auto char tbuf[100];
- int i;
- static short first = 1;
+ char *endp = s + maxsize;
+ char *start = s;
+ auto char tbuf[100];
+ long off;
+ int i, w, y;
+ static short first = 1;
#ifdef POSIX_SEMANTICS
- static char *savetz = NULL;
- static int savetzlen = 0;
- char *tz;
+ static char *savetz = NULL;
+ static int savetzlen = 0;
+ char *tz;
#endif /* POSIX_SEMANTICS */
#ifndef HAVE_TM_ZONE
- extern char *timezone();
- struct timeval tv;
- struct timezone zone;
+#ifndef HAVE_TM_NAME
+#ifndef HAVE_TZNAME
+ extern char *timezone();
+ struct timeval tv;
+ struct timezone zone;
+#endif /* HAVE_TZNAME */
+#endif /* HAVE_TM_NAME */
#endif /* HAVE_TM_ZONE */
- /* various tables, useful in North America */
- static const char *days_a[] = {
- "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat",
- };
- static const char *days_l[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday",
- };
- static const char *months_a[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- };
- static const char *months_l[] = {
- "January", "February", "March", "April",
- "May", "June", "July", "August", "September",
- "October", "November", "December",
- };
- static const char *ampm[] = { "AM", "PM", };
-
- if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
- return 0;
-
- /* quick check if we even need to bother */
- if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
- return 0;
+ /* various tables, useful in North America */
+ static const char *days_a[] = {
+ "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat",
+ };
+ static const char *days_l[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday",
+ };
+ static const char *months_a[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+ static const char *months_l[] = {
+ "January", "February", "March", "April",
+ "May", "June", "July", "August", "September",
+ "October", "November", "December",
+ };
+ static const char *ampm[] = { "AM", "PM", };
+
+ if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
+ return 0;
+
+ /* quick check if we even need to bother */
+ if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
+ return 0;
#ifndef POSIX_SEMANTICS
- if (first) {
- tzset();
- first = 0;
- }
+ if (first) {
+ tzset();
+ first = 0;
+ }
#else /* POSIX_SEMANTICS */
- tz = getenv("TZ");
- if (first) {
- if (tz != NULL) {
- int tzlen = strlen(tz);
-
- savetz = (char *) malloc(tzlen + 1);
- if (savetz != NULL) {
- savetzlen = tzlen + 1;
- strcpy(savetz, tz);
- }
+ tz = getenv("TZ");
+ if (first) {
+ if (tz != NULL) {
+ int tzlen = strlen(tz);
+
+ savetz = (char *) malloc(tzlen + 1);
+ if (savetz != NULL) {
+ savetzlen = tzlen + 1;
+ strcpy(savetz, tz);
+ }
+ }
+ tzset();
+ first = 0;
+ }
+ /* if we have a saved TZ, and it is different, recapture and reset */
+ if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
+ i = strlen(tz) + 1;
+ if (i > savetzlen) {
+ savetz = (char *) realloc(savetz, i);
+ if (savetz) {
+ savetzlen = i;
+ strcpy(savetz, tz);
+ }
+ } else
+ strcpy(savetz, tz);
+ tzset();
}
- tzset();
- first = 0;
- }
- /* if we have a saved TZ, and it is different, recapture and reset */
- if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
- i = strlen(tz) + 1;
- if (i > savetzlen) {
- savetz = (char *) realloc(savetz, i);
- if (savetz) {
- savetzlen = i;
- strcpy(savetz, tz);
- }
- } else
- strcpy(savetz, tz);
- tzset();
- }
#endif /* POSIX_SEMANTICS */
- for (; *format && s < endp - 1; format++) {
- tbuf[0] = '\0';
- if (*format != '%') {
- *s++ = *format;
- continue;
- }
- again:
- switch (*++format) {
- case '\0':
- *s++ = '%';
- goto out;
-
- case '%':
- *s++ = '%';
- continue;
-
- case 'a': /* abbreviated weekday name */
- if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
- strcpy(tbuf, "?");
- else
- strcpy(tbuf, days_a[timeptr->tm_wday]);
- break;
-
- case 'A': /* full weekday name */
- if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
- strcpy(tbuf, "?");
- else
- strcpy(tbuf, days_l[timeptr->tm_wday]);
- break;
+ for (; *format && s < endp - 1; format++) {
+ tbuf[0] = '\0';
+ if (*format != '%') {
+ *s++ = *format;
+ continue;
+ }
+ again:
+ switch (*++format) {
+ case '\0':
+ *s++ = '%';
+ goto out;
+
+ case '%':
+ *s++ = '%';
+ continue;
+
+ case 'a': /* abbreviated weekday name */
+ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, days_a[timeptr->tm_wday]);
+ break;
+
+ case 'A': /* full weekday name */
+ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, days_l[timeptr->tm_wday]);
+ break;
#ifdef SYSV_EXT
- case 'h': /* abbreviated month name */
+ case 'h': /* abbreviated month name */
#endif
- case 'b': /* abbreviated month name */
- if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
- strcpy(tbuf, "?");
- else
- strcpy(tbuf, months_a[timeptr->tm_mon]);
- break;
-
- case 'B': /* full month name */
- if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
- strcpy(tbuf, "?");
- else
- strcpy(tbuf, months_l[timeptr->tm_mon]);
- break;
-
- case 'c': /* appropriate date and time representation */
- sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d",
- days_a[range(0, timeptr->tm_wday, 6)],
- months_a[range(0, timeptr->tm_mon, 11)],
- range(1, timeptr->tm_mday, 31),
- range(0, timeptr->tm_hour, 23),
- range(0, timeptr->tm_min, 59),
- range(0, timeptr->tm_sec, 61),
- timeptr->tm_year + 1900);
- break;
-
- case 'd': /* day of the month, 01 - 31 */
- i = range(1, timeptr->tm_mday, 31);
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'H': /* hour, 24-hour clock, 00 - 23 */
- i = range(0, timeptr->tm_hour, 23);
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'I': /* hour, 12-hour clock, 01 - 12 */
- i = range(0, timeptr->tm_hour, 23);
- if (i == 0)
- i = 12;
- else if (i > 12)
- i -= 12;
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'j': /* day of the year, 001 - 366 */
- sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
- break;
-
- case 'm': /* month, 01 - 12 */
- i = range(0, timeptr->tm_mon, 11);
- sprintf(tbuf, "%02d", i + 1);
- break;
-
- case 'M': /* minute, 00 - 59 */
- i = range(0, timeptr->tm_min, 59);
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'p': /* am or pm based on 12-hour clock */
- i = range(0, timeptr->tm_hour, 23);
- if (i < 12)
- strcpy(tbuf, ampm[0]);
- else
- strcpy(tbuf, ampm[1]);
- break;
-
- case 'S': /* second, 00 - 61 */
- i = range(0, timeptr->tm_sec, 61);
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'U': /* week of year, Sunday is first day of week */
- sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
- break;
-
- case 'w': /* weekday, Sunday == 0, 0 - 6 */
- i = range(0, timeptr->tm_wday, 6);
- sprintf(tbuf, "%d", i);
- break;
-
- case 'W': /* week of year, Monday is first day of week */
- sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
- break;
-
- case 'x': /* appropriate date representation */
- sprintf(tbuf, "%s %s %2d %d",
- days_a[range(0, timeptr->tm_wday, 6)],
- months_a[range(0, timeptr->tm_mon, 11)],
- range(1, timeptr->tm_mday, 31),
- timeptr->tm_year + 1900);
- break;
-
- case 'X': /* appropriate time representation */
- sprintf(tbuf, "%02d:%02d:%02d",
- range(0, timeptr->tm_hour, 23),
- range(0, timeptr->tm_min, 59),
- range(0, timeptr->tm_sec, 61));
- break;
-
- case 'y': /* year without a century, 00 - 99 */
- i = timeptr->tm_year % 100;
- sprintf(tbuf, "%02d", i);
- break;
-
- case 'Y': /* year with century */
- sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
- break;
-
- case 'Z': /* time zone name or abbrevation */
+ case 'b': /* abbreviated month name */
+ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, months_a[timeptr->tm_mon]);
+ break;
+
+ case 'B': /* full month name */
+ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, months_l[timeptr->tm_mon]);
+ break;
+
+ case 'c': /* appropriate date and time representation */
+ strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
+ break;
+
+ case 'd': /* day of the month, 01 - 31 */
+ i = range(1, timeptr->tm_mday, 31);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'H': /* hour, 24-hour clock, 00 - 23 */
+ i = range(0, timeptr->tm_hour, 23);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'I': /* hour, 12-hour clock, 01 - 12 */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i == 0)
+ i = 12;
+ else if (i > 12)
+ i -= 12;
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'j': /* day of the year, 001 - 366 */
+ sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
+ break;
+
+ case 'm': /* month, 01 - 12 */
+ i = range(0, timeptr->tm_mon, 11);
+ sprintf(tbuf, "%02d", i + 1);
+ break;
+
+ case 'M': /* minute, 00 - 59 */
+ i = range(0, timeptr->tm_min, 59);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'p': /* am or pm based on 12-hour clock */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i < 12)
+ strcpy(tbuf, ampm[0]);
+ else
+ strcpy(tbuf, ampm[1]);
+ break;
+
+ case 'S': /* second, 00 - 61 */
+ i = range(0, timeptr->tm_sec, 61);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'U': /* week of year, Sunday is first day of week */
+ sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
+ break;
+
+ case 'w': /* weekday, Sunday == 0, 0 - 6 */
+ i = range(0, timeptr->tm_wday, 6);
+ sprintf(tbuf, "%d", i);
+ break;
+
+ case 'W': /* week of year, Monday is first day of week */
+ sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
+ break;
+
+ case 'x': /* appropriate date representation */
+ strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
+ break;
+
+ case 'X': /* appropriate time representation */
+ strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
+ break;
+
+ case 'y': /* year without a century, 00 - 99 */
+ i = timeptr->tm_year % 100;
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'Y': /* year with century */
+ sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
+ break;
+
+#ifdef MAILHEADER_EXT
+ /*
+ * From: Chip Rosenthal <chip@chinacat.unicom.com>
+ * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
+ *
+ * Warning: the %z [code] is implemented by inspecting the
+ * timezone name conditional compile settings, and
+ * inferring a method to get timezone offsets. I've tried
+ * this code on a couple of machines, but I don't doubt
+ * there is some system out there that won't like it.
+ * Maybe the easiest thing to do would be to bracket this
+ * with an #ifdef that can turn it off. The %z feature
+ * would be an admittedly obscure one that most folks can
+ * live without, but it would be a great help to those of
+ * us that muck around with various message processors.
+ */
+ case 'z': /* time zone offset east of GMT e.g. -0600 */
+#ifdef HAVE_TM_NAME
+ /*
+ * Systems with tm_name probably have tm_tzadj as
+ * secs west of GMT. Convert to mins east of GMT.
+ */
+ off = -timeptr->tm_tzadj / 60;
+#else /* !HAVE_TM_NAME */
+#ifdef HAVE_TM_ZONE
+ /*
+ * Systems with tm_zone probably have tm_gmtoff as
+ * secs east of GMT. Convert to mins east of GMT.
+ */
+ off = timeptr->tm_gmtoff / 60;
+#else /* !HAVE_TM_ZONE */
+#if HAVE_TZNAME
+ /*
+ * Systems with tzname[] probably have timezone as
+ * secs west of GMT. Convert to mins east of GMT.
+ */
+ off = -(daylight ? timezone : altzone) / 60;
+#else /* !HAVE_TZNAME */
+ off = -zone.tz_minuteswest;
+#endif /* !HAVE_TZNAME */
+#endif /* !HAVE_TM_ZONE */
+#endif /* !HAVE_TM_NAME */
+ if (off < 0) {
+ tbuf[0] = '-';
+ off = -off;
+ } else {
+ tbuf[0] = '+';
+ }
+ sprintf(tbuf+1, "%02d%02d", off/60, off%60);
+ break;
+#endif /* MAILHEADER_EXT */
+
+ case 'Z': /* time zone name or abbrevation */
#ifdef HAVE_TZNAME
-#ifdef HAVE_DAYLIGHT
- i = (daylight && timeptr->tm_isdst); /* 0 or 1 */
-#else
- i = timeptr->tm_isdst;
-#endif
- strcpy(tbuf, tzname[i]);
+ i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
+ strcpy(tbuf, tzname[i]);
#else
#ifdef HAVE_TM_ZONE
- strcpy(tbuf, timeptr->tm_zone);
+ strcpy(tbuf, timeptr->tm_zone);
#else
- gettimeofday(& tv, & zone);
- strcpy(tbuf, timezone(zone.tz_minuteswest,
- timeptr->tm_isdst));
-#endif
-#endif
- break;
+#ifdef HAVE_TM_NAME
+ strcpy(tbuf, timeptr->tm_name);
+#else
+ gettimeofday(& tv, & zone);
+ strcpy(tbuf, timezone(zone.tz_minuteswest,
+ timeptr->tm_isdst > 0));
+#endif /* HAVE_TM_NAME */
+#endif /* HAVE_TM_ZONE */
+#endif /* HAVE_TZNAME */
+ break;
#ifdef SYSV_EXT
- case 'n': /* same as \n */
- tbuf[0] = '\n';
- tbuf[1] = '\0';
- break;
-
- case 't': /* same as \t */
- tbuf[0] = '\t';
- tbuf[1] = '\0';
- break;
-
- case 'D': /* date as %m/%d/%y */
- strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
- break;
-
- case 'e': /* day of month, blank padded */
- sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
- break;
-
- case 'r': /* time as %I:%M:%S %p */
- strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
- break;
-
- case 'R': /* time as %H:%M */
- strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
- break;
-
- case 'T': /* time as %H:%M:%S */
- strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
- break;
+ case 'n': /* same as \n */
+ tbuf[0] = '\n';
+ tbuf[1] = '\0';
+ break;
+
+ case 't': /* same as \t */
+ tbuf[0] = '\t';
+ tbuf[1] = '\0';
+ break;
+
+ case 'D': /* date as %m/%d/%y */
+ strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
+ break;
+
+ case 'e': /* day of month, blank padded */
+ sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
+ break;
+
+ case 'r': /* time as %I:%M:%S %p */
+ strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
+ break;
+
+ case 'R': /* time as %H:%M */
+ strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
+ break;
+
+ case 'T': /* time as %H:%M:%S */
+ strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
+ break;
#endif
#ifdef SUNOS_EXT
- case 'k': /* hour, 24-hour clock, blank pad */
- sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
- break;
-
- case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */
- i = range(0, timeptr->tm_hour, 23);
- if (i == 0)
- i = 12;
- else if (i > 12)
- i -= 12;
- sprintf(tbuf, "%2d", i);
- break;
+ case 'k': /* hour, 24-hour clock, blank pad */
+ sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
+ break;
+
+ case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i == 0)
+ i = 12;
+ else if (i > 12)
+ i -= 12;
+ sprintf(tbuf, "%2d", i);
+ break;
#endif
#ifdef VMS_EXT
- case 'v': /* date as dd-bbb-YYYY */
- sprintf(tbuf, "%02d-%3.3s-%4d",
- range(1, timeptr->tm_mday, 31),
- months_a[range(0, timeptr->tm_mon, 11)],
- timeptr->tm_year + 1900);
- for (i = 3; i < 6; i++)
- if (islower(tbuf[i]))
- tbuf[i] = toupper(tbuf[i]);
- break;
+ case 'v': /* date as dd-bbb-YYYY */
+ sprintf(tbuf, "%02d-%3.3s-%4d",
+ range(1, timeptr->tm_mday, 31),
+ months_a[range(0, timeptr->tm_mon, 11)],
+ timeptr->tm_year + 1900);
+ for (i = 3; i < 6; i++)
+ if (islower(tbuf[i]))
+ tbuf[i] = toupper(tbuf[i]);
+ break;
#endif
#ifdef POSIX2_DATE
- case 'C':
- sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
- break;
-
-
- case 'E':
- case 'O':
- /* POSIX locale extensions, ignored for now */
- goto again;
-
- case 'V': /* week of year according ISO 8601 */
-#if defined(GAWK) && defined(VMS_EXT)
- {
- extern int do_lint;
- extern void warning();
- static int warned = 0;
-
- if (! warned && do_lint) {
- warned = 1;
- warning(
- "conversion %%V added in P1003.2; for VMS style date, use %%v");
- }
- }
-#endif
- sprintf(tbuf, "%02d", iso8601wknum(timeptr));
- break;
-
- case 'u':
- /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
- sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
- timeptr->tm_wday);
- break;
+ case 'C':
+ sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
+ break;
+
+
+ case 'E':
+ case 'O':
+ /* POSIX locale extensions, ignored for now */
+ goto again;
+
+ case 'V': /* week of year according ISO 8601 */
+ sprintf(tbuf, "%02d", iso8601wknum(timeptr));
+ break;
+
+ case 'u':
+ /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
+ sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
+ timeptr->tm_wday);
+ break;
#endif /* POSIX2_DATE */
- default:
- tbuf[0] = '%';
- tbuf[1] = *format;
- tbuf[2] = '\0';
- break;
- }
- i = strlen(tbuf);
- if (i) {
- if (s + i < endp - 1) {
- strcpy(s, tbuf);
- s += i;
- } else
- return 0;
+
+#ifdef ISO_DATE_EXT
+ case 'G':
+ case 'g':
+ /*
+ * Year of ISO week.
+ *
+ * If it's December but the ISO week number is one,
+ * that week is in next year.
+ * If it's January but the ISO week number is 52 or
+ * 53, that week is in last year.
+ * Otherwise, it's this year.
+ */
+ w = iso8601wknum(timeptr);
+ if (timeptr->tm_mon == 11 && w == 1)
+ y = 1900 + timeptr->tm_year + 1;
+ else if (timeptr->tm_mon == 0 && w >= 52)
+ y = 1900 + timeptr->tm_year - 1;
+ else
+ y = 1900 + timeptr->tm_year;
+
+ if (*format == 'G')
+ sprintf(tbuf, "%d", y);
+ else
+ sprintf(tbuf, "%02d", y % 100);
+ break;
+#endif ISO_DATE_EXT
+ default:
+ tbuf[0] = '%';
+ tbuf[1] = *format;
+ tbuf[2] = '\0';
+ break;
+ }
+ i = strlen(tbuf);
+ if (i) {
+ if (s + i < endp - 1) {
+ strcpy(s, tbuf);
+ s += i;
+ } else
+ return 0;
+ }
}
- }
out:
- if (s < endp && *format == '\0') {
- *s = '\0';
- return (s - start);
- } else
- return 0;
+ if (s < endp && *format == '\0') {
+ *s = '\0';
+ return (s - start);
+ } else
+ return 0;
}
/* isleap --- is a year a leap year? */
@@ -517,7 +597,7 @@ static int
isleap(int year)
#endif
{
- return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+ return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
@@ -533,85 +613,108 @@ static int
iso8601wknum(const struct tm *timeptr)
#endif
{
- /*
- * From 1003.2:
- * If the week (Monday to Sunday) containing January 1
- * has four or more days in the new year, then it is week 1;
- * otherwise it is the highest numbered week of the previous
- * (52 or 53) year, and the next week is week 1.
- *
- * ADR: This means if Jan 1 was Monday through Thursday,
- * it was week 1, otherwise week 52 or 53.
- *
- * XPG4 erroneously included POSIX.2 rationale text in the
- * main body of the standard. Thus it requires week 53.
- */
-
- int weeknum, jan1day, diff;
-
- /* get week number, Monday as first day of the week */
- weeknum = weeknumber(timeptr, 1);
-
- /*
- * With thanks and tip of the hatlo to tml@tik.vtt.fi
- *
- * What day of the week does January 1 fall on?
- * We know that
- * (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
- * (timeptr->tm_wday - jan1.tm_wday) MOD 7
- * and that
- * jan1.tm_yday == 0
- * and that
- * timeptr->tm_wday MOD 7 == timeptr->tm_wday
- * from which it follows that. . .
- */
- jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
- if (jan1day < 0)
- jan1day += 7;
-
- /*
- * If Jan 1 was a Monday through Thursday, it was in
- * week 1. Otherwise it was last year's highest week, which is
- * this year's week 0.
- *
- * What does that mean?
- * If Jan 1 was Monday, the week number is exactly right, it can
- * never be 0.
- * If it was Tuesday through Thursday, the weeknumber is one
- * less than it should be, so we add one.
- * Otherwise, Friday, Saturday or Sunday, the week number is
- * OK, but if it is 0, it needs to be 52 or 53.
- */
- switch (jan1day) {
- case 1: /* Monday */
- break;
- case 2: /* Tuesday */
- case 3: /* Wednedsday */
- case 4: /* Thursday */
- weeknum++;
- break;
- case 5: /* Friday */
- case 6: /* Saturday */
- case 0: /* Sunday */
- if (weeknum == 0) {
+ /*
+ * From 1003.2:
+ * If the week (Monday to Sunday) containing January 1
+ * has four or more days in the new year, then it is week 1;
+ * otherwise it is the highest numbered week of the previous
+ * year (52 or 53), and the next week is week 1.
+ *
+ * ADR: This means if Jan 1 was Monday through Thursday,
+ * it was week 1, otherwise week 52 or 53.
+ *
+ * XPG4 erroneously included POSIX.2 rationale text in the
+ * main body of the standard. Thus it requires week 53.
+ */
+
+ int weeknum, jan1day, diff;
+
+ /* get week number, Monday as first day of the week */
+ weeknum = weeknumber(timeptr, 1);
+
+ /*
+ * With thanks and tip of the hatlo to tml@tik.vtt.fi
+ *
+ * What day of the week does January 1 fall on?
+ * We know that
+ * (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
+ * (timeptr->tm_wday - jan1.tm_wday) MOD 7
+ * and that
+ * jan1.tm_yday == 0
+ * and that
+ * timeptr->tm_wday MOD 7 == timeptr->tm_wday
+ * from which it follows that. . .
+ */
+ jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
+ if (jan1day < 0)
+ jan1day += 7;
+
+ /*
+ * If Jan 1 was a Monday through Thursday, it was in
+ * week 1. Otherwise it was last year's highest week, which is
+ * this year's week 0.
+ *
+ * What does that mean?
+ * If Jan 1 was Monday, the week number is exactly right, it can
+ * never be 0.
+ * If it was Tuesday through Thursday, the weeknumber is one
+ * less than it should be, so we add one.
+ * Otherwise, Friday, Saturday or Sunday, the week number is
+ * OK, but if it is 0, it needs to be 52 or 53.
+ */
+ switch (jan1day) {
+ case 1: /* Monday */
+ break;
+ case 2: /* Tuesday */
+ case 3: /* Wednesday */
+ case 4: /* Thursday */
+ weeknum++;
+ break;
+ case 5: /* Friday */
+ case 6: /* Saturday */
+ case 0: /* Sunday */
+ if (weeknum == 0) {
#ifdef USE_BROKEN_XPG4
- /* XPG4 (as of March 1994) says 53 unconditionally */
- weeknum = 53;
+ /* XPG4 (as of March 1994) says 53 unconditionally */
+ weeknum = 53;
#else
- /* get week number of last week of last year */
- struct tm dec31ly; /* 12/31 last year */
- dec31ly = *timeptr;
- dec31ly.tm_year--;
- dec31ly.tm_mon = 11;
- dec31ly.tm_mday = 31;
- dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
- dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
- weeknum = iso8601wknum(& dec31ly);
+ /* get week number of last week of last year */
+ struct tm dec31ly; /* 12/31 last year */
+ dec31ly = *timeptr;
+ dec31ly.tm_year--;
+ dec31ly.tm_mon = 11;
+ dec31ly.tm_mday = 31;
+ dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
+ dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
+ weeknum = iso8601wknum(& dec31ly);
#endif
+ }
+ break;
}
- break;
- }
- return weeknum;
+
+ if (timeptr->tm_mon == 11) {
+ /*
+ * The last week of the year
+ * can be in week 1 of next year.
+ * Sigh.
+ *
+ * This can only happen if
+ * M T W
+ * 29 30 31
+ * 30 31
+ * 31
+ */
+ int wday, mday;
+
+ wday = timeptr->tm_wday;
+ mday = timeptr->tm_mday;
+ if ( (wday == 1 && (mday >= 29 && mday <= 31))
+ || (wday == 2 && (mday == 30 || mday == 31))
+ || (wday == 3 && mday == 31))
+ weeknum = 1;
+ }
+
+ return weeknum;
}
#endif
@@ -629,25 +732,25 @@ static int
weeknumber(const struct tm *timeptr, int firstweekday)
#endif
{
- int wday = timeptr->tm_wday;
- int ret;
-
- if (firstweekday == 1) {
- if (wday == 0) /* sunday */
- wday = 6;
- else
- wday--;
- }
- ret = ((timeptr->tm_yday + 7 - wday) / 7);
- if (ret < 0)
- ret = 0;
- return ret;
+ int wday = timeptr->tm_wday;
+ int ret;
+
+ if (firstweekday == 1) {
+ if (wday == 0) /* sunday */
+ wday = 6;
+ else
+ wday--;
+ }
+ ret = ((timeptr->tm_yday + 7 - wday) / 7);
+ if (ret < 0)
+ ret = 0;
+ return ret;
}
#if 0
/* ADR --- I'm loathe to mess with ado's code ... */
-Date: Wed, 24 Apr 91 20:54:08 MDT
+Date: Wed, 24 Apr 91 20:54:08 MDT
From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
To: arnold@audiofax.com
@@ -661,7 +764,7 @@ in the following form:
*/
{
return (timeptr->tm_yday - timeptr->tm_wday +
- (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
+ (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
}
How nicer it depends on a compiler, of course, but always a tiny bit.
@@ -674,38 +777,38 @@ How nicer it depends on a compiler, of course, but always a tiny bit.
/*
* NAME:
- * tst
+ * tst
*
* SYNOPSIS:
- * tst
+ * tst
*
* DESCRIPTION:
- * "tst" is a test driver for the function "strftime".
+ * "tst" is a test driver for the function "strftime".
*
* OPTIONS:
- * None.
+ * None.
*
* AUTHOR:
- * Karl Vogel
- * Control Data Systems, Inc.
- * vogelke@c-17igp.wpafb.af.mil
+ * Karl Vogel
+ * Control Data Systems, Inc.
+ * vogelke@c-17igp.wpafb.af.mil
*
* BUGS:
- * None noticed yet.
+ * None noticed yet.
*
* COMPILE:
- * cc -o tst -DTEST_STRFTIME strftime.c
+ * cc -o tst -DTEST_STRFTIME strftime.c
*/
/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
#ifndef NULL
-#include <stdio.h>
+#include <stdio.h>
#endif
-#include <sys/time.h>
-#include <string.h>
+#include <sys/time.h>
+#include <string.h>
-#define MAXTIME 132
+#define MAXTIME 132
/*
* Array of time formats.
@@ -713,42 +816,43 @@ How nicer it depends on a compiler, of course, but always a tiny bit.
static char *array[] =
{
- "(%%A) full weekday name, var length (Sunday..Saturday) %A",
- "(%%B) full month name, var length (January..December) %B",
- "(%%C) Century %C",
- "(%%D) date (%%m/%%d/%%y) %D",
- "(%%E) Locale extensions (ignored) %E",
- "(%%H) hour (24-hour clock, 00..23) %H",
- "(%%I) hour (12-hour clock, 01..12) %I",
- "(%%M) minute (00..59) %M",
- "(%%O) Locale extensions (ignored) %O",
- "(%%R) time, 24-hour (%%H:%%M) %R",
- "(%%S) second (00..61) %S",
- "(%%T) time, 24-hour (%%H:%%M:%%S) %T",
- "(%%U) week of year, Sunday as first day of week (00..53) %U",
- "(%%V) week of year according to ISO 8601 %V",
- "(%%W) week of year, Monday as first day of week (00..53) %W",
- "(%%X) appropriate locale time representation (%H:%M:%S) %X",
- "(%%Y) year with century (1970...) %Y",
- "(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
- "(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
- "(%%b) locale's abbreviated month name (Jan..Dec) %b",
- "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
- "(%%d) day of the month (01..31) %d",
- "(%%e) day of the month, blank-padded ( 1..31) %e",
- "(%%h) should be same as (%%b) %h",
- "(%%j) day of the year (001..366) %j",
- "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
- "(%%l) hour, 12-hour clock, blank pad ( 0..12) %l",
- "(%%m) month (01..12) %m",
- "(%%p) locale's AM or PM based on 12-hour clock %p",
- "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
- "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
- "(%%v) VAX date (dd-bbb-YYYY) %v",
- "(%%w) day of week (0..6, Sunday == 0) %w",
- "(%%x) appropriate locale date representation %x",
- "(%%y) last two digits of year (00..99) %y",
- (char *) NULL
+ "(%%A) full weekday name, var length (Sunday..Saturday) %A",
+ "(%%B) full month name, var length (January..December) %B",
+ "(%%C) Century %C",
+ "(%%D) date (%%m/%%d/%%y) %D",
+ "(%%E) Locale extensions (ignored) %E",
+ "(%%H) hour (24-hour clock, 00..23) %H",
+ "(%%I) hour (12-hour clock, 01..12) %I",
+ "(%%M) minute (00..59) %M",
+ "(%%O) Locale extensions (ignored) %O",
+ "(%%R) time, 24-hour (%%H:%%M) %R",
+ "(%%S) second (00..61) %S",
+ "(%%T) time, 24-hour (%%H:%%M:%%S) %T",
+ "(%%U) week of year, Sunday as first day of week (00..53) %U",
+ "(%%V) week of year according to ISO 8601 %V",
+ "(%%W) week of year, Monday as first day of week (00..53) %W",
+ "(%%X) appropriate locale time representation (%H:%M:%S) %X",
+ "(%%Y) year with century (1970...) %Y",
+ "(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
+ "(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
+ "(%%b) locale's abbreviated month name (Jan..Dec) %b",
+ "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
+ "(%%d) day of the month (01..31) %d",
+ "(%%e) day of the month, blank-padded ( 1..31) %e",
+ "(%%h) should be same as (%%b) %h",
+ "(%%j) day of the year (001..366) %j",
+ "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
+ "(%%l) hour, 12-hour clock, blank pad ( 0..12) %l",
+ "(%%m) month (01..12) %m",
+ "(%%p) locale's AM or PM based on 12-hour clock %p",
+ "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
+ "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
+ "(%%v) VMS date (dd-bbb-YYYY) %v",
+ "(%%w) day of week (0..6, Sunday == 0) %w",
+ "(%%x) appropriate locale date representation %x",
+ "(%%y) last two digits of year (00..99) %y",
+ "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z",
+ (char *) NULL
};
/* main routine. */
@@ -758,28 +862,28 @@ main(argc, argv)
int argc;
char **argv;
{
- long time();
+ long time();
- char *next;
- char string[MAXTIME];
+ char *next;
+ char string[MAXTIME];
- int k;
- int length;
+ int k;
+ int length;
- struct tm *tm;
+ struct tm *tm;
- long clock;
+ long clock;
- /* Call the function. */
+ /* Call the function. */
- clock = time((long *) 0);
- tm = localtime(&clock);
+ clock = time((long *) 0);
+ tm = localtime(&clock);
- for (k = 0; next = array[k]; k++) {
- length = strftime(string, MAXTIME, next, tm);
- printf("%s\n", string);
- }
+ for (k = 0; next = array[k]; k++) {
+ length = strftime(string, MAXTIME, next, tm);
+ printf("%s\n", string);
+ }
- exit(0);
+ exit(0);
}
#endif /* TEST_STRFTIME */
diff --git a/node.h b/node.h
index f69258ab4c..eb91c01196 100644
--- a/node.h
+++ b/node.h
@@ -6,14 +6,14 @@
$Date: 1995/01/10 10:42:41 $
created at: Fri May 28 15:14:02 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#ifndef NODE_H
#define NODE_H
-struct global_entry* rb_global_entry();
+struct global_entry *rb_global_entry();
enum node_type {
NODE_METHOD,
@@ -22,13 +22,18 @@ enum node_type {
NODE_SCOPE,
NODE_BLOCK,
NODE_IF,
+ NODE_UNLESS,
NODE_CASE,
NODE_WHEN,
+ NODE_OPT_N,
NODE_WHILE,
- NODE_WHILE2,
+ NODE_UNTIL,
NODE_ITER,
NODE_FOR,
NODE_BEGIN,
+ NODE_RESCUE,
+ NODE_RESBODY,
+ NODE_ENSURE,
NODE_AND,
NODE_OR,
NODE_NOT,
@@ -47,12 +52,7 @@ enum node_type {
NODE_ARRAY,
NODE_ZARRAY,
NODE_HASH,
- NODE_REDO,
- NODE_BREAK,
- NODE_CONTINUE,
NODE_RETURN,
- NODE_RETRY,
- NODE_FAIL,
NODE_YIELD,
NODE_LVAR,
NODE_DVAR,
@@ -62,12 +62,17 @@ enum node_type {
NODE_CONST,
NODE_NTH_REF,
NODE_BACK_REF,
+ NODE_MATCH_REF,
+ NODE_LASTLINE,
+ NODE_MATCH,
NODE_LIT,
NODE_STR,
- NODE_STR2,
+ NODE_DSTR,
NODE_XSTR,
- NODE_XSTR2,
+ NODE_DXSTR,
+ NODE_EVSTR,
NODE_DREGX,
+ NODE_DREGX_ONCE,
NODE_ARGS,
NODE_DEFN,
NODE_DEFS,
@@ -76,6 +81,7 @@ enum node_type {
NODE_CLASS,
NODE_MODULE,
NODE_COLON2,
+ NODE_CNAME,
NODE_CREF,
NODE_DOT2,
NODE_DOT3,
@@ -85,12 +91,12 @@ enum node_type {
NODE_SELF,
NODE_NIL,
NODE_DEFINED,
+ NODE_TAG,
};
typedef struct RNode {
UINT flags;
char *file;
- unsigned int line;
union {
struct RNode *node;
ID id;
@@ -102,6 +108,7 @@ typedef struct RNode {
struct RNode *node;
ID id;
int argc;
+ VALUE value;
} u2;
union {
struct RNode *node;
@@ -115,9 +122,13 @@ typedef struct RNode {
#define RNODE(obj) (R_CAST(RNode)(obj))
-#define nd_type(n) (((RNODE(n))->flags>>10)&0xff)
+#define nd_type(n) (((RNODE(n))->flags>>10)&0x7f)
#define nd_set_type(n,t) \
- RNODE(n)->flags=((RNODE(n)->flags&~FL_UMASK)|(((t)<<10)&FL_UMASK))
+ RNODE(n)->flags=(RNODE(n)->flags&~FL_UMASK|(((t)<<10)&FL_UMASK))
+
+#define nd_line(n) (((RNODE(n))->flags>>17)&0x7fff)
+#define nd_set_line(n,l) \
+ RNODE(n)->flags=(RNODE(n)->flags&~(-1<<17)|(((l)&0x7fff)<<17))
#define nd_head u1.node
#define nd_alen u2.argc
@@ -126,7 +137,6 @@ typedef struct RNode {
#define nd_cond u1.node
#define nd_body u2.node
#define nd_else u3.node
-#define nd_break u3.state
#define nd_orig u3.value
@@ -181,40 +191,42 @@ typedef struct RNode {
#define nd_beg u1.node
#define nd_end u2.node
#define nd_state u3.state
-#define nd_rval u3.node
+#define nd_rval u3.value
#define nd_nth u2.argc
+#define nd_tag u1.id
+#define nd_tlev u3.cnt
+#define nd_tval u2.value
+
#define NEW_METHOD(n,x) newnode(NODE_METHOD,x,n,0)
#define NEW_FBODY(n,i,o) newnode(NODE_FBODY,n,i,o)
#define NEW_DEFN(i,a,d,p) newnode(NODE_DEFN,p,i,NEW_RFUNC(a,d))
#define NEW_DEFS(r,i,a,d) newnode(NODE_DEFS,r,i,NEW_RFUNC(a,d))
#define NEW_CFUNC(f,c) newnode(NODE_CFUNC,f,c,0)
#define NEW_RFUNC(b1,b2) NEW_SCOPE(block_append(b1,b2))
-#define NEW_SCOPE(b) newnode(NODE_SCOPE,local_tbl(),(b),local_cnt(0))
+#define NEW_SCOPE(b) newnode(NODE_SCOPE,local_tbl(),(b),cur_cref)
#define NEW_BLOCK(a) newnode(NODE_BLOCK,a,1,0)
#define NEW_IF(c,t,e) newnode(NODE_IF,c,t,e)
+#define NEW_UNLESS(c,t,e) newnode(NODE_UNLESS,c,t,e)
#define NEW_EXNOT(c) newnode(NODE_EXNOT,c,0,0)
#define NEW_CASE(h,b) newnode(NODE_CASE,h,b,0)
#define NEW_WHEN(c,t,e) newnode(NODE_WHEN,c,t,e)
-#define NEW_WHILE(c,b) newnode(NODE_WHILE,c,b,0)
-#define NEW_WHILE2(c,b) newnode(NODE_WHILE2,c,b,0)
+#define NEW_OPT_N(b) newnode(NODE_OPT_N,0,b,0)
+#define NEW_WHILE(c,b,n) newnode(NODE_WHILE,c,b,n)
+#define NEW_UNTIL(c,b,n) newnode(NODE_UNTIL,c,b,n)
#define NEW_FOR(v,i,b) newnode(NODE_FOR,v,b,i)
#define NEW_ITER(v,i,b) newnode(NODE_ITER,v,b,i)
-#define NEW_BEGIN(b,ex,en) newnode(NODE_BEGIN,b,ex,en)
-#define NEW_REDO() newnode(NODE_REDO,0,0,0)
-#define NEW_BREAK() newnode(NODE_BREAK,0,0,0)
-#define NEW_CONT() newnode(NODE_CONTINUE,0,0,0)
-#define NEW_RETRY() newnode(NODE_RETRY,0,0,0)
+#define NEW_BEGIN(b) newnode(NODE_BEGIN,0,b,0)
+#define NEW_RESCUE(b,res) newnode(NODE_RESCUE,b,res,0)
+#define NEW_RESBODY(a,ex,n) newnode(NODE_RESBODY,n,ex,a)
+#define NEW_ENSURE(b,en) newnode(NODE_ENSURE,b,0,en)
#define NEW_RET(s) newnode(NODE_RETURN,s,0,0)
-#define NEW_FAIL(s) newnode(NODE_FAIL,s,0,0)
#define NEW_YIELD(a) newnode(NODE_YIELD,a,0,0)
#define NEW_LIST(a) NEW_ARRAY(a)
#define NEW_ARRAY(a) newnode(NODE_ARRAY,a,1,0)
#define NEW_ZARRAY() newnode(NODE_ZARRAY,0,0,0)
#define NEW_HASH(a) newnode(NODE_HASH,a,0,0)
-#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,0,r)
#define NEW_GASGN(v,val) newnode(NODE_GASGN,v,val,rb_global_entry(v))
@@ -223,19 +235,22 @@ typedef struct RNode {
#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_OP_ASGN2(r,i,o,val) newnode(NODE_OP_ASGN2,r,val,NEW_OP_ASGN3(i,o))
+#define NEW_OP_ASGN3(i,o) newnode(NODE_OP_ASGN2,i,o,0)
#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_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_CVAR(v) newnode(NODE_CVAR,v,0,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_MATCH(c) newnode(NODE_MATCH,c,0,0)
#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)
+#define NEW_DSTR(s) newnode(NODE_DSTR,s,0,0)
#define NEW_XSTR(s) newnode(NODE_XSTR,s,0,0)
-#define NEW_XSTR2(s) newnode(NODE_XSTR2,s,0,0)
+#define NEW_DXSTR(s) newnode(NODE_DXSTR,s,0,0)
+#define NEW_EVSTR(s,l) newnode(NODE_EVSTR,str_new(s,l),0,0)
#define NEW_CALL(r,m,a) newnode(NODE_CALL,r,m,a)
#define NEW_FCALL(m,a) newnode(NODE_FCALL,0,m,a)
#define NEW_SUPER(a) newnode(NODE_SUPER,0,0,a)
@@ -246,9 +261,9 @@ typedef struct RNode {
#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_CREF0() (cur_cref=newnode(NODE_CREF,RNODE(the_frame->cbase)->nd_clss,0,0))
+#define NEW_CREF() (cur_cref=newnode(NODE_CREF,0,0,cur_cref))
+#define NEW_CBODY(b) (cur_cref->nd_body=NEW_SCOPE(b),cur_cref)
#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)
@@ -262,4 +277,7 @@ VALUE rb_method_booundp();
#define NOEX_PUBLIC 0
#define NOEX_PRIVATE 1
+NODE *compile_string();
+NODE *compile_file();
+
#endif
diff --git a/numeric.c b/numeric.c
index ba91d0de21..0ff94dc046 100644
--- a/numeric.c
+++ b/numeric.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:42 $
created at: Fri Aug 13 18:33:09 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -21,114 +21,64 @@ VALUE cFloat;
VALUE cInteger;
VALUE cFixnum;
+VALUE eZeroDiv;
+
ID rb_frame_last_func();
+VALUE float_new();
double big2dbl();
-VALUE
-float_new(d)
- double d;
+void
+num_zerodiv()
{
- NEWOBJ(flt, struct RFloat);
- OBJSETUP(flt, cFloat, T_FLOAT);
-
- flt->value = d;
- return (VALUE)flt;
+ rb_raise(exc_new(eZeroDiv, "divided by 0"));
}
static VALUE
-num_coerce_bin(x, y)
+num_coerce(x, y)
VALUE x, y;
{
- return rb_funcall(rb_funcall(y, coerce, 1, x),
- rb_frame_last_func(), 1, y);
-}
-
-static VALUE
-num_uplus(num)
- VALUE num;
-{
- return num;
-}
-
-static VALUE
-num_uminus(num)
- VALUE num;
-{
- return rb_funcall(rb_funcall(num, coerce, 1, INT2FIX(0)), 1, num);
-}
-
-static VALUE
-num_next(num)
- VALUE num;
-{
- num = rb_funcall(num, rb_intern("to_i"), 0);
- return rb_funcall(num, '+', 1, INT2FIX(1));
+ return assoc_new(f_float(x,x),f_float(y,y));
}
VALUE
-num_upto(from, to)
- VALUE from, to;
+num_coerce_bin(x, y)
+ VALUE x, y;
{
- int i, end;
+ VALUE ary;
- end = NUM2INT(to);
- for (i = NUM2INT(from); i <= end; i++) {
- rb_yield(INT2FIX(i));
+ ary = rb_funcall(y, coerce, 1, x);
+ if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) {
+ TypeError("coerce must return [x, y]");
}
- return from;
-}
-
-static VALUE
-num_downto(from, to)
- VALUE from, to;
-{
- int i, end;
-
- end = NUM2INT(to);
- for (i=NUM2INT(from); i >= end; i--) {
- rb_yield(INT2FIX(i));
- }
+ x = RARRAY(ary)->ptr[0];
+ y = RARRAY(ary)->ptr[1];
- return from;
+ return rb_funcall(x, rb_frame_last_func(), 1, y);
}
static VALUE
-num_step(from, to, step)
- VALUE from, to, step;
+num_uplus(num)
+ VALUE num;
{
- int i, end, diff;
-
- end = NUM2INT(to);
- diff = NUM2INT(step);
-
- if (diff == 0) {
- Fail("step cannot be 0");
- }
- else if (diff > 0) {
- for (i=NUM2INT(from); i <= end; i+=diff) {
- rb_yield(INT2FIX(i));
- }
- }
- else {
- for (i=NUM2INT(from); i >= end; i+=diff) {
- rb_yield(INT2FIX(i));
- }
- }
- return from;
+ return num;
}
static VALUE
-num_dotimes(num)
+num_uminus(num)
VALUE num;
{
- int i, end;
+ VALUE ary, x, y;
- end = NUM2INT(num);
- for (i=0; i<end; i++) {
- rb_yield(INT2FIX(i));
+ ary = rb_funcall(num, coerce, 1, INT2FIX(0));
+ if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) {
+ TypeError("coerce must return [x, y]");
}
- return num;
+
+ x = RARRAY(ary)->ptr[0];
+ y = RARRAY(ary)->ptr[1];
+
+ return rb_funcall(x, '-', 1, y);
}
static VALUE
@@ -170,12 +120,45 @@ num_chr(num)
}
static VALUE
+num_abs(num)
+ VALUE num;
+{
+ if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) {
+ return rb_funcall(num, rb_intern("-@"), 0);
+ }
+ return num;
+}
+
+VALUE
+float_new(d)
+ double d;
+{
+ NEWOBJ(flt, struct RFloat);
+ OBJSETUP(flt, cFloat, T_FLOAT);
+
+ flt->value = d;
+ return (VALUE)flt;
+}
+
+static VALUE
flo_to_s(flt)
struct RFloat *flt;
{
char buf[32];
sprintf(buf, "%g", flt->value);
+ if (index(buf, '.') == 0) {
+ int len = strlen(buf);
+
+ if (len > 1 && buf[1] == 'e') {
+ memmove(buf+3, buf+1, len-1);
+ buf[1] = '.';
+ buf[2] = '0';
+ }
+ else {
+ strcat(buf, ".0");
+ }
+ }
return str_new2(buf);
}
@@ -184,18 +167,7 @@ static VALUE
flo_coerce(x, y)
VALUE x, y;
{
- switch (TYPE(y)) {
- case T_FIXNUM:
- return float_new((double)FIX2INT(y));
- case T_FLOAT:
- return y;
- case T_BIGNUM:
- return big_to_f(y);
- default:
- Fail("can't coerce %s to Float", rb_class2name(CLASS_OF(y)));
- }
- /* not reached */
- return Qnil;
+ return assoc_new(f_float(x, y), x);
}
static VALUE
@@ -267,14 +239,14 @@ flo_div(x, y)
switch (TYPE(y)) {
case T_FIXNUM:
f_y = FIX2INT(y);
- if (f_y == 0) Fail("devided by 0");
+ if (f_y == 0) num_zerodiv();
return float_new(x->value / (double)f_y);
case T_BIGNUM:
d = big2dbl(y);
- if (d == 0.0) Fail("devided by 0");
- return float_new(x->value + d);
+ if (d == 0.0) num_zerodiv();
+ return float_new(x->value / d);
case T_FLOAT:
- if (y->value == 0.0) Fail("devided by 0");
+ if (y->value == 0.0) num_zerodiv();
return float_new(x->value / y->value);
default:
return num_coerce_bin(x, y);
@@ -333,30 +305,21 @@ flo_pow(x, y)
}
}
-struct xy {
- VALUE x, y;
-};
-
static VALUE
-eq(arg)
- struct xy *arg;
+num_eql(x, y)
+ VALUE x, y;
{
- return rb_funcall(arg->y, rb_intern("=="), 1, arg->x);
-}
+ if (TYPE(x) != TYPE(y)) return FALSE;
-static VALUE
-eq_rescue()
-{
- return FALSE;
+ return rb_equal(x, y);
}
+
static VALUE
num_equal(x, y)
VALUE x, y;
{
- struct xy arg;
- arg.x = x; arg.y = y;
- return rb_rescue(eq, &arg, eq_rescue, Qnil);
+ return rb_equal(y, x);
}
static VALUE
@@ -364,13 +327,11 @@ flo_eq(x, y)
struct RFloat *x, *y;
{
switch (TYPE(y)) {
- case T_NIL:
- return Qnil;
case T_FIXNUM:
if (x->value == FIX2INT(y)) return TRUE;
return FALSE;
case T_BIGNUM:
- return float_new(x->value == big2dbl(y));
+ return (x->value == big2dbl(y))?TRUE:FALSE;
case T_FLOAT:
return (x->value == y->value)?TRUE:FALSE;
default:
@@ -463,14 +424,15 @@ static VALUE
fail_to_integer(val)
VALUE val;
{
- Fail("failed to convert %s into integer", rb_class2name(CLASS_OF(val)));
+ TypeError("failed to convert %s into Integer",
+ rb_class2name(CLASS_OF(val)));
}
int
num2int(val)
VALUE val;
{
- if (val == Qnil) return 0;
+ if (NIL_P(val)) return 0;
switch (TYPE(val)) {
case T_FIXNUM:
@@ -483,7 +445,6 @@ num2int(val)
}
else {
Fail("float %g out of rang of integer", RFLOAT(val)->value);
- return Qnil; /* not reached */
}
case T_BIGNUM:
@@ -501,7 +462,7 @@ num2fix(val)
{
int v;
- if (val == Qnil) return INT2FIX(0);
+ if (NIL_P(val)) return INT2FIX(0);
switch (TYPE(val)) {
case T_FIXNUM:
return val;
@@ -511,7 +472,7 @@ num2fix(val)
default:
v = num2int(val);
if (!FIXABLE(v))
- Fail("integer %d out of rang of Fixnum", v);
+ Fail("integer %d out of range of Fixnum", v);
return INT2FIX(v);
}
}
@@ -524,6 +485,13 @@ int_int_p(num)
}
static VALUE
+int_succ(num)
+ VALUE num;
+{
+ return rb_funcall(num, '+', 1, INT2FIX(1));
+}
+
+static VALUE
fix_uminus(num)
VALUE num;
{
@@ -541,7 +509,7 @@ fix2str(x, base)
if (base == 10) fmt[1] = 'd';
else if (base == 16) fmt[1] = 'x';
else if (base == 8) fmt[1] = 'o';
- else Fail("fixnum cannot treat base %d", base);
+ else Fatal("fixnum cannot treat base %d", base);
sprintf(buf, fmt, FIX2INT(x));
return str_new2(buf);
@@ -618,11 +586,17 @@ fix_mul(x, y)
switch (TYPE(y)) {
case T_FIXNUM:
{
- int a = FIX2INT(x), b = FIX2INT(y);
- int c = a * b;
- VALUE r = INT2FIX(c);
+ int a, b, c;
+ VALUE r;
- if (FIX2INT(r) != c) {
+ a = FIX2INT(x);
+ if (a == 0) return x;
+
+ b = FIX2INT(y);
+ c = a * b;
+ r = INT2FIX(c);
+
+ if (FIX2INT(r) != c || c/a != b) {
r = big_mul(int2big(a), int2big(b));
}
return r;
@@ -643,7 +617,7 @@ fix_div(x, y)
if (TYPE(y) == T_FIXNUM) {
i = FIX2INT(y);
- if (i == 0) Fail("devided by 0");
+ if (i == 0) num_zerodiv();
i = FIX2INT(x)/i;
return INT2FIX(i);
}
@@ -658,7 +632,7 @@ fix_mod(x, y)
if (TYPE(y) == T_FIXNUM) {
i = FIX2INT(y);
- if (i == 0) Fail("devided by 0");
+ if (i == 0) num_zerodiv();
i = FIX2INT(x)%i;
return INT2FIX(i);
}
@@ -669,8 +643,6 @@ static VALUE
fix_pow(x, y)
VALUE x, y;
{
- extern double pow();
-
if (FIXNUM_P(y)) {
int a, b;
@@ -695,9 +667,6 @@ fix_equal(x, y)
if (FIXNUM_P(y)) {
return (FIX2INT(x) == FIX2INT(y))?TRUE:FALSE;
}
- else if (NIL_P(y)) {
- return Qnil;
- }
else {
return num_equal(x, y);
}
@@ -837,7 +806,7 @@ fix_lshift(x, y)
val = NUM2INT(x);
width = NUM2INT(y);
if (width > (sizeof(VALUE)*CHAR_BIT-1)
- || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-width) > 0) {
+ || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) {
return big_lshift(int2big(val), y);
}
val = val << width;
@@ -848,10 +817,15 @@ static VALUE
fix_rshift(x, y)
VALUE x, y;
{
- long val;
+ long i, val;
- val = RSHIFT(NUM2INT(x), NUM2INT(y));
- return INT2FIX(val);
+ i = NUM2INT(y);
+ if (y < 32) {
+ val = RSHIFT(FIX2INT(x), i);
+ return INT2FIX(val);
+ }
+
+ return INT2FIX(0);
}
static VALUE
@@ -890,7 +864,7 @@ static VALUE
fix_type(fix)
VALUE fix;
{
- return cFixnum;
+ return str_new2("Fixnum");
}
static VALUE
@@ -914,7 +888,7 @@ fix_id2name(fix)
}
static VALUE
-fix_next(fix)
+fix_succ(fix)
VALUE fix;
{
int i = FIX2INT(fix) + 1;
@@ -922,7 +896,153 @@ fix_next(fix)
return int2inum(i);
}
+static VALUE
+fix_size(fix)
+ VALUE fix;
+{
+ return INT2FIX(sizeof(VALUE));
+}
+
+VALUE
+num_upto(from, to)
+ VALUE from, to;
+{
+ VALUE i = from;
+
+ for (;;) {
+ if (RTEST(rb_funcall(i, '>', 1, to))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, INT2FIX(1));
+ }
+ return from;
+}
+
+static VALUE
+num_downto(from, to)
+ VALUE from, to;
+{
+ VALUE i = from;
+
+ for (;;) {
+ if (RTEST(rb_funcall(i, '<', 1, to))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '-', 1, INT2FIX(1));
+ }
+ return from;
+}
+
+static VALUE
+num_step(from, to, step)
+ VALUE from, to, step;
+{
+ VALUE i = from;
+ ID cmp;
+
+ if (step == INT2FIX(0)) {
+ IndexError("step cannot be 0");
+ }
+
+ if (RTEST(rb_funcall(step, '>', 1, INT2FIX(0)))) {
+ cmp = '>';
+ }
+ else {
+ cmp = '<';
+ }
+ for (;;) {
+ if (RTEST(rb_funcall(i, cmp, 1, to))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, step);
+ }
+ return from;
+}
+
+static VALUE
+num_dotimes(num)
+ VALUE num;
+{
+ VALUE i = INT2FIX(0);
+
+ for (;;) {
+ if (!RTEST(rb_funcall(i, '<', 1, num))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, INT2FIX(1));
+ }
+ return num;
+}
+
+VALUE
+fix_upto(from, to)
+ VALUE from, to;
+{
+ int i, end;
+
+ if (!FIXNUM_P(to)) return num_upto(from, to);
+ end = FIX2INT(to);
+ for (i = FIX2INT(from); i <= end; i++) {
+ rb_yield(INT2FIX(i));
+ }
+
+ return from;
+}
+
+static VALUE
+fix_downto(from, to)
+ VALUE from, to;
+{
+ int i, end;
+
+ if (!FIXNUM_P(to)) return num_downto(from, to);
+ end = FIX2INT(to);
+ for (i=FIX2INT(from); i >= end; i--) {
+ rb_yield(INT2FIX(i));
+ }
+
+ return from;
+}
+
+static VALUE
+fix_step(from, to, step)
+ VALUE from, to, step;
+{
+ int i, end, diff;
+
+ if (!FIXNUM_P(to) || !FIXNUM_P(step))
+ return num_step(from, to, step);
+
+ end = FIX2INT(to);
+ diff = FIX2INT(step);
+
+ if (diff == 0) {
+ ArgError("step cannot be 0");
+ }
+ else if (diff > 0) {
+ for (i=FIX2INT(from); i <= end; i+=diff) {
+ rb_yield(INT2FIX(i));
+ }
+ }
+ else {
+ for (i=FIX2INT(from); i >= end; i+=diff) {
+ rb_yield(INT2FIX(i));
+ }
+ }
+ return from;
+}
+
+static VALUE
+fix_dotimes(num)
+ VALUE num;
+{
+ int i, end;
+
+ end = FIX2INT(num);
+ for (i=0; i<end; i++) {
+ rb_yield(INT2FIX(i));
+ }
+ return num;
+}
+
extern VALUE mComparable;
+extern VALUE eException;
void
Init_Numeric()
@@ -930,16 +1050,18 @@ Init_Numeric()
coerce = rb_intern("coerce");
to_i = rb_intern("to_i");
+ eZeroDiv = rb_define_class("ZeroDivisionError", eException);
cNumeric = rb_define_class("Numeric", cObject);
- rb_undef_method(CLASS_OF(cNumeric), "new");
-
rb_include_module(cNumeric, mComparable);
+ rb_define_method(cNumeric, "coerce", num_coerce, 1);
+
rb_define_method(cNumeric, "+@", num_uplus, 0);
rb_define_method(cNumeric, "-@", num_uminus, 0);
+ rb_define_method(cNumeric, "eql?", num_eql, 1);
rb_define_method(cNumeric, "divmod", num_divmod, 1);
+ rb_define_method(cNumeric, "abs", num_abs, 0);
- 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);
@@ -949,9 +1071,12 @@ Init_Numeric()
cInteger = rb_define_class("Integer", cNumeric);
rb_define_method(cInteger, "integer?", int_int_p, 0);
+ rb_define_method(cInteger, "succ", int_succ, 0);
cFixnum = rb_define_class("Fixnum", cInteger);
+ rb_undef_method(CLASS_OF(cFixnum), "new");
+
rb_define_method(cFixnum, "to_s", fix_to_s, 0);
rb_define_method(cFixnum, "type", fix_type, 0);
@@ -986,10 +1111,18 @@ Init_Numeric()
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);
+ rb_define_method(cFixnum, "succ", fix_succ, 0);
+ rb_define_method(cFixnum, "size", fix_size, 0);
+
+ rb_define_method(cFixnum, "upto", fix_upto, 1);
+ rb_define_method(cFixnum, "downto", fix_downto, 1);
+ rb_define_method(cFixnum, "step", fix_step, 2);
+ rb_define_method(cFixnum, "times", fix_dotimes, 0);
cFloat = rb_define_class("Float", cNumeric);
+ rb_undef_method(CLASS_OF(cFloat), "new");
+
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);
diff --git a/object.c b/object.c
index 74324b77e7..5b18835613 100644
--- a/object.c
+++ b/object.c
@@ -6,7 +6,7 @@
$Date: 1995/01/12 08:54:49 $
created at: Thu Jul 15 12:01:24 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -18,24 +18,39 @@ VALUE cKernel;
VALUE cObject;
VALUE cModule;
VALUE cClass;
-VALUE cNil;
+VALUE cFixnum;
VALUE cData;
+static VALUE cNil;
+static VALUE cTrue;
+static VALUE cFalse;
+
struct st_table *new_idhash();
VALUE f_sprintf();
VALUE obj_alloc();
-static ID eq;
-
-static ID init;
+static ID eq, eql;
+static ID inspect;
-VALUE
+int
rb_equal(obj1, obj2)
VALUE obj1, obj2;
{
- return rb_funcall(obj1, eq, 1, obj2);
+ VALUE result;
+
+ result = rb_funcall(obj1, eq, 1, obj2);
+ if (result == FALSE || NIL_P(result))
+ return FALSE;
+ return TRUE;
+}
+
+int
+rb_eql(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ return rb_funcall(obj1, eql, 1, obj2);
}
static VALUE
@@ -67,11 +82,13 @@ krn_id(obj)
return obj | FIXNUM_FLAG;
}
+char *rb_class2path();
+
static VALUE
krn_type(obj)
struct RBasic *obj;
{
- return obj->class;
+ return rb_class_path(obj->class);
}
static VALUE
@@ -81,7 +98,7 @@ krn_clone(obj)
VALUE clone;
if (TYPE(obj) != T_OBJECT) {
- Fail("can't clone %s", rb_class2name(CLASS_OF(obj)));
+ TypeError("can't clone %s", rb_class2name(CLASS_OF(obj)));
}
clone = obj_alloc(RBASIC(obj)->class);
@@ -89,6 +106,7 @@ krn_clone(obj)
ROBJECT(clone)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl);
}
RBASIC(clone)->class = singleton_class_clone(RBASIC(obj)->class);
+ RBASIC(clone)->flags = RBASIC(obj)->flags;
return clone;
}
@@ -111,6 +129,13 @@ krn_to_s(obj)
}
VALUE
+rb_inspect(obj)
+ VALUE obj;
+{
+ return rb_funcall(obj, inspect, 0, 0);
+}
+
+static VALUE
krn_inspect(obj)
VALUE obj;
{
@@ -126,8 +151,11 @@ inspect_i(id, value, str)
VALUE str2;
char *ivname;
+ /* need not to show internal data */
+ if (TYPE(value) == T_DATA) return ST_CONTINUE;
if (str->ptr[0] == '-') {
str->ptr[0] = '#';
+ str_cat(str, ": ", 2);
}
else {
str_cat(str, ", ", 2);
@@ -135,7 +163,12 @@ inspect_i(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, 0);
+ if (TYPE(value) == T_OBJECT) {
+ str2 = krn_to_s(value);
+ }
+ else {
+ str2 = rb_inspect(value);
+ }
str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
return ST_CONTINUE;
@@ -145,25 +178,20 @@ static VALUE
obj_inspect(obj)
struct RObject *obj;
{
- VALUE str;
- char buf[256];
+ if (TYPE(obj) == T_OBJECT && obj->iv_tbl) {
+ VALUE str;
+ char buf[256];
- 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, inspect_i, str);
+ str_cat(str, ">", 1);
+ if (RSTRING(str)->ptr[0] == '-') /* no instance-var */
+ return krn_inspect(obj);
- sprintf(buf, "-<%s: ", rb_class2name(CLASS_OF(obj)));
- str = str_new2(buf);
- st_foreach(obj->iv_tbl, inspect_i, str);
- str_cat(str, ">", 1);
-
- return str;
+ return str;
+ }
+ return krn_inspect(obj);
}
VALUE
@@ -177,10 +205,10 @@ obj_is_instance_of(obj, c)
case T_CLASS:
break;
default:
- Fail("class or module required");
+ TypeError("class or module required");
}
- while (FL_TEST(class, FL_SINGLE)) {
+ while (FL_TEST(class, FL_SINGLETON)) {
class = class->super;
}
if (c == (VALUE)class) return TRUE;
@@ -198,7 +226,7 @@ obj_is_kind_of(obj, c)
case T_CLASS:
break;
default:
- Fail("class or module required");
+ TypeError("class or module required");
}
while (class) {
@@ -234,14 +262,21 @@ static VALUE
nil_to_s(obj)
VALUE obj;
{
+ return str_new2("");
+}
+
+static VALUE
+nil_inspect(obj)
+ VALUE obj;
+{
return str_new2("nil");
}
static VALUE
-nil_type(nil)
- VALUE nil;
+nil_type(obj)
+ VALUE obj;
{
- return cNil;
+ return str_new2("Nil");
}
static VALUE
@@ -256,10 +291,10 @@ nil_plus(x, y)
case T_ARRAY:
return y;
default:
- Fail("tried to add %s(%s) to nil",
- RSTRING(obj_as_string(y))->ptr, rb_class2name(CLASS_OF(y)));
+ TypeError("tried to add %s(%s) to nil",
+ RSTRING(obj_as_string(y))->ptr, rb_class2name(CLASS_OF(y)));
}
- return Qnil; /* not reached */
+ /* not reached */
}
static VALUE
@@ -273,29 +308,38 @@ static VALUE
true_to_s(obj)
VALUE obj;
{
- return str_new2("t");
+ return str_new2("TRUE");
}
-VALUE
-obj_alloc(class)
- VALUE class;
+static VALUE
+true_type(obj)
+ VALUE obj;
{
- NEWOBJ(obj, struct RObject);
- OBJSETUP(obj, class, T_OBJECT);
+ return str_new2("TRUE");
+}
- return (VALUE)obj;
+static VALUE
+false_to_s(obj)
+ VALUE obj;
+{
+ return str_new2("FALSE");
}
static VALUE
-mod_new(argc, argv, class)
- int argc;
- VALUE *argv;
+false_type(obj)
+ VALUE obj;
+{
+ return str_new2("FALSE");
+}
+
+VALUE
+obj_alloc(class)
VALUE class;
{
- VALUE obj = obj_alloc(class);
+ NEWOBJ(obj, struct RObject);
+ OBJSETUP(obj, class, T_OBJECT);
- rb_funcall2(obj, init, argc, argv);
- return obj;
+ return (VALUE)obj;
}
static VALUE
@@ -311,8 +355,6 @@ mod_clone(module)
return (VALUE)clone;
}
-char *rb_class2path();
-
static VALUE
mod_to_s(class)
VALUE class;
@@ -320,6 +362,20 @@ mod_to_s(class)
return rb_class_path(class);
}
+VALUE class_s_new(); /* moved to eval.c */
+
+static VALUE
+class_superclass(class)
+ struct RClass *class;
+{
+ struct RClass *super = class->super;
+
+ while (TYPE(super) == T_ICLASS)
+ super = super->super;
+
+ return (VALUE)super;
+}
+
ID
rb_to_id(name)
VALUE name;
@@ -327,7 +383,8 @@ rb_to_id(name)
if (TYPE(name) == T_STRING) {
return rb_intern(RSTRING(name)->ptr);
}
- return NUM2INT(name);
+ Check_Type(name, T_FIXNUM);
+ return FIX2INT(name);
}
static VALUE
@@ -360,8 +417,82 @@ mod_private_attr(class, name)
return Qnil;
}
-static
-VALUE boot_defclass(name, super)
+static VALUE
+f_integer(obj, arg)
+ VALUE obj, arg;
+{
+ int i;
+
+ switch (TYPE(arg)) {
+ case T_FLOAT:
+ if (RFLOAT(arg)->value <= (double)FIXNUM_MAX
+ && RFLOAT(arg)->value >= (double)FIXNUM_MIN) {
+ i = (int)RFLOAT(arg)->value;
+ break;
+ }
+ return dbl2big(RFLOAT(arg)->value);
+
+ case T_BIGNUM:
+ return arg;
+
+ case T_STRING:
+ return str2inum(RSTRING(arg)->ptr, 0);
+
+ default:
+ i = NUM2INT(arg);
+ }
+ return INT2NUM(i);
+}
+
+static VALUE
+to_flo(val)
+ VALUE val;
+{
+ return rb_funcall(val, rb_intern("to_f"), 0);
+}
+
+static VALUE
+fail_to_flo(val)
+ VALUE val;
+{
+ TypeError("failed to convert %s into Float", rb_class2name(CLASS_OF(val)));
+}
+
+double big2dbl();
+
+VALUE
+f_float(obj, arg)
+ VALUE obj, arg;
+{
+
+ switch (TYPE(arg)) {
+ case T_FLOAT:
+ return arg;
+
+ case T_BIGNUM:
+ return float_new(big2dbl(arg));
+
+ default:
+ return rb_rescue(to_flo, arg, fail_to_flo, arg);
+ }
+}
+
+static VALUE
+f_string(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_funcall(arg, rb_intern("to_s"), 0);
+}
+
+static VALUE
+f_array(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_funcall(arg, rb_intern("to_a"), 0);
+}
+
+static VALUE
+boot_defclass(name, super)
char *name;
VALUE super;
{
@@ -371,19 +502,53 @@ VALUE boot_defclass(name, super)
rb_name_class(obj, id);
st_add_direct(rb_class_tbl, id, obj);
- rb_set_class_path(obj, 0, name);
return (VALUE)obj;
}
+VALUE
+rb_class_of(obj)
+ VALUE obj;
+{
+ if (FIXNUM_P(obj)) return cFixnum;
+ if (obj == Qnil) return cNil;
+ if (obj == FALSE) return cFalse;
+ if (obj == TRUE) return cTrue;
+
+ return RBASIC(obj)->class;
+}
+
+int
+rb_type(obj)
+ VALUE obj;
+{
+ if (FIXNUM_P(obj)) return T_FIXNUM;
+ if (obj == Qnil) return T_NIL;
+ if (obj == FALSE) return T_FALSE;
+ if (obj == TRUE) return T_TRUE;
+
+ return BUILTIN_TYPE(obj);
+}
+
+int
+rb_special_const_p(obj)
+ VALUE obj;
+{
+ if (FIXNUM_P(obj)) return TRUE;
+ if (obj == Qnil) return TRUE;
+ if (obj == FALSE) return TRUE;
+ if (obj == TRUE) return TRUE;
+
+ return FALSE;
+}
+
VALUE TopSelf;
-VALUE TRUE = INT2FIX(1);
void
Init_Object()
{
VALUE metaclass;
- cKernel = boot_defclass("Kernel", Qnil);
+ cKernel = boot_defclass("kernel", 0);
cObject = boot_defclass("Object", cKernel);
cModule = boot_defclass("Module", cObject);
cClass = boot_defclass("Class", cModule);
@@ -396,39 +561,40 @@ Init_Object()
/*
* Ruby's Class Hierarchy Chart
*
- * +-------nil +---------------------+
- * | ^ | |
- * | | | |
- * | Kernel----->(Kernel) |
- * | ^ ^ ^ ^ |
- * | | | | | |
- * | +---+ +----+ | +---+ |
- * | | +-----|----+ | |
- * | | | | | |
- * +->Nil->(Nil) Object---->(Object) |
- * ^ ^ ^ ^ |
- * | | | | |
- * | | +-------+ | |
- * | | | | |
- * | +---------+ +------+ |
- * | | | | |
- * +---------+ | Module--->(Module) |
- * | | ^ ^ |
- * OtherClass-->(OtherClass) | | |
- * Class---->(Class) |
- * ^ |
- * | |
- * +----------------+
+ * +------------------------+
+ * | |
+ * kernel----->(kernel) |
+ * ^ ^ ^ ^ |
+ * | | | | |
+ * +---+ +----+ | +---+ |
+ * | +-----|----+ | |
+ * | | | | |
+ * Nil->(Nil) Object---->(Object) |
+ * ^ ^ ^ ^ |
+ * | | | | |
+ * | | +-----+ +---------+ |
+ * | | | | |
+ * | +-----------+ | |
+ * | | | | |
+ * +------+ | Module--->(Module) |
+ * | | ^ ^ |
+ * OtherClass-->(OtherClass) | | |
+ * Class---->(Class) |
+ * ^ |
+ * | |
+ * +----------------+
*
* + All metaclasses are instances of the class `Class'.
*/
-
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_alias(cKernel, "=~", "==");
+ rb_define_method(cKernel, "eql?", rb_equal, 1);
+
rb_define_method(cKernel, "hash", krn_id, 0);
rb_define_method(cKernel, "id", krn_id, 0);
rb_define_method(cKernel, "type", krn_type, 0);
@@ -440,14 +606,32 @@ Init_Object()
rb_define_method(cKernel, "to_s", krn_to_s, 0);
rb_define_method(cKernel, "inspect", krn_inspect, 0);
+ rb_define_method(cKernel, "instance_of?", obj_is_instance_of, 1);
+ rb_define_method(cKernel, "kind_of?", obj_is_kind_of, 1);
+ rb_define_method(cKernel, "is_a?", obj_is_kind_of, 1);
+
rb_define_private_method(cKernel, "sprintf", f_sprintf, -1);
rb_define_alias(cKernel, "format", "sprintf");
+ rb_define_private_method(cKernel, "Integer", f_integer, 1);
+ rb_define_private_method(cKernel, "Float", f_float, 1);
+
+ rb_define_private_method(cKernel, "String", f_string, 1);
+ rb_define_private_method(cKernel, "Array", f_array, 1);
+
+ cNil = rb_define_class("nil", cKernel);
+ rb_define_method(cNil, "type", nil_type, 0);
+ rb_define_method(cNil, "to_s", nil_to_s, 0);
+ rb_define_method(cNil, "inspect", nil_inspect, 0);
+
+ rb_define_method(cNil, "nil?", nil_nil_p, 0);
+
+ /* default addition */
+ rb_define_method(cNil, "+", nil_plus, 1);
+
rb_define_private_method(cObject, "initialize", obj_initialize, -1);
rb_define_private_method(cObject, "singleton_method_added", obj_s_added, 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(cModule, "to_s", mod_to_s, 0);
@@ -455,29 +639,27 @@ Init_Object()
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_private_method(cModule, "object_extended", obj_s_added, 1);
- rb_define_method(cClass, "new", mod_new, -1);
-
- 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(cNil, "nil?", nil_nil_p, 0);
-
- /* default addition */
- rb_define_method(cNil, "+", nil_plus, 1);
+ rb_define_method(cClass, "new", class_s_new, -1);
+ rb_define_method(cClass, "superclass", class_superclass, -1);
cData = rb_define_class("Data", cKernel);
- eq = rb_intern("==");
-
- Qself = TopSelf = obj_alloc(cObject);
+ TopSelf = obj_alloc(cObject);
rb_define_singleton_method(TopSelf, "to_s", main_to_s, 0);
- 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);
+ cTrue = rb_define_class("true", cKernel);
+ rb_define_method(cTrue, "to_s", true_to_s, 0);
+ rb_define_method(cTrue, "type", true_type, 0);
+ rb_define_global_const("TRUE", TRUE);
- init = rb_intern("initialize");
+ cFalse = rb_define_class("false", cKernel);
+ rb_define_method(cFalse, "to_s", false_to_s, 0);
+ rb_define_method(cFalse, "type", false_type, 0);
+ rb_define_global_const("FALSE", FALSE);
+
+ eq = rb_intern("==");
+ eql = rb_intern("eql?");
+ inspect = rb_intern("inspect");
}
diff --git a/pack.c b/pack.c
index ed98970d36..3e54b2ba3a 100644
--- a/pack.c
+++ b/pack.c
@@ -6,7 +6,7 @@
$Date: 1994/12/09 09:40:22 $
created at: Thu Feb 10 15:17:05 JST 1994
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -20,19 +20,23 @@
+(((x)&0x0000FF00)<<8) \
+(((x)&0x00FF0000)>>8) )
#ifdef WORDS_BIGENDIAN
+#ifndef ntohs
#define ntohs(x) (x)
#define ntohl(x) (x)
#define htons(x) (x)
#define htonl(x) (x)
+#endif
#define htovs(x) swaps(x)
#define htovl(x) swapl(x)
#define vtohs(x) swaps(x)
#define vtohl(x) swapl(x)
#else /* LITTLE ENDIAN */
+#ifndef ntohs
#define ntohs(x) swaps(x)
#define ntohl(x) swapl(x)
#define htons(x) swaps(x)
#define htonl(x) swapl(x)
+#endif
#define htovs(x) (x)
#define htovl(x) (x)
#define vtohs(x) (x)
@@ -70,7 +74,7 @@ pack_pack(ary, fmt)
items = ary->len;
idx = 0;
-#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : (Fail(toofew),0))
+#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : (ArgError(toofew),0))
while (p < pend) {
type = *p++; /* get data type */
@@ -91,7 +95,7 @@ pack_pack(ary, fmt)
case 'B': case 'b':
case 'H': case 'h':
from = NEXTFROM;
- if (from == Qnil) {
+ if (NIL_P(from)) {
ptr = 0;
plen = 0;
}
@@ -106,30 +110,20 @@ pack_pack(ary, fmt)
switch (type) {
case 'a':
- if (plen > len)
- str_cat(res, ptr, len);
- else {
- str_cat(res, ptr, plen);
- len = plen;
- while (len >= 10) {
- str_cat(res, nul10, 10);
- len -= 10;
- }
- str_cat(res, nul10, len);
- }
- break;
-
case 'A':
- if (plen > len)
+ if (plen >= len)
str_cat(res, ptr, len);
else {
str_cat(res, ptr, plen);
- len = plen;
+ len -= plen;
while (len >= 10) {
- str_cat(res, spc10, 10);
+ if (type == 'A')
+ str_cat(res, spc10, 10);
+ else
+ str_cat(res, nul10, 10);
len -= 10;
}
- str_cat(res, spc10, len);
+ str_cat(res, nul10, len);
}
break;
@@ -244,7 +238,7 @@ pack_pack(ary, fmt)
char c;
from = NEXTFROM;
- if (from == Qnil) c = 0;
+ if (NIL_P(from)) c = 0;
else {
c = NUM2INT(from);
}
@@ -258,7 +252,7 @@ pack_pack(ary, fmt)
short s;
from = NEXTFROM;
- if (from == Qnil) s = 0;
+ if (NIL_P(from)) s = 0;
else {
s = NUM2INT(from);
}
@@ -272,7 +266,7 @@ pack_pack(ary, fmt)
int i;
from = NEXTFROM;
- if (from == Qnil) i = 0;
+ if (NIL_P(from)) i = 0;
else {
i = NUM2INT(from);
}
@@ -286,7 +280,7 @@ pack_pack(ary, fmt)
long l;
from = NEXTFROM;
- if (from == Qnil) l = 0;
+ if (NIL_P(from)) l = 0;
else {
l = NUM2INT(from);
}
@@ -299,7 +293,7 @@ pack_pack(ary, fmt)
short s;
from = NEXTFROM;
- if (from == Qnil) s = 0;
+ if (NIL_P(from)) s = 0;
else {
s = NUM2INT(from);
}
@@ -313,7 +307,7 @@ pack_pack(ary, fmt)
long l;
from = NEXTFROM;
- if (from == Qnil) l = 0;
+ if (NIL_P(from)) l = 0;
else {
l = NUM2INT(from);
}
@@ -367,7 +361,7 @@ pack_pack(ary, fmt)
short s;
from = NEXTFROM;
- if (from == Qnil) s = 0;
+ if (NIL_P(from)) s = 0;
else {
s = NUM2INT(from);
}
@@ -381,7 +375,7 @@ pack_pack(ary, fmt)
long l;
from = NEXTFROM;
- if (from == Qnil) l = 0;
+ if (NIL_P(from)) l = 0;
else {
l = NUM2INT(from);
}
@@ -402,7 +396,7 @@ pack_pack(ary, fmt)
case 'X':
shrink:
if (RSTRING(res)->len < len)
- Fail("X outside of string");
+ ArgError("X outside of string");
RSTRING(res)->len -= len;
RSTRING(res)->ptr[RSTRING(res)->len] = '\0';
break;
@@ -415,7 +409,7 @@ pack_pack(ary, fmt)
break;
case '%':
- Fail("% may only be used in unpack");
+ ArgError("% may only be used in unpack");
break;
case 'u':
@@ -457,7 +451,6 @@ encodes(str, s, len)
char hunk[4];
char *p, *pend;
- p = str->ptr + str->len;
*hunk = len + ' ';
str_cat(str, hunk, 1);
while (len > 0) {
@@ -469,6 +462,7 @@ encodes(str, s, len)
s += 3;
len -= 3;
}
+ p = str->ptr;
pend = str->ptr + str->len;
while (p < pend) {
if (*p == ' ')
@@ -512,7 +506,7 @@ pack_unpack(str, fmt)
switch (type) {
case '%':
- Fail("% is not supported(yet)");
+ ArgError("% is not supported(yet)");
break;
case 'A':
@@ -616,7 +610,10 @@ pack_unpack(str, fmt)
if (len > send - s)
len = send - s;
while (len-- > 0) {
- char c = *s++;
+ int c = *s++;
+#ifdef __CHAR_UNSIGNED__
+ if (c > (char)127) c-=256;
+#endif
ary_push(ary, INT2FIX(c));
}
break;
@@ -781,6 +778,7 @@ pack_unpack(str, fmt)
hunk[3] = '\0';
len = (*s++ - ' ') & 077;
total += len;
+
while (len > 0) {
if (s < send && *s >= ' ')
a = (*s++ - ' ') & 077;
@@ -805,10 +803,10 @@ pack_unpack(str, fmt)
ptr += 3;
len -= 3;
}
- if (*s == '\n')
+ if (*s == '\n' || *s == '\r')
s++;
- else if (s[1] == '\n') /* possible checksum byte */
- s += 2;
+ else if (s+1 == send || s[1] == '\n' || s[1] == '\r')
+ s += 2; /* possible checksum byte */
}
RSTRING(str)->len = total;
ary_push(ary, str);
@@ -821,13 +819,13 @@ pack_unpack(str, fmt)
case 'X':
if (len > s - str->ptr)
- Fail("X outside of string");
+ ArgError("X outside of string");
s -= len;
break;
case 'x':
if (len > send - s)
- Fail("x outside of string");
+ ArgError("x outside of string");
s += len;
break;
diff --git a/parse.y b/parse.y
index cdecb958bc..741dd2573b 100644
--- a/parse.y
+++ b/parse.y
@@ -6,7 +6,7 @@
$Date: 1995/01/12 08:54:50 $
created at: Fri May 28 18:02:42 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -26,11 +26,11 @@
#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_LOCAL 0x01
+#define ID_INSTANCE 0x02
+#define ID_GLOBAL 0x03
+#define ID_ATTRSET 0x04
+#define ID_CONST 0x05
#define is_id_nonop(id) ((id)>LAST_TOKEN)
#define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
@@ -50,6 +50,7 @@ char *sourcefile; /* current source file */
int sourceline; /* current line no. */
static int yylex();
+static int yyerror();
static enum lex_state {
EXPR_BEG, /* ignore newline, +/- is a sign. */
@@ -65,6 +66,7 @@ static ID cur_mid = 0;
static int value_expr();
static NODE *cond();
+static NODE *logop();
static NODE *block_append();
static NODE *list_append();
@@ -90,12 +92,12 @@ 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)
+VALUE dyna_var_asgn();
+VALUE dyna_var_defined();
-#define cref_push() NEW_CREF(0)
+#define cref_push() NEW_CREF()
static void cref_pop();
-static NODE *cref_list;
+static NODE *cur_cref;
static void top_local_init();
static void top_local_setup();
@@ -118,46 +120,45 @@ static void top_local_setup();
ENSURE
END
IF
+ UNLESS
THEN
ELSIF
ELSE
CASE
WHEN
WHILE
+ UNTIL
FOR
IN
- REDO
- BREAK
- CONTINUE
+ DO
RETURN
- FAIL
YIELD
SUPER
- RETRY
SELF
NIL
AND
OR
NOT
- _FILE_
- _LINE_
IF_MOD
+ UNLESS_MOD
WHILE_MOD
+ UNTIL_MOD
ALIAS
DEFINED
%token <id> IDENTIFIER FID GVAR IVAR CONSTANT
%token <val> INTEGER FLOAT STRING XSTRING REGEXP
-%token <node> STRING2 XSTRING2 DREGEXP NTH_REF BACK_REF
+%token <node> DSTRING DXSTRING 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 rescue ensure
-%type <node> call_args call_args0 args args2 opt_args
+%type <node> compexpr exprs expr arg primary command_call method_call
+%type <node> if_tail opt_else case_body cases rescue ensure iterator
+%type <node> call_args call_args0 args args2 opt_args var_ref
%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 <node> iter_var opt_iter_var iter_block iter_do_block
+%type <node> mlhs mlhs_head mlhs_tail lhs backref
%type <id> variable symbol operation
%type <id> cname fname op rest_arg
%type <num> f_arg
@@ -166,6 +167,7 @@ static void top_local_setup();
%token POW /* ** */
%token CMP /* <=> */
%token EQ /* == */
+%token EQQ /* === */
%token NEQ /* != <> */
%token GEQ /* >= */
%token LEQ /* <= */
@@ -187,14 +189,15 @@ static void top_local_setup();
* precedence table
*/
-%left IF_MOD WHILE_MOD
-%right NOT
+%left IF_MOD UNLESS_MOD WHILE_MOD UNTIL_MOD
%left OR AND
+%right NOT
+%nonassoc DEFINED
%right '=' OP_ASGN
%nonassoc DOT2 DOT3
%left OROP
%left ANDOP
-%nonassoc CMP EQ NEQ MATCH NMATCH
+%nonassoc CMP EQ EQQ NEQ MATCH NMATCH
%left '>' GEQ '<' LEQ
%left '|' '^'
%left '&'
@@ -203,7 +206,6 @@ static void top_local_setup();
%left '*' '/' '%'
%right '!' '~' UPLUS UMINUS
%right POW
-%left COLON2
%token LAST_TOKEN
@@ -211,32 +213,29 @@ static void top_local_setup();
program : {
lex_state = EXPR_BEG;
top_local_init();
+ NEW_CREF0(); /* initialize constant c-ref */
}
compexpr
{
eval_tree = block_append(eval_tree, $2);
top_local_setup();
+ cur_cref = 0;
}
-compexpr : exprs opt_term
+compexpr : exprs opt_terms
exprs : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| expr
- | exprs term expr
+ | exprs terms expr
{
$$ = block_append($1, $3);
}
- | exprs error
- {
- lex_state = EXPR_BEG;
- }
- expr
+ | error expr
{
- yyerrok;
- $$ = block_append($1, $4);
+ $$ = $2;
}
expr : mlhs '=' args2
@@ -253,41 +252,18 @@ expr : mlhs '=' args2
{
value_expr($2);
if (!cur_mid && !in_single)
- Error("return appeared outside of method");
+ yyerror("return appeared outside of method");
$$ = NEW_RET($2);
}
- | FAIL args2
- {
- value_expr($2);
- $$ = NEW_FAIL($2);
- }
| YIELD args2
{
value_expr($2);
$$ = NEW_YIELD($2);
}
- | DEFINED {in_defined = 1;} arg
- {
- in_defined = 0;
- $$ = NEW_DEFINED($3);
- }
- | operation call_args0
- {
- $$ = NEW_FCALL($1, $2);
- }
- | primary '.' operation call_args0
- {
- value_expr($1);
- $$ = NEW_CALL($1, $3, $4);
- }
- | SUPER call_args0
- {
- if (!cur_mid && !in_single && !in_defined)
- Error("super called outside of method");
- $$ = NEW_SUPER($2);
- }
- | UNDEF undef_list
+ | command_call
+ | iterator iter_do_block
{
+ $2->nd_iter = $1;
$$ = $2;
}
| ALIAS fname {lex_state = EXPR_FNAME;} fname
@@ -297,38 +273,76 @@ expr : mlhs '=' args2
| expr IF_MOD expr
{
value_expr($3);
- $$ = NEW_IF(cond($3), $1, Qnil);
+ $$ = NEW_IF(cond($3), $1, 0);
+ }
+ | expr UNLESS_MOD expr
+ {
+ value_expr($3);
+ $$ = NEW_UNLESS(cond($3), $1, 0);
}
| expr WHILE_MOD expr
{
value_expr($3);
if (nd_type($1) == NODE_BEGIN) {
- $$ = NEW_WHILE2(cond($3), $1);
+ $$ = NEW_WHILE(cond($3), $1->nd_body, 0);
}
else {
- $$ = NEW_WHILE(cond($3), $1);
+ $$ = NEW_WHILE(cond($3), $1, 1);
+ }
+ }
+ | expr UNTIL_MOD expr
+ {
+ value_expr($3);
+ if (nd_type($1) == NODE_BEGIN) {
+ $$ = NEW_UNTIL(cond($3), $1->nd_body, 0);
+ }
+ else {
+ $$ = NEW_UNTIL(cond($3), $1, 1);
}
}
| expr AND expr
{
- value_expr($1);
- $$ = NEW_AND(cond($1), cond($3));
+ $$ = logop(NODE_AND, $1, $3);
}
| expr OR expr
{
- value_expr($1);
- $$ = NEW_OR(cond($1), cond($3));
+ $$ = logop(NODE_OR, $1, $3);
}
| NOT expr
{
value_expr($2);
$$ = NEW_NOT(cond($2));
}
+ | '!' command_call
+ {
+ value_expr($2);
+ $$ = NEW_NOT(cond($2));
+ }
| arg
+command_call : operation call_args0
+ {
+ $$ = NEW_FCALL($1, $2);
+ }
+ | primary '.' operation call_args0
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, $4);
+ }
+ | SUPER call_args0
+ {
+ if (!cur_mid && !in_single && !in_defined)
+ yyerror("super called outside of method");
+ $$ = NEW_SUPER($2);
+ }
+ | UNDEF undef_list
+ {
+ $$ = $2;
+ }
+
mlhs : mlhs_head
{
- $$ = NEW_MASGN(NEW_LIST($1), Qnil);
+ $$ = NEW_MASGN(NEW_LIST($1), 0);
}
| mlhs_head STAR lhs
{
@@ -336,51 +350,55 @@ mlhs : mlhs_head
}
| mlhs_head mlhs_tail
{
- $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2),Qnil);
+ $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2), 0);
}
- | mlhs_head mlhs_tail comma STAR lhs
+ | mlhs_head mlhs_tail ',' STAR lhs
{
$$ = NEW_MASGN(list_concat(NEW_LIST($1),$2),$5);
}
+ | STAR lhs
+ {
+ $$ = NEW_MASGN(0, $2);
+ }
-mlhs_head : lhs comma
+mlhs_head : lhs ','
mlhs_tail : lhs
{
$$ = NEW_LIST($1);
}
- | mlhs_tail comma lhs
+ | mlhs_tail ',' lhs
{
$$ = list_append($1, $3);
}
lhs : variable
{
- $$ = asignable($1, Qnil);
+ $$ = asignable($1, 0);
}
- | primary '[' opt_args opt_nl rbracket
+ | primary '[' opt_args opt_nl ']'
{
- $$ = aryset($1, $3, Qnil);
+ $$ = aryset($1, $3, 0);
}
| primary '.' IDENTIFIER
{
- $$ = attrset($1, $3, Qnil);
+ $$ = attrset($1, $3, 0);
}
| backref
{
backref_error($1);
- $$ = Qnil;
+ $$ = 0;
}
cname : IDENTIFIER
{
- Error("class/module name must be CONSTANT");
+ yyerror("class/module name must be CONSTANT");
}
| CONSTANT
fname : IDENTIFIER
- | FID
| CONSTANT
+ | FID
| op
{
lex_state = EXPR_END;
@@ -391,9 +409,9 @@ undef_list : fname
{
$$ = NEW_UNDEF($1);
}
- | undef_list ',' fname
+ | undef_list ',' {lex_state = EXPR_FNAME;} fname
{
- $$ = block_append($1, NEW_UNDEF($3));
+ $$ = block_append($1, NEW_UNDEF($4));
}
op : DOT2 { $$ = DOT2; }
@@ -402,6 +420,7 @@ op : DOT2 { $$ = DOT2; }
| '&' { $$ = '&'; }
| CMP { $$ = CMP; }
| EQ { $$ = EQ; }
+ | EQQ { $$ = EQQ; }
| MATCH { $$ = MATCH; }
| '>' { $$ = '>'; }
| GEQ { $$ = GEQ; }
@@ -421,13 +440,14 @@ op : DOT2 { $$ = DOT2; }
| UMINUS { $$ = UPLUS; }
| AREF { $$ = AREF; }
| ASET { $$ = ASET; }
+ | '`' { $$ = '`'; }
arg : variable '=' arg
{
value_expr($3);
$$ = asignable($1, $3);
}
- | primary '[' opt_args opt_nl rbracket '=' arg
+ | primary '[' opt_args opt_nl ']' '=' arg
{
$$ = aryset($1, $3, $7);
}
@@ -439,29 +459,16 @@ arg : variable '=' arg
{
value_expr($3);
backref_error($1);
- $$ = Qnil;
+ $$ = 0;
}
| variable OP_ASGN arg
{
- NODE *val;
-
value_expr($3);
- if (is_local_id($1)) {
- if (local_id($1)) val = NEW_LVAR($1);
- else val = NEW_DVAR($1);
- }
- else if (is_global_id($1)) {
- val = NEW_GVAR($1);
- }
- else if (is_instance_id($1)) {
- val = NEW_IVAR($1);
- }
- else {
- val = NEW_CVAR($1);
- }
- $$ = asignable($1, call_op(val, $2, 1, $3));
+ if (is_local_id($1)&&!local_id($1)&&dyna_in_block())
+ dyna_var_asgn($1, TRUE);
+ $$ = asignable($1, call_op(gettable($1), $2, 1, $3));
}
- | primary '[' opt_args opt_nl rbracket OP_ASGN arg
+ | primary '[' opt_args opt_nl ']' OP_ASGN arg
{
NODE *args = NEW_LIST($7);
@@ -470,12 +477,12 @@ arg : variable '=' arg
}
| primary '.' IDENTIFIER OP_ASGN arg
{
- $$ = NEW_OP_ASGN2($1, $4, $5);
+ $$ = NEW_OP_ASGN2($1, $3, $4, $5);
}
| backref OP_ASGN arg
{
backref_error($1);
- $$ = Qnil;
+ $$ = 0;
}
| arg DOT2 arg
{
@@ -553,6 +560,10 @@ arg : variable '=' arg
{
$$ = call_op($1, EQ, 1, $3);
}
+ | arg EQQ arg
+ {
+ $$ = call_op($1, EQQ, 1, $3);
+ }
| arg NEQ arg
{
$$ = NEW_NOT(call_op($1, EQ, 1, $3));
@@ -582,19 +593,18 @@ arg : variable '=' arg
{
$$ = call_op($1, RSHFT, 1, $3);
}
- | arg COLON2 cname
- {
- $$ = NEW_COLON2($1, $3);
- }
| arg ANDOP arg
{
- value_expr($1);
- $$ = NEW_AND(cond($1), cond($3));
+ $$ = logop(NODE_AND, $1, $3);
}
| arg OROP arg
{
- value_expr($1);
- $$ = NEW_OR(cond($1), cond($3));
+ $$ = logop(NODE_OR, $1, $3);
+ }
+ | DEFINED {in_defined = 1;} arg
+ {
+ in_defined = 0;
+ $$ = NEW_DEFINED($3);
}
| primary
{
@@ -603,25 +613,34 @@ arg : variable '=' arg
call_args : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| call_args0 opt_nl
call_args0 : args
+ | command_call
+ {
+ value_expr($1);
+ $$ = NEW_LIST($1);
+ }
| assocs
{
$$ = NEW_LIST(NEW_HASH($1));
}
- | args comma assocs
+ | args ',' command_call
+ {
+ $$ = list_append($1, $3);
+ }
+ | args ',' assocs
{
$$ = list_append($1, NEW_HASH($3));
}
- | args comma assocs comma STAR arg
+ | args ',' assocs ',' STAR arg
{
$$ = list_append($1, NEW_HASH($3));
$$ = call_op($$, '+', 1, $6);
}
- | args comma STAR arg
+ | args ',' STAR arg
{
$$ = call_op($1, '+', 1, $4);
}
@@ -632,7 +651,7 @@ call_args0 : args
opt_args : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| args
@@ -641,7 +660,7 @@ args : arg
value_expr($1);
$$ = NEW_LIST($1);
}
- | args comma arg
+ | args ',' arg
{
value_expr($3);
$$ = list_append($1, $3);
@@ -649,7 +668,7 @@ args : arg
args2 : args
{
- if ($1 && $1->nd_next == Qnil) {
+ if ($1 && $1->nd_next == 0) {
$$ = $1->nd_head;
}
else {
@@ -659,7 +678,7 @@ args2 : args
array : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| args trailer
@@ -667,85 +686,57 @@ primary : literal
{
$$ = NEW_LIT($1);
}
+ | primary COLON2 cname
+ {
+ $$ = NEW_COLON2($1, $3);
+ }
| STRING
{
$$ = NEW_STR($1);
}
- | STRING2
+ | DSTRING
| XSTRING
{
$$ = NEW_XSTR($1);
}
- | XSTRING2
+ | DXSTRING
| DREGEXP
| var_ref
| backref
- | SUPER '(' call_args rparen
+ | SUPER '(' call_args ')'
{
if (!cur_mid && !in_single && !in_defined)
- Error("super called outside of method");
+ yyerror("super called outside of method");
$$ = NEW_SUPER($3);
}
| SUPER
{
if (!cur_mid && !in_single && !in_defined)
- Error("super called outside of method");
+ yyerror("super called outside of method");
$$ = NEW_ZSUPER();
}
- | primary '[' opt_args opt_nl rbracket
+ | primary '[' opt_args opt_nl ']'
{
value_expr($1);
$$ = NEW_CALL($1, AREF, $3);
}
- | LBRACK array rbracket
+ | LBRACK array ']'
{
- if ($2 == Qnil)
+ if ($2 == 0)
$$ = NEW_ZARRAY(); /* zero length array*/
else {
$$ = $2;
}
}
- | LBRACE assoc_list rbrace
+ | LBRACE assoc_list '}'
{
$$ = NEW_HASH($2);
}
- | REDO
- {
- $$ = NEW_REDO();
- }
- | BREAK
- {
- $$ = NEW_BREAK();
- }
- | CONTINUE
- {
- $$ = NEW_CONT();
- }
- | RETRY
- {
- $$ = NEW_RETRY();
- }
| RETURN
{
if (!cur_mid && !in_single)
- Error("return appeared outside of method");
- $$ = NEW_RET(Qnil);
- }
- | FAIL '(' args2 ')'
- {
- if (nd_type($3) == NODE_ARRAY) {
- Error("wrong number of argument to fail(0 or 1)");
- }
- value_expr($3);
- $$ = NEW_FAIL($3);
- }
- | FAIL '(' ')'
- {
- $$ = NEW_FAIL(Qnil);
- }
- | FAIL
- {
- $$ = NEW_FAIL(Qnil);
+ yyerror("return appeared outside of method");
+ $$ = NEW_RET(0);
}
| YIELD '(' args2 ')'
{
@@ -754,48 +745,31 @@ primary : literal
}
| YIELD '(' ')'
{
- $$ = NEW_YIELD(Qnil);
+ $$ = NEW_YIELD(0);
}
| YIELD
{
- $$ = NEW_YIELD(Qnil);
- }
- | DEFINED '(' {in_defined = 1;} arg ')'
+ $$ = NEW_YIELD(0);
+ }
+ | DEFINED '(' {in_defined = 1;} expr ')'
{
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_CVAR) {
- $1 = NEW_FCALL($1->nd_vid, Qnil);
- }
- $$ = NEW_ITER($4, $1, $5);
- dyna_pop($<vars>3);
- }
| FID
{
- $$ = NEW_FCALL($1, Qnil);
- }
- | operation '(' call_args rparen
- {
- $$ = NEW_FCALL($1, $3);
+ $$ = NEW_FCALL($1, 0);
}
- | primary '.' operation '(' call_args rparen
+ | operation iter_block
{
- value_expr($1);
- $$ = NEW_CALL($1, $3, $5);
+ $2->nd_iter = NEW_FCALL($1, 0);
+ $$ = $2;
}
- | primary '.' operation
+ | method_call
+ | method_call iter_block
{
- value_expr($1);
- $$ = NEW_CALL($1, $3, Qnil);
+ $2->nd_iter = $1;
+ $$ = $2;
}
| IF expr then
compexpr
@@ -805,10 +779,23 @@ primary : literal
value_expr($2);
$$ = NEW_IF(cond($2), $4, $5);
}
+ | UNLESS expr then
+ compexpr
+ opt_else
+ END
+ {
+ value_expr($2);
+ $$ = NEW_UNLESS(cond($2), $4, $5);
+ }
| WHILE expr term compexpr END
{
value_expr($2);
- $$ = NEW_WHILE(cond($2), $4);
+ $$ = NEW_WHILE(cond($2), $4, 1);
+ }
+ | UNTIL expr term compexpr END
+ {
+ value_expr($2);
+ $$ = NEW_UNTIL(cond($2), $4, 1);
}
| CASE compexpr
case_body
@@ -828,16 +815,22 @@ primary : literal
ensure
END
{
- $$ = NEW_BEGIN($2, $3, $4);
+ if (!$3 && !$4)
+ $$ = NEW_BEGIN($2);
+ else {
+ if ($3) $2 = NEW_RESCUE($2, $3);
+ if ($4) $2 = NEW_ENSURE($2, $4);
+ $$ = $2;
+ }
}
- | LPAREN exprs opt_nl rparen
+ | LPAREN compexpr ')'
{
$$ = $2;
}
| CLASS cname superclass
{
if (cur_mid || in_single)
- Error("class definition in method body");
+ yyerror("class definition in method body");
class_nest++;
cref_push();
@@ -854,7 +847,7 @@ primary : literal
| MODULE cname
{
if (cur_mid || in_single)
- Error("module definition in method body");
+ yyerror("module definition in method body");
class_nest++;
cref_push();
local_push();
@@ -870,7 +863,7 @@ primary : literal
| DEF fname
{
if (cur_mid || in_single)
- Error("nested method definition");
+ yyerror("nested method definition");
cur_mid = $2;
local_push();
}
@@ -882,7 +875,7 @@ primary : literal
local_pop();
cur_mid = 0;
}
- | DEF singleton '.' fname
+ | DEF singleton '.' {lex_state = EXPR_FNAME;} fname
{
value_expr($2);
in_single++;
@@ -893,7 +886,7 @@ primary : literal
compexpr
END
{
- $$ = NEW_DEFS($2, $4, $6, $7);
+ $$ = NEW_DEFS($2, $5, $7, $8);
local_pop();
in_single--;
}
@@ -913,7 +906,7 @@ if_tail : opt_else
opt_else : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| ELSE compexpr
{
@@ -925,21 +918,79 @@ iter_var : lhs
opt_iter_var : /* node */
{
- $$ = Qnil;
+ $$ = 0;
}
| '|' /* none */ '|'
{
- $$ = Qnil;
+ $$ = 0;
}
| OROP
{
- $$ = Qnil;
+ $$ = 0;
}
| '|' iter_var '|'
{
$$ = $2;
}
+iter_do_block : DO
+ {
+ $<vars>$ = dyna_push();
+ }
+ opt_iter_var
+ compexpr
+ END
+ {
+ $$ = NEW_ITER($3, 0, $4);
+ dyna_pop($<vars>2);
+ }
+
+iter_block : '{'
+ {
+ $<vars>$ = dyna_push();
+ }
+ opt_iter_var
+ compexpr '}'
+ {
+ $$ = NEW_ITER($3, 0, $4);
+ dyna_pop($<vars>2);
+ }
+
+iterator : IDENTIFIER
+ {
+ $$ = NEW_FCALL($1, 0);
+ }
+ | CONSTANT
+ {
+ $$ = NEW_FCALL($1, 0);
+ }
+ | FID
+ {
+ $$ = NEW_FCALL($1, 0);
+ }
+ | method_call
+ | command_call
+
+method_call : operation '(' call_args ')'
+ {
+ $$ = NEW_FCALL($1, $3);
+ }
+ | primary '.' operation '(' call_args ')'
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, $5);
+ }
+ | primary '.' operation
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, 0);
+ }
+ | primary COLON2 operation '(' call_args ')'
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, $5);
+ }
+
case_body : WHEN args then
compexpr
cases
@@ -950,21 +1001,19 @@ case_body : WHEN args then
cases : opt_else
| case_body
-rescue : /* none */
+rescue : RESCUE opt_args term compexpr
+ rescue
{
- $$ = Qnil;
+ $$ = NEW_RESBODY($2, $4, $5);
}
- | RESCUE compexpr
+ | /* none */
{
- if ($2 == Qnil)
- $$ = (NODE*)1;
- else
- $$ = $2;
+ $$ = 0;
}
ensure : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| ENSURE compexpr
{
@@ -972,9 +1021,9 @@ ensure : /* none */
}
literal : numeric
- | SYMBEG symbol
+ | SYMBEG {lex_state = EXPR_FNAME;} symbol
{
- $$ = INT2FIX($2);
+ $$ = INT2FIX($3);
}
| REGEXP
@@ -1008,9 +1057,9 @@ backref : NTH_REF
superclass : term
{
- $$ = Qnil;
+ $$ = 0;
}
- | colon
+ | '<'
{
lex_state = EXPR_BEG;
}
@@ -1018,10 +1067,12 @@ superclass : term
{
$$ = $3;
}
+ | error term {yyerrok;}
-f_arglist : '(' f_args rparen
+f_arglist : '(' f_args ')'
{
$$ = $2;
+ lex_state = EXPR_BEG;
}
| f_args term
{
@@ -1036,15 +1087,15 @@ f_args : /* no arg */
{
$$ = NEW_ARGS($1, 0, -1);
}
- | f_arg comma rest_arg
+ | f_arg ',' rest_arg
{
$$ = NEW_ARGS($1, 0, $3);
}
- | f_arg comma f_optarg
+ | f_arg ',' f_optarg
{
$$ = NEW_ARGS($1, $3, -1);
}
- | f_arg comma f_optarg comma rest_arg
+ | f_arg ',' f_optarg ',' rest_arg
{
$$ = NEW_ARGS($1, $3, $5);
}
@@ -1052,7 +1103,7 @@ f_args : /* no arg */
{
$$ = NEW_ARGS(0, $1, -1);
}
- | f_optarg comma rest_arg
+ | f_optarg ',' rest_arg
{
$$ = NEW_ARGS(0, $1, $3);
}
@@ -1060,23 +1111,18 @@ f_args : /* no arg */
{
$$ = NEW_ARGS(0, 0, $1);
}
- | error
- {
- lex_state = EXPR_BEG;
- $$ = NEW_ARGS(0, 0, -1);
- }
f_arg : IDENTIFIER
{
if (!is_local_id($1))
- Error("formal argument must be local variable");
+ yyerror("formal argument must be local variable");
local_cnt($1);
$$ = 1;
}
- | f_arg comma IDENTIFIER
+ | f_arg ',' IDENTIFIER
{
if (!is_local_id($3))
- Error("formal argument must be local variable");
+ yyerror("formal argument must be local variable");
local_cnt($3);
$$ += 1;
}
@@ -1084,7 +1130,7 @@ f_arg : IDENTIFIER
f_opt : IDENTIFIER '=' arg
{
if (!is_local_id($1))
- Error("formal argument must be local variable");
+ yyerror("formal argument must be local variable");
$$ = asignable($1, $3);
}
@@ -1092,7 +1138,7 @@ f_optarg : f_opt
{
$$ = NEW_BLOCK($1);
}
- | f_optarg comma f_opt
+ | f_optarg ',' f_opt
{
$$ = block_append($1, $3);
}
@@ -1100,7 +1146,7 @@ f_optarg : f_opt
rest_arg : STAR IDENTIFIER
{
if (!is_local_id($2))
- Error("rest argument must be local variable");
+ yyerror("rest argument must be local variable");
$$ = local_cnt($2);
}
@@ -1110,25 +1156,25 @@ singleton : var_ref
$$ = NEW_SELF();
}
else if (nd_type($1) == NODE_NIL) {
- Error("Can't define single method for nil.");
- $$ = Qnil;
+ yyerror("Can't define single method for nil.");
+ $$ = 0;
}
else {
$$ = $1;
}
}
- | LPAREN expr opt_nl rparen
+ | LPAREN expr opt_nl ')'
{
switch (nd_type($2)) {
case NODE_STR:
- case NODE_STR2:
+ case NODE_DSTR:
case NODE_XSTR:
- case NODE_XSTR2:
+ case NODE_DXSTR:
case NODE_DREGX:
case NODE_LIT:
case NODE_ARRAY:
case NODE_ZARRAY:
- Error("Can't define single method for literals.");
+ yyerror("Can't define single method for literals.");
default:
break;
}
@@ -1137,7 +1183,7 @@ singleton : var_ref
assoc_list : /* none */
{
- $$ = Qnil;
+ $$ = 0;
}
| assocs trailer
{
@@ -1146,13 +1192,13 @@ assoc_list : /* none */
| args trailer
{
if ($1->nd_alen%2 != 0) {
- Error("odd number list for Hash");
+ yyerror("odd number list for Hash");
}
$$ = $1;
}
assocs : assoc
- | assocs comma assoc
+ | assocs ',' assoc
{
$$ = list_concat($1, $3);
}
@@ -1163,32 +1209,24 @@ assoc : arg ASSOC arg
}
operation : IDENTIFIER
+ | CONSTANT
| FID
-opt_term : /* none */
- | term
+opt_terms : /* none */
+ | terms
opt_nl : /* none */
- | nl
+ | '\n'
trailer : /* none */
- | nl
- | comma
-
-term : sc
- | nl
+ | '\n'
+ | ','
-sc : ';' { yyerrok; }
-nl : '\n' { yyerrok; }
-
-colon : ':'
- | SYMBEG
-
-rparen : ')' { yyerrok; }
-rbracket : ']' { yyerrok; }
-rbrace : '}' { yyerrok; }
-comma : ',' { yyerrok; }
+term : ';' {yyerrok;}
+ | '\n'
+terms : term
+ | terms ';' {yyerrok;}
%%
#include <ctype.h>
#include <sys/types.h>
@@ -1206,51 +1244,138 @@ VALUE newfloat();
VALUE newinteger();
char *strdup();
-static NODE *var_extend();
+static NODE *str_extend();
#define LEAVE_BS 1
+static VALUE lex_input; /* non-nil if File */
+static char *lex_pbeg;
static char *lex_p;
-static int lex_len;
+static char *lex_pend;
-void
-lex_setsrc(src, ptr, len)
- char *src;
- char *ptr;
+static int
+yyerror(msg)
+ char *msg;
+{
+ char *p, *pe, *buf;
+ int len, i;
+
+ Error("%s", msg);
+ p = lex_p;
+ while (lex_pbeg <= p) {
+ if (*p == '\n') break;
+ p--;
+ }
+ p++;
+
+ pe = lex_p;
+ while (pe < lex_pend) {
+ if (*pe == '\n') break;
+ pe++;
+ }
+
+ len = pe - p;
+ if (len > 4) {
+ buf = ALLOCA_N(char, len+2);
+ MEMCPY(buf, p, char, len);
+ buf[len] = '\0';
+ Error_Append("%s", buf);
+
+ i = lex_p - p;
+ p = buf; pe = p + len;
+
+ while (p < pe) {
+ if (*p != '\t') *p = ' ';
+ p++;
+ }
+ buf[i] = '^';
+ buf[i+1] = '\0';
+ Error_Append("%s", buf);
+ }
+
+ return 0;
+}
+
+static int newline_seen;
+
+static NODE*
+yycompile(f)
+ char *f;
+{
+ int n;
+
+ newline_seen = 0;
+ sourcefile = strdup(f);
+ eval_tree = 0;
+ n = yyparse();
+ if (n == 0) return eval_tree;
+
+ return 0;
+}
+
+NODE*
+compile_string(f, s, len)
+ char *f, *s;
int len;
{
- sourcefile = strdup(src);
+ lex_pbeg = lex_p = s;
+ lex_pend = s + len;
+ lex_input = 0;
+ if (!sourcefile || strcmp(f, sourcefile)) /* not in eval() */
+ sourceline = 1;
- sourceline = 1;
- lex_p = ptr;
- lex_len = len;
+ return yycompile(f);
}
-#define nextc() ((--lex_len>=0)?(*lex_p++):-1)
-#define pushback() (lex_len++, lex_p--)
-
-#define SCAN_HEX(i) \
-do { \
- int numlen; \
- i=scan_hex(lex_p, 2, &numlen); \
- lex_p += numlen; \
- lex_len -= numlen; \
-} while (0)
-
-#define SCAN_OCT(i) \
-do { \
- int numlen; \
- i=scan_oct(lex_p, 3, &numlen); \
- lex_p += numlen; \
- lex_len -= numlen; \
-} while (0)
+NODE*
+compile_file(f, file, start)
+ char *f;
+ VALUE file;
+ int start;
+{
+ lex_input = file;
+ lex_pbeg = lex_p = lex_pend = 0;
+ sourceline = start;
+
+ return yycompile(f);
+}
+
+int
+nextc()
+{
+ int c;
+
+ if (lex_p == lex_pend) {
+ if (lex_input) {
+ VALUE v = io_gets(lex_input);
+
+ if (NIL_P(v)) return -1;
+ lex_pbeg = lex_p = RSTRING(v)->ptr;
+ lex_pend = lex_p + RSTRING(v)->len;
+ }
+ else {
+ return -1;
+ }
+ }
+ c = *lex_p++;
+
+ return c;
+}
+
+void
+pushback(c)
+ int c;
+{
+ if (c == -1) return;
+ lex_p--;
+}
#define tokfix() (tokenbuf[tokidx]='\0')
#define tok() tokenbuf
#define toklen() tokidx
#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0)
-char *
+char*
newtok()
{
tokidx = 0;
@@ -1307,12 +1432,38 @@ read_escape()
case '0': case '1': case '2': case '3': /* octal constant */
case '4': case '5': case '6': case '7':
- pushback();
- SCAN_OCT(c);
+ pushback(c);
+ {
+ char buf[3];
+ int i;
+
+ for (i=0; i<3; i++) {
+ buf[i] = nextc();
+ if (buf[i] == -1) goto eof;
+ if (buf[i] < '0' || '7' < buf[i]) {
+ pushback(buf[i]);
+ break;
+ }
+ }
+ c = scan_oct(buf, i+1, &i);
+ }
return c;
case 'x': /* hex constant */
- SCAN_HEX(c);
+ {
+ char buf[2];
+ int i;
+
+ for (i=0; i<2; i++) {
+ buf[i] = nextc();
+ if (buf[i] == -1) goto eof;
+ if (!isxdigit(buf[i])) {
+ pushback(buf[i]);
+ break;
+ }
+ }
+ c = scan_hex(buf, i+1, &i);
+ }
return c;
case 'b': /* backspace */
@@ -1320,7 +1471,8 @@ read_escape()
case 'M':
if ((c = nextc()) != '-') {
- Error("Invalid escape character syntax");
+ yyerror("Invalid escape character syntax");
+ pushback(c);
return '\0';
}
if ((c = nextc()) == '\\') {
@@ -1333,7 +1485,8 @@ read_escape()
case 'C':
if ((c = nextc()) != '-') {
- Error("Invalid escape character syntax");
+ yyerror("Invalid escape character syntax");
+ pushback(c);
return '\0';
}
case 'c':
@@ -1348,8 +1501,7 @@ read_escape()
eof:
case -1:
- Error("Invalid escape character syntax");
- pushback();
+ yyerror("Invalid escape character syntax");
return '\0';
default:
@@ -1358,16 +1510,23 @@ read_escape()
}
static int
-parse_regx()
+parse_regx(term)
+ int term;
{
register int c;
int casefold = 0;
int in_brack = 0;
int re_start = sourceline;
- NODE *list = Qnil;
+ int once = 0;
+ int quote = 0;
+ NODE *list = 0;
newtok();
while ((c = nextc()) != -1) {
+ if (!in_brack && c == term) {
+ goto regx_end;
+ }
+
switch (c) {
case '[':
in_brack = 1;
@@ -1377,7 +1536,7 @@ parse_regx()
break;
case '#':
- list = var_extend(list, '/');
+ list = str_extend(list, term);
if (list == (NODE*)-1) return 0;
continue;
@@ -1405,6 +1564,11 @@ parse_regx()
tokadd(c);
break;
+ case '^': /* no \^ escape in regexp */
+ tokadd('\\');
+ tokadd('^');
+ break;
+
case 'b':
if (!in_brack) {
tokadd('\\');
@@ -1413,22 +1577,44 @@ parse_regx()
}
/* fall through */
default:
- pushback();
- tokadd('\\');
- tokadd(read_escape());
+ if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == term) {
+ tokadd(c);
+ }
+ else {
+ pushback(c);
+ tokadd('\\');
+ tokadd(read_escape());
+ }
}
continue;
- case '/': /* end of the regexp */
- if (in_brack)
- break;
+ case -1:
+ Error("unterminated regexp");
+ return 0;
- if ('i' == nextc()) {
- casefold = 1;
+ default:
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
}
- else {
- casefold = 0;
- pushback();
+ break;
+
+ regx_end:
+ for (;;) {
+ c = nextc();
+ if (c == 'i') {
+ casefold = 1;
+ }
+ else if (c == 'o') {
+ once = 1;
+ }
+ else {
+ pushback(c);
+ break;
+ }
}
tokfix();
@@ -1438,8 +1624,8 @@ parse_regx()
VALUE ss = str_new(tok(), toklen());
list_append(list, NEW_STR(ss));
}
- nd_set_type(list, NODE_DREGX);
- if (casefold) list->nd_cflag = 1;
+ nd_set_type(list, once?NODE_DREGX_ONCE:NODE_DREGX);
+ list->nd_cflag = casefold;
yylval.node = list;
return DREGEXP;
}
@@ -1447,16 +1633,6 @@ parse_regx()
yylval.val = reg_new(tok(), toklen(), casefold);
return REGEXP;
}
- case -1:
- Error("unterminated regexp");
- return 0;
-
- default:
- if (ismbchar(c)) {
- tokadd(c);
- c = nextc();
- }
- break;
}
tokadd(c);
}
@@ -1465,16 +1641,18 @@ parse_regx()
}
static int
-parse_string(term)
- int term;
+parse_string(func,term)
+ int func, term;
{
int c;
- NODE *list = Qnil;
+ NODE *list = 0;
int strstart;
strstart = sourceline;
newtok();
+
while ((c = nextc()) != term) {
+ str_retry:
if (c == -1) {
unterm_str:
sourceline = strstart;
@@ -1489,7 +1667,7 @@ parse_string(term)
sourceline++;
}
else if (c == '#') {
- list = var_extend(list, term);
+ list = str_extend(list, term);
if (list == (NODE*)-1) goto unterm_str;
continue;
}
@@ -1502,34 +1680,89 @@ parse_string(term)
tokadd(c);
}
else {
- pushback();
- if (term != '"') tokadd('\\');
+ pushback(c);
+ if (func != '"') tokadd('\\');
tokadd(read_escape());
}
continue;
}
tokadd(c);
}
+
tokfix();
lex_state = EXPR_END;
- if (list == Qnil) {
- yylval.val = str_new(tok(), toklen());
- return (term == '`') ? XSTRING : STRING;
- }
- else {
+ if (list) {
if (toklen() > 0) {
VALUE ss = str_new(tok(), toklen());
list_append(list, NEW_STR(ss));
}
yylval.node = list;
- if (term == '`') {
- nd_set_type(list, NODE_XSTR2);
- return XSTRING2;
+ if (func == '`') {
+ nd_set_type(list, NODE_DXSTR);
+ return DXSTRING;
}
else {
- return STRING2;
+ return DSTRING;
}
}
+ else {
+ yylval.val = str_new(tok(), toklen());
+ return (func == '`') ? XSTRING : STRING;
+ }
+}
+
+static int
+parse_qstring(term)
+ int term;
+{
+ int quote = 0;
+ int strstart;
+ int c;
+
+ strstart = sourceline;
+ newtok();
+ while ((c = nextc()) != term) {
+ qstr_retry:
+ if (c == -1) {
+ sourceline = strstart;
+ Error("unterminated string meets end of file");
+ return 0;
+ }
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ else if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == '\\') {
+ c = nextc();
+ switch (c) {
+ case '\n':
+ sourceline++;
+ continue;
+
+ case '\\':
+ c = '\\';
+ break;
+
+ case '\'':
+ if (term == '\'') {
+ c = '\'';
+ break;
+ }
+ /* fall through */
+ default:
+ tokadd('\\');
+ }
+ }
+ tokadd(c);
+ }
+
+ tokfix();
+ yylval.val = str_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return STRING;
}
#define LAST(v) ((v)-1 + sizeof(v)/sizeof(v[0]))
@@ -1540,22 +1773,18 @@ static struct kwtable {
enum lex_state state;
} kwtable [] = {
"__END__", 0, EXPR_BEG,
- "__FILE__", _FILE_, EXPR_END,
- "__LINE__", _LINE_, EXPR_END,
"alias", ALIAS, EXPR_FNAME,
"and", AND, EXPR_BEG,
"begin", BEGIN, EXPR_BEG,
- "break", BREAK, EXPR_END,
"case", CASE, EXPR_BEG,
"class", CLASS, EXPR_BEG,
- "continue", CONTINUE, EXPR_END,
"def", DEF, EXPR_FNAME,
"defined?", DEFINED, EXPR_END,
+ "do", DO, EXPR_BEG,
"else", ELSE, EXPR_BEG,
"elsif", ELSIF, EXPR_BEG,
"end", END, EXPR_END,
"ensure", ENSURE, EXPR_BEG,
- "fail", FAIL, EXPR_END,
"for", FOR, EXPR_BEG,
"if", IF, EXPR_BEG,
"in", IN, EXPR_BEG,
@@ -1563,14 +1792,14 @@ static struct kwtable {
"nil", NIL, EXPR_END,
"not", NOT, EXPR_BEG,
"or", OR, EXPR_BEG,
- "redo", REDO, EXPR_END,
- "rescue", RESCUE, EXPR_BEG,
- "retry", RETRY, EXPR_END,
+ "rescue", RESCUE, EXPR_MID,
"return", RETURN, EXPR_MID,
"self", SELF, EXPR_END,
"super", SUPER, EXPR_END,
"then", THEN, EXPR_BEG,
"undef", UNDEF, EXPR_FNAME,
+ "unless", UNLESS, EXPR_BEG,
+ "until", UNTIL, EXPR_BEG,
"when", WHEN, EXPR_BEG,
"while", WHILE, EXPR_BEG,
"yield", YIELD, EXPR_END,
@@ -1589,6 +1818,11 @@ yylex()
int space_seen = 0;
struct kwtable *low = kwtable, *mid, *high = LAST(kwtable);
+ if (newline_seen) {
+ sourceline++;
+ newline_seen = 0;
+ }
+
retry:
switch (c = nextc()) {
case '\0': /* NUL */
@@ -1614,10 +1848,11 @@ retry:
}
/* fall through */
case '\n':
- sourceline++;
- if (lex_state == EXPR_BEG
- || lex_state == EXPR_FNAME)
+ if (lex_state == EXPR_BEG || lex_state == EXPR_FNAME) {
+ sourceline++;
goto retry;
+ }
+ newline_seen++;
lex_state = EXPR_BEG;
return '\n';
@@ -1628,7 +1863,7 @@ retry:
yylval.id = POW;
return OP_ASGN;
}
- pushback();
+ pushback(c);
return POW;
}
if (c == '=') {
@@ -1636,7 +1871,7 @@ retry:
lex_state = EXPR_BEG;
return OP_ASGN;
}
- pushback();
+ pushback(c);
if (lex_state == EXPR_ARG && space_seen && !isspace(c)){
arg_ambiguous();
lex_state = EXPR_BEG;
@@ -1656,12 +1891,16 @@ retry:
if (c == '~') {
return NMATCH;
}
- pushback();
+ pushback(c);
return '!';
case '=':
lex_state = EXPR_BEG;
if ((c = nextc()) == '=') {
+ if ((c = nextc()) == '=') {
+ return EQQ;
+ }
+ pushback(c);
return EQ;
}
if (c == '~') {
@@ -1670,7 +1909,7 @@ retry:
else if (c == '>') {
return ASSOC;
}
- pushback();
+ pushback(c);
return '=';
case '<':
@@ -1679,7 +1918,7 @@ retry:
if ((c = nextc()) == '>') {
return CMP;
}
- pushback();
+ pushback(c);
return LEQ;
}
if (c == '<') {
@@ -1687,10 +1926,10 @@ retry:
yylval.id = LSHFT;
return OP_ASGN;
}
- pushback();
+ pushback(c);
return LSHFT;
}
- pushback();
+ pushback(c);
return '<';
case '>':
@@ -1699,64 +1938,24 @@ retry:
return GEQ;
}
if (c == '>') {
- if (nextc() == '=') {
+ if ((c = nextc()) == '=') {
yylval.id = RSHFT;
return OP_ASGN;
}
- pushback();
+ pushback(c);
return RSHFT;
}
- pushback();
+ pushback(c);
return '>';
case '"':
+ return parse_string(c,c);
case '`':
- return parse_string(c);
+ if (lex_state == EXPR_FNAME) return c;
+ return parse_string(c,c);
case '\'':
- {
- int strstart;
-
- strstart = sourceline;
- newtok();
- while ((c = nextc()) != '\'') {
- if (c == -1) {
- sourceline = strstart;
- Error("unterminated string meets end of file");
- return 0;
- }
- if (ismbchar(c)) {
- tokadd(c);
- c = nextc();
- }
- else if (c == '\n') {
- sourceline++;
- }
- else if (c == '\\') {
- c = nextc();
- switch (c) {
- case '\n':
- sourceline++;
- continue;
-
- case '\'':
- c = '\'';
- break;
- case '\\':
- c = '\\';
- break;
-
- default:
- tokadd('\\');
- }
- }
- tokadd(c);
- }
- tokfix();
- yylval.val = str_new(tok(), toklen());
- lex_state = EXPR_END;
- return STRING;
- }
+ return parse_qstring(c);
case '?':
if ((c = nextc()) == '\\') {
@@ -1776,7 +1975,7 @@ retry:
yylval.id = '&';
return OP_ASGN;
}
- pushback();
+ pushback(c);
return '&';
case '|':
@@ -1788,7 +1987,7 @@ retry:
yylval.id = '|';
return OP_ASGN;
}
- pushback();
+ pushback(c);
return '|';
case '+':
@@ -1797,29 +1996,32 @@ retry:
if (c == '@') {
return UPLUS;
}
- pushback();
+ pushback(c);
return '+';
}
+ if (c == '=') {
+ lex_state = EXPR_BEG;
+ yylval.id = '+';
+ return OP_ASGN;
+ }
if (lex_state == EXPR_ARG) {
- if (!space_seen || c == '=' || isspace(c)) {
+ if (space_seen && !isspace(c)) {
arg_ambiguous();
+ }
+ else {
lex_state = EXPR_END;
}
}
if (lex_state != EXPR_END) {
- pushback();
- if (isdigit(c)) {
+ if (isdigit(c)) {
goto start_num;
}
+ pushback(c);
lex_state = EXPR_BEG;
return UPLUS;
}
lex_state = EXPR_BEG;
- if (c == '=') {
- yylval.id = '+';
- return OP_ASGN;
- }
- pushback();
+ pushback(c);
return '+';
case '-':
@@ -1828,31 +2030,34 @@ retry:
if (c == '@') {
return UMINUS;
}
- pushback();
+ pushback(c);
return '-';
}
+ if (c == '=') {
+ lex_state = EXPR_BEG;
+ yylval.id = '-';
+ return OP_ASGN;
+ }
if (lex_state == EXPR_ARG) {
- if (!space_seen || c == '=' || isspace(c)) {
+ if (space_seen && !isspace(c)) {
arg_ambiguous();
+ }
+ else {
lex_state = EXPR_END;
}
}
if (lex_state != EXPR_END) {
if (isdigit(c)) {
- pushback();
+ pushback(c);
c = '-';
goto start_num;
}
lex_state = EXPR_BEG;
- pushback();
+ pushback(c);
return UMINUS;
}
lex_state = EXPR_BEG;
- if (c == '=') {
- yylval.id = '-';
- return OP_ASGN;
- }
- pushback();
+ pushback(c);
return '-';
case '.':
@@ -1861,10 +2066,10 @@ retry:
if ((c = nextc()) == '.') {
return DOT3;
}
- pushback();
+ pushback(c);
return DOT2;
}
- pushback();
+ pushback(c);
if (!isdigit(c)) {
return '.';
}
@@ -1877,6 +2082,7 @@ retry:
{
int is_float, seen_point, seen_e;
+ is_float = seen_point = seen_e = 0;
lex_state = EXPR_END;
newtok();
if (c == '0') {
@@ -1887,30 +2093,39 @@ retry:
if (!isxdigit(c)) break;
tokadd(c);
}
- pushback();
+ pushback(c);
tokfix();
yylval.val = str2inum(tok(), 16);
return INTEGER;
}
- else if (c >= '0' && c <= '9') {
+ else if (c >= '0' && c <= '7') {
/* octal */
do {
tokadd(c);
c = nextc();
} while (c >= '0' && c <= '9');
- pushback();
+ pushback(c);
tokfix();
yylval.val = str2inum(tok(), 8);
return INTEGER;
}
+ else if (c > '7' && c <= '9') {
+ Error("Illegal octal digit");
+ }
+ else if (c == '.') {
+ tokadd('0');
+ }
+ else {
+ pushback(c);
+ yylval.val = INT2FIX(0);
+ return INTEGER;
+ }
}
if (c == '-' || c == '+') {
tokadd(c);
c = nextc();
}
- is_float = seen_point = seen_e = 0;
-
for (;;) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
@@ -1922,10 +2137,13 @@ retry:
if (seen_point) {
goto decode_num;
}
- c = nextc();
- if (!isdigit(c)) {
- pushback();
- goto decode_num;
+ else {
+ int c0 = nextc();
+ if (!isdigit(c0)) {
+ pushback(c0);
+ goto decode_num;
+ }
+ c = c0;
}
tokadd('.');
tokadd(c);
@@ -1957,7 +2175,7 @@ retry:
}
decode_num:
- pushback();
+ pushback(c);
tokfix();
if (is_float) {
double atof();
@@ -1981,30 +2199,29 @@ retry:
lex_state = EXPR_BEG;
return COLON2;
}
- pushback();
+ pushback(c);
if (isspace(c))
return ':';
return SYMBEG;
case '/':
- if (lex_state == EXPR_BEG
- || lex_state == EXPR_MID) {
- return parse_regx();
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ return parse_regx('/');
+ }
+ if ((c = nextc()) == '=') {
+ lex_state = EXPR_BEG;
+ yylval.id = '/';
+ return OP_ASGN;
}
- c = nextc();
if (lex_state == EXPR_ARG) {
- if (space_seen && c != '=' && !isspace(c)) {
- pushback();
+ if (space_seen && !isspace(c)) {
+ pushback(c);
arg_ambiguous();
- return parse_regx();
+ return parse_regx('/');
}
}
lex_state = EXPR_BEG;
- if (c == '=') {
- yylval.id = '/';
- return OP_ASGN;
- }
- pushback();
+ pushback(c);
return '/';
case '^':
@@ -2013,7 +2230,7 @@ retry:
yylval.id = '^';
return OP_ASGN;
}
- pushback();
+ pushback(c);
return c;
case ',':
@@ -2027,15 +2244,14 @@ retry:
case '~':
if (lex_state == EXPR_FNAME) {
if ((c = nextc()) != '@') {
- pushback();
+ pushback(c);
}
}
lex_state = EXPR_BEG;
return c;
case '(':
- if (lex_state == EXPR_BEG
- || lex_state == EXPR_MID) {
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
c = LPAREN;
lex_state = EXPR_BEG;
}
@@ -2055,14 +2271,13 @@ retry:
if ((c = nextc()) == '=') {
return ASET;
}
- pushback();
+ pushback(c);
return AREF;
}
- pushback();
+ pushback(c);
return '[';
}
- else if (lex_state == EXPR_BEG
- || lex_state == EXPR_MID) {
+ else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
c = LBRACK;
}
else if (lex_state == EXPR_ARG && space_seen) {
@@ -2085,25 +2300,71 @@ retry:
space_seen = 1;
goto retry; /* skip \\n */
}
- pushback();
+ pushback(c);
return '\\';
case '%':
- lex_state = EXPR_BEG;
- if (nextc() == '=') {
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ int term;
+
+ c = nextc();
+ quotation:
+ if (!isalnum(c)) {
+ term = c;
+ c = '"';
+ }
+ else {
+ term = nextc();
+ }
+ if (c == -1 || term == -1) {
+ Error("unterminated quoted string meets end of file");
+ return 0;
+ }
+ if (term == '(') term = ')';
+ else if (term == '[') term = ']';
+ else if (term == '{') term = '}';
+ else if (term == '<') term = '>';
+
+ switch (c) {
+ case 'Q':
+ return parse_string('"', term);
+
+ case 'q':
+ return parse_qstring(term);
+
+ case 'x':
+ return parse_string('`', term);
+
+ case 'r':
+ return parse_regx(term);
+
+ default:
+ Error("unknown type of string `%c'", c);
+ return 0;
+ }
+ }
+ if ((c = nextc()) == '=') {
yylval.id = '%';
return OP_ASGN;
}
- pushback();
- return c;
+ if (lex_state == EXPR_ARG) {
+ if (space_seen && !isspace(c)) {
+ arg_ambiguous();
+ goto quotation;
+ }
+ }
+ lex_state = EXPR_BEG;
+ pushback(c);
+ return '%';
case '$':
lex_state = EXPR_END;
newtok();
c = nextc();
switch (c) {
+ case '_': /* $_: last read line string */
case '~': /* $~: match-data */
- local_cnt('~');
+ local_cnt(c);
/* fall through */
case '*': /* $*: argv */
case '$': /* $$: pid */
@@ -2112,9 +2373,9 @@ retry:
case '@': /* $@: error position */
case '/': /* $/: input record separator */
case '\\': /* $\: output record separator */
+ case ';': /* $;: field separator */
case ',': /* $,: output field separator */
case '.': /* $.: last read line number */
- case '_': /* $_: last read line string */
case '=': /* $=: ignorecase */
case ':': /* $:: load path */
case '<': /* $<: reading filename */
@@ -2140,14 +2401,14 @@ retry:
tokadd(c);
c = nextc();
}
- pushback();
+ pushback(c);
tokfix();
yylval.node = NEW_NTH_REF(atoi(tok()));
return NTH_REF;
default:
if (!is_identchar(c)) {
- pushback();
+ pushback(c);
return '$';
}
case '0':
@@ -2158,7 +2419,7 @@ retry:
case '@':
c = nextc();
if (!is_identchar(c)) {
- pushback();
+ pushback(c);
return '@';
}
newtok();
@@ -2187,7 +2448,7 @@ retry:
tokadd(c);
}
else {
- pushback();
+ pushback(c);
}
tokfix();
@@ -2213,7 +2474,9 @@ retry:
if (state != EXPR_BEG
&& state != EXPR_BEG) {
if (mid->id == IF) return IF_MOD;
+ if (mid->id == UNLESS) return UNLESS_MOD;
if (mid->id == WHILE) return WHILE_MOD;
+ if (mid->id == UNTIL) return UNTIL_MOD;
}
return mid->id;
}
@@ -2231,7 +2494,7 @@ retry:
tokadd(c);
}
else {
- pushback();
+ pushback(c);
}
}
else if (lex_state == EXPR_BEG){
@@ -2255,68 +2518,43 @@ retry:
}
static NODE*
-var_extend(list, term)
+str_extend(list, term)
NODE *list;
char term;
{
- int c, t, brace;
+ int c;
VALUE ss;
NODE *node;
ID id;
+ int nest;
c = nextc();
switch (c) {
case '$':
+ case '@':
case '{':
break;
- case '@':
- t = nextc();
- pushback();
- if (!is_identchar(t)) {
- tokadd('#');
- tokadd(c);
- return list;
- }
default:
tokadd('#');
- pushback();
+ pushback(c);
return list;
}
ss = str_new(tok(), toklen());
- if (list == Qnil) {
- list = NEW_STR2(ss);
+ if (list == 0) {
+ list = NEW_DSTR(ss);
}
else if (toklen() > 0) {
list_append(list, NEW_STR(ss));
}
newtok();
- if (c == '{') {
- brace = 1;
- c = nextc();
- }
- else {
- brace = 0;
- }
switch (c) {
case '$':
+ tokadd('$');
c = nextc();
if (c == -1) return (NODE*)-1;
switch (c) {
- case '&':
- case '`':
- case '\'':
- case '+':
- node = NEW_BACK_REF(c);
- c = nextc();
- 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':
@@ -2324,55 +2562,108 @@ var_extend(list, term)
tokadd(c);
c = nextc();
}
- tokfix();
- node = NEW_NTH_REF(atoi(tok()));
- goto append_node;
- }
+ pushback(c);
+ goto fetch_id;
- tokadd('$');
- if (!is_identchar(c)) {
+ case '&': case '+':
+ case '_': case '~':
+ case '*': case '$': case '?':
+ case '!': case '@': case ',':
+ case '.': case '=': case ':':
+ case '<': case '>': case '\\':
+ refetch:
tokadd(c);
goto fetch_id;
+
+ default:
+ if (c == term) {
+ list_append(list, NEW_STR(str_new2("#$")));
+ pushback(c);
+ return list;
+ }
+ switch (c) {
+ case '\"':
+ case '/':
+ case '\'':
+ case '`':
+ goto refetch;
+ }
+ if (!is_identchar(c)) {
+ yyerror("bad global variable in string");
+ newtok();
+ return list;
+ }
}
/* through */
- case '@':
+
+ case '@':
tokadd(c);
c = nextc();
- break;
- }
- while (is_identchar(c)) {
- tokadd(c);
- if (ismbchar(c)) {
- c = nextc();
+ while (is_identchar(c)) {
tokadd(c);
+ if (ismbchar(c)) {
+ c = nextc();
+ tokadd(c);
+ }
+ c = nextc();
}
- c = nextc();
+ pushback(c);
+ break;
+
+ case '{':
+ nest = 0;
+ do {
+ loop_again:
+ c = nextc();
+ switch (c) {
+ case -1:
+ if (nest > 0) {
+ bad_sub:
+ Error("bad substitution in string");
+ newtok();
+ return list;
+ }
+ return (NODE*)-1;
+ case '}':
+ if (nest == 0) break;
+ nest--;
+ tokadd(c);
+ goto loop_again;
+ case '\\':
+ c = read_escape();
+ tokadd(c);
+ goto loop_again;
+ case '{':
+ nest++;
+ case '\"':
+ case '/':
+ case '`':
+ if (c == term) {
+ pushback(c);
+ list_append(list, NEW_STR(str_new2("#")));
+ Warning("bad substitution in string");
+ tokfix();
+ list_append(list, NEW_STR(str_new(tok(), toklen())));
+ newtok();
+ return list;
+ }
+ add_char:
+ default:
+ tokadd(c);
+ break;
+ }
+ } while (c != '}');
}
fetch_id:
tokfix();
- if (strcmp("__LINE__", tok()) == 0)
- id = _LINE_;
- else if (strcmp("__FILE__", tok()) == 0)
- id = _FILE_;
- else
- id = rb_intern(tok());
- id_node:
- node = gettable(id);
-
- append_node:
- if (brace) {
- if (c != '}')
- Error("Invalid variable name in string");
- }
- else pushback();
-
+ node = NEW_EVSTR(tok(),toklen());
list_append(list, node);
newtok();
+
return list;
}
-
NODE*
newnode(type, a0, a1, a2)
enum node_type type;
@@ -2382,7 +2673,7 @@ newnode(type, a0, a1, a2)
n->flags |= T_NODE;
nd_set_type(n, type);
- n->line = sourceline;
+ nd_set_line(n, sourceline);
n->file = sourcefile;
n->u1.node = a0;
@@ -2406,8 +2697,8 @@ block_append(head, tail)
extern int verbose;
NODE *last;
- if (tail == Qnil) return head;
- if (head == Qnil) return tail;
+ if (tail == 0) return head;
+ if (head == 0) return tail;
if (nd_type(head) != NODE_BLOCK)
head = last = NEW_BLOCK(head);
@@ -2420,11 +2711,7 @@ block_append(head, tail)
if (verbose) {
switch (nd_type(last->nd_head)) {
- case NODE_BREAK:
- case NODE_CONTINUE:
- case NODE_REDO:
case NODE_RETURN:
- case NODE_RETRY:
Warning("statement not reached");
break;
@@ -2447,7 +2734,7 @@ list_append(head, tail)
{
NODE *last;
- if (head == Qnil) return NEW_LIST(tail);
+ if (head == 0) return NEW_LIST(tail);
last = head;
while (last->nd_next) {
@@ -2465,11 +2752,6 @@ list_concat(head, tail)
{
NODE *last;
-#if 0
- if (nd_type(head) != NODE_ARRAY || nd_type(tail) != NODE_ARRAY)
- Bug("list_concat() called with non-list");
-#endif
-
last = head;
while (last->nd_next) {
last = last->nd_next;
@@ -2493,7 +2775,7 @@ call_op(recv, id, narg, arg1)
value_expr(arg1);
}
- return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):Qnil);
+ return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):0);
}
static NODE*
@@ -2506,19 +2788,11 @@ gettable(id)
else if (id == NIL) {
return NEW_NIL();
}
- else if (id == _LINE_) {
- return NEW_LIT(INT2FIX(sourceline));
- }
- else if (id == _FILE_) {
- VALUE s = str_new2(sourcefile);
-
- return NEW_STR(s);
- }
else if (is_local_id(id)) {
if (local_id(id)) return NEW_LVAR(id);
- if (dyna_id(id)) return NEW_DVAR(id);
+ if (dyna_var_defined(id)) return NEW_DVAR(id);
/* method call without arguments */
- return NEW_FCALL(id, Qnil);
+ return NEW_FCALL(id, 0);
}
else if (is_global_id(id)) {
return NEW_GVAR(id);
@@ -2530,7 +2804,7 @@ gettable(id)
return NEW_CVAR(id);
}
Bug("invalid id for gettable");
- return Qnil;
+ return 0;
}
static NODE*
@@ -2538,17 +2812,13 @@ asignable(id, val)
ID id;
NODE *val;
{
- extern VALUE dyna_var_asgn();
- NODE *lhs = Qnil;
+ NODE *lhs = 0;
if (id == SELF) {
- Error("Can't change the value of self");
+ yyerror("Can't change the value of self");
}
else if (id == NIL) {
- Error("Can't asign to nil");
- }
- else if (id == _LINE_ || id == _FILE_) {
- Error("Can't asign to special identifier");
+ yyerror("Can't asign to nil");
}
else if (is_local_id(id)) {
if (local_id(id) || !dyna_in_block())
@@ -2566,7 +2836,7 @@ asignable(id, val)
}
else if (is_const_id(id)) {
if (cur_mid || in_single)
- Error("class constant asigned in method body");
+ yyerror("dynamic constant asignment");
lhs = NEW_CASGN(id, val);
}
else {
@@ -2625,22 +2895,17 @@ static int
value_expr(node)
NODE *node;
{
- if (node == Qnil) return TRUE;
+ if (node == 0) return TRUE;
switch (nd_type(node)) {
case NODE_RETURN:
- case NODE_CONTINUE:
- case NODE_BREAK:
- case NODE_REDO:
- case NODE_RETRY:
- case NODE_FAIL:
case NODE_WHILE:
- case NODE_WHILE2:
+ case NODE_UNTIL:
case NODE_CLASS:
case NODE_MODULE:
case NODE_DEFN:
case NODE_DEFS:
- Error("void value expression");
+ yyerror("void value expression");
return FALSE;
break;
@@ -2666,19 +2931,26 @@ cond0(node)
{
enum node_type type = nd_type(node);
- if (type == NODE_STR || type == NODE_STR2 || type == NODE_DREGX) {
+ switch (type) {
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
return call_op(NEW_GVAR(rb_intern("$_")),MATCH,1,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) {
+
+ case NODE_DOT2:
+ case 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;
+
+ case NODE_LIT:
+ if (TYPE(node->nd_lit) == T_REGEXP) {
+ return NEW_MATCH(node);
+ }
+ default:
+ return node;
}
- return node;
}
static NODE*
@@ -2700,7 +2972,7 @@ cond(node)
node = cond0(node);
if (type == NODE_CALL && node->nd_mid == '!') {
- if (node->nd_args || node->nd_recv == Qnil) {
+ if (node->nd_args || node->nd_recv == 0) {
Bug("method `!' called with wrong # of operand");
}
node->nd_recv = cond0(node->nd_recv);
@@ -2719,6 +2991,16 @@ cond2(node)
return node;
}
+static NODE*
+logop(type, left, right)
+ enum node_type type;
+ NODE *left, *right;
+{
+ value_expr(left);
+
+ return newnode(type, cond(left), cond(right));
+}
+
st_table *new_idhash();
static struct local_vars {
@@ -2797,10 +3079,8 @@ local_id(id)
static void
top_local_init()
{
- if (lvtbl == 0) {
- local_push();
- }
- else if (the_scope->local_tbl) {
+ local_push();
+ if (the_scope->local_tbl) {
lvtbl->cnt = the_scope->local_tbl[0];
}
else {
@@ -2813,7 +3093,6 @@ top_local_init()
else {
lvtbl->tbl = 0;
}
- NEW_CREF0(); /* initialize constant c-ref */
lvtbl->dlev = (the_dyna_vars?1:0);
}
@@ -2832,26 +3111,23 @@ top_local_setup()
the_scope->local_vars = ALLOC_N(VALUE, len);
if (vars) {
MEMCPY(the_scope->local_vars, vars, VALUE, i);
- MEMZERO(the_scope->local_vars+i, VALUE, len-i);
+ memclear(the_scope->local_vars+i, len-i);
}
else {
- MEMZERO(the_scope->local_vars, VALUE, len);
+ memclear(the_scope->local_vars, len);
}
the_scope->flag = SCOPE_MALLOC;
}
else {
REALLOC_N(the_scope->local_vars, VALUE, len);
- MEMZERO(the_scope->local_vars+i, VALUE, len-i);
+ memclear(the_scope->local_vars+i, len-i);
free(the_scope->local_tbl);
}
lvtbl->tbl[0] = len;
the_scope->local_tbl = lvtbl->tbl;
}
- else if (lvtbl->tbl) {
- free(lvtbl->tbl);
- }
}
- cref_list = Qnil;
+ local_pop();
}
static struct RVarmap*
@@ -2878,10 +3154,9 @@ dyna_in_block()
static void
cref_pop()
{
- NODE *cref = cref_list;
+ NODE *cref = cur_cref;
- cref_list = cref_list->nd_next;
- cref->nd_next = Qnil;
+ cur_cref = cur_cref->nd_next;
}
void
@@ -2893,22 +3168,22 @@ yyappend_print()
}
void
-yywhole_loop(chop, split)
+yywhile_loop(chop, split)
int chop, split;
{
if (split) {
eval_tree =
block_append(NEW_GASGN(rb_intern("$F"),
NEW_CALL(NEW_GVAR(rb_intern("$_")),
- rb_intern("split"), Qnil)),
+ rb_intern("split"), 0)),
eval_tree);
}
if (chop) {
eval_tree =
block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")),
- rb_intern("chop!"), Qnil), eval_tree);
+ rb_intern("chop!"), 0), eval_tree);
}
- eval_tree = NEW_WHILE(NEW_FCALL(rb_intern("gets"),0),eval_tree);
+ eval_tree = NEW_OPT_N(eval_tree);
}
static struct op_tbl rb_op_tbl[] = {
@@ -2934,6 +3209,7 @@ static struct op_tbl rb_op_tbl[] = {
'<', "<",
LEQ, "<=",
EQ, "==",
+ EQQ, "===",
NEQ, "!=",
MATCH, "=~",
NMATCH, "!~",
@@ -2948,7 +3224,8 @@ static struct op_tbl rb_op_tbl[] = {
LSHFT, "<<",
RSHFT, ">>",
COLON2, "::",
- Qnil, Qnil,
+ '`', "`",
+ 0, 0,
};
char *rb_id2name();
@@ -2963,8 +3240,8 @@ Init_sym()
{
int strcmp();
- sym_tbl = st_init_table(strcmp, st_strhash);
- rb_global_variable(&cref_list);
+ sym_tbl = st_init_strtable();
+ rb_global_variable(&cur_cref);
}
ID
@@ -3027,15 +3304,19 @@ rb_intern(name)
return id;
}
-static char *find_ok;
+struct find_ok {
+ ID id;
+ char *name;
+};
static int
-id_find(name, id1, id2)
+id_find(name, id1, ok)
char *name;
- ID id1, id2;
+ ID id1;
+ struct find_ok *ok;
{
- if (id1 == id2) {
- find_ok = name;
+ if (id1 == ok->id) {
+ ok->name = name;
return ST_STOP;
}
return ST_CONTINUE;
@@ -3045,7 +3326,7 @@ char *
rb_id2name(id)
ID id;
{
- find_ok = 0;
+ struct find_ok ok;
if (id < LAST_TOKEN) {
int i = 0;
@@ -3056,8 +3337,10 @@ rb_id2name(id)
}
}
- st_foreach(sym_tbl, id_find, id);
- if (!find_ok && is_attrset_id(id)) {
+ ok.name = 0;
+ ok.id = id;
+ st_foreach(sym_tbl, id_find, &ok);
+ if (!ok.name && is_attrset_id(id)) {
char *res;
ID id2;
@@ -3073,7 +3356,7 @@ rb_id2name(id)
return rb_id2name(id);
}
}
- return find_ok;
+ return ok.name;
}
static int
@@ -3095,3 +3378,84 @@ rb_const_check(class, module)
{
st_foreach(module->iv_tbl, const_check, class);
}
+
+void
+local_var_append(id)
+ ID id;
+{
+ struct local_vars tmp;
+ struct local_vars *save = lvtbl;
+
+ if (the_scope->local_tbl) {
+ tmp.cnt = the_scope->local_tbl[0];
+ tmp.tbl = the_scope->local_tbl;
+ lvtbl->dlev = 0;
+ }
+ lvtbl = &tmp;
+ local_cnt(id);
+ lvtbl = save;
+}
+
+static VALUE
+special_local_get(c)
+ char c;
+{
+ 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] == c) {
+ return the_scope->local_vars[cnt-1];
+ }
+ }
+ return Qnil;
+}
+
+static void
+special_local_set(c, val)
+ char c;
+ VALUE val;
+{
+ int cnt, max;
+
+ if (the_scope->local_tbl) {
+ for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) {
+ if (the_scope->local_tbl[cnt] == c) {
+ the_scope->local_vars[cnt-1] = val;
+ return;
+ }
+ }
+ }
+ top_local_init();
+ cnt = local_cnt(c);
+ top_local_setup();
+ the_scope->local_vars[cnt] = val;
+}
+
+VALUE
+backref_get()
+{
+ return special_local_get('~');
+}
+
+void
+backref_set(val)
+ VALUE val;
+{
+ special_local_set('~', val);
+}
+
+VALUE
+lastline_get()
+{
+ VALUE v = special_local_get('_');
+ if (v == 1) return Qnil; /* $_ undefined */
+ return v;
+}
+
+void
+lastline_set(val)
+ VALUE val;
+{
+ special_local_set('_', val);
+}
diff --git a/process.c b/process.c
index 116a93f965..29f61ddd42 100644
--- a/process.c
+++ b/process.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:47 $
created at: Tue Aug 10 14:30:50 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -25,7 +25,12 @@ struct timeval {
};
#endif
-#include <sys/resource.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_GETPRIORITY
+# include <sys/resource.h>
+#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
@@ -51,7 +56,7 @@ get_ppid()
#define HAVE_WAITPID
#endif
-static VALUE status;
+VALUE last_status = Qnil;
#if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4)
static st_table *pid_tbl;
@@ -59,55 +64,72 @@ static st_table *pid_tbl;
# define WAIT_CALL
#endif
-int
-rb_waitpid(pid, flags)
+static int
+rb_waitpid(pid, flags, st)
int pid;
int flags;
+ int *st;
{
- int result, st;
+ int result;
#ifdef HAVE_WAITPID
- result = waitpid(pid, &st, flags);
+ retry:
+ result = waitpid(pid, st, flags);
+ if (result < 0) {
+ if (errno == EINTR) goto retry;
+ return -1;
+ }
#else
#ifdef HAVE_WAIT4
- result = wait4(pid, &st, flags, NULL);
+ retry:
+ result = wait4(pid, st, flags, NULL);
+ if (result < 0) {
+ if (errno == EINTR) goto retry;
+ return -1;
+ }
#else
- if (pid_tbl && st_lookup(pid_tbl, pid, &st)) {
- status = INT2FIX(st);
+ if (pid_tbl && st_lookup(pid_tbl, pid, st)) {
+ last_status = INT2FIX(*st);
st_delete(pid_tbl, &pid, NULL);
return pid;
}
if (flags)
- Fail("Can't do waitpid with flags");
+ Fatal("Can't do waitpid with flags");
for (;;) {
- result = wait(&st);
- if (result < 0) return -1;
+ result = wait(st);
+ if (result < 0) {
+ if (errno != EINTR) continue;
+ return -1;
+ }
if (result == pid) {
break;
}
if (!pid_tbl)
- pid_tbl = st_init_table(ST_NUMCMP, ST_NUMHASH);
+ pid_tbl = st_init_numtable();
st_insert(pid_tbl, pid, st);
}
#endif
#endif
- status = INT2FIX(st);
+ last_status = INT2FIX(*st);
return result;
}
#ifndef WAIT_CALL
-static int wait_pid;
-static int wait_status;
+struct wait_data {
+ int pid;
+ int status;
+}
-static wait_each(key, value)
+static wait_each(key, value, data)
int key, value;
+ struct wait_data *data;
{
- if (wait_status != -1) return ST_STOP;
+ if (data->status != -1) return ST_STOP;
- wait_pid = key;
- wait_status = value;
+ data->pid = key;
+ data->status = value;
return ST_DELETE;
}
#endif
@@ -116,21 +138,22 @@ static VALUE
f_wait()
{
int pid, state;
-
#ifndef WAIT_CALL
- wait_status = -1;
- st_foreach(pid_tbl, wait_each, NULL);
- if (wait_status != -1) {
- status = wait_status;
- return wait_pid;
+ struct wait_data data;
+
+ data.status = -1;
+ st_foreach(pid_tbl, wait_each, &data);
+ if (data.status != -1) {
+ status = data.status;
+ return data.pid;
}
#endif
if ((pid = wait(&state)) < 0) {
if (errno == ECHILD) return Qnil;
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
- status = INT2FIX(state);
+ last_status = INT2FIX(state);
return INT2FIX(pid);
}
@@ -138,18 +161,88 @@ static VALUE
f_waitpid(obj, vpid, vflags)
VALUE obj, vpid, vflags;
{
- int pid, flags;
+ int pid, flags, status;
- if (vflags == Qnil) flags = 0;
+ if (NIL_P(vflags)) flags = 0;
else flags = FIX2UINT(vflags);
- if ((pid = rb_waitpid(FIX2UINT(vpid), flags)) < 0)
- rb_sys_fail(Qnil);
+ if ((pid = rb_waitpid(FIX2UINT(vpid), flags, &status)) < 0)
+ rb_sys_fail(0);
return INT2FIX(pid);
}
char *strtok();
+#if defined(THREAD) && defined(HAVE_SETITIMER)
+static void
+before_exec()
+{
+ {
+ struct itimerval tval;
+
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+ }
+}
+
+static void
+after_exec()
+{
+ {
+ struct itimerval tval;
+
+ tval.it_interval.tv_sec = 1;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+ }
+}
+#else
+#define before_exec()
+#define after_exec()
+#endif
+
+extern char *dln_find_exe();
+
+static int
+proc_exec_v(argv)
+ char **argv;
+{
+ char *prog;
+
+ prog = dln_find_exe(argv[0], 0);
+ if (!prog) {
+ errno = ENOENT;
+ return -1;
+ }
+ before_exec();
+ execv(prog, argv);
+ after_exec();
+ return -1;
+}
+
+static int
+proc_exec_n(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ char **args;
+ int i;
+
+ args = ALLOCA_N(char*, argc+1);
+ for (i=0; i<argc; i++) {
+ Check_Type(argv[i], T_STRING);
+ args[i] = RSTRING(argv[i])->ptr;
+ }
+ args[i] = 0;
+ if (args[0]) {
+ return proc_exec_v(args);
+ }
+ return -1;
+}
+
int
rb_proc_exec(str)
char *str;
@@ -159,7 +252,13 @@ rb_proc_exec(str)
for (s=str; *s; s++) {
if (*s != ' ' && !isalpha(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
+ before_exec();
+#if defined(MSDOS)
+ system(str);
+#else
execl("/bin/sh", "sh", "-c", str, (char *)NULL);
+#endif
+ after_exec();
return -1;
}
}
@@ -173,19 +272,25 @@ rb_proc_exec(str)
*a = NULL;
}
if (argv[0]) {
- execvp(argv[0], argv);
+ return proc_exec_v(argv);
}
+ errno = ENOENT;
return -1;
}
static VALUE
-f_exec(obj, str)
- VALUE obj;
- struct RString *str;
+f_exec(argc, argv)
+ int argc;
+ VALUE *argv;
{
- Check_Type(str, T_STRING);
- rb_proc_exec(str->ptr);
- rb_sys_fail(str->ptr);
+ if (argc == 1) {
+ Check_Type(argv[0], T_STRING);
+ rb_proc_exec(RSTRING(argv[0])->ptr);
+ }
+ else {
+ proc_exec_n(argc, argv);
+ }
+ rb_sys_fail(RSTRING(argv[0])->ptr);
}
static VALUE
@@ -224,7 +329,6 @@ f_exit_bang(obj, status)
_exit(code);
/* not reached */
- return Qnil;
}
void
@@ -232,12 +336,13 @@ rb_syswait(pid)
int pid;
{
RETSIGTYPE (*hfunc)(), (*ifunc)(), (*qfunc)();
+ int status;
hfunc = signal(SIGHUP, SIG_IGN);
ifunc = signal(SIGINT, SIG_IGN);
qfunc = signal(SIGQUIT, SIG_IGN);
- if (rb_waitpid(pid, 0) < 0) rb_sys_fail("wait");
+ if (rb_waitpid(pid, 0, &status) < 0) rb_sys_fail("wait");
signal(SIGHUP, hfunc);
signal(SIGINT, ifunc);
@@ -245,33 +350,58 @@ rb_syswait(pid)
}
static VALUE
-f_system(obj, str)
- VALUE obj;
- struct RString *str;
+f_system(argc, argv)
+ int argc;
+ VALUE *argv;
{
#ifdef NT
+ VALUE cmd;
+ int state;
+
+ cmd = ary_join(ary_new4(argc, argv), str_new2(" "));
+
+ state = do_spawn(RSTRING(cmd)->ptr);
+ last_status = INT2FIX(state);
+
+ if (state == 0) return TRUE;
+ return FALSE;
+#else
+#if defined(DJGPP)
+ VALUE cmd;
int state;
- Check_Type(str, T_STRING);
- state = do_spawn(str->ptr);
- status = INT2FIX(state);
+ cmd = ary_join(ary_new4(argc, argv), str_new2(" "));
+
+ state = system(RSTRING(cmd)->ptr);
+ last_status = INT2FIX(state);
if (state == 0) return TRUE;
return FALSE;
#else
+ int i;
int pid;
- Check_Type(str, T_STRING);
-
fflush(stdin); /* is it really needed? */
fflush(stdout);
fflush(stderr);
- if (*str->ptr == '\0') return INT2FIX(0);
+ if (argc == 0) {
+ last_status = INT2FIX(0);
+ return INT2FIX(0);
+ }
+
+ for (i=0; i<argc; i++) {
+ Check_Type(argv[i], T_STRING);
+ }
retry:
switch (pid = vfork()) {
case 0:
- rb_proc_exec(str->ptr);
+ if (argc == 1) {
+ rb_proc_exec(RSTRING(argv[0])->ptr);
+ }
+ else {
+ proc_exec_n(argc, argv);
+ }
_exit(127);
break; /* not reached */
@@ -280,62 +410,99 @@ f_system(obj, str)
sleep(5);
goto retry;
}
- rb_sys_fail(str->ptr);
+ rb_sys_fail(0);
break;
default:
rb_syswait(pid);
}
- if (status == INT2FIX(0)) return TRUE;
+ if (last_status == INT2FIX(0)) return TRUE;
return FALSE;
#endif
+#endif
}
+struct timeval time_timeval();
+
VALUE
f_sleep(argc, argv)
int argc;
VALUE *argv;
{
int beg, end;
+ int n;
beg = time(0);
+#ifdef THREAD
if (argc == 0) {
+ thread_sleep();
+ }
+ else if (argc == 1) {
+ thread_wait_for(time_timeval(argv[0]));
+ }
+#else
+ if (argc == 0) {
+ TRAP_BEG;
sleep((32767<<16)+32767);
+ TRAP_END;
}
else if (argc == 1) {
+ struct timeval tv;
+
+ tv = time_timeval(argv[0]);
TRAP_BEG;
- sleep(NUM2INT(argv[0]));
+ sleep(tv.tv_sec);
TRAP_END;
+ if (n<0) rb_sys_fail(0);
}
+#endif
else {
- Fail("wrong # of arguments");
+ ArgError("wrong # of arguments");
}
end = time(0) - beg;
- return int2inum(end);
+ return INT2FIX(end);
}
-#ifndef NT
+#if !defined(NT) && !defined(DJGPP)
+#ifdef _POSIX_SOURCE
+static VALUE
+proc_getpgrp()
+{
+ int pgrp;
+
+ pgrp = getpgrp();
+ if (pgrp < 0) rb_sys_fail(0);
+ return INT2FIX(pgrp);
+}
+
+static VALUE
+proc_setpgrp(obj)
+ VALUE obj;
+{
+ int pgrp;
+
+ if (setpgrp() < 0) rb_sys_fail(0);
+ return Qnil;
+}
+
+#else
+
static VALUE
-proc_getpgrp(argc, argv, obj)
+proc_getpgrp(argc, argv)
int argc;
VALUE *argv;
- VALUE obj;
{
VALUE vpid;
- int pid, pgrp;
+ int pgrp, pid;
rb_scan_args(argc, argv, "01", &vpid);
- if (vpid == Qnil) {
- pid = 0;
- }
- else {
- pid = NUM2INT(vpid);
- }
-
+ if (NIL_P(vpid)) pid = 0;
+ else pid = NUM2INT(vpid);
pgrp = getpgrp(pid);
+ if (pgrp < 0) rb_sys_fail(0);
return INT2FIX(pgrp);
}
@@ -347,11 +514,25 @@ proc_setpgrp(obj, pid, pgrp)
ipid = NUM2INT(pid);
ipgrp = NUM2INT(pgrp);
+ if (setpgrp(ipid, ipgrp) < 0) rb_sys_fail(0);
+ return Qnil;
+}
+#endif
- if (getpgrp(ipid, ipgrp) == -1) rb_sys_fail(Qnil);
+#ifdef HAVE_SETPGID
+static VALUE
+proc_setpgid(obj, pid, pgrp)
+ VALUE obj, pid, pgrp;
+{
+ int ipid, ipgrp;
- return INT2FIX(0);
+ ipid = NUM2INT(pid);
+ ipgrp = NUM2INT(pgrp);
+
+ if (setpgid(ipid, ipgrp) == -1) rb_sys_fail(0);
+ return Qnil;
}
+#endif
static VALUE
proc_getpriority(obj, which, who)
@@ -364,10 +545,10 @@ proc_getpriority(obj, which, who)
iwho = NUM2INT(who);
prio = getpriority(iwhich, iwho);
- if (prio == -1) rb_sys_fail(Qnil);
+ if (prio == -1) rb_sys_fail(0);
return INT2FIX(prio);
#else
- Fail("The getpriority() function is unimplemented on this machine");
+ rb_notimplement();
#endif
}
@@ -383,10 +564,10 @@ proc_setpriority(obj, which, who, prio)
iprio = NUM2INT(prio);
if (setpriority(iwhich, iwho, iprio) == -1)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
return INT2FIX(0);
#else
- Fail("The setpriority() function is unimplemented on this machine");
+ rb_notimplement();
#endif
}
#endif
@@ -416,7 +597,7 @@ proc_setuid(obj, id)
if (geteuid() == uid)
setuid(uid);
else
- Fail("getruid not implemented");
+ rb_notimplement();
}
#endif
#endif
@@ -448,7 +629,7 @@ proc_setgid(obj, id)
if (getegid() == gid)
setgid(gid);
else
- Fail("getrgid not implemented");
+ rb_notimplement();
}
#endif
#endif
@@ -468,16 +649,16 @@ proc_seteuid(obj, euid)
VALUE obj, euid;
{
#ifdef HAVE_SETEUID
- if (seteuid(NUM2INT(euid)) == -1) rb_sys_fail(Qnil);
+ if (seteuid(NUM2INT(euid)) == -1) rb_sys_fail(0);
#else
#ifdef HAVE_SETREUID
- if (setreuid(-1, NUM2INT(euid)) == -1) rb_sys_fail(Qnil);
+ if (setreuid(-1, NUM2INT(euid)) == -1) rb_sys_fail(0);
#else
euid = NUM2INT(euid);
if (euid == getuid())
setuid(euid);
else
- Fail("seteuid() not implemented");
+ rb_notimplement();
#endif
#endif
return euid;
@@ -496,16 +677,16 @@ proc_setegid(obj, egid)
VALUE obj, egid;
{
#ifdef HAVE_SETEGID
- if (setegid(NUM2INT(egid)) == -1) rb_sys_fail(Qnil);
+ if (setegid(NUM2INT(egid)) == -1) rb_sys_fail(0);
#else
#ifdef HAVE_SETREGID
- if (setregid(-1, NUM2INT(egid)) == -1) rb_sys_fail(Qnil);
+ if (setregid(-1, NUM2INT(egid)) == -1) rb_sys_fail(0);
#else
egid = NUM2INT(egid);
if (egid == getgid())
setgid(egid);
else
- Fail("setegid() not implemented");
+ rb_notimplement();
#endif
#endif
return egid;
@@ -520,41 +701,60 @@ Init_process()
{
extern VALUE cKernel;
- rb_define_virtual_variable("$$", get_pid, Qnil);
- rb_define_readonly_variable("$?", &status);
+ rb_define_virtual_variable("$$", get_pid, 0);
+ rb_define_readonly_variable("$?", &last_status);
#ifndef NT
- rb_define_private_method(cKernel, "exec", f_exec, 1);
+ 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, "system", f_system, -1);
rb_define_private_method(cKernel, "sleep", f_sleep, -1);
mProcess = rb_define_module("Process");
+#ifdef WNOHANG
+ rb_define_const(mProcess, "WNOHANG", INT2FIX(WNOHANG));
+#else
+ rb_define_const(mProcess, "WNOHANG", INT2FIX(0));
+#endif
+#ifdef WUNTRACED
+ rb_define_const(mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
+#else
+ rb_define_const(mProcess, "WUNTRACED", INT2FIX(0));
+#endif
+#endif
+
#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, "kill", f_kill, -1);
+ rb_define_module_function(mProcess, "wait", f_wait, 0);
+ rb_define_module_function(mProcess, "waitpid", f_waitpid, 2);
rb_define_module_function(mProcess, "pid", get_pid, 0);
rb_define_module_function(mProcess, "ppid", get_ppid, 0);
-#ifndef NT
+#if !defined(NT) && !defined(DJGPP)
+#ifdef _POSIX_SOURCE
+ rb_define_module_function(mProcess, "getpgrp", proc_getpgrp, 0);
+ rb_define_module_function(mProcess, "setpgrp", proc_setpgrp, 0);
+#else
rb_define_module_function(mProcess, "getpgrp", proc_getpgrp, -1);
rb_define_module_function(mProcess, "setpgrp", proc_setpgrp, 2);
+#endif
+#ifdef HAVE_SETPGID
+ rb_define_module_function(mProcess, "setpgid", proc_setpgid, 2);
+#endif
+#ifdef HAVE_GETPRIORITY
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));
+#endif
rb_define_module_function(mProcess, "uid", proc_getuid, 0);
rb_define_module_function(mProcess, "uid=", proc_setuid, 1);
diff --git a/random.c b/random.c
index f7bc59569b..ebe5a35dee 100644
--- a/random.c
+++ b/random.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:48 $
created at: Fri Dec 24 16:39:21 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -54,19 +54,35 @@ f_srand(argc, argv, obj)
}
static VALUE
-f_rand(obj, max)
- VALUE obj, max;
+f_rand(obj, vmax)
+ VALUE obj, vmax;
{
- int val;
+ int val, max;
#ifdef HAVE_RANDOM
if (first == 1) {
initstate(1, state, sizeof state);
first = 0;
}
- val = random() % NUM2INT(max);
+#endif
+
+ switch (TYPE(vmax)) {
+ case T_BIGNUM:
+ return big_rand(vmax);
+
+ case T_FLOAT:
+ if (RFLOAT(vmax)->value > LONG_MAX || RFLOAT(vmax)->value < LONG_MIN)
+ return big_rand(dbl2big(RFLOAT(vmax)->value));
+ break;
+ }
+
+ max = NUM2INT(vmax);
+ if (max == 0) ArgError("rand(0)");
+
+#ifdef HAVE_RANDOM
+ val = random() % max;
#else
- val = rand() % NUM2INT(max);
+ val = rand() % max;
#endif
if (val < 0) val = -val;
diff --git a/range.c b/range.c
index 147c28ae7c..b7d2f3b75e 100644
--- a/range.c
+++ b/range.c
@@ -6,7 +6,7 @@
$Date: 1994/12/06 09:30:12 $
created at: Thu Aug 19 17:46:47 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -14,55 +14,58 @@
VALUE mComparable;
static VALUE cRange;
+extern VALUE cNumeric;
static ID upto;
static VALUE
-range_s_new(class, start, end)
- VALUE class, start, end;
+range_s_new(class, first, last)
+ VALUE class, first, last;
{
VALUE obj;
- 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");
+ if (!(FIXNUM_P(first) && FIXNUM_P(last))
+ && (TYPE(first) != TYPE(last)
+ || CLASS_OF(first) != CLASS_OF(last)
+ || !rb_respond_to(first, upto))
+ && !(obj_is_kind_of(first, cNumeric)
+ && obj_is_kind_of(last, cNumeric))) {
+ ArgError("bad value for range");
}
obj = obj_alloc(class);
- rb_iv_set(obj, "start", start);
- rb_iv_set(obj, "end", end);
+ rb_iv_set(obj, "first", first);
+ rb_iv_set(obj, "last", last);
return obj;
}
VALUE
-range_new(start, end)
- VALUE start, end;
+range_new(first, last)
+ VALUE first, last;
{
- return range_s_new(cRange, start, end);
+ return range_s_new(cRange, first, last);
}
static VALUE
range_match(rng, obj)
VALUE rng, obj;
{
- VALUE beg, end;
+ VALUE first, last;
- beg = rb_iv_get(rng, "start");
- end = rb_iv_get(rng, "end");
+ first = rb_iv_get(rng, "first");
+ last = rb_iv_get(rng, "last");
- if (FIXNUM_P(beg) && FIXNUM_P(obj)) {
- if (FIX2INT(beg) <= FIX2INT(obj) && FIX2INT(obj) <= FIX2INT(end)) {
+ if (FIXNUM_P(first) && FIXNUM_P(obj)) {
+ if (FIX2INT(first) <= FIX2INT(obj) && FIX2INT(obj) <= FIX2INT(last)) {
return TRUE;
}
return FALSE;
}
else {
- if (rb_funcall(beg, rb_intern("<="), 1, obj) &&
- rb_funcall(end, rb_intern(">="), 1, obj)) {
+ if (rb_funcall(first, rb_intern("<="), 1, obj) &&
+ rb_funcall(last, rb_intern(">="), 1, obj)) {
return TRUE;
}
return FALSE;
@@ -70,23 +73,15 @@ range_match(rng, obj)
}
struct upto_data {
- VALUE beg;
- VALUE end;
+ VALUE first;
+ VALUE last;
};
static VALUE
range_upto(data)
struct upto_data *data;
{
- return rb_funcall(data->beg, upto, 1, data->end);
-}
-
-static VALUE
-range_upto_yield(v)
- VALUE v;
-{
- rb_yield(v);
- return Qnil;
+ return rb_funcall(data->first, upto, 1, data->last);
}
static VALUE
@@ -95,73 +90,105 @@ range_each(obj)
{
VALUE b, e;
- b = rb_iv_get(obj, "start");
- e = rb_iv_get(obj, "end");
+ b = rb_iv_get(obj, "first");
+ e = rb_iv_get(obj, "last");
if (FIXNUM_P(b)) { /* fixnum is a special case(for performance) */
num_upto(b, e);
}
- else if (TYPE(b) == T_STRING) {
- str_upto(b, e);
- }
else {
struct upto_data data;
- data.beg = b;
- data.end = e;
+ data.first = b;
+ data.last = e;
- rb_iterate(range_upto, &data, range_upto_yield, Qnil);
+ rb_iterate(range_upto, &data, rb_yield, 0);
}
return Qnil;
}
static VALUE
-range_start(obj)
+range_first(obj)
VALUE obj;
{
VALUE b;
- b = rb_iv_get(obj, "start");
+ b = rb_iv_get(obj, "first");
return b;
}
static VALUE
-range_end(obj)
+range_last(obj)
VALUE obj;
{
VALUE e;
- e = rb_iv_get(obj, "end");
+ e = rb_iv_get(obj, "last");
return e;
}
-static VALUE
-range_to_s(obj)
- VALUE obj;
-{
- VALUE args[4];
-
- args[0] = str_new2("%d..%d");
- args[1] = rb_iv_get(obj, "start");
- args[2] = rb_iv_get(obj, "end");
- return f_sprintf(3, args);
-}
-
VALUE
range_beg_end(range, begp, endp)
VALUE range;
int *begp, *endp;
{
- int beg, end;
+ VALUE first, last;
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);
+ first = rb_iv_get(range, "first"); *begp = NUM2INT(first);
+ last = rb_iv_get(range, "last"); *endp = NUM2INT(last);
return TRUE;
}
+static VALUE
+range_to_s(range)
+ VALUE range;
+{
+ VALUE str, str2;
+
+ str = obj_as_string(rb_iv_get(range, "first"));
+ str2 = obj_as_string(rb_iv_get(range, "last"));
+ str_cat(str, "..", 2);
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+
+ return str;
+}
+
+static VALUE
+range_inspect(range)
+ VALUE range;
+{
+ VALUE str, str2;
+
+ str = rb_inspect(rb_iv_get(range, "first"));
+ str2 = rb_inspect(rb_iv_get(range, "last"));
+ str_cat(str, "..", 2);
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+
+ return str;
+}
+
+static VALUE
+range_length(rng)
+ VALUE rng;
+{
+ VALUE first, last;
+ VALUE size;
+
+ first = rb_iv_get(rng, "first");
+ last = rb_iv_get(rng, "last");
+
+ if (!obj_is_kind_of(first, cNumeric)) {
+ return enum_length(rng);
+ }
+ size = rb_funcall(last, '-', 1, first);
+ size = rb_funcall(size, '+', 1, INT2FIX(1));
+
+ return size;
+}
+
extern VALUE mEnumerable;
void
@@ -170,11 +197,15 @@ Init_Range()
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, "===", 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, "first", range_first, 0);
+ rb_define_method(cRange, "last", range_last, 0);
rb_define_method(cRange, "to_s", range_to_s, 0);
+ rb_define_method(cRange, "inspect", range_inspect, 0);
+
+ rb_define_method(cRange, "length", range_length, 0);
+ rb_define_method(cRange, "size", range_length, 0);
upto = rb_intern("upto");
}
diff --git a/re.c b/re.c
index f6dde5a4eb..bd6c2fb9b8 100644
--- a/re.c
+++ b/re.c
@@ -6,13 +6,15 @@
$Date: 1995/01/10 10:42:49 $
created at: Mon Aug 9 18:24:49 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#include "ruby.h"
#include "re.h"
+static VALUE eRegxpError;
+
#define BEG(no) regs->beg[no]
#define END(no) regs->end[no]
@@ -104,6 +106,73 @@ static int reg_kcode =
# endif
#endif
+extern int rb_in_eval;
+
+static VALUE
+reg_desc(s, len, re)
+ char *s;
+ int len;
+ VALUE re;
+{
+ VALUE str = str_new2("/");
+ char *p, *pend;
+ int slash = 0;
+
+ p = s; pend = p + len;
+ while (p<pend) {
+ if (*p == '/') {
+ slash = 1;
+ break;
+ }
+ p++;
+ }
+ if (!slash) {
+ str_cat(str, s, len);
+ }
+ else {
+ p = s;
+ while (p<pend) {
+ if (*p == '/') {
+ char c = '\\';
+ str_cat(str, &c, 1);
+ str_cat(str, p, 1);
+ }
+ else {
+ str_cat(str, p, 1);
+ }
+ p++;
+ }
+ }
+ str_cat(str, "/", 1);
+ if (re && FL_TEST(re, REG_IGNORECASE)) {
+ str_cat(str, "i", 1);
+ }
+ return str;
+}
+
+static VALUE
+reg_inspect(re)
+ struct RRegexp *re;
+{
+ return reg_desc(re->str, re->len, re);
+}
+
+static void
+reg_raise(s, len, err, compile, re)
+ char *s;
+ int len;
+ char *err;
+ int compile;
+ VALUE re;
+{
+ VALUE desc = reg_desc(s, len, re);
+
+ if (!compile)
+ Raise(eRegxpError, "%s: %s", err, RSTRING(desc)->ptr);
+ else
+ Error("%s: %s", err, RSTRING(desc)->ptr);
+}
+
static Regexp*
make_regexp(s, len)
char *s;
@@ -125,17 +194,52 @@ make_regexp(s, len)
rp->allocated = 16;
rp->fastmap = ALLOC_N(char, 256);
- if ((err = re_compile_pattern(s, (size_t)len, rp)) != NULL)
- Fail("%s: /%s/", err, s);
+ if ((err = re_compile_pattern(s, (size_t)len, rp)) != NULL) {
+ reg_raise(s, len, err, !rb_in_eval, 0);
+ }
return rp;
}
+extern VALUE cData;
+static VALUE cMatch;
+
+static VALUE
+match_to_a(match)
+ struct RMatch *match;
+{
+ struct re_registers *regs = match->regs;
+ VALUE ary = ary_new(regs->num_regs);
+ int i;
+
+ for (i=0; i<regs->num_regs; i++) {
+ if (regs->beg[0] == -1) ary_push(ary, Qnil);
+ else ary_push(ary, str_new(match->ptr+regs->beg[i],
+ regs->end[i]-regs->beg[i]));
+ }
+ return ary;
+}
+
+static VALUE
+match_to_s(match)
+ struct RMatch *match;
+{
+ int beg, len;
+
+ if (match->regs->allocated == 0) return Qnil;
+
+ beg = match->regs->beg[0];
+ if (beg == -1) return Qnil;
+
+ len = match->regs->end[0] - beg;
+ return str_new(match->ptr+beg, len);
+}
+
static VALUE
match_alloc()
{
NEWOBJ(match, struct RMatch);
- OBJSETUP(match, cData, T_MATCH);
+ OBJSETUP(match, cMatch, T_MATCH);
match->ptr = 0;
match->len = 0;
@@ -155,10 +259,12 @@ reg_search(reg, str, start, regs)
struct re_registers *regs;
{
int result;
- int casefold = ignorecase;
+ int casefold = RTEST(ignorecase);
VALUE match = 0;
struct re_registers *regs0 = 0;
+ if (start > str->len) return -1;
+
/* case-flag set for the object */
if (FL_TEST(reg, REG_IGNORECASE)) {
casefold = TRUE;
@@ -174,16 +280,11 @@ reg_search(reg, str, start, regs)
reg->ptr->fastmap_accurate = 0;
}
- if (start > str->len) return -1;
-
- if (regs == (struct re_registers *)-1) {
+ if (regs == (struct re_registers*)-1) {
regs = 0;
}
- else if (match = backref_get()) {
- if (match == 1) {
- match = match_alloc();
- backref_set(match);
- }
+ else {
+ match = match_alloc();
regs0 = RMATCH(match)->regs;
}
@@ -192,8 +293,9 @@ reg_search(reg, str, start, 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);
+ if ((err = re_compile_pattern(reg->str, reg->len, reg->ptr)) != NULL) {
+ reg_raise(reg->str, reg->len, err, reg);
+ }
RBASIC(reg)->flags = RBASIC(reg)->flags & ~KCODE_MASK;
RBASIC(reg)->flags |= reg_kcode;
}
@@ -201,11 +303,18 @@ reg_search(reg, str, start, regs)
result = re_search(reg->ptr, str->ptr, str->len,
start, str->len - start, regs0);
- if (match && result >= 0) {
+ if (start == -2) {
+ reg_raise(reg->str, reg->len, "Stack overfow in regexp matcher", reg);
+ }
+ if (result < 0) {
+ backref_set(Qnil);
+ }
+ else if (match) {
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';
+ backref_set(match);
}
if (regs && regs0 && regs0 != regs) re_copy_registers(regs, regs0);
@@ -217,7 +326,7 @@ reg_nth_defined(nth, match)
int nth;
struct RMatch *match;
{
- if (!match) return FALSE;
+ if (NIL_P(match)) return Qnil;
if (nth >= match->regs->num_regs) {
return FALSE;
}
@@ -232,7 +341,7 @@ reg_nth_match(nth, match)
{
int start, end, len;
- if (!match) return Qnil;
+ if (NIL_P(match)) return Qnil;
if (nth >= match->regs->num_regs) {
return Qnil;
}
@@ -254,7 +363,7 @@ VALUE
reg_match_pre(match)
struct RMatch *match;
{
- if (!match) return Qnil;
+ if (NIL_P(match)) return Qnil;
if (match->BEG(0) == -1) return Qnil;
return str_new(match->ptr, match->BEG(0));
}
@@ -263,7 +372,7 @@ VALUE
reg_match_post(match)
struct RMatch *match;
{
- if (!match) return Qnil;
+ if (NIL_P(match)) return Qnil;
if (match->BEG(0) == -1) return Qnil;
return str_new(match->ptr+match->END(0),
match->len-match->END(0));
@@ -275,7 +384,7 @@ reg_match_last(match)
{
int i;
- if (!match) return Qnil;
+ if (NIL_P(match)) return Qnil;
if (match->BEG(0) == -1) return Qnil;
for (i=match->regs->num_regs-1; match->BEG(i) == -1 && i > 0; i--)
@@ -293,13 +402,6 @@ Regexp *rp;
free(rp);
}
-void
-reg_error(s)
-const char *s;
-{
- Fail(s);
-}
-
VALUE cRegexp;
static VALUE
@@ -331,19 +433,22 @@ reg_new(s, len, ci)
return reg_new_1(cRegexp, s, len, ci);
}
-static VALUE reg_cache, ign_cache;
+int ign_cache;
+static VALUE reg_cache;
VALUE
reg_regcomp(str)
struct RString *str;
{
+ int ignc = RTEST(ignorecase);
+
if (reg_cache && RREGEXP(reg_cache)->len == str->len
- && ign_cache == ignorecase
+ && ign_cache == ignc
&& memcmp(RREGEXP(reg_cache)->str, str->ptr, str->len) == 0)
return reg_cache;
- ign_cache = ignorecase;
- return reg_cache = reg_new(str->ptr, str->len, ignorecase);
+ ign_cache = ignc;
+ return reg_cache = reg_new(str->ptr, str->len, ignc);
}
VALUE
@@ -356,7 +461,7 @@ reg_match(re, str)
if (TYPE(str) != T_STRING) return FALSE;
start = reg_search(re, str, 0, 0);
if (start < 0) {
- return Qnil;
+ return FALSE;
}
return INT2FIX(start);
}
@@ -365,15 +470,15 @@ VALUE
reg_match2(re)
struct RRegexp *re;
{
- extern VALUE rb_lastline;
int start;
+ VALUE line = lastline_get();
- if (TYPE(rb_lastline) != T_STRING)
- Fail("$_ is not a string");
+ if (TYPE(line) != T_STRING)
+ return FALSE;
- start = reg_search(re, rb_lastline, 0, 0);
- if (start == -1) {
- return Qnil;
+ start = reg_search(re, line, 0, 0);
+ if (start < 0) {
+ return FALSE;
}
return INT2FIX(start);
}
@@ -384,11 +489,11 @@ reg_s_new(argc, argv, self)
VALUE *argv;
VALUE self;
{
- VALUE src, reg;
+ VALUE src;
int ci = 0;
if (argc == 0 || argc > 2) {
- Fail("wrong # of argument");
+ ArgError("wrong # of argument");
}
if (argc == 2 && argv[1]) {
ci = 1;
@@ -397,10 +502,12 @@ reg_s_new(argc, argv, self)
src = argv[0];
switch (TYPE(src)) {
case T_STRING:
- reg = reg_new_1(self, RREGEXP(src)->ptr, RREGEXP(src)->len, ci);
+ return reg_new_1(self, RSTRING(src)->ptr, RSTRING(src)->len, ci);
+ break;
case T_REGEXP:
- reg = reg_new_1(self, RREGEXP(src)->str, RREGEXP(src)->len, ci);
+ return reg_new_1(self, RREGEXP(src)->str, RREGEXP(src)->len, ci);
+ break;
default:
Check_Type(src, T_STRING);
@@ -472,7 +579,7 @@ reg_regsub(str, src, regs)
no = -1;
if (no >= 0) {
- if (val == Qnil) {
+ if (NIL_P(val)) {
val = str_new(p, ss-p);
}
else {
@@ -490,7 +597,7 @@ reg_regsub(str, src, regs)
}
}
- if (val == Qnil) return (VALUE)str;
+ if (NIL_P(val)) return (VALUE)str;
if (p < e) {
str_cat(val, p, e-p);
}
@@ -501,20 +608,6 @@ reg_regsub(str, src, regs)
}
static VALUE
-reg_to_s(re)
- struct RRegexp *re;
-{
- 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) {
@@ -569,26 +662,7 @@ kcode_setter(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;
+ return backref_get();
}
static void
@@ -598,11 +672,16 @@ match_setter(val)
backref_set(val);
}
+VALUE krn_to_s();
+
void
Init_Regexp()
{
+ extern VALUE eException;
+
+ eRegxpError = rb_define_class("RegxpError", eException);
+
re_set_syntax(RE_NO_BK_PARENS | RE_NO_BK_VBAR
- | RE_AWK_CLASS_HACK
| RE_INTERVALS
| RE_NO_BK_BRACES
| RE_BACKSLASH_ESCAPE_IN_LISTS
@@ -623,8 +702,14 @@ Init_Regexp()
rb_define_method(cRegexp, "clone", reg_clone, 0);
rb_define_method(cRegexp, "=~", reg_match, 1);
+ 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_define_method(cRegexp, "inspect", reg_inspect, 0);
rb_global_variable(&reg_cache);
+
+ cMatch = rb_define_class("MatchingData", cData);
+ rb_define_method(cMatch, "to_a", match_to_a, 0);
+ rb_define_method(cMatch, "to_s", match_to_s, 0);
+ rb_define_method(cMatch, "inspect", krn_to_s, 0);
}
diff --git a/re.h b/re.h
index babbe413e5..ebbe0673f5 100644
--- a/re.h
+++ b/re.h
@@ -7,7 +7,7 @@
$Date: 1994/08/12 04:47:52 $
created at: Thu Sep 30 14:18:32 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
diff --git a/regex.c b/regex.c
index 4b4ff8a193..c6bbb3027f 100644
--- a/regex.c
+++ b/regex.c
@@ -62,6 +62,10 @@ char *alloca();
#endif
#endif /* __GNUC__ */
+#ifdef _AIX
+#pragma alloca
+#endif
+
#define RE_ALLOCATE alloca
#define FREE_VARIABLES() alloca(0)
@@ -144,6 +148,8 @@ init_syntax_once()
for (c = '0'; c <= '9'; c++)
re_syntax_table[c] = Sword;
+ re_syntax_table['_'] = Sword;
+
/* Add specific syntax for ISO Latin-1. */
for (c = 0300; c <= 0377; c++)
re_syntax_table[c] = Sword;
@@ -238,7 +244,7 @@ enum regexpcode
#define NFAILURES 80
#endif
-#ifdef CHAR_UNSIGNED
+#if defined(CHAR_UNSIGNED) || defined(__CHAR_UNSIGNED__)
#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */
#endif
#ifndef SIGN_EXTEND_CHAR
@@ -795,7 +801,7 @@ re_compile_pattern(pattern, size, bufp)
while (1)
{
int size;
- unsigned last = -1;
+ unsigned last = (unsigned)-1;
if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH]))) {
/* Ensure the space is enough to hold another interval
@@ -830,8 +836,8 @@ re_compile_pattern(pattern, size, bufp)
PATFETCH(c);
switch (c) {
case 'w':
- for (c = 0; c < 256; c++)
- if (isalnum(c))
+ for (c = 0; c < (1 << BYTEWIDTH); c++)
+ if (SYNTAX(c) == Sword)
SET_LIST_BIT(c);
last = -1;
continue;
@@ -839,8 +845,8 @@ re_compile_pattern(pattern, size, bufp)
case 'W':
if (re_syntax_options & RE_MBCTYPE_MASK)
goto invalid_char;
- for (c = 0; c < 256; c++)
- if (!isalnum(c))
+ for (c = 0; c < (1 << BYTEWIDTH); c++)
+ if (SYNTAX(c) != Sword)
SET_LIST_BIT(c);
last = -1;
continue;
@@ -1072,7 +1078,9 @@ re_compile_pattern(pattern, size, bufp)
|| *laststart == charset
|| *laststart == charset_not
|| *laststart == start_memory
- || (*laststart == exactn && laststart[1] == 1)
+ || (*laststart == exactn
+ && (laststart[1] == 1
+ || laststart[1] == 2 && ismbchar(laststart[2])))
|| (! (re_syntax_options & RE_NO_BK_REFS)
&& *laststart == duplicate)))
{
@@ -1277,6 +1285,7 @@ re_compile_pattern(pattern, size, bufp)
c1 = 0;
GET_UNSIGNED_NUMBER(c1);
+ PATUNFETCH;
if (c1 >= regnum) {
if (c1 < RE_NREGS)
@@ -1722,27 +1731,27 @@ re_compile_fastmap(bufp)
-/* Using the compiled pattern in PBUFP->buffer, first tries to match
+/* Using the compiled pattern in BUFP->buffer, first tries to match
STRING, starting first at index STARTPOS, then at STARTPOS + 1, and
so on. RANGE is the number of places to try before giving up. If
RANGE is negative, it searches backwards, i.e., the starting
positions tried are STARTPOS, STARTPOS - 1, etc. STRING is of SIZE.
In REGS, return the indices of STRING that matched the entire
- PBUFP->buffer and its contained subexpressions.
+ BUFP->buffer and its contained subexpressions.
The value returned is the position in the strings at which the match
was found, or -1 if no match was found, or -2 if error (such as
failure stack overflow). */
int
-re_search(pbufp, string, size, startpos, range, regs)
- struct re_pattern_buffer *pbufp;
+re_search(bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
char *string;
int size, startpos, range;
struct re_registers *regs;
{
- register char *fastmap = pbufp->fastmap;
- register unsigned char *translate = (unsigned char *) pbufp->translate;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *translate = (unsigned char *) bufp->translate;
int val;
/* Check for out-of-range starting position. */
@@ -1750,8 +1759,8 @@ re_search(pbufp, string, size, startpos, range, regs)
return -1;
/* Update the fastmap now if not correct already. */
- if (fastmap && !pbufp->fastmap_accurate) {
- re_compile_fastmap (pbufp);
+ if (fastmap && !bufp->fastmap_accurate) {
+ re_compile_fastmap (bufp);
}
while (1)
@@ -1762,7 +1771,7 @@ re_search(pbufp, string, size, startpos, range, regs)
test it at each starting point so that we take the first null
string we get. */
- if (fastmap && startpos < size && pbufp->can_be_null != 1)
+ if (fastmap && startpos < size && bufp->can_be_null != 1)
{
if (range > 0) /* Searching forwards. */
{
@@ -1781,7 +1790,7 @@ re_search(pbufp, string, size, startpos, range, regs)
p++;
range--;
}
- else
+ else
if (fastmap[translate ? translate[c] : c])
break;
range--;
@@ -1799,11 +1808,12 @@ re_search(pbufp, string, size, startpos, range, regs)
}
}
- if (range >= 0 && startpos == size
- && fastmap && pbufp->can_be_null == 0)
- return -1;
+ if (range >= 0 && startpos == size && fastmap) {
+ if (bufp->can_be_null == 0 || (bufp->can_be_null == 2 && size > 0))
+ return -1;
+ }
- val = re_match(pbufp, string, size, startpos, regs);
+ val = re_match(bufp, string, size, startpos, regs);
if (val >= 0)
return startpos;
if (val == -2)
@@ -2006,12 +2016,12 @@ init_regs(regs, num_regs)
}
}
-/* Match the pattern described by PBUFP against STRING, which is of
+/* Match the pattern described by BUFP against STRING, which is of
SIZE. Start the match at index POS in STRING. In REGS, return the
- indices of STRING that matched the entire PBUFP->buffer and its
+ indices of STRING that matched the entire BUFP->buffer and its
contained subexpressions.
- If pbufp->fastmap is nonzero, then it had better be up to date.
+ If bufp->fastmap is nonzero, then it had better be up to date.
The reason that the data to match are specified as two components
which are to be regarded as concatenated is so this function can be
@@ -2022,24 +2032,24 @@ init_regs(regs, num_regs)
length of the substring which was matched. */
int
-re_match(pbufp, string_arg, size, pos, regs)
- struct re_pattern_buffer *pbufp;
+re_match(bufp, string_arg, size, pos, regs)
+ struct re_pattern_buffer *bufp;
char *string_arg;
int size, pos;
struct re_registers *regs;
{
- register unsigned char *p = (unsigned char *) pbufp->buffer;
+ register unsigned char *p = (unsigned char *) bufp->buffer;
/* Pointer to beyond end of buffer. */
- register unsigned char *pend = p + pbufp->used;
+ register unsigned char *pend = p + bufp->used;
- unsigned num_regs = pbufp->re_nsub + 1;
+ unsigned num_regs = bufp->re_nsub;
unsigned char *string = (unsigned char *) string_arg;
register unsigned char *d, *dend;
register int mcnt; /* Multipurpose. */
- unsigned char *translate = (unsigned char *) pbufp->translate;
+ unsigned char *translate = (unsigned char *) bufp->translate;
unsigned is_a_jump_n = 0;
/* Failure point stack. Each place that can handle a failure further
@@ -2133,7 +2143,7 @@ re_match(pbufp, string_arg, size, pos, regs)
#ifdef DEBUG_REGEX
fprintf(stderr,
"regex loop(%d): matching 0x%02d\n",
- p - (unsigned char *) pbufp->buffer,
+ p - (unsigned char *) bufp->buffer,
*p);
#endif
is_a_jump_n = 0;
@@ -2666,21 +2676,25 @@ re_copy_registers(regs1, regs2)
if (regs1->allocated == 0) {
regs1->beg = TMALLOC(regs2->num_regs, int);
regs1->end = TMALLOC(regs2->num_regs, int);
+ regs1->allocated = regs2->num_regs;
}
else if (regs1->allocated < regs2->num_regs) {
TREALLOC(regs1->beg, regs2->num_regs, int);
TREALLOC(regs1->end, regs2->num_regs, int);
+ regs1->allocated = regs2->num_regs;
}
for (i=0; i<regs2->num_regs; i++) {
regs1->beg[i] = regs2->beg[i];
regs1->end[i] = regs2->end[i];
}
+ regs1->num_regs = regs2->num_regs;
}
void
re_free_registers(regs)
struct re_registers *regs;
{
+ if (regs->allocated == 0) return;
if (regs->beg) free(regs->beg);
if (regs->end) free(regs->end);
}
diff --git a/regex.h b/regex.h
index 971c4e5c74..7b31b87b62 100644
--- a/regex.h
+++ b/regex.h
@@ -17,7 +17,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto)
Last change: May 21, 1993 by t^2 */
-
+/* modifis for Ruby by matz@caelum.co.jp */
#ifndef __REGEXP_LIBRARY
#define __REGEXP_LIBRARY
diff --git a/ruby.c b/ruby.c
index 9d437f8895..8e4ab30148 100644
--- a/ruby.c
+++ b/ruby.c
@@ -6,38 +6,39 @@
$Date: 1995/01/10 10:42:51 $
created at: Tue Aug 10 12:47:31 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#include "ruby.h"
#include "re.h"
+#include "dln.h"
#include <stdio.h>
-#include <fcntl.h>
#include <sys/types.h>
-#include <sys/stat.h>
-#include "dln.h"
+#include <fcntl.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
char *strchr();
+char *strrchr();
char *strstr();
#endif
static int version, copyright;
-int debug = 0;
-int verbose = 0;
+int debug = FALSE;
+int verbose = FALSE;
static int sflag = FALSE;
-char *inplace = 0;
+char *inplace = FALSE;
char *strdup();
extern int yydebug;
extern int nerrs;
-int xflag = FALSE;
+static int xflag = FALSE;
+extern VALUE RS, RS_default, ORS, FS;
static void load_stdin();
static void load_file();
@@ -49,10 +50,18 @@ static int do_split = FALSE;
static char *script;
#ifndef RUBY_LIB
-#define RUBY_LIB ".:/usr/local/lib/ruby"
+#if defined(MSDOS)
+#define RUBY_LIB "/usr/local/lib/ruby;."
+#else
+#define RUBY_LIB "/usr/local/lib/ruby:."
+#endif
#endif
+#if defined(MSDOS)
+#define RUBY_LIB_SEP ';'
+#else
#define RUBY_LIB_SEP ':'
+#endif
extern VALUE rb_load_path;
VALUE Frequire();
@@ -62,21 +71,24 @@ addpath(path)
char *path;
{
char *p, *s;
+ VALUE ary;
if (path == 0) return;
+ ary = ary_new();
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)));
+ if (s = strrchr(p, RUBY_LIB_SEP)) {
+ ary_push(ary, str_new(p, (int)(s-p)));
p = s + 1;
}
else {
- ary_push(rb_load_path, str_new2(p));
+ ary_push(ary, str_new2(p));
break;
}
}
+ rb_load_path = ary_plus(ary, rb_load_path);
}
static void
@@ -89,13 +101,11 @@ proc_options(argcp, argvp)
int script_given, do_search;
char *s;
- extern VALUE RS, ORS, FS;
-
if (argc == 0) return;
version = FALSE;
- script_given = FALSE;
do_search = FALSE;
+ script_given = 0;
for (argc--,argv++; argc > 0; argc--,argv++) {
if (argv[0][0] != '-' || !argv[0][1]) break;
@@ -127,8 +137,10 @@ proc_options(argcp, argvp)
goto reswitch;
case 'v':
- verbose = TRUE;
show_version();
+ verbose = 2;
+ case 'w':
+ verbose |= 1;
s++;
goto reswitch;
@@ -157,13 +169,12 @@ proc_options(argcp, argvp)
script_given++;
if (script == 0) script = "-e";
if (argv[1]) {
- lex_setsrc("-e", argv[1], strlen(argv[1]));
+ compile_string("-e", argv[1], strlen(argv[1]));
argc--,argv++;
}
else {
- lex_setsrc("-e", "", 0);
+ compile_string("-e", "", 0);
}
- yyparse();
break;
case 'r':
@@ -211,9 +222,9 @@ proc_options(argcp, argvp)
case 'I':
if (*++s)
- ary_push(rb_load_path, str_new2(s));
+ addpath(s);
else if (argv[1]) {
- ary_push(rb_load_path, str_new2(argv[1]));
+ addpath(argv[1]);
argc--,argv++;
}
break;
@@ -250,7 +261,7 @@ proc_options(argcp, argvp)
else if (strcmp("version", s) == 0)
version = 1;
else if (strcmp("verbose", s) == 0)
- verbose = 1;
+ verbose = 2;
else if (strcmp("yydebug", s) == 0)
yydebug = 1;
else {
@@ -277,22 +288,29 @@ proc_options(argcp, argvp)
show_copyright();
}
- if (script_given == 0) {
+ if (script_given == FALSE) {
if (argc == 0) { /* no more args */
- if (verbose) exit(0);
+ if (verbose == 3) exit(0);
script = "-";
load_stdin();
}
else {
script = argv[0];
- if (do_search) {
- script = dln_find_file(script, getenv("PATH"));
- if (!script) script = argv[0];
+ if (script[0] == '\0') {
+ script = "-";
+ load_stdin();
+ }
+ else {
+ if (do_search) {
+ script = dln_find_file(script, getenv("PATH"));
+ if (!script) script = argv[0];
+ }
+ load_file(script, 1);
}
- load_file(script, 1);
- argc--,argv++;
+ argc--; argv++;
}
}
+ if (verbose) verbose = TRUE;
xflag = FALSE;
*argvp = argv;
@@ -321,93 +339,85 @@ proc_options(argcp, argvp)
}
+static VALUE
+open_to_load(fname)
+ char *fname;
+{
+}
+
static void
-readin(fd, fname, script)
- int fd;
+load_file(fname, script)
char *fname;
int script;
{
- struct stat st;
- char *ptr, *p, *pend;
+ extern VALUE rb_stdin;
+ VALUE f;
+ int line_start = 1;
- if (fstat(fd, &st) < 0) rb_sys_fail(fname);
- if (!S_ISREG(st.st_mode))
- Fail("script is not a regular file - %s", fname);
-
- p = ptr = ALLOC_N(char, st.st_size+1);
- if (read(fd, ptr, st.st_size) != st.st_size) {
- free(ptr);
- rb_sys_fail(fname);
+ if (strcmp(fname, "-") == 0) {
+ f = rb_stdin;
+ }
+ else {
+ f = file_open(fname, "r");
}
- pend = p + st.st_size;
- *pend = '\0';
if (script) {
+ VALUE c;
+ VALUE line;
+ VALUE rs = RS;
+
+ RS = RS_default;
if (xflag) {
xflag = FALSE;
- while (p < pend) {
- if (p[0] == '#' && p[1] == '!') {
- char *s = p;
- while (s < pend && *s != '\n') s++;
- if (*s == '\n') {
- *s = '\0';
- if (strstr(p, "ruby")) {
- *s = '\n';
- goto start_read;
- }
+ while (!NIL_P(line = io_gets(f))) {
+ line_start++;
+ if (RSTRING(line)->len > 2
+ || RSTRING(line)->ptr[0] != '#'
+ || RSTRING(line)->ptr[1] != '!') {
+ if (strstr(RSTRING(line)->ptr, "ruby")) {
+ goto start_read;
}
- p = s + 1;
- }
- else {
- while (p < pend && *p++ != '\n')
- ;
- if (p >= pend) break;
}
}
- free(ptr);
- Fail("No Ruby script found in input");
+ RS = rs;
+ LoadError("No Ruby script found in input");
}
- start_read:
- if (p[0] == '#' && p[1] == '!') {
- char *s = p, *q;
+ c = io_getc(f);
+ if (c == INT2FIX('#')) {
+ line = io_gets(f);
+ line_start++;
+
+ if (RSTRING(line)->len > 2
+ || RSTRING(line)->ptr[0] != '#'
+ || RSTRING(line)->ptr[1] != '!') {
- while (s < pend && *s != '\n') s++;
- if (*s == '\n') {
- *s = '\0';
- if (q = strstr(p, "ruby -")) {
+ char *p;
+
+ start_read:
+ if (p = strstr(RSTRING(line)->ptr, "ruby -")) {
int argc; char *argv[2]; char **argvp = argv;
- argc = 2; argv[0] = Qnil; argv[1] = q + 5;
+ char *s;
+
+ s = RSTRING(line)->ptr;
+ while (isspace(*s++))
+ ;
+ *s = '\0';
+ RSTRING(line)->ptr[RSTRING(line)->len-1] = '\0';
+ if (RSTRING(line)->ptr[RSTRING(line)->len-2] == '\r')
+ RSTRING(line)->ptr[RSTRING(line)->len-2] = '\0';
+ argc = 2; argv[0] = 0; argv[1] = p + 5;
proc_options(&argc, &argvp);
- p = s + 1;
- }
- else {
- *s = '\n';
}
}
}
+ else if (!NIL_P(c)) {
+ io_ungetc(f, c);
+ }
+ RS = rs;
}
- lex_setsrc(fname, p, pend - p);
- yyparse();
- free(ptr);
-}
-
-static void
-load_file(fname, script)
- char *fname;
- int script;
-{
- int fd;
-
- if (fname[0] == '\0') {
- load_stdin();
- return;
- }
-
- fd = open(fname, O_RDONLY, 0);
- if (fd < 0) rb_sys_fail(fname);
- readin(fd, fname, script);
- close(fd);
+ compile_file(fname, f, line_start);
+ if (f != rb_stdin) io_close(f);
}
void
@@ -420,21 +430,7 @@ rb_load_file(fname)
static void
load_stdin()
{
- char buf[32];
- FILE *f;
- char c;
- int fd;
-
- sprintf(buf, "/tmp/ruby-f%d", getpid());
- f = fopen(buf, "w");
- fd = open(buf, O_RDONLY, 0);
- if (fd < 0) rb_sys_fail(buf);
- unlink(buf);
- while ((c = getchar()) != EOF) {
- putc(c, f);
- }
- fclose(f);
- readin(fd, "-");
+ load_file("-", 1);
}
VALUE Progname;
@@ -500,16 +496,28 @@ ruby_options(argc, argv, envp)
origargc = argc; origargv = argv; origenvp = envp;
- rb_define_variable("$@", &errat);
errat = str_new2(argv[0]);
rb_define_variable("$VERBOSE", &verbose);
rb_define_variable("$DEBUG", &debug);
+ addpath(getenv("RUBYLIB"));
+ addpath(RUBY_LIB);
+#ifdef RUBY_ARCHLIB
+ addpath(RUBY_ARCHLIB);
+#endif
#if defined(USE_DLN_A_OUT)
dln_argv0 = argv[0];
#endif
+ rb_define_hooked_variable("$0", &Progname, 0, set_arg0);
+
+ Argv = ary_new2(argc);
+ rb_define_readonly_variable("$*", &Argv);
+ rb_define_global_const("ARGV", Argv);
+
proc_options(&argc, &argv);
+ ruby_script(script);
+
if (do_check && nerrs == 0) {
printf("Syntax OK\n");
exit(0);
@@ -518,18 +526,9 @@ ruby_options(argc, argv, envp)
yyappend_print();
}
if (do_loop) {
- yywhole_loop(do_line, do_split);
+ yywhile_loop(do_line, do_split);
}
- rb_define_hooked_variable("$0", &Progname, 0, set_arg0);
- ruby_script(script);
-
- 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]));
}
diff --git a/ruby.h b/ruby.h
index 5ea5a237af..13c96e1a73 100644
--- a/ruby.h
+++ b/ruby.h
@@ -6,7 +6,7 @@
$Date: 1995/01/12 08:54:52 $
created at: Thu Jun 10 14:26:32 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
*************************************************/
@@ -26,12 +26,19 @@
# else
# define const
# endif
+# define _(args)
+#else
+# define _(args) args
#endif
#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
#include <alloca.h>
#endif
+#ifdef _AIX
+#pragma alloca
+#endif
+
typedef unsigned int UINT;
typedef UINT VALUE;
typedef UINT ID;
@@ -66,6 +73,7 @@ typedef unsigned short USHORT;
#define FIXNUM_FLAG 0x01
#define INT2FIX(i) (VALUE)(((int)(i))<<1 | FIXNUM_FLAG)
VALUE int2inum();
+#define INT2NUM(v) int2inum(v)
#if (-1==(((-1)<<1)&FIXNUM_FLAG)>>1)
# define RSHIFT(x,y) ((x)>>y)
@@ -80,19 +88,20 @@ VALUE int2inum();
#define NEGFIXABLE(f) ((f) >= FIXNUM_MIN)
#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f))
-#define NIL_P(p) ((p) == Qnil)
-
+/* special contants - i.e. non-zero and non-fixnum constants */
+#define FALSE 0
#undef TRUE
-extern VALUE TRUE;
-#define FALSE Qnil
+#define TRUE 2
+#define Qnil 4
+
+int rb_test_false_or_nil();
+# define RTEST(v) rb_test_false_or_nil(v)
+#define NIL_P(v) ((VALUE)(v) == Qnil)
extern VALUE cObject;
-extern VALUE cNil;
-extern VALUE cFixnum;
-extern VALUE cData;
-#define CLASS_OF(obj) (FIXNUM_P(obj)?cFixnum: NIL_P(obj)?cNil:\
- RBASIC(obj)->class)
+VALUE rb_class_of();
+#define CLASS_OF(v) rb_class_of(v)
#define T_NIL 0x00
#define T_OBJECT 0x01
@@ -107,9 +116,12 @@ extern VALUE cData;
#define T_HASH 0x0a
#define T_STRUCT 0x0b
#define T_BIGNUM 0x0c
+#define T_FILE 0x0d
-#define T_DATA 0x10
-#define T_MATCH 0x11
+#define T_TRUE 0x20
+#define T_FALSE 0x21
+#define T_DATA 0x22
+#define T_MATCH 0x23
#define T_VARMAP 0xfd
#define T_SCOPE 0xfe
@@ -118,8 +130,11 @@ extern VALUE cData;
#define T_MASK 0xff
#define BUILTIN_TYPE(x) (((struct RBasic*)(x))->flags & T_MASK)
-#define TYPE(x) (FIXNUM_P(x)?T_FIXNUM:NIL_P(x)?T_NIL:BUILTIN_TYPE(x))
-#define Check_Type(x,t) {if (TYPE(x)!=(t)) WrongType(x,t);}
+
+int rb_type();
+#define TYPE(x) rb_type(x)
+
+void Check_Type();
#define Need_Fixnum(x) {if (!FIXNUM_P(x)) (x) = num2fix(x);}
#define NUM2INT(x) (FIXNUM_P(x)?FIX2INT(x):num2int(x))
VALUE num2fix();
@@ -180,6 +195,11 @@ struct RHash {
struct st_table *tbl;
};
+struct RFile {
+ struct RBasic basic;
+ struct OpenFile *fptr;
+};
+
struct RData {
struct RBasic basic;
void (*dmark)();
@@ -189,23 +209,16 @@ struct RData {
#define DATA_PTR(dta) (RDATA(dta)->data)
-VALUE data_new();
-VALUE rb_ivar_get();
-VALUE rb_ivar_set();
-
-#define Get_Data_Struct(obj, iv, type, sval) {\
- VALUE _data_;\
- _data_ = rb_ivar_get(obj, iv);\
- Check_Type(_data_, T_DATA);\
- sval = (type*)DATA_PTR(_data_);\
-}
+VALUE data_object_alloc();
+#define Make_Data_Struct(class,type,mark,free,sval) (\
+ sval = ALLOC(type),\
+ memset(sval, 0, sizeof(type)),\
+ data_object_alloc(class,sval,mark,free)\
+)
-#define Make_Data_Struct(obj, iv, type, mark, free, sval) {\
- VALUE _new_;\
- sval = ALLOC(type);\
- _new_ = data_new(sval,mark,free);\
- memset(sval, 0, sizeof(type));\
- rb_ivar_set(obj, iv, _new_);\
+#define Get_Data_Struct(obj,type,sval) {\
+ Check_Type(obj, T_DATA); \
+ sval = (type*)DATA_PTR(obj);\
}
struct RStruct {
@@ -233,28 +246,27 @@ struct RBignum {
#define RDATA(obj) (R_CAST(RData)(obj))
#define RSTRUCT(obj) (R_CAST(RStruct)(obj))
#define RBIGNUM(obj) (R_CAST(RBignum)(obj))
+#define RFILE(obj) (R_CAST(RFile)(obj))
-#define FL_SINGLE (1<<8)
-#define FL_MARK (1<<9)
+#define FL_SINGLETON (1<<8)
+#define FL_MARK (1<<9)
-#define FL_USER0 (1<<10)
-#define FL_USER1 (1<<11)
-#define FL_USER2 (1<<12)
-#define FL_USER3 (1<<13)
-#define FL_USER4 (1<<14)
-#define FL_USER5 (1<<15)
-#define FL_USER6 (1<<16)
-#define FL_USER7 (1<<17)
+#define FL_USER0 (1<<10)
+#define FL_USER1 (1<<11)
+#define FL_USER2 (1<<12)
+#define FL_USER3 (1<<13)
+#define FL_USER4 (1<<14)
+#define FL_USER5 (1<<15)
+#define FL_USER6 (1<<16)
-#define FL_UMASK (0xff<<10)
+#define FL_UMASK (0x7f<<10)
-#define FL_ABLE(x) (!(FIXNUM_P(x)||NIL_P(x)))
+int rb_special_const_p();
+#define FL_ABLE(x) (!(FIXNUM_P(x)||rb_special_const_p(x)))
#define FL_TEST(x,f) (FL_ABLE(x)?(RBASIC(x)->flags&(f)):0)
#define FL_SET(x,f) if (FL_ABLE(x)) {RBASIC(x)->flags |= (f);}
#define FL_UNSET(x,f) if(FL_ABLE(x)){RBASIC(x)->flags &= ~(f);}
-
-extern VALUE Qself;
-#define Qnil 0
+#define FL_REVERSE(x,f) if(FL_ABLE(x)){RBASIC(x)->flags ^= f;}
#define ALLOC_N(type,n) (type*)xmalloc(sizeof(type)*(n))
#define ALLOC(type) (type*)xmalloc(sizeof(type))
@@ -277,6 +289,7 @@ void rb_extend_object();
void rb_define_variable();
void rb_define_const();
+void rb_define_global_const();
void rb_define_method();
void rb_define_singleton_method();
@@ -289,26 +302,30 @@ char *rb_id2name();
ID rb_to_id();
char *rb_class2name();
-VALUE rb_method_boundp();
+int rb_method_boundp();
VALUE rb_eval_string();
VALUE rb_funcall();
VALUE rb_funcall2();
int rb_scan_args();
+VALUE rb_ivar_get();
+VALUE rb_ivar_set();
+
VALUE rb_iv_get();
VALUE rb_iv_set();
void rb_const_set();
VALUE rb_yield();
-VALUE iterator_p();
+int iterator_p();
-VALUE rb_equal();
+int rb_equal();
extern int verbose, debug;
#ifdef __GNUC__
typedef void voidfn ();
+volatile voidfn Raise;
volatile voidfn Fail;
volatile voidfn Fatal;
volatile voidfn Bug;
@@ -316,8 +333,11 @@ volatile voidfn WrongType;
volatile voidfn rb_sys_fail;
volatile voidfn rb_break;
volatile voidfn rb_exit;
-volatile voidfn rb_fail;
+volatile voidfn rb_fatal;
+volatile voidfn rb_raise;
+volatile voidfn rb_notimplement;
#else
+void Raise();
void Fail();
void Fatal();
void Bug();
@@ -325,9 +345,12 @@ void WrongType();
void rb_sys_fail();
void rb_break();
void rb_exit();
-void rb_fail();
+void rb_fatal();
+void rb_raise();
+void rb_notimplement();
#endif
+void Error();
void Warning();
#if defined(EXTLIB) && defined(USE_DLN_A_OUT)
diff --git a/ruby.texi b/ruby.texi
deleted file mode 100644
index 50ffb63a16..0000000000
--- a/ruby.texi
+++ /dev/null
@@ -1,5044 +0,0 @@
-\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/clnt.rb b/sample/clnt.rb
index c8c4b2db9f..7998379aa2 100644
--- a/sample/clnt.rb
+++ b/sample/clnt.rb
@@ -3,15 +3,15 @@
require "socket"
-host=(if $ARGV.length == 2; $ARGV.shift; else "localhost"; end)
+host=(if ARGV.length == 2; ARGV.shift; else "localhost"; end)
print("Trying ", host, " ...")
STDOUT.flush
-s = TCPsocket.open(host, $ARGV.shift)
+s = TCPsocket.open(host, ARGV.shift)
print(" done\n")
print("addr: ", s.addr.join(":"), "\n")
print("peer: ", s.peeraddr.join(":"), "\n")
while gets()
s.write($_)
- print(s.gets)
+ print(s.readline)
end
s.close
diff --git a/sample/dir.rb b/sample/dir.rb
index 3349dc7b6d..1fc0bb2aa8 100644
--- a/sample/dir.rb
+++ b/sample/dir.rb
@@ -1,9 +1,9 @@
# directory access
# list all files but .*/*~/*.o
dirp = Dir.open(".")
-dirp.rewind
for f in dirp
- if !(~/^\./ || ~/~$/ || ~/\.o/)
+ $_ = f
+ if (~/^\./ || ~/~$/ || ~/\.o/)
print f, "\n"
end
end
diff --git a/sample/eval.rb b/sample/eval.rb
new file mode 100644
index 0000000000..da31b77153
--- /dev/null
+++ b/sample/eval.rb
@@ -0,0 +1,41 @@
+line = ''
+indent=0
+print "ruby> "
+while TRUE
+ l = gets
+ if not l
+ break if line == ''
+ else
+ line = line + l
+ if l =~ /,\s*$/
+ print "ruby| "
+ next
+ end
+ if l =~ /^\s*(class|module|def|if|case|while|for|begin)\b[^_]/
+ indent += 1
+ end
+ if l =~ /^\s*end\b[^_]/
+ indent -= 1
+ end
+ if l =~ /{\s*(\|.*\|)?\s*$/
+ indent += 1
+ end
+ if l =~ /^\s*\}/
+ indent -= 1
+ end
+ if indent > 0
+ print "ruby| "
+ next
+ end
+ end
+ begin
+ print eval(line).inspect, "\n"
+ rescue
+ $! = 'exception raised' if not $!
+ print "ERR: ", $!, "\n"
+ end
+ break if not l
+ line = ''
+ print "ruby> "
+end
+print "\n"
diff --git a/sample/evaldef.rb b/sample/evaldef.rb
deleted file mode 100644
index 2cedd54999..0000000000
--- a/sample/evaldef.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# method definition by eval()
-# output:
-# bar
-# (eval):26: method `baz' not available for "#<foo: 0xbfc5c>"(foo)
-
-class Foo
- def foo
- eval("
-def baz
- print(\"bar\n\")
-end")
- end
-end
-
-class Bar : Foo
- def bar
- baz()
- end
-end
-
-f = Foo.new
-b = Bar.new
-
-b.foo
-b.bar
-f.baz
diff --git a/sample/export.rb b/sample/export.rb
index 2d05d8afd7..750b5c1948 100644
--- a/sample/export.rb
+++ b/sample/export.rb
@@ -28,7 +28,7 @@ f.printf "%s\n", Foo
f.quux
-class Bar : Foo
+class Bar<Foo
def quux
super
baz()
diff --git a/sample/fact.rb b/sample/fact.rb
new file mode 100644
index 0000000000..49678bc9d0
--- /dev/null
+++ b/sample/fact.rb
@@ -0,0 +1,8 @@
+def fact(n)
+ if n == 0
+ 1
+ else
+ n * fact(n-1)
+ end
+end
+print fact(ARGV[0].to_i), "\n"
diff --git a/sample/from.rb b/sample/from.rb
index 2f5fcebe12..2ef000face 100755
--- a/sample/from.rb
+++ b/sample/from.rb
@@ -5,15 +5,15 @@ require "base64"
include ParseDate
-if $ARGV[0] == '-w'
+if ARGV[0] == '-w'
wait = TRUE
- $ARGV.shift
+ ARGV.shift
end
class Mail
def Mail.new(f)
- if !f.is_kind_of?(IO)
+ if !f.kind_of?(IO)
f = open(f, "r")
me = super
f.close
@@ -28,7 +28,7 @@ class Mail
@body = []
while f.gets()
$_.chop!
- continue if /^From / # skip From-line
+ next if /^From / # skip From-line
break if /^$/ # end of header
if /^(\S+):\s*(.*)/
@header[attr = $1.capitalize] = $2
@@ -56,7 +56,7 @@ class Mail
end
-$ARGV[0] = '/usr/spool/mail/' + ENV['USER'] if $ARGV.length == 0
+ARGV[0] = '/usr/spool/mail/' + ENV['USER'] if ARGV.length == 0
$outcount = 0;
def fromout(date, from, subj)
@@ -72,8 +72,8 @@ def fromout(date, from, subj)
$outcount += 1
end
-for file in $ARGV
- continue if !File.exists?(file)
+for file in ARGV
+ next if !File.exist?(file)
f = open(file, "r")
while !f.eof
mail = Mail.new(f)
diff --git a/sample/fullpath.pl b/sample/fullpath.pl
deleted file mode 100644
index a07b90edd4..0000000000
--- a/sample/fullpath.pl
+++ /dev/null
@@ -1,22 +0,0 @@
-#! /usr/local/bin/perl
-# convert ls-lR filename into fullpath.
-
-$path = shift;
-if (!defined $path) {
- $path = "";
-}
-elsif ($path !~ /\/$/) {
- $path .= "/"
-}
-
-while (<>) {
- if (/:$/) {
- chop; chop;
- $path = $_ . "/";
- } elsif (/^total/ || /^d/) {
- next;
- } elsif (/^(.*\d )(.+)$/) {
- print $1, $path, $2, "\n";
- }
-}
-
diff --git a/sample/fullpath.rb b/sample/fullpath.rb
index 6c528f6f96..ce268e20b9 100644
--- a/sample/fullpath.rb
+++ b/sample/fullpath.rb
@@ -1,9 +1,9 @@
#! /usr/local/bin/ruby
# convert ls-lR filename into fullpath.
-if $ARGV[0] =~ /-p/
- $ARGV.shift
- path = $ARGV.shift
+if ARGV[0] =~ /-p/
+ ARGV.shift
+ path = ARGV.shift
end
if path == nil
diff --git a/sample/getopts.test b/sample/getopts.test
index adef7628db..2866bccea8 100755
--- a/sample/getopts.test
+++ b/sample/getopts.test
@@ -3,13 +3,16 @@
load("parsearg.rb")
def usage()
- printf("Usage:\n")
- printf("This is Getopt test program \n")
+ printf "Usage:\n"
+ printf "%s -d [-x x] [-y y] [--geometry geom] [--version] [string ...]\n", $0
end
$USAGE = 'usage'
-parseArgs(0, !nil, "d", "x:", "y:", "version", "geometry:")
+parseArgs(0, "d&(x|y)", "dfg", "x:", "y:", "geometry:800x600", "version")
if ($OPT_d)
+ if $OPT_version
+ printf "version 1.0\n"
+ end
if ($OPT_x)
printf("x = %d\n", $OPT_x.to_i)
end
@@ -19,13 +22,15 @@ if ($OPT_d)
if ($OPT_geometry)
printf("geometry = %s\n", $OPT_geometry)
end
+ if $OPT_f
+ printf "f = TRUE\n"
+ end
+ if $OPT_g
+ printf "g = TRUE\n"
+ end
end
-if ($OPT_version)
- printf("version 1.00\n")
-end
-
-while ($ARGV.length != 0)
- print ("other = ", $ARGV[0], "\n")
- $ARGV.shift
+while (ARGV.length != 0)
+ print "other = ", ARGV[0], "\n"
+ ARGV.shift
end
diff --git a/sample/io.rb b/sample/io.rb
index c12e4f4498..0b38d2112d 100644
--- a/sample/io.rb
+++ b/sample/io.rb
@@ -15,8 +15,8 @@ for i in "abc\n\ndef\nghi\n"
print("tt: ", i)
end
-printf("%s:(%d)%s\n", $0, $ARGV.length, $ARGV[0])
-passwd = open($ARGV[0], "r")
+printf("%s:(%d)%s\n", $0, ARGV.length, ARGV[0])
+passwd = open(ARGV[0], "r")
#printf("%s", passwd.find{i|i =~ /\*/})
n = 1
diff --git a/sample/less.rb b/sample/less.rb
index b0906d5d22..8be359108f 100755
--- a/sample/less.rb
+++ b/sample/less.rb
@@ -3,8 +3,8 @@
ZCAT = "/usr/local/bin/zcat"
LESS = "/usr/local/bin/less"
-FILE = $ARGV.pop
-OPTION = (if $ARGV.length == 0; "" else $ARGV.join(" "); end)
+FILE = ARGV.pop
+OPTION = (if ARGV.length == 0; "" else ARGV.join(" "); end)
if FILE =~ /\.(Z|gz)$/
exec(format("%s %s | %s %s", ZCAT, FILE, LESS, OPTION))
diff --git a/sample/list.rb b/sample/list.rb
index 93e3182f84..76035e67d6 100644
--- a/sample/list.rb
+++ b/sample/list.rb
@@ -4,20 +4,20 @@ class MyElem
def initialize(item)
# @変数はインスタンス変数(宣言は要らない)
@data = item
- @next = nil
+ @succ = nil
end
def data
@data
end
- def next
- @next
+ def succ
+ @succ
end
# 「obj.data = val」としたときに暗黙に呼ばれるメソッド
- def next=(new)
- @next = new
+ def succ=(new)
+ @succ = new
end
end
@@ -25,7 +25,7 @@ class MyList
def add_to_list(obj)
elt = MyElem.new(obj)
if @head
- @tail.next = elt
+ @tail.succ = elt
else
@head = elt
end
@@ -36,7 +36,7 @@ class MyList
elt = @head
while elt
yield elt
- elt = elt.next
+ elt = elt.succ
end
end
diff --git a/sample/mpart.rb b/sample/mpart.rb
index 2374ae0938..6c40d50e18 100755
--- a/sample/mpart.rb
+++ b/sample/mpart.rb
@@ -4,12 +4,12 @@
lines = 1000
-if ($ARGV[0] =~ /^-(\d+)$/ )
+if (ARGV[0] =~ /^-(\d+)$/ )
lines = $1.to_i;
- $ARGV.shift;
+ ARGV.shift;
end
-basename = $ARGV[0]
+basename = ARGV[0]
extname = "part"
part = 1
diff --git a/sample/observ.rb b/sample/observ.rb
new file mode 100644
index 0000000000..f7b1e73137
--- /dev/null
+++ b/sample/observ.rb
@@ -0,0 +1,31 @@
+#! /usr/local/bin/ruby
+
+require "thread"
+require "observer"
+
+class Tick
+ include Observable
+ def initialize
+ Thread.start do
+ while TRUE
+ sleep 0.999
+ changed
+ notify_observers(Time.now.strftime("%H:%M:%S"))
+ end
+ end
+ end
+end
+
+class Clock
+ def initialize
+ @tick = Tick.new
+ @tick.add_observer(self)
+ end
+ def update(time)
+ print "\e[8D", time
+ STDOUT.flush
+ end
+end
+
+clock = Clock.new
+sleep
diff --git a/sample/philos.rb b/sample/philos.rb
new file mode 100644
index 0000000000..ee0a8cd5fc
--- /dev/null
+++ b/sample/philos.rb
@@ -0,0 +1,54 @@
+#
+# The Dining Philosophers - thread example
+#
+require "thread"
+
+srand
+#srand
+N=9 # number of philosophers
+$forks = []
+for i in 0..N-1
+ $forks[i] = Mutex.new
+end
+$state = "-o"*N
+
+def wait
+ sleep rand(20)/10.0
+end
+
+def think(n)
+ wait
+end
+
+def eat(n)
+ wait
+end
+
+def philosopher(n)
+ while TRUE
+ think n
+ $forks[n].lock
+ if not $forks[(n+1)%N].try_lock
+ $forks[n].unlock # avoid deadlock
+ continue
+ end
+ $state[n*2] = ?|;
+ $state[(n+1)%N*2] = ?|;
+ $state[n*2+1] = ?*;
+ print $state, "\n"
+ eat(n)
+ $state[n*2] = ?-;
+ $state[(n+1)%N*2] = ?-;
+ $state[n*2+1] = ?o;
+ print $state, "\n"
+ $forks[n].unlock
+ $forks[(n+1)%N].unlock
+ end
+end
+
+for i in 0..N-1
+ Thread.start{philosopher(i)}
+ sleep 0.1
+end
+
+sleep
diff --git a/sample/pi.rb b/sample/pi.rb
new file mode 100644
index 0000000000..49067cc347
--- /dev/null
+++ b/sample/pi.rb
@@ -0,0 +1,18 @@
+#!/usr/local/bin/ruby
+
+k, a, b, a1, b1 = 2, 4, 1, 12, 4
+
+while TRUE
+ # Next approximation
+ p, q, k = k*k, 2*k+1, k+1
+ a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
+ # Print common digits
+ d = a / b
+ d1 = a1 / b1
+ while d == d1
+ print d
+ $stdout.flush
+ a, a1 = 10*(a%b), 10*(a1%b1)
+ d, d1 = a/b, a1/b1
+ end
+end
diff --git a/sample/rcs.rb b/sample/rcs.rb
index 13476267b2..3f74da9ef2 100644
--- a/sample/rcs.rb
+++ b/sample/rcs.rb
@@ -7,7 +7,7 @@ hdw = dw / 2.0
w = 20.0 #
h =1.0 #
d = 0.2 # P
-ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./"
+ss="abcdefghijklmnopqrstuvwxyz0123456789#!$%^&*()-=\\[];'`,./"
rnd = srand()
while gets()
diff --git a/sample/regx.rb b/sample/regx.rb
new file mode 100644
index 0000000000..b9d8ca6e14
--- /dev/null
+++ b/sample/regx.rb
@@ -0,0 +1,23 @@
+st = "\033[7m"
+en = "\033[m"
+#st = "<<"
+#en = ">>"
+
+while TRUE
+ print "str> "
+ STDOUT.flush
+ input = gets
+ break if not input
+ if input != ""
+ str = input
+ str.chop!
+ end
+ print "pat> "
+ STDOUT.flush
+ re = gets
+ break if not re
+ re.chop!
+ str.gsub! re, "#{st}&#{en}"
+ print str, "\n"
+end
+print "\n"
diff --git a/sample/ruby-mode.el b/sample/ruby-mode.el
index b555994fea..9dfde8588c 100644
--- a/sample/ruby-mode.el
+++ b/sample/ruby-mode.el
@@ -7,8 +7,18 @@
;;; created at: Fri Feb 4 14:49:13 JST 1994
;;;
+(defconst ruby-mode-version "1.0.2")
+
(defconst ruby-block-beg-re
- "class\\|module\\|def\\|if\\|case\\|while\\|for\\|begin"
+ "class\\|module\\|def\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin\\|do"
+ )
+
+(defconst ruby-indent-beg-re
+ "\\(\\s *\\(class\\|module\\|def\\)\\)\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin"
+ )
+
+(defconst ruby-modifier-re
+ "if\\|unless\\|while\\|until"
)
(defconst ruby-block-mid-re
@@ -18,13 +28,13 @@
(defconst ruby-block-end-re "end")
(defconst ruby-delimiter
- (concat "[?$/(){}#\"'`]\\|\\[\\|\\]\\|\\<\\("
- ruby-block-beg-re "\\|" ruby-block-end-re "\\)\\>")
+ (concat "[?$/%(){}#\"'`]\\|\\[\\|\\]\\|\\<\\("
+ ruby-block-beg-re "\\|" ruby-block-end-re "\\)\\>")
)
(defconst ruby-negative
- (concat "^[ \t]*\\(\\b\\(" ruby-block-mid-re "\\)\\|\\("
- ruby-block-end-re "\\)\\>\\|\\}\\|\\]\\)")
+ (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\|\\("
+ ruby-block-end-re "\\)\\>\\|\\}\\|\\]\\)")
)
(defconst ruby-operator-chars "[,.+*/%-&|^~=<>:]")
@@ -44,6 +54,10 @@
(define-key ruby-mode-map "}" 'ruby-electric-brace)
(define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
(define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
+ (define-key ruby-mode-map "\e\C-b" 'ruby-beginning-of-block)
+ (define-key ruby-mode-map "\e\C-f" 'ruby-end-of-block)
+ (define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
+ (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
(define-key ruby-mode-map "\t" 'ruby-indent-command)
(define-key ruby-mode-map "\C-m" 'ruby-reindent-then-newline-and-indent)
(define-key ruby-mode-map "\C-j" 'newline))
@@ -60,7 +74,7 @@
(modify-syntax-entry ?\n ">" 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)
@@ -121,17 +135,6 @@ The variable ruby-indent-level controls the amount of indentation.
(back-to-indentation)
(current-column)))
-(defun ruby-delete-indentation ()
- (let
- ((b nil)
- (m nil))
- (save-excursion
- (beginning-of-line)
- (setq b (point))
- (back-to-indentation)
- (setq m (point)))
- (delete-region b m)))
-
(defun ruby-indent-line (&optional flag)
"Correct indentation of the current ruby line."
(ruby-indent-to (ruby-calculate-indent)))
@@ -141,222 +144,294 @@ The variable ruby-indent-level controls the amount of indentation.
(ruby-indent-line t))
(defun ruby-indent-to (x)
- (let ((p nil) beg end)
- (if (null x)
- nil
- (setq p (- (current-column) (ruby-current-indentation)))
- (ruby-delete-indentation)
- (beginning-of-line)
- (save-excursion
+ (if x
+ (let (shift top beg)
+ (and (< x 0)
+ (error "invalid nest"))
+ (setq shift (current-column))
+ (beginning-of-line)
(setq beg (point))
- (forward-line 1)
- (setq end (point)))
- (indent-to x)
- (if (> p 0) (forward-char p)))))
+ (back-to-indentation)
+ (setq top (current-column))
+ (skip-chars-backward " \t")
+ (cond
+ ((>= x shift)
+ (setq shift 0))
+ ((>= shift top)
+ (setq shift (- shift top)))
+ (t (setq shift 0)))
+ (if (and (bolp)
+ (= x top))
+ (move-to-column (+ x shift))
+ (move-to-column top)
+ (delete-region beg (point))
+ (beginning-of-line)
+ (indent-to x)
+ (move-to-column (+ x shift))))))
(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))))))
+ (if (looking-at "\\?")
+ (progn
+ (or (bolp) (forward-char -1))
+ (not (looking-at "\\sw")))
+ (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)
- (in-string nil)
- (in-paren nil)
- (depth 0)
- (nest nil))
+ (indent 0)
+ (in-string nil)
+ (in-paren nil)
+ (depth 0)
+ (nest nil)
+ (pcol nil))
(save-excursion
- (if start
- (goto-char start)
- (ruby-beginning-of-defun))
- (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))))
- ((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
+ (if start
+ (goto-char start)
+ (ruby-beginning-of-indent))
+ (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
- ((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))
- (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
+ ((or (looking-at "\"") ;skip string
+ (looking-at "'")
+ (looking-at "`"))
+ (setq w (char-after (point)))
+ (cond
+ ((and (not (eobp))
+ (re-search-forward (format "[^\\]%c" w) indent-point t))
+ nil)
+ (t
+ (setq in-string (point))
+ (goto-char indent-point))))
+ ((looking-at "/")
+ (cond
+ ((and (not (eobp)) (ruby-expr-beg))
+ (if (re-search-forward "[^\\]/" indent-point t)
+ nil
+ (setq in-string (point))
+ (goto-char indent-point)))
+ (t
+ (goto-char pnt))))
+ ((looking-at "%")
+ (cond
+ ((and (not (eobp)) (ruby-expr-beg)
+ (looking-at "%[Qq\"'Rr/Xx`]\\(.\\)"))
+ (setq w (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ (cond
+ ((string= w "[") (setq w "]"))
+ ((string= w "{") (setq w "}"))
+ ((string= w "(") (setq w ")"))
+ ((string= w "<") (setq w ">")))
+ (goto-char (match-end 0))
+ (if (search-forward w indent-point t)
+ nil
+ (setq in-string (point))
+ (goto-char indent-point)))
+ (t
+ (goto-char pnt))))
+ ((looking-at "\\?") ;skip ?char
+ (cond
+ ((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 (point))
+ )
+ ((looking-at "(")
+ (setq nest (cons (cons (char-after (point)) pnt) nest))
+ (setq pcol (cons (cons pnt depth) pcol))
+ (setq depth 0)
+ (goto-char pnt)
+ )
+ ((looking-at "[\\[{]")
+ (setq nest (cons (cons (char-after (point)) pnt) nest))
+ (setq depth (1+ depth))
+ (goto-char pnt)
+ )
+ ((looking-at ")")
(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))))
+ (setq depth (cdr (car pcol)))
+ (setq pcol (cdr pcol))
+ (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)
+ (setq w (char-after (point)))
+ (and (not (eq ?_ w))
+ (not (eq ?! w))
+ (not (eq ?? w))))
+ (progn
+ (goto-char (match-beginning 0))
+ (if (looking-at ruby-modifier-re)
+ (ruby-expr-beg)
+ t))
+ (progn
+ (setq nest (cons (cons nil pnt) nest))
+ (setq depth (1+ depth))))
+ (if (looking-at "def\\s *[/`]")
+ (goto-char (match-end 0))
+ (goto-char pnt)))
+ (t
+ (error (format "bad string %s"
+ (buffer-substring (point) pnt)
+ )))))))
+ (list in-string (car nest) depth (car (car pcol))))))
(defun ruby-calculate-indent (&optional parse-start)
(save-excursion
(beginning-of-line)
(let ((indent-point (point))
- (case-fold-search nil)
- state bol eol
- (indent 0))
- (if parse-start
- (goto-char parse-start)
- (ruby-beginning-of-defun)
- (setq parse-start (point)))
- (setq state (ruby-parse-region parse-start indent-point))
- (cond
- ((nth 0 state) ; within string
- (setq indent nil)) ; do nothing
-
- ((nth 1 state) ; in paren
- (goto-char (cdr (nth 1 state)))
- (setq indent
- (if (and (eq (car (nth 1 state)) ?\( )
- (not (looking-at "(\\s *$")))
- (current-column)
- (+ (current-indentation) ruby-indent-level))))
-
- ((> (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)))
-
- (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))
+ (case-fold-search nil)
+ state bol eol
+ (indent 0))
+ (if parse-start
+ (goto-char parse-start)
+ (ruby-beginning-of-indent)
+ (setq parse-start (point)))
+ (back-to-indentation)
+ (setq indent (current-column))
+ (setq state (ruby-parse-region parse-start indent-point))
+ (cond
+ ((nth 0 state) ; within string
+ (setq indent nil)) ; do nothing
+
+ ((car (nth 1 state)) ; in paren
+ (goto-char (cdr (nth 1 state)))
+ (if (eq (car (nth 1 state)) ?\( )
+ (let ((column (current-column))
+ (s (ruby-parse-region (point) indent-point)))
+ (cond
+ ((> (nth 2 s) 0)
+ (goto-char (cdr (nth 1 s)))
+ (forward-word -1)
+ (setq indent (+ (current-column) ruby-indent-level)))
+ (t
+ (setq indent (current-column)))))
+ (cond
+ ((nth 3 state)
+ (goto-char (nth 3 state))
+ (setq indent (+ (current-column) ruby-indent-level)))
+ (t
+ (goto-char parse-start)
+ (back-to-indentation)
+ (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level)))))
+ ))
+
+ ((> (nth 2 state) 0) ; in nest
+ (goto-char (cdr (nth 1 state)))
+ (forward-word -1) ; skip back a keyword
+ (cond
+ ((looking-at "do") ; iter block is a special case
+ (cond
+ ((nth 3 state)
+ (goto-char (nth 3 state))
+ (setq indent (+ (current-column) ruby-indent-level)))
+ (t
+ (goto-char parse-start)
+ (back-to-indentation)
+ (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level))))))
+ (t
+ (setq indent (+ (current-column) ruby-indent-level)))))
+
+ ((< (nth 2 state) 0) ; in negative nest
+ (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level)))))
+
+ (cond
+ (indent
+ (goto-char indent-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)))
-
+ (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)
+ (skip-chars-backward " \t")
+ (or (bobp) (forward-char -1))
+ (and (looking-at ruby-operator-chars)
+ (or (not (or (eq ?/ (char-after (point)))))
+ (null (nth 0 (ruby-parse-region parse-start (point)))))
+ (save-excursion
+ (goto-char parse-start))
+ (not (eq (char-after (1- (point))) ?$))
+ (or (not (eq ?| (char-after (point))))
+ (save-excursion
+ (or (eolp) (forward-char -1))
+ (and (search-backward "|")
+ (skip-chars-backward " \t\n")
+ (and (not (eolp))
+ (progn
+ (forward-char -1)
+ (not (looking-at "\\{")))
+ (progn
+ (forward-word -1)
+ (not (looking-at "do\\>[^_]")))))))
+ (setq indent (+ indent ruby-indent-level)))))))
+ indent)))
+
(defun ruby-electric-brace (arg)
(interactive "P")
(self-insert-command (prefix-numeric-value arg))
(ruby-indent-line t))
(defun ruby-beginning-of-defun (&optional arg)
- "Move backward to next beginning-of-defun.
+ "Move backward to next beginning-of-defun.
With argument, do this that many times.
Returns t unless search stops due to end of buffer."
(interactive "p")
@@ -364,6 +439,13 @@ Returns t unless search stops due to end of buffer."
nil 'move (or arg 1))
(progn (beginning-of-line) t)))
+(defun ruby-beginning-of-indent ()
+ (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b")
+ nil 'move)
+ (progn
+ (beginning-of-line)
+ t)))
+
(defun ruby-end-of-defun (&optional arg)
"Move forward to next end of defun.
An end of a defun is found by moving forward from the beginning of one."
@@ -373,6 +455,42 @@ An end of a defun is found by moving forward from the beginning of one."
(progn (beginning-of-line) t))
(forward-line 1))
+(defun ruby-move-to-block (n)
+ (let (start pos done down)
+ (setq start (ruby-calculate-indent))
+ (if (eobp)
+ nil
+ (while (and (not (bobp)) (not done))
+ (forward-line n)
+ (cond
+ ((looking-at "^$"))
+ ((looking-at "^\\s *#"))
+ (t
+ (setq pos (current-indentation))
+ (cond
+ ((< start pos)
+ (setq down t))
+ ((and down (= pos start))
+ (setq done t))
+ ((> start pos)
+ (setq done t)))))
+ (if done
+ (progn
+ (back-to-indentation)
+ (if (looking-at ruby-block-mid-re)
+ (setq done nil)))))))
+ (back-to-indentation))
+
+(defun ruby-beginning-of-block ()
+ "Move backward to next beginning-of-block"
+ (interactive)
+ (ruby-move-to-block -1))
+
+(defun ruby-end-of-block ()
+ "Move forward to next beginning-of-block"
+ (interactive)
+ (ruby-move-to-block 1))
+
(defun ruby-reindent-then-newline-and-indent ()
(interactive "*")
(save-excursion
@@ -383,39 +501,32 @@ An end of a defun is found by moving forward from the beginning of one."
(indent-according-to-mode))
(indent-according-to-mode))
-(defun ruby-encomment-region (beg end)
- (interactive "r")
- (save-excursion
- (goto-char beg)
- (while (re-search-forward "^" end t)
- (replace-match "#" nil nil))))
+(fset 'ruby-encomment-region (symbol-function 'comment-region))
(defun ruby-decomment-region (beg end)
(interactive "r")
(save-excursion
(goto-char beg)
(while (re-search-forward "^\\([ \t]*\\)#" end t)
- (replace-match "\\1" nil nil))))
+ (replace-match "\\1" nil nil)
+ (save-excursion
+ (ruby-indent-line)))))
(if (featurep 'hilit19)
(hilit-set-mode-patterns
'ruby-mode
- '(("\\s #.*$" nil comment)
- ("^#.*$" nil comment)
- ("\\$\\(.\\|\\sw+\\)" nil type)
- ("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string)
+ '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\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)
+ ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string)
+ ("^\\s *#.*$" nil comment)
+ ("[^$@?\\]\\(#[^$@{].*$\\)" 1 comment)
+ ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string)
+ ("^\\s *\\(require\\|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)
+ ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\)\\>[^_]" 1 defun)
+ ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\)\\>[^_]" 1 keyword)
+ ("[^_]\\<\\(self\\|nil\\|[A-Z][a-zA-Z_0-9]*\\)\\>[^_]" 1 define)
+ ("\\$\\(.\\|\\sw+\\)" nil type)
+ ("[$@].[a-zA-Z_0-9]*" nil struct)
("^__END__" nil label))))
diff --git a/sample/sieve.rb b/sample/sieve.rb
index a953784284..03ff8a67f4 100644
--- a/sample/sieve.rb
+++ b/sample/sieve.rb
@@ -1,6 +1,6 @@
# sieve of Eratosthenes
sieve = []
-if ! max = $ARGV.shift; max = 100; end
+if ! max = ARGV.shift; max = 100; end
max = max.to_i
print "1"
diff --git a/sample/svr.rb b/sample/svr.rb
index 460c16bedf..14aded8c3f 100644
--- a/sample/svr.rb
+++ b/sample/svr.rb
@@ -11,7 +11,7 @@ socks = [gs]
while TRUE
nsock = select(socks);
- if nsock == nil; continue end
+ next if nsock == nil
for s in nsock[0]
if s == gs
ns = s.accept
diff --git a/sample/test.rb b/sample/test.rb
index 7f26433181..aacddbc56a 100644
--- a/sample/test.rb
+++ b/sample/test.rb
@@ -1,6 +1,8 @@
#! /usr/local/bin/ruby
$testnum=0
+$ntest=0
+$failed = 0
def check(what)
printf "%s\n", what
@@ -8,15 +10,16 @@ def check(what)
$testnum = 0
end
-def ok
+def ok(cond)
$testnum+=1
- printf "ok %d\n", $testnum
-end
-
-def notok
- $testnum+=1
- printf "not ok %s %d\n", $what, $testnum
- $failed = TRUE
+ $ntest+=1
+ if cond
+ printf "ok %d\n", $testnum
+ else
+ where = caller[0]
+ printf "not ok %s %d -- %s\n", $what, $testnum, where
+ $failed+=1
+ end
end
# make sure conditional operators work
@@ -25,50 +28,62 @@ check "condition"
$x = '0';
-$x == $x && ok
-$x != $x && notok
-$x == $x || notok
-$x != $x || ok
+$x == $x && ok(TRUE)
+$x != $x && ok(FALSE)
+$x == $x || ok(FALSE)
+$x != $x || ok(TRUE)
# first test to see if we can run the tests.
-check "if";
+check "if/unless";
$x = 'test';
-if $x == $x then ok else notok end
-if $x != $x then notok else ok end
+ok(if $x == $x then TRUE else FALSE end)
+$bad = FALSE
+unless $x == $x
+ $bad = TRUE
+end
+ok(!$bad)
+ok(unless $x != $x then TRUE else FALSE end)
check "case"
case 5
when 1, 2, 3, 4, 6, 7, 8
- notok
+ ok(FALSE)
when 5
- ok
+ ok(TRUE)
end
case 5
when 5
- ok
+ ok(TRUE)
when 1..10
- notok
+ ok(FALSE)
+end
+
+case 5
+when 1..10
+ ok(TRUE)
+else
+ ok(FALSE)
end
case 5
when 5
- ok
+ ok(TRUE)
else
- notok
+ ok(FALSE)
end
case "foobar"
when /^f.*r$/
- ok
+ ok(TRUE)
else
- notok
+ ok(FALSE)
end
-check "while";
+check "while/until";
tmp = open("while_tmp", "w")
tmp.print "tvi925\n";
@@ -81,30 +96,23 @@ tmp.close
# test break
tmp = open("while_tmp", "r")
+ok(tmp.type == "File")
while tmp.gets()
break if /vt100/
end
-if !tmp.eof && /vt100/ then
- ok
-else
- notok
-end
+ok(!tmp.eof && /vt100/)
tmp.close
-# test continue
+# test next
$bad = FALSE
tmp = open("while_tmp", "r")
while tmp.gets()
- continue if /vt100/;
+ next if /vt100/;
$bad = 1 if /vt100/;
end
-if !tmp.eof || /vt100/ || $bad
- notok
-else
- ok
-end
+ok(!(!tmp.eof || /vt100/ || $bad))
tmp.close
# test redo
@@ -118,37 +126,39 @@ while tmp.gets()
$bad = 1 if /vt100/;
$bad = 1 if /VT100/;
end
-if !tmp.eof || $bad
- notok
-else
- ok
-end
+ok(tmp.eof && !$bad)
tmp.close
# test interval
$bad = FALSE
tmp = open("while_tmp", "r")
while tmp.gets()
- break if not 1..2
+ break unless 1..2
if /vt100/ || /Amiga/ || /paper/
$bad = TRUE
- notok
break
end
end
-ok if not $bad
+ok(!$bad)
tmp.close
File.unlink "while_tmp" or `/bin/rm -f "while_tmp"`
+ok(!File.exist?("while_tmp"))
+
+i = 0
+until i>4
+ i+=1
+end
+ok(i>4)
# exception handling
check "exception";
begin
fail "this must be handled"
- notok
+ ok(FALSE)
rescue
- ok
+ ok(TRUE)
end
$bad = TRUE
@@ -158,10 +168,10 @@ rescue
if $bad
$bad = FALSE
retry
- notok
+ ok(FALSE)
end
end
-ok
+ok(TRUE)
$bad = TRUE
$string = "this must be handled no.3"
@@ -170,9 +180,9 @@ begin
rescue
ensure
$bad = FALSE
- ok
+ ok(TRUE)
end
-notok if $bad || $! != $string
+ok(FALSE) if $bad || $! != $string
# exception in rescue clause
begin
@@ -181,164 +191,163 @@ begin
rescue
fail "exception in rescue clause"
end
- notok
+ ok(FALSE)
rescue
- ok
+ ok(TRUE)
end
-check "array"
-$x = [0, 1, 2, 3, 4, 5]
-if $x[2] == 2
- ok
-else
- notok
+# exception in ensure clause
+begin
+ begin
+ fail "this must be handled no.5"
+ ensure
+ fail "exception in ensure clause"
+ end
+ ok(FALSE)
+rescue
+ ok(TRUE)
end
-if $x[1..3] == [1, 2, 3]
- ok
-else
- notok
+$bad = TRUE
+begin
+ begin
+ fail "this must be handled no.5"
+ ensure
+ $bad = FALSE
+ end
+rescue
end
+ok(!$bad)
-if $x[1,3] == [1, 2, 3]
- ok
-else
- notok
+$bad = TRUE
+begin
+ begin
+ fail "this must be handled no.5"
+ ensure
+ $bad = FALSE
+ end
+rescue
end
+ok(!$bad)
-if [1, 2] + [3, 4] == [1, 2, 3, 4]
- ok
-else
- notok
+$bad = TRUE
+while TRUE
+ begin
+ break
+ ensure
+ $bad = FALSE
+ end
end
+ok(!$bad)
+
+check "array"
+ok([1, 2] + [3, 4] == [1, 2, 3, 4])
+ok([1, 2] * 2 == [1, 2, 1, 2])
+ok([1, 2] * ":" == "1:2")
+
+ok([1, 2].hash == [1, 2].hash)
+
+ok([1,2,3] & [2,3,4] == [2,3])
+ok([1,2,3] | [2,3,4] == [1,2,3,4])
+ok([1,2,3] - [2,3] == [1])
+
+$x = [0, 1, 2, 3, 4, 5]
+ok($x[2] == 2)
+ok($x[1..3] == [1, 2, 3])
+ok($x[1,3] == [1, 2, 3])
$x[0, 2] = 10
-if $x[0] == 10 && $x[1] == 2
- ok
-else
- notok
-end
+ok($x[0] == 10 && $x[1] == 2)
$x[0, 0] = -1
-if $x[0] == -1 && $x[1] == 10
- ok
-else
- notok
-end
+ok($x[0] == -1 && $x[1] == 10)
$x[-1, 1] = 20
-if $x[-1] == 20 && $x.pop == 20
- ok
-else
- notok
-end
+ok($x[-1] == 20 && $x.pop == 20)
+# compact
+$x = [nil, 1, nil, nil, 5, nil, nil]
+$x.compact!
+ok($x == [1, 5])
+
+# empty?
+ok(!$x.empty?)
+$x = []
+ok($x.empty?)
+
+# sort
$x = ["it", "came", "to", "pass", "that", "..."]
$x = $x.sort.join(" ")
-if $x == "... came it pass that to"
- ok
-else
- notok
-end
+ok($x == "... came it pass that to")
+$x = [2,5,3,1,7]
+$x.sort!{|a,b| a<=>b} # sort with condition
+ok($x == [1,2,3,5,7])
+$x.sort!{|a,b| b-a} # reverse sort
+ok($x == [7,5,3,2,1])
# 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 = "The Book of Mormon"
+ok($x.split(//).reverse!.join == "nomroM fo kooB ehT")
+ok("1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1")
+$x = "a b c d"
+ok($x.split == ['a', 'b', 'c', 'd'])
+ok($x.split(' ') == ['a', 'b', 'c', 'd'])
$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
+ok(($x * 5).join(":") == '1:1:1:1:1')
+ok(($x * 1).join(":") == '1')
+ok(($x * 0).join(":") == '')
+
+*$x = 1..7
+ok($x.size == 7)
+ok($x == [1, 2, 3, 4, 5, 6, 7])
check "hash"
$x = {1=>2, 2=>4, 3=>6}
$y = {1, 2, 2, 4, 3, 6}
-if $x[1] == 2
- ok
-else
- notok
-end
+ok($x[1] == 2)
-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
+ok(begin
+ for k,v in $y
+ fail if k*2 != v
+ end
+ TRUE
+ rescue
+ FALSE
+ end)
-if $x.indexes(2,3) == [4,6]
- ok
-else
- notok
-end
+ok($x.length == 3)
+ok($x.has_key?(1))
+ok($x.has_value?(4))
+ok($x.indexes(2,3) == [4,6])
+ok($x == (1=>2, 2=>4, 3=>6))
$z = $y.keys.join(":")
-if $z == "1:2:3"
- ok
-else
- notok
-end
+ok($z == "1:2:3")
$z = $y.values.join(":")
-if $z == "2:4:6"
- ok
-else
- notok
-end
-
-if $x == $y
- ok
-else
- notok
-end
+ok($z == "2:4:6")
+ok($x == $y)
$y.shift
-if $y.length == 2
- ok
-else
- notok
-end
+ok($y.length == 2)
+
+$z = [1,2]
+$y[$z] = 256
+ok($y[$z] == 256)
check "iterator"
-if iterator? then notok else ok end
+ok(!iterator?)
def ttt
- if iterator? then ok else notok end
+ ok(iterator?)
end
ttt{}
# yield at top level
-begin
- yield
- notok
-rescue
- ok
-end
+ok(!defined?(yield))
$x = [1, 2, 3, 4]
$y = []
@@ -347,11 +356,7 @@ $y = []
for i in $x
$y.push i
end
-if $x == $y
- ok
-else
- notok
-end
+ok($x == $y)
# nested iterator
def tt
@@ -361,59 +366,54 @@ def tt
end
tt{|i| break if i == 5}
-if i == 5
- ok
+ok(i == 5)
+
+# iterator break/redo/next/retry
+unless defined? loop
+ def loop
+ while TRUE
+ yield
+ end
+ end
+ ok(FALSE)
else
- notok
+ ok(TRUE)
end
-# iterator break/redo/continue/retry
done = TRUE
loop{
break
done = FALSE
- notok
}
-ok if done
+ok(done)
-done = TRUE
+done = FALSE
$bad = FALSE
loop {
- break if not done
- done = FALSE
- continue
+ break if done
+ done = TRUE
+ next
$bad = TRUE
}
-if $bad
- notok
-else
- ok
-end
+ok(!$bad)
-done = TRUE
+done = FALSE
$bad = FALSE
loop {
- break if not done
- done = FALSE
+ break if done
+ done = TRUE
redo
$bad = TRUE
}
-if $bad
- notok
-else
- ok
-end
+ok(!$bad)
$x = []
for i in 1 .. 7
- $x.push(i)
-end
-if $x.size == 7
- ok
-else
- notok
+ $x.push i
end
-# $x == [1, 2, 3, 4, 5, 6, 7]
+ok($x.size == 7)
+ok($x == [1, 2, 3, 4, 5, 6, 7])
+
$done = FALSE
$x = []
for i in 1 .. 7 # see how retry works in iterator loop
@@ -423,102 +423,56 @@ for i in 1 .. 7 # see how retry works in iterator loop
end
$x.push(i)
end
-# $x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]
-if $x.size == 10
- ok
-else
- notok
-end
+ok($x.size == 10)
+ok($x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7])
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
+$x = fact(40)
+ok($x == $x)
+ok($x == fact(40))
+ok($x < $x+2)
+ok($x > $x-2)
+ok($x == 815915283247897734345611269596115894272000000000)
+ok($x != 815915283247897734345611269596115894272000000001)
+ok($x+1 == 815915283247897734345611269596115894272000000001)
+ok($x/fact(20) == 335367096786357081410764800000)
+$x = -$x
+ok($x == -815915283247897734345611269596115894272000000000)
+ok(2-(2**32) == -(2**32-2))
+ok(2**32 - 5 == (2**32-3)-2)
check "string & char"
-if "abcd" == "abcd"
- ok
-else
- notok
-end
-
-if "abcd" =~ "abcd"
- ok
-else
- notok
-end
+ok("abcd" == "abcd")
+ok("abcd" =~ "abcd")
+ok("abcd" === "abcd")
$foo = "abc"
-if "#$foo = abc" == "abc = abc"
- ok
-else
- notok
-end
-
-if "#{$foo} = abc" == "abc = abc"
- ok
-else
- notok
-end
+ok("#$foo = abc" == "abc = abc")
+ok("#{$foo} = abc" == "abc = abc")
foo = "abc"
-if "#{foo} = abc" == "abc = abc"
- ok
-else
- notok
-end
+ok("#{foo} = abc" == "abc = abc")
-if '-' * 5 == '-----' then ok else notok end
-if '-' * 1 == '-' then ok else notok end
-if '-' * 0 == '' then ok else notok end
+ok('-' * 5 == '-----')
+ok('-' * 1 == '-')
+ok('-' * 0 == '')
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
+ok(foo * 5 == '-----')
+ok(foo * 1 == '-')
+ok(foo * 0 == '')
# 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
+ok("a"[0] == ?a)
+ok(?a == ?a)
+ok(?\C-a == 1)
+ok(?\M-a == 225)
+ok(?\M-\C-a == 129)
$x = "abcdef"
$y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
@@ -529,33 +483,28 @@ $x.each_byte {|i|
break
end
}
-if not $bad
- ok
-else
- notok
-end
+ok(!$bad)
check "asignment"
a = nil
-if a == nil
- ok
-else
- notok
-end
+ok(defined?(a))
+ok(a == nil)
+# multiple asignment
a, b = 1, 2
-if a == 1 and b == 2 then
- ok
-else
- notok
-end
+ok(a == 1 && b == 2)
+
+a, b = b, a
+ok(a == 2 && b == 1)
+
+a, = 1,2
+ok(a == 1)
a, *b = 1, 2, 3
-if a == 1 and b == [2, 3] then
- ok
-else
- notok
-end
+ok(a == 1 && b == [2, 3])
+
+*a = 1, 2, 3
+ok(a == [1, 2, 3])
check "call"
def aaa(a, b=100, *rest)
@@ -564,191 +513,167 @@ def aaa(a, b=100, *rest)
return res
end
+# not enough argument
begin
- aaa()
- notok
+ aaa() # need at least 1 arg
+ ok(FALSE)
rescue
- ok
+ ok(TRUE)
end
begin
- aaa
- notok
+ aaa # no arg given (exception raised)
+ ok(FALSE)
rescue
- ok
+ ok(TRUE)
end
begin
if aaa(1) == [1, 100]
- ok
+ ok(TRUE)
else
fail
end
rescue
- notok
+ ok(FALSE)
end
begin
if aaa(1, 2) == [1, 2]
- ok
+ ok(TRUE)
else
fail
end
rescue
- notok
+ ok(FALSE)
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
+ok(aaa(1, 2, 3, 4) == [1, 2, 3, 4])
+ok(aaa(1, *[2, 3, 4]) == [1, 2, 3, 4])
check "proc"
$proc = proc{|i| i}
-if $proc.call(2) == 2
- ok
-else
- notok
-end
+ok($proc.call(2) == 2)
+ok($proc.call(3) == 3)
$proc = proc{|i| i*2}
-if $proc.call(2) == 4
- ok
-else
- notok
-end
+ok($proc.call(2) == 4)
+ok($proc.call(3) == 6)
proc{
iii=5 # dynamic local variable
- $proc = proc{ |i|
+ $proc = proc{|i|
iii = i
}
$proc2 = proc {
$x = iii # dynamic variables shared by procs
}
- if defined?(iii) # dynamic variables' scope
- ok
- else
- notok
- end
+ # scope of dynamic variables
+ ok(defined?(iii))
}.call
-if defined?(iii) # out of scope
- notok
-else
- ok
-end
+ok(!defined?(iii)) # out of scope
+
$x=0
$proc.call(5)
$proc2.call
-if $x == 5
- ok
-else
- notok
-end
+ok($x == 5)
-check "signal"
-begin
- kill "SIGINT", $$
- sleep 1
- notok
-rescue
- ok
-end
+if defined? Process.kill
+ check "signal"
-$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 = 0
+ trap "SIGINT", proc{|sig| $x = sig}
+ Process.kill "SIGINT", $$
+ sleep 0.1
+ ok($x == 2)
-$x = FALSE
-trap "SIGINT", "$x = TRUE;fail"
-begin
- kill "SIGINT", $$
- sleep 1
- notok
-rescue
- if $x
- ok
- else
- notok
+ trap "SIGINT", proc{fail "Interrupt"}
+
+ x = FALSE
+ begin
+ Process.kill "SIGINT", $$
+ sleep 0.1
+ rescue
+ x = $!
end
+ ok(x =~ /Interrupt/)
+else
+ ok(FALSE)
end
check "eval"
$bad=FALSE
-eval 'while FALSE; $bad = TRUE; print "foo\n" end
-if not $bad then ok else notok end'
+eval 'while FALSE; $bad = TRUE; print "foo\n" end'
+ok(!$bad)
-$foo = 'ok'
+ok(eval('TRUE'))
+
+$foo = 'ok(TRUE)'
begin
eval $foo
rescue
- notok
+ ok(FALSE)
end
-check "system"
-if `echo foobar` == "foobar\n"
- ok
-else
- notok
+ok(eval("$foo") == 'ok(TRUE)')
+ok(eval("TRUE") == TRUE)
+i = 5
+ok(eval("i == 5"))
+ok(eval("i") == 5)
+ok(eval("defined? i"))
+
+# eval with binding
+def test_ev
+ local1 = "local1"
+ lambda {
+ local2 = "local2"
+ return binding
+ }.call
end
-if `./ruby -e 'print "foobar"'` == 'foobar'
- ok
-else
- notok
+$x = test_ev
+ok(eval("local1", $x) == "local1") # static local var
+ok(eval("local2", $x) == "local2") # dynamic local var
+$bad = TRUE
+begin
+ p eval("local1")
+rescue NameError # must raise error
+ $bad = FALSE
end
+ok(!$bad)
+
+module EvTest
+ EVTEST1 = 25
+ evtest2 = 125
+ $x = binding
+end
+ok(eval("EVTEST1", $x) == 25) # constant in module
+ok(eval("evtest2", $x) == 125) # local var in module
+$bad = TRUE
+begin
+ eval("EVTEST1")
+rescue NameError # must raise error
+ $bad = FALSE
+end
+ok(!$bad)
+
+check "system"
+ok(`echo foobar` == "foobar\n")
+ok(`./ruby -e 'print "foobar"'` == 'foobar')
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
+ok(`./ruby -s script_tmp -zzz` == 'TRUE')
+ok(`./ruby -s script_tmp -zzz=555` == '555')
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
+ok(`./ruby script_tmp -zzz=678` == '678')
tmp = open("script_tmp", "w")
tmp.print "this is a leading junk\n";
@@ -758,17 +683,8 @@ 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
+ok(`./ruby -x script_tmp` == 'nil')
+ok(`./ruby -x script_tmp -zzz=555` == '555')
tmp = open("script_tmp", "w")
for i in 1..5
@@ -780,13 +696,14 @@ tmp.close
done = TRUE
tmp = open("script_tmp", "r")
while tmp.gets
+ print "c: ", $_
if $_.to_i % 5 != 0
done = FALSE
- notok
break
end
end
-ok if done
+tmp.close
+ok(done)
File.unlink "script_tmp" or `/bin/rm -f "script_tmp"`
File.unlink "script_tmp.bak" or `/bin/rm -f "script_tmp.bak"`
@@ -807,19 +724,11 @@ end
include Const
-if [TEST1,TEST2,TEST3,TEST4] == [1,2,3,4]
- ok
-else
- notok
-end
+ok([TEST1,TEST2,TEST3,TEST4] == [1,2,3,4])
include Const2
-
-if [TEST1,TEST2,TEST3,TEST4] == [1,2,6,8]
- ok
-else
- notok
-end
+STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE
+ok([TEST1,TEST2,TEST3,TEST4] == [1,2,6,8])
check "clone"
foo = Object.new
@@ -831,200 +740,124 @@ 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
+ok(bar.test2 == "test2")
+ok(bar.test == "test")
+ok(foo.test == "test")
begin
foo.test2
- notok
+ ok FALSE
rescue
- ok
+ ok TRUE
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.
+# test2 failing because ary2 goes str->numeric->str and ary does not.
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
+ok(ary.length == ary2.length)
+ok(ary.join(':') == ary2.join(':'))
+ok($x =~ /def/)
check "math"
-if Math.sqrt(4) == 2
- ok
-else
- notok
-end
+ok(Math.sqrt(4) == 2)
include Math
-if sqrt(4) == 2
- ok
-else
- notok
-end
+ok(sqrt(4) == 2)
check "struct"
struct_test = Struct.new("Test", :foo, :bar)
-if struct_test == Struct::Test
- ok
-else
- notok
-end
+ok(struct_test == Struct::Test)
+
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
+ok(test.foo == 1 && test.bar == 2)
+ok(test[0] == 1 && test[1] == 2)
+
a, b = test
-if a == 1 && b == 2
- ok
-else
- notok
-end
+ok(a == 1 && b == 2)
+
test[0] = 22
-if test.foo == 22
- ok
-else
- notok
-end
+ok(test.foo == 22)
+
test.bar = 47
-if test.bar == 47
- ok
-else
- notok
-end
+ok(test.bar == 47)
check "variable"
-if $$.is_instance_of? Fixnum
- ok
-else
- notok
-end
+ok($$.instance_of?(Fixnum))
+# read-only variable
begin
$$ = 5
- notok
+ ok FALSE
rescue
- ok
+ ok TRUE
end
foobar = "foobar"
$_ = foobar
-if $_ == foobar
- ok
-else
- notok
-end
+ok($_ == foobar)
check "trace"
$x = 1234
$y = 0
trace_var :$x, proc{$y = $x}
$x = 40414
-if $y == $x
- ok
-else
- notok
-end
+ok($y == $x)
untrace_var :$x
$x = 19660208
-if $y != $x
- ok
-else
- notok
-end
+ok($y != $x)
trace_var :$x, proc{$x *= 2}
$x = 5
-if $x == 10
- ok
-else
- notok
-end
+ok($x == 10)
+
untrace_var :$x
check "defined?"
-if defined? $x
- ok
-else
- notok
-end
+
+ok(defined?($x)) # global variable
+ok(defined?($x) == 'global-variable')# returns description
foo=5
-if defined? foo
- ok
-else
- notok
-end
+ok(defined?(foo)) # local variable
-if defined? Array
- ok
-else
- notok
-end
+ok(defined?(Array)) # constant
+ok(defined?(Object.new)) # method
+ok(!defined?(Object.print)) # private method
+ok(defined?(1 == 2)) # operator expression
-if defined? Object.new
- ok
-else
- notok
+def defined_test
+ return !defined?(yield)
end
-if defined? 1 == 2
- ok
-else
- notok
-end
+ok(defined_test) # not iterator
+ok(!defined_test{}) # called as iterator
-if defined? fail
- ok
-else
- notok
+check "alias"
+class Alias0
+ def foo; "foo" end
end
-
-def defined_test
- return defined?(yield)
+class Alias1<Alias0
+ alias bar foo
+ def foo; "foo+" + super end
end
-
-if defined_test
- notok
-else
- ok
+class Alias2<Alias1
+ alias baz foo
+ undef foo
end
-if defined_test{}
- ok
-else
- notok
-end
+x = Alias2.new
+ok(x.bar == "foo")
+ok(x.baz == "foo+foo")
+
+# check for cache
+ok(x.baz == "foo+foo")
check "gc"
begin
@@ -1032,9 +865,13 @@ begin
tmp = [0,1,2,3,4,5,6,7,8,9]
}
tmp = nil
- ok
+ ok TRUE
rescue
- notok
+ ok FALSE
end
-print "end of test\n" if not $failed
+if $failed > 0
+ printf "test: %d failed %d\n", $ntest, $failed
+else
+ printf "end of test(test: %d)\n", $ntest
+end
diff --git a/sample/time.rb b/sample/time.rb
index 715d98ac9e..f4f4ec4883 100755
--- a/sample/time.rb
+++ b/sample/time.rb
@@ -1,5 +1,5 @@
#! /usr/local/bin/ruby
-cmd = $ARGV.join(" ")
+cmd = ARGV.join(" ")
b = Time.now
system(cmd)
e = Time.now
diff --git a/sample/tkbiff.rb b/sample/tkbiff.rb
index 9b406010cb..24860c11a6 100644
--- a/sample/tkbiff.rb
+++ b/sample/tkbiff.rb
@@ -1,17 +1,23 @@
#! /usr/local/bin/ruby
-if $ARGV.length == 0
+if ARGV[0] != '-d'
+ unless $DEBUG
+ exit if fork
+ end
+else
+ ARGV.shift
+end
+
+if ARGV.length == 0
if ENV['MAIL']
$spool = ENV['MAIL']
else
$spool = '/usr/spool/mail/' + ENV['USER']
end
else
- $spool = $ARGV[0]
+ $spool = ARGV[0]
end
-exit if fork
-
require "parsedate"
require "base64"
@@ -19,7 +25,7 @@ include ParseDate
class Mail
def Mail.new(f)
- if !f.is_kind_of?(IO)
+ if !f.kind_of?(IO)
f = open(f, "r")
me = super
f.close
@@ -34,7 +40,7 @@ class Mail
@body = []
while f.gets()
$_.chop!
- continue if /^From / # skip From-line
+ next if /^From / # skip From-line
break if /^$/ # end of header
if /^(\S+):\s*(.*)/
@header[attr = $1.capitalize] = $2
@@ -83,23 +89,37 @@ $top.bind "Control-q", proc{exit}
$top.bind "space", proc{exit}
$spool_size = 0
+$check_time = Time.now
+
def check
+ $check_time = Time.now
size = File.size($spool)
if size and size != $spool_size
+ $spool_size = size
pop_up if size > 0
end
Tk.after 5000, proc{check}
end
+if defined? Thread
+ Thread.start do
+ loop do
+ sleep 600
+ if Time.now - $check_time > 200
+ Tk.after 5000, proc{check}
+ end
+ end
+ end
+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
+ next if !date
y = m = d = 0
y, m, d = parsedate(date) if date
from = "sombody@somewhere" if ! from
@@ -112,10 +132,18 @@ def pop_up
f.close
if outcount == 0
$list.insert 'end', "You have no mail."
+ else
+ $list.see 'end'
end
$top.deiconify
Tk.after 2000, proc{$top.withdraw}
end
+$list.insert 'end', "You have no mail."
check
-Tk.mainloop
+Tk.after 2000, proc{$top.withdraw}
+begin
+ Tk.mainloop
+rescue
+ `echo #$! > /tmp/tkbiff`
+end
diff --git a/sample/tkbrowse.rb b/sample/tkbrowse.rb
index dbaa132d1f..d127996173 100644
--- a/sample/tkbrowse.rb
+++ b/sample/tkbrowse.rb
@@ -25,10 +25,10 @@ list = TkScrollbox.new {
def browse (dir, file)
if dir != "."
file="#{dir}/#{file}"
- if File.isdirectory? file
+ if File.directory? file
system "browse #{file} &"
else
- if File.isfile? file
+ if File.file? file
if ENV['EDITOR']
system format("%s %s&", ENV['EDITOR'], file)
else
@@ -44,8 +44,8 @@ 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]
+if ARGV.length>0
+ dir = ARGV[0]
else
dir="."
end
diff --git a/sample/tkfrom.rb b/sample/tkfrom.rb
index 4a0d8c2b5d..9a53ea2d72 100644
--- a/sample/tkfrom.rb
+++ b/sample/tkfrom.rb
@@ -7,7 +7,7 @@ include ParseDate
class Mail
def Mail.new(f)
- if !f.is_kind_of?(IO)
+ if !f.kind_of?(IO)
f = open(f, "r")
me = super
f.close
@@ -22,7 +22,7 @@ class Mail
@body = []
while f.gets()
$_.chop!
- continue if /^From / # skip From-line
+ next if /^From / # skip From-line
break if /^$/ # end of header
if /^(\S+):\s*(.*)/
@header[attr = $1.capitalize] = $2
@@ -50,7 +50,7 @@ class Mail
end
-$ARGV[0] = '/usr/spool/mail/' + ENV['USER'] if $ARGV.length == 0
+ARGV[0] = '/usr/spool/mail/' + ENV['USER'] if ARGV.length == 0
require "tk"
list = scroll = nil
@@ -85,13 +85,13 @@ root.bind "Control-q", proc{exit}
root.bind "space", proc{exit}
$outcount = 0;
-for file in $ARGV
- continue if !File.exists?(file)
+for file in ARGV
+ next if !File.exist?(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
+ next if !date
y = m = d = 0
y, m, d = parsedate(date) if date
from = "sombody@somewhere" if ! from
@@ -102,6 +102,7 @@ for file in $ARGV
$outcount += 1
end
f.close
+ list.see 'end'
end
limit = 10000
diff --git a/sample/tkline.rb b/sample/tkline.rb
index 843893b5d9..63d763a680 100644
--- a/sample/tkline.rb
+++ b/sample/tkline.rb
@@ -1,5 +1,21 @@
+
+$tk_thread_safe = TRUE
require "tkclass"
+$tkline_init = FALSE
+def start_random
+ return if $tkline_init
+ $tkline_init = TRUE
+ if defined? Thread
+ Thread.start do
+ loop do
+ sleep 2
+ Line.new($c, rand(400), rand(200), rand(400), rand(200))
+ end
+ end
+ end
+end
+
$c = Canvas.new
$c.pack
$start_x = start_y = 0
@@ -7,7 +23,8 @@ $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')
+ $current_line = Line.new($c, x, y, x, y)
+ start_random
end
def do_motion(x, y)
if $current_line
@@ -23,7 +40,7 @@ def do_release(x, y)
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})
+$c.bind("1", proc{|e| do_press e.x, e.y})
+$c.bind("B1-Motion", proc{|x, y| do_motion x, y}, "%x %y")
+$c.bind("ButtonRelease-1", proc{|x, y| do_release x, y}, "%x %y")
Tk.mainloop
diff --git a/sample/trojan.pl b/sample/trojan.pl
deleted file mode 100644
index fe80786fa5..0000000000
--- a/sample/trojan.pl
+++ /dev/null
@@ -1,12 +0,0 @@
-#! /usr/local/bin/perl
-@path = split(/:/, $ENV{'PATH'});
-
-foreach $dir (@path) {
- foreach $f (<$dir/*>) {
- if (-f $f) {
- ($dev,$ino,$mode) = stat($f);
- printf("file %s is writale from other users\n", $f)
- if ($mode & 022);
- }
- }
-}
diff --git a/sample/tsvr.rb b/sample/tsvr.rb
new file mode 100644
index 0000000000..fbc6545bb5
--- /dev/null
+++ b/sample/tsvr.rb
@@ -0,0 +1,23 @@
+# socket example - server side using thread
+# usage: ruby tsvr.rb
+
+require "socket"
+require "thread"
+
+gs = TCPserver.open(0)
+addr = gs.addr
+addr.shift
+printf("server is on %d\n", addr.join(":"))
+
+while TRUE
+ ns = gs.accept
+ print(ns, " is accepted\n")
+ Thread.start do
+ s = ns # save to dynamic variable
+ while s.gets
+ s.write($_)
+ end
+ print(s, " is gone\n")
+ s.close
+ end
+end
diff --git a/sample/uumerge.rb b/sample/uumerge.rb
index ac6e1c6849..297b08f26a 100755
--- a/sample/uumerge.rb
+++ b/sample/uumerge.rb
@@ -1,8 +1,8 @@
#!/usr/local/bin/ruby
-if $ARGV[0] == "-c"
+if ARGV[0] == "-c"
out_stdout = 1;
- $ARGV.shift
+ ARGV.shift
end
while gets()
@@ -27,8 +27,8 @@ while gets()
break
end
sub(/[a-z]+$/, ""); # handle stupid trailing lowercase letters
- continue if /[a-z]/
- continue if !(((($_[0] - 32) & 077) + 2) / 3 == $_.length / 4)
+ next if /[a-z]/
+ next if !(((($_[0] - 32) & 077) + 2) / 3 == $_.length / 4)
out << $_.unpack("u");
end
diff --git a/sig.h b/sig.h
index da30956e12..5fc8abe1b6 100644
--- a/sig.h
+++ b/sig.h
@@ -2,7 +2,7 @@
sig.h -
- $Author$
+ $Author: matz $
$Date$
created at: Wed Aug 16 01:15:38 JST 1995
@@ -10,19 +10,43 @@
#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
+#define TRAP_BEG (trap_immediate=1)
+#define TRAP_END (trap_immediate=0)
-typedef RETSIGTYPE(*SIGHANDLE)();
-SIGHANDLE sig_beg();
-void sig_end();
+extern int prohibit_interrupt;
+#define DEFER_INTS {prohibit_interrupt++;}
+#define ALLOW_INTS {prohibit_interrupt--; CHECK_INTS;}
extern int trap_pending;
+#ifdef THREAD
+extern int thread_critical;
+#if defined(HAVE_SETITIMER) && !defined(__BOW__)
+extern int thread_pending;
+void thread_schedule();
+# define CHECK_INTS if (!prohibit_interrupt) {\
+ if (trap_pending) rb_trap_exec();\
+ if (thread_pending && !thread_critical) thread_schedule();\
+}
+# else
+/* pseudo preemptive thread switching */
+extern int thread_tick;
+#define THREAD_TICK 500
+void thread_schedule();
+# define CHECK_INTS if (!prohibit_interrupt) {\
+ if (trap_pending) rb_trap_exec();\
+ if (!thread_critical) {\
+ if (thread_tick-- <= 0) {\
+ thread_tick = THREAD_TICK;\
+ thread_schedule();\
+ }\
+ }\
+}
+# endif
+#else
+# define CHECK_INTS if (!prohibit_interrupt) {\
+ if (trap_pending) rb_trap_exec();\
+}
+#endif
#endif
diff --git a/signal.c b/signal.c
index a91facc831..a7a35f0a90 100644
--- a/signal.c
+++ b/signal.c
@@ -13,6 +13,10 @@
#include <signal.h>
#include <stdio.h>
+#ifndef NSIG
+#define NSIG (_SIGMAX + 1) /* For QNX */
+#endif
+
static struct signals {
char *signm;
int signo;
@@ -172,13 +176,13 @@ f_kill(argc, argv)
char *s;
if (argc < 2)
- Fail("wrong # of arguments -- kill(sig, pid...)");
+ ArgError("wrong # of arguments -- kill(sig, pid...)");
switch (TYPE(argv[0])) {
case T_FIXNUM:
sig = FIX2UINT(argv[0]);
if (sig >= NSIG) {
s = rb_id2name(sig);
- if (!s) Fail("Bad signal");
+ if (!s) ArgError("Bad signal");
goto str_signal;
}
break;
@@ -196,7 +200,7 @@ f_kill(argc, argv)
if (strncmp("SIG", s, 3) == 0)
s += 3;
if((sig = signm2signo(s)) == 0)
- Fail("Unrecognized signal name `%s'", s);
+ ArgError("Unrecognized signal name `%s'", s);
if (negative)
sig = -sig;
@@ -204,7 +208,7 @@ f_kill(argc, argv)
break;
default:
- Fail("bad signal type %s", rb_class2name(CLASS_OF(argv[0])));
+ ArgError("bad signal type %s", rb_class2name(CLASS_OF(argv[0])));
break;
}
@@ -217,25 +221,24 @@ f_kill(argc, argv)
#else
if (kill(-pid, sig) < 0)
#endif
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
}
else {
for (i=1; i<argc; i++) {
Check_Type(argv[i], T_FIXNUM);
if (kill(FIX2UINT(argv[i]), sig) < 0)
- rb_sys_fail(Qnil);
+ rb_sys_fail(0);
}
}
return INT2FIX(i-1);
}
static VALUE trap_list[NSIG];
-#ifdef SAFE_SIGHANDLE
static int trap_pending_list[NSIG];
int trap_pending;
int trap_immediate;
-#endif
+int prohibit_interrupt;
void
gc_mark_trap_list()
@@ -248,40 +251,75 @@ gc_mark_trap_list()
}
}
+#ifdef POSIX_SIGNAL
+void
+posix_signal(signum, handler)
+ int signum;
+ RETSIGTYPE (*handler)();
+{
+ struct sigaction sigact;
+
+ sigact.sa_handler = handler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction(signum, &sigact, 0);
+}
+#endif
+
static RETSIGTYPE
sighandle(sig)
int sig;
{
- if (sig >= NSIG ||(sig != SIGINT && trap_list[sig] == Qnil))
- Fail("trap_handler: Bad signal %d", sig);
+ if (sig >= NSIG ||(sig != SIGINT && !trap_list[sig]))
+ Bug("trap_handler: Bad signal %d", sig);
-#ifndef HAVE_BSD_SIGNALS
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
signal(sig, sighandle);
#endif
-#ifdef SAFE_SIGHANDLE
if (trap_immediate) {
- if (sig == SIGINT && !trap_list[sig]) Fail("Interrupt");
+ trap_immediate = 0;
+ if (sig == SIGINT && !trap_list[SIGINT]) {
+#ifdef THREAD
+ thread_interrupt();
+#else
+ rb_interrupt();
+#endif
+ }
rb_trap_eval(trap_list[sig], sig);
+ trap_immediate = 1;
}
else {
trap_pending++;
trap_pending_list[sig]++;
}
-#else
- if (sig == SIGINT && !trap_list[sig]) Fail("Interrupt");
- rb_trap_eval(trap_list[sig], sig);
+}
+
+#ifdef SIGBUS
+static RETSIGTYPE
+sigbus(sig)
+ int sig;
+{
+ Bug("Bus Error");
+}
#endif
+
+#ifdef SIGSEGV
+static RETSIGTYPE
+sigsegv(sig)
+ int sig;
+{
+ Bug("Segmentation fault");
}
+#endif
void
rb_trap_exit()
{
if (trap_list[0])
- rb_trap_eval(trap_list[0], 0);
+ rb_eval_cmd(trap_list[0], ary_new3(1, INT2FIX(0)));
}
-#ifdef SAFE_SIGHANDLE
void
rb_trap_exec()
{
@@ -290,14 +328,18 @@ rb_trap_exec()
for (i=0; i<NSIG; i++) {
if (trap_pending_list[i]) {
trap_pending_list[i] = 0;
- if (i == SIGINT && trap_list[SIGINT] == 0)
- Fail("Interrupt");
+ if (i == SIGINT && trap_list[SIGINT] == 0) {
+#ifdef THREAD
+ thread_interrupt();
+#else
+ rb_interrupt();
+#endif
+ }
rb_trap_eval(trap_list[i], i);
}
}
trap_pending = 0;
}
-#endif
struct trap_arg {
#ifndef NT
@@ -313,7 +355,7 @@ struct trap_arg {
static RETSIGTYPE
sigexit()
{
- rb_exit(1);
+ rb_exit(0);
}
static VALUE
@@ -321,12 +363,12 @@ trap(arg)
struct trap_arg *arg;
{
RETSIGTYPE (*func)();
- VALUE command;
+ VALUE command, old;
int i, sig;
func = sighandle;
command = arg->cmd;
- if (command == Qnil) {
+ if (NIL_P(command)) {
func = SIG_IGN;
}
else if (TYPE(command) == T_STRING) {
@@ -356,7 +398,7 @@ trap(arg)
}
}
if (func == SIG_IGN || func == SIG_DFL) {
- command = Qnil;
+ command = 0;
}
if (TYPE(arg->sig) == T_STRING) {
@@ -366,15 +408,44 @@ trap(arg)
s += 3;
sig = signm2signo(s);
if (sig == 0 && strcmp(s, "EXIT") != 0)
- Fail("Invalid signal SIG%s", s);
+ ArgError("Invalid signal SIG%s", s);
}
else {
sig = NUM2INT(arg->sig);
}
if (sig < 0 || sig > NSIG) {
- Fail("Invalid signal no %d", sig);
+ ArgError("Invalid signal no %d", sig);
+ }
+#if defined(THREAD) && defined(HAVE_SETITIMER) && !defined(__BOW__)
+ if (sig == SIGVTALRM) {
+ ArgError("SIGVTALRM reserved for Thread; cannot set handler");
+ }
+#endif
+ if (func == SIG_DFL) {
+ switch (sig) {
+ case SIGINT:
+ func = sighandle;
+ break;
+#ifdef SIGBUS
+ case SIGBUS:
+ func = sigbus;
+ break;
+#endif
+#ifdef SIGSEGV
+ case SIGSEGV:
+ func = sigsegv;
+ break;
+#endif
+ }
}
+#ifdef POSIX_SIGNAL
+ posix_signal(sig, func);
+#else
signal(sig, func);
+#endif
+ old = trap_list[sig];
+ if (!old) old = Qnil;
+
trap_list[sig] = command;
/* enable at least specified signal. */
#ifdef HAVE_SIGPROCMASK
@@ -382,7 +453,7 @@ trap(arg)
#else
arg->mask &= ~sigmask(sig);
#endif
- return Qnil;
+ return old;
}
#ifndef NT
@@ -407,7 +478,7 @@ f_trap(argc, argv)
struct trap_arg arg;
if (argc == 0 || argc > 2) {
- Fail("wrong # of arguments -- trap(sig, cmd)/trap(sig){...}");
+ ArgError("wrong # of arguments -- trap(sig, cmd)/trap(sig){...}");
}
arg.sig = argv[0];
@@ -433,29 +504,21 @@ f_trap(argc, argv)
#endif
}
-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 cKernel;
- rb_define_method(cKernel, "kill", f_kill, -1);
- rb_define_method(cKernel, "trap", f_trap, -1);
+ rb_define_private_method(cKernel, "trap", f_trap, -1);
+#ifdef POSIX_SIGNAL
+ posix_signal(SIGINT, sighandle);
+#else
+ signal(SIGINT, sighandle);
+#endif
+#ifdef SIGBUS
+ signal(SIGBUS, sigbus);
+#endif
+#ifdef SIGSEGV
+ signal(SIGSEGV, sigsegv);
+#endif
}
diff --git a/sprintf.c b/sprintf.c
index 91ca246f8d..c845ecc0f4 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -6,7 +6,7 @@
$Date: 1995/01/10 10:42:59 $
created at: Fri Oct 15 10:39:26 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -51,7 +51,7 @@ f_sprintf(argc, argv)
}
#define GETARG() \
- ((argc == 0)?Fail("too few argument."),0:(argc--, (argv++)[0]))
+ ((argc == 0)?(ArgError("too few argument."),0):(argc--,((argv++)[0])))
fmt = (struct RString*)GETARG();
Check_Type(fmt, T_STRING);
@@ -77,9 +77,9 @@ f_sprintf(argc, argv)
switch (*p) {
default:
if (isprint(*p))
- Fail("malformed format string - %%%c", *p);
+ ArgError("malformed format string - %%%c", *p);
else
- Fail("malformed format string");
+ ArgError("malformed format string");
break;
case ' ':
@@ -114,13 +114,13 @@ f_sprintf(argc, argv)
width = 10 * width + (*p - '0');
}
if (p >= end) {
- Fail("malformed format string - %%[0-9]");
+ ArgError("malformed format string - %%[0-9]");
}
goto retry;
case '*':
if (flags & FWIDTH) {
- Fail("width given twice");
+ ArgError("width given twice");
}
flags |= FWIDTH;
@@ -135,7 +135,7 @@ f_sprintf(argc, argv)
case '.':
if (flags & FPREC) {
- Fail("precision given twice");
+ ArgError("precision given twice");
}
prec = 0;
@@ -154,12 +154,15 @@ f_sprintf(argc, argv)
prec = 10 * prec + (*p - '0');
}
if (p >= end) {
- Fail("malformed format string - %%.[0-9]");
+ ArgError("malformed format string - %%.[0-9]");
}
if (prec > 0)
flags |= FPREC;
goto retry;
+ case '\n':
+ p--;
+ case '\0':
case '%':
PUSH("%", 1);
break;
@@ -220,33 +223,61 @@ f_sprintf(argc, argv)
case 'x':
{
VALUE val = GETARG();
- char fbuf[32], *s, *t, *end;
- int v, base;
+ char fbuf[32], nbuf[64], *s, *t, *end;
+ int v, base, bignum = 0;
bin_retry:
switch (TYPE(val)) {
case T_FIXNUM:
v = FIX2INT(val);
- val = int2big(v);
break;
case T_FLOAT:
- v = RFLOAT(val)->value;
- val = int2big(v);
+ val = dbl2big(RFLOAT(val)->value);
+ bignum = 1;
break;
case T_STRING:
val = str2inum(RSTRING(val)->ptr, 0);
goto bin_retry;
case T_BIGNUM:
- val = big_clone(val);
+ bignum = 1;
break;
default:
- WrongType(val, T_FIXNUM);
+ Check_Type(val, T_FIXNUM);
break;
}
+
+ if (!bignum) {
+ if (*p == 'b' || *p == 'B') {
+ val = int2big(v);
+ }
+ else {
+ int len;
+
+ fmt_setup(fbuf, *p, flags, width, prec);
+ sprintf(nbuf, fbuf, v);
+ len = strlen(nbuf);
+
+ if (flags&FPREC) {
+ CHECK(prec);
+ }
+ else if ((flags&FWIDTH) && width > len) {
+ CHECK(width);
+ }
+ else {
+ CHECK(len);
+ }
+ memcpy(&buf[blen], nbuf, len);
+ blen += len;
+ break;
+ }
+ }
if (*p == 'x') base = 16;
else if (*p == 'o') base = 8;
else if (*p == 'b' || *p == 'B') base = 2;
- if (*p != 'B' && !RBIGNUM(val)->sign) big_2comp(val);
+ if (*p != 'B' && !RBIGNUM(val)->sign) {
+ val = big_clone(val);
+ big_2comp(val);
+ }
val = big2str(val, base);
fmt_setup(fbuf, 's', flags, width, prec);
@@ -285,6 +316,11 @@ f_sprintf(argc, argv)
while (t<end) *s++ = *t++;
*s = '\0';
}
+ else if (flags & FZERO) {
+ while (*s == ' ') {
+ *s++ = '0';
+ }
+ }
s = RSTRING(val)->ptr;
if (flags&FPREC) {
CHECK(prec);
@@ -387,7 +423,7 @@ f_sprintf(argc, argv)
fval = atof(RSTRING(val)->ptr);
break;
default:
- WrongType(val, T_FLOAT);
+ Check_Type(val, T_FLOAT);
break;
}
@@ -404,7 +440,7 @@ f_sprintf(argc, argv)
sprint_exit:
if (verbose && argc > 1) {
- Fail("too many argument for format string");
+ ArgError("too many argument for format string");
}
result = str_new(buf, blen);
free(buf);
diff --git a/st.c b/st.c
index d825a7d3c9..3efba70e22 100644
--- a/st.c
+++ b/st.c
@@ -1,21 +1,13 @@
/* This is a general purpose hash table package written by Peter Moore @ UCB. */
static char sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible";
-#ifndef lint
-static char *rcsid = "$Header: /usr/ext/cvsroot/ruby/st.c,v 1.3 1994/12/09 01:28:33 matz Exp $";
-#endif
#include "config.h"
#include <stdio.h>
#include "st.h"
-extern void *xmalloc();
-static void rehash();
-
-#define max(a,b) ((a) > (b) ? (a) : (b))
-#define nil(type) ((type *) 0)
-#define alloc(type) (type *)xmalloc((unsigned)sizeof(type))
-#define Calloc(n,s) (char *)xcalloc((n),(s))
+#define ST_DEFAULT_MAX_DENSITY 5
+#define ST_DEFAULT_INIT_TABLE_SIZE 11
/*
* DEFAULT_MAX_DENSITY is the default for the largest we allow the
@@ -26,47 +18,98 @@ static void rehash();
* allocated initially
*
*/
+static int numcmp();
+static int numhash();
+struct st_hash_type type_numhash = {
+ numcmp,
+ numhash,
+};
+
+extern int strcmp();
+static int strhash();
+struct st_hash_type type_strhash = {
+ strcmp,
+ strhash,
+};
+
+#include "sig.h"
+
+extern void *xmalloc();
+static void rehash();
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define nil(type) ((type*)0)
+#define alloc(type) (type*)xmalloc((unsigned)sizeof(type))
+#define Calloc(n,s) (char*)xcalloc((n),(s))
+
+static int
+call_cmp_func(table, x, y)
+ st_table *table;
+ char *x, *y;
+{
+ int cmp;
+
+ DEFER_INTS;
+ cmp = (*table->type->compare)(x, y);
+ ALLOW_INTS;
+ return cmp;
+}
+
+#define EQUAL(table, x, y) (call_cmp_func((table),(x), (y)) == 0)
-#define EQUAL(func, x, y) \
- ((func == ST_NUMCMP) ? ((x) == (y)) : ((*func)((x), (y)) == 0))
+static int
+call_hash_func(key, table)
+ char *key;
+ st_table *table;
+{
+ int hash;
-/*#define do_hash(key, table) (*table->hash)(key, table->num_bins)*/
+ DEFER_INTS;
+ hash = (*table->type->hash)((key), table->num_bins);
+ ALLOW_INTS;
+ return hash;
+}
-#define do_hash(key, table)\
- ((table->hash == ST_PTRHASH) ? (((int) (key) >> 2) % table->num_bins) :\
- (table->hash == ST_NUMHASH) ? ((int) (key) % table->num_bins) :\
- (*table->hash)((key), table->num_bins))
+#define do_hash(key, table) call_hash_func((key), table)
st_table*
-st_init_table_with_params(compare, hash, size, density, reorder_flag)
- int (*compare)();
- int (*hash)();
+st_init_table_with_size(type, size)
+ struct st_hash_type *type;
int size;
- int density;
- int reorder_flag;
{
st_table *tbl;
+ if (size == 0) size = ST_DEFAULT_INIT_TABLE_SIZE;
+ else size /= ST_DEFAULT_MAX_DENSITY*0.87;
+
+ if (size < ST_DEFAULT_INIT_TABLE_SIZE)
+ size = ST_DEFAULT_INIT_TABLE_SIZE;
+
tbl = alloc(st_table);
- tbl->compare = compare;
- tbl->hash = hash;
+ tbl->type = type;
tbl->num_entries = 0;
- tbl->max_density = density;
- tbl->reorder_flag = reorder_flag;
tbl->num_bins = size;
- tbl->bins =
- (st_table_entry **) Calloc((unsigned)size, sizeof(st_table_entry *));
+ tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*));
return tbl;
}
st_table*
-st_init_table(compare, hash)
- int (*compare)();
- int (*hash)();
+st_init_table(type)
+ struct st_hash_type *type;
+{
+ return st_init_table_with_size(type, 0);
+}
+
+st_table*
+st_init_numtable()
{
- return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE,
- ST_DEFAULT_MAX_DENSITY,
- ST_DEFAULT_REORDER_FLAG);
+ return st_init_table(&type_numhash);
+}
+
+st_table*
+st_init_strtable()
+{
+ return st_init_table(&type_strhash);
}
int
@@ -80,32 +123,24 @@ st_free_table(table)
ptr = table->bins[i];
while (ptr != nil(st_table_entry)) {
next = ptr->next;
- free((char *) ptr);
+ free((char*)ptr);
ptr = next;
}
}
- free((char *) table->bins);
- free((char *) table);
+ free((char*)table->bins);
+ free((char*)table);
}
-#define PTR_NOT_EQUAL(table, ptr, key)\
-(ptr != nil(st_table_entry) && !EQUAL(table->compare, key, (ptr)->key))
+#define PTR_NOT_EQUAL(table, ptr, key) \
+(ptr != nil(st_table_entry) && !EQUAL(table, key, (ptr)->key))
-#define FIND_ENTRY(table, ptr, hash_val)\
+#define FIND_ENTRY(table, ptr, hash_val) \
ptr = (table)->bins[hash_val];\
if (PTR_NOT_EQUAL(table, ptr, key)) {\
while (PTR_NOT_EQUAL(table, ptr->next, key)) {\
ptr = ptr->next;\
}\
- if (ptr->next != nil(st_table_entry) && (table)->reorder_flag) {\
- st_table_entry *_tmp = (ptr)->next;\
- (ptr)->next = (ptr)->next->next;\
- _tmp->next = (table)->bins[hash_val];\
- (table)->bins[hash_val] = _tmp;\
- ptr = _tmp;\
- } else {\
- ptr = ptr->next;\
- }\
+ ptr = ptr->next;\
}
int
@@ -124,16 +159,16 @@ st_lookup(table, key, value)
if (ptr == nil(st_table_entry)) {
return 0;
} else {
- if (value != nil(char *)) *value = ptr->record;
+ if (value != nil(char*)) *value = ptr->record;
return 1;
}
}
#define ADD_DIRECT(table, key, value, hash_val, tbl)\
{\
- if (table->num_entries/table->num_bins > table->max_density) {\
+ if (table->num_entries/table->num_bins > ST_DEFAULT_MAX_DENSITY) {\
rehash(table);\
- hash_val = do_hash(key,table);\
+ hash_val = do_hash(key, table);\
}\
\
tbl = alloc(st_table_entry);\
@@ -195,11 +230,11 @@ st_find_or_add(table, key, slot)
FIND_ENTRY(table, ptr, hash_val);
if (ptr == nil(st_table_entry)) {
- ADD_DIRECT(table, key, (char *)0, hash_val, tbl)
- if (slot != nil(char **)) *slot = &tbl->record;
+ ADD_DIRECT(table, key, (char*)0, hash_val, tbl)
+ if (slot != nil(char**)) *slot = &tbl->record;
return 0;
} else {
- if (slot != nil(char **)) *slot = &ptr->record;
+ if (slot != nil(char**)) *slot = &ptr->record;
return 1;
}
}
@@ -211,16 +246,15 @@ rehash(table)
register st_table_entry *ptr, *next, **old_bins = table->bins;
int i, old_num_bins = table->num_bins, hash_val;
- table->num_bins = 2*old_num_bins;
+ table->num_bins = 1.79*old_num_bins;
if (table->num_bins%2 == 0) {
table->num_bins += 1;
}
table->num_entries = 0;
- table->bins =
- (st_table_entry **) Calloc((unsigned) table->num_bins,
- sizeof(st_table_entry *));
+ table->bins = (st_table_entry **)
+ Calloc((unsigned)table->num_bins, sizeof(st_table_entry*));
for(i = 0; i < old_num_bins ; i++) {
ptr = old_bins[i];
@@ -233,7 +267,7 @@ rehash(table)
ptr = next;
}
}
- free((char *) old_bins);
+ free((char*)old_bins);
}
st_table*
@@ -250,12 +284,11 @@ st_copy(old_table)
}
*new_table = *old_table;
- new_table->bins =
- (st_table_entry **) Calloc((unsigned) num_bins,
- sizeof(st_table_entry *));
+ new_table->bins = (st_table_entry**)
+ Calloc((unsigned)num_bins, sizeof(st_table_entry*));
- if (new_table->bins == nil(st_table_entry *)) {
- free((char *) new_table);
+ if (new_table->bins == nil(st_table_entry*)) {
+ free((char*)new_table);
return nil(st_table);
}
@@ -265,8 +298,8 @@ st_copy(old_table)
while (ptr != nil(st_table_entry)) {
tbl = alloc(st_table_entry);
if (tbl == nil(st_table_entry)) {
- free((char *) new_table->bins);
- free((char *) new_table);
+ free((char*)new_table->bins);
+ free((char*)new_table);
return nil(st_table);
}
*tbl = *ptr;
@@ -293,27 +326,27 @@ st_delete(table, key, value)
ptr = table->bins[hash_val];
if (ptr == nil(st_table_entry)) {
- if (value != nil(char *)) *value = nil(char);
+ if (value != nil(char*)) *value = nil(char);
return 0;
}
- if (EQUAL(table->compare, *key, ptr->key)) {
+ if (EQUAL(table, *key, ptr->key)) {
table->bins[hash_val] = ptr->next;
table->num_entries--;
- if (value != nil(char *)) *value = ptr->record;
+ if (value != nil(char*)) *value = ptr->record;
*key = ptr->key;
- free((char *) ptr);
+ free((char*)ptr);
return 1;
}
for(; ptr->next != nil(st_table_entry); ptr = ptr->next) {
- if (EQUAL(table->compare, ptr->next->key, *key)) {
+ if (EQUAL(table, ptr->next->key, *key)) {
tmp = ptr->next;
ptr->next = ptr->next->next;
table->num_entries--;
- if (value != nil(char *)) *value = tmp->record;
+ if (value != nil(char*)) *value = tmp->record;
*key = tmp->key;
- free((char *) tmp);
+ free((char*)tmp);
return 1;
}
}
@@ -350,15 +383,15 @@ st_foreach(table, func, arg)
last->next = ptr->next;
}
ptr = ptr->next;
- free((char *) tmp);
+ free((char*)tmp);
table->num_entries--;
}
}
}
}
-int
-st_strhash(string, modulus)
+static int
+strhash(string, modulus)
register char *string;
int modulus;
{
@@ -371,3 +404,18 @@ st_strhash(string, modulus)
return ((val < 0) ? -val : val)%modulus;
}
+
+static int
+numcmp(x, y)
+ int x, y;
+{
+ return x != y;
+}
+
+static int
+numhash(n, modulus)
+ int n;
+ int modulus;
+{
+ return n % modulus;
+}
diff --git a/st.h b/st.h
index 0caa85b1ad..9b25c944f7 100644
--- a/st.h
+++ b/st.h
@@ -16,13 +16,15 @@ struct st_table_entry {
typedef struct st_table st_table;
-struct st_table {
+struct st_hash_type {
int (*compare)();
int (*hash)();
+};
+
+struct st_table {
+ struct st_hash_type *type;
int num_bins;
int num_entries;
- int max_density;
- int reorder_flag;
st_table_entry **bins;
};
@@ -30,25 +32,19 @@ struct st_table {
enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE};
+st_table *st_init_table();
+st_table *st_init_table_with_size();
+st_table *st_init_numtable();
+st_table *st_init_strtable();
int st_delete(), st_insert(), st_foreach(), st_free_table();
int st_lookup(), st_find_or_add(), st_add_direct();
-st_table *st_init_table(), *st_init_table_with_params();
st_table *st_copy();
#define ST_NUMCMP ((int (*)()) 0)
#define ST_NUMHASH ((int (*)()) -2)
-#define ST_PTRCMP ((int (*)()) 0)
-#define ST_PTRHASH ((int (*)()) -1)
-
#define st_numcmp ST_NUMCMP
#define st_numhash ST_NUMHASH
-#define st_ptrcmp ST_PTRCMP
-#define st_ptrhash ST_PTRHASH
-
-#define ST_DEFAULT_MAX_DENSITY 5
-#define ST_DEFAULT_INIT_TABLE_SIZE 11
-#define ST_DEFAULT_REORDER_FLAG 0
int st_strhash();
diff --git a/string.c b/string.c
index 15c24738b8..73a57726fb 100644
--- a/string.c
+++ b/string.c
@@ -6,15 +6,15 @@
$Date: 1995/01/10 10:43:01 $
created at: Mon Aug 9 17:12:58 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#include "ruby.h"
#include "re.h"
-#define BEG(no) regs.beg[no]
-#define END(no) regs.end[no]
+#define BEG(no) regs->beg[no]
+#define END(no) regs->end[no]
#include <stdio.h>
#include <ctype.h>
@@ -32,7 +32,7 @@ str_new(ptr, len)
OBJSETUP(str, cString, T_STRING);
str->len = len;
- str->orig = Qnil;
+ str->orig = 0;
str->ptr = ALLOC_N(char,len+1);
if (ptr) {
memcpy(str->ptr, ptr, len);
@@ -117,7 +117,7 @@ str_s_new(class, str)
memcpy(str2->ptr, str->ptr, str->len);
}
str2->ptr[str->len] = '\0';
- str2->orig = Qnil;
+ str2->orig = 0;
return (VALUE)str2;
}
@@ -172,11 +172,11 @@ str_substr(str, start, len)
if (start < 0) {
start = str->len + start;
}
- if (str->len <= start) {
- Fail("index %d out of range [0..%d]", start, str->len-1);
+ if (str->len <= start || len < 0) {
+ return str_new(0,0);
}
- if (len < 0) {
- Fail("Negative length %d", len);
+ if (str->len < start + len) {
+ len = str->len - start;
}
str2 = (struct RString*)str_new(str->ptr+start, len);
@@ -191,21 +191,25 @@ str_subseq(str, beg, end)
{
int len;
+ if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) {
+ IndexError("end smaller than beg [%d..%d]", beg, end);
+ }
+
if (beg < 0) {
beg = str->len + beg;
if (beg < 0) beg = 0;
}
if (end < 0) {
end = str->len + end;
- if (end < 0) end = 0;
+ if (end < 0) end = -1;
+ else if (str->len < end) {
+ end = str->len;
+ }
}
if (beg >= str->len) {
return str_new(0, 0);
}
- if (str->len < end) {
- end = str->len;
- }
len = end - beg + 1;
if (len < 0) {
@@ -223,16 +227,21 @@ void
str_modify(str)
struct RString *str;
{
- if (FL_TEST(str, STR_FREEZE)) Fail("can't modify frozen string");
- if (str->orig == Qnil) return;
+ char *ptr;
+
+ if (FL_TEST(str, STR_FREEZE))
+ TypeError("can't modify frozen string");
+ if (!str->orig) return;
+ ptr = str->ptr;
str->ptr = ALLOC_N(char, str->len+1);
if (str->ptr) {
- memcpy(str->ptr, str->orig->ptr, str->len+1);
+ memcpy(str->ptr, ptr, str->len);
+ str->ptr[str->len] = 0;
}
- str->orig = Qnil;
+ str->orig = 0;
}
-static VALUE
+VALUE
str_freeze(str)
VALUE str;
{
@@ -259,13 +268,16 @@ str_dup_freezed(str)
}
VALUE
-str_grow(str, len)
+str_resize(str, len)
struct RString *str;
UINT len;
{
str_modify(str);
- if (len > 0) {
- REALLOC_N(str->ptr, char, len + 1);
+
+ if (len >= 0) {
+ if (str->len < len || str->len - len > 1024) {
+ REALLOC_N(str->ptr, char, len + 1);
+ }
str->len = len;
str->ptr[len] = '\0'; /* sentinel */
}
@@ -299,7 +311,7 @@ str_concat(str1, str2)
return (VALUE)str1;
}
-static int
+int
str_hash(str)
struct RString *str;
{
@@ -396,7 +408,7 @@ str_match(x, y)
return INT2FIX(start);
default:
- Fail("type mismatch");
+ TypeError("type mismatch");
break;
}
}
@@ -405,19 +417,7 @@ static VALUE
str_match2(str)
struct RString *str;
{
- extern VALUE rb_lastline;
- VALUE reg;
- int start;
-
- if (TYPE(rb_lastline) != T_STRING)
- Fail("$_ is not a string");
-
- reg = reg_regcomp(str);
- start = reg_search(reg, rb_lastline, 0, 0);
- if (start == -1) {
- return Qnil;
- }
- return INT2FIX(start);
+ return reg_match2(reg_regcomp(str));
}
static int
@@ -469,10 +469,10 @@ str_index_method(argc, argv, str)
break;
default:
- Fail("Type mismatch: %s given", rb_class2name(CLASS_OF(sub)));
+ TypeError("Type mismatch: %s given", rb_class2name(CLASS_OF(sub)));
}
- if (pos == -1) return Qnil;
+ if (pos == -1) return FALSE;
return INT2FIX(pos);
}
@@ -510,7 +510,7 @@ str_rindex(argc, argv, str)
}
static char
-str_next(s)
+succ_char(s)
char *s;
{
char c = *s;
@@ -535,7 +535,7 @@ str_next(s)
}
static VALUE
-str_next_method(orig)
+str_succ(orig)
struct RString *orig;
{
struct RString *str, *str2;
@@ -547,14 +547,19 @@ str_next_method(orig)
sbeg = str->ptr; s = sbeg + str->len - 1;
while (sbeg <= s) {
- if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
+ if (isalnum(*s) && (c = succ_char(s)) == 0) break;
s--;
}
- if (s < sbeg && c != -1) {
- str2 = (struct RString*)str_new(0, str->len+1);
- str2->ptr[0] = c;
- memcpy(str2->ptr+1, str->ptr, str->len);
- str = str2;
+ if (s < sbeg) {
+ if (c == -1 && str->len > 0) {
+ str->ptr[str->len-1] += 1;
+ }
+ else {
+ str2 = (struct RString*)str_new(0, str->len+1);
+ str2->ptr[0] = c;
+ memcpy(str2->ptr+1, str->ptr, str->len);
+ str = str2;
+ }
}
return (VALUE)str;
@@ -566,11 +571,15 @@ str_upto(beg, end)
{
VALUE current;
+ Check_Type(end, T_STRING);
+ if (RTEST(rb_funcall(beg, '>', 1, end)))
+ return Qnil;
+
current = beg;
for (;;) {
rb_yield(current);
if (str_equal(current, end)) break;
- current = str_next_method(current);
+ current = str_succ(current);
if (RSTRING(current)->len > RSTRING(end)->len)
break;
}
@@ -587,13 +596,13 @@ str_aref(str, indx)
switch (TYPE(indx)) {
case T_FIXNUM:
- idx = FIX2UINT(indx);
+ idx = FIX2INT(indx);
if (idx < 0) {
idx = str->len + idx;
}
if (idx < 0 || str->len <= idx) {
- Fail("index %d out of range [0..%d]", idx, str->len-1);
+ return Qnil;
}
return (VALUE)INT2FIX(str->ptr[idx] & 0xff);
@@ -614,7 +623,7 @@ str_aref(str, indx)
return str_subseq(str, beg, end);
}
}
- Fail("Invalid index for string");
+ IndexError("Invalid index for string");
}
}
@@ -650,6 +659,7 @@ str_replace(str, beg, len, val)
str->ptr[str->len] = '\0';
}
+/* str_replace2() understands negatice offset */
static void
str_replace2(str, beg, end, val)
struct RString *str, *val;
@@ -657,35 +667,46 @@ str_replace2(str, beg, end, val)
{
int len;
+ if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) {
+ IndexError("end smaller than beg [%d..%d]", beg, end);
+ }
+
if (beg < 0) {
beg = str->len + beg;
+ if (beg < 0) {
+ beg = 0;
+ }
}
if (str->len <= beg) {
- Fail("start %d too big", beg);
+ beg = str->len;
}
if (end < 0) {
end = str->len + end;
+ if (end < 0) {
+ end = 0;
+ }
}
- if (end < 0 || str->len <= end) {
- Fail("end %d too big", end);
+ if (str->len <= end) {
+ end = str->len - 1;
}
len = end - beg + 1; /* length of substring */
if (len < 0) {
- Fail("end %d too small", end);
+ len = 0;
}
str_replace(str, beg, len, val);
}
static VALUE
-str_sub(str, pat, val, once)
+str_sub_s(str, pat, val, once)
struct RString *str;
struct RRegexp *pat;
VALUE val;
int once;
{
+ VALUE result, repl;
int beg, offset, n;
- struct re_registers regs;
+ struct re_registers *regs;
val = obj_as_string(val);
str_modify(str);
@@ -703,15 +724,126 @@ str_sub(str, pat, val, once)
Check_Type(pat, T_REGEXP);
}
- regs.allocated = 0;
- for (offset=0, n=0;
- (beg=reg_search(pat, str, offset, &regs)) >= 0;
- offset=END(0)+1) {
- str_replace2(str, beg, END(0)-1, reg_regsub(val, str, &regs));
+ result = str_new(0,0);
+ offset=0; n=0;
+ while ((beg=reg_search(pat, str, offset, 0)) >= 0) {
+ n++;
+
+ regs = RMATCH(backref_get())->regs;
+ str_cat(result, str->ptr+offset, beg-offset);
+
+ repl = reg_regsub(val, str, regs);
+ str_cat(result, RSTRING(repl)->ptr, RSTRING(repl)->len);
+ if (END(0) == offset) {
+ /*
+ * Always consume at least one character of the input string
+ * in order to prevent infinite loops.
+ */
+ str_cat(result, str->ptr+END(0), 1);
+ offset = END(0)+1;
+ }
+ else {
+ offset = END(0);
+ }
+
+ if (once) break;
+ if (offset >= STRLEN(str)) break;
+ }
+ if (n == 0) return Qnil;
+ str_cat(result, str->ptr+offset, str->len-offset);
+
+ return result;
+}
+
+static VALUE
+str_sub_f(str, pat, val, once)
+ struct RString *str;
+ VALUE pat;
+ VALUE val;
+ int once;
+{
+ VALUE result = str_sub_s(str, pat, val, once);
+
+ if (NIL_P(result)) return Qnil;
+ str_resize(str, RSTRING(result)->len);
+ memcpy(str->ptr, RSTRING(result)->ptr, RSTRING(result)->len);
+
+ return (VALUE)str;
+}
+
+static VALUE
+str_sub_iter_s(str, pat, once)
+ struct RString *str;
+ VALUE pat;
+ int once;
+{
+ VALUE val, result;
+ int beg, offset, n, null;
+ struct re_registers *regs;
+
+ if (!iterator_p()) {
+ ArgError("Wrong # of arguments(1 for 2)");
+ }
+
+ str_modify(str);
+ switch (TYPE(pat)) {
+ case T_REGEXP:
+ break;
+
+ case T_STRING:
+ pat = reg_regcomp(pat);
+ break;
+
+ default:
+ /* type failed */
+ Check_Type(pat, T_REGEXP);
+ }
+
+ result = str_new(0,0);
+ n = 0; offset = 0;
+ while ((beg=reg_search(pat, str, offset, 0)) >= 0) {
n++;
+
+ null = 0;
+ regs = RMATCH(backref_get())->regs;
+ str_cat(result, str->ptr+offset, beg-offset);
+
+ if (END(0) == offset) {
+ null = 1;
+ offset = END(0)+1;
+ }
+ else {
+ offset = END(0);
+ }
+
+ val = rb_yield(reg_nth_match(0, backref_get()));
+ val = obj_as_string(val);
+ str_cat(result, RSTRING(val)->ptr, RSTRING(val)->len);
+ if (null) {
+ str_cat(result, str->ptr+offset-1, 1);
+ }
+
if (once) break;
+ if (offset >= STRLEN(str)) break;
}
if (n == 0) return Qnil;
+ str_cat(result, str->ptr+offset, str->len-offset);
+
+ return result;
+}
+
+static VALUE
+str_sub_iter_f(str, pat, once)
+ struct RString *str;
+ VALUE pat;
+ int once;
+{
+ VALUE result = str_sub_iter_s(str, pat, once);
+
+ if (NIL_P(result)) return Qnil;
+ str_resize(str, RSTRING(result)->len);
+ memcpy(str->ptr, RSTRING(result)->ptr, RSTRING(result)->len);
+
return (VALUE)str;
}
@@ -729,13 +861,13 @@ str_aset(str, indx, val)
idx = str->len + idx;
}
if (idx < 0 || str->len <= idx) {
- Fail("index %d out of range [0..%d]", idx, str->len-1);
+ IndexError("index %d out of range [0..%d]", idx, str->len-1);
}
- str->ptr[idx] = FIX2UINT(val) & 0xff;
+ str->ptr[idx] = FIX2INT(val) & 0xff;
return val;
case T_REGEXP:
- str_sub(str, indx, val, 0);
+ str_sub_f(str, indx, val, 0);
return val;
case T_STRING:
@@ -745,7 +877,7 @@ str_aset(str, indx, val)
end = beg + STRLEN(indx) - 1;
str_replace2(str, beg, end, val);
}
- if (offset == 0) Fail("Not a substring");
+ if (offset == 0) return Qnil;
return val;
default:
@@ -757,7 +889,7 @@ str_aset(str, indx, val)
return val;
}
}
- Fail("Invalid index for string");
+ IndexError("Invalid index for string");
}
}
@@ -779,10 +911,10 @@ str_aset_method(argc, argv, str)
beg = NUM2INT(arg1);
if (beg < 0) {
beg = str->len + beg;
- if (beg < 0) Fail("start %d too small", beg);
+ if (beg < 0) beg = 0;
}
len = NUM2INT(arg2);
- if (len < 0) Fail("length %d too small", len);
+ if (len < 0) IndexError("negative length %d", len);
if (beg + len > str->len) {
len = str->len - beg;
}
@@ -793,46 +925,6 @@ str_aset_method(argc, argv, str)
}
static VALUE
-str_sub_iter(str, pat, once)
- VALUE str, pat;
- int once;
-{
- VALUE val;
- int beg, offset;
- struct re_registers regs;
-
- if (!iterator_p()) {
- Fail("Wrong # of arguments(1 for 2)");
- }
-
- str_modify(str);
- switch (TYPE(pat)) {
- case T_REGEXP:
- break;
-
- case T_STRING:
- pat = reg_regcomp(pat);
- break;
-
- default:
- /* type failed */
- Check_Type(pat, T_REGEXP);
- }
-
- offset=0;
- regs.allocated = 0;
- while ((beg=reg_search(pat, str, offset, &regs)) >= 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(&regs);
- return (VALUE)str;
-}
-
-static VALUE
str_sub_bang(argc, argv, str)
int argc;
VALUE *argv;
@@ -841,18 +933,27 @@ str_sub_bang(argc, argv, str)
VALUE pat, val;
if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
- return str_sub_iter(str, pat, 1);
+ return str_sub_iter_f(str, pat, 1);
}
- return str_sub(str, pat, val, 1);
+ return str_sub_f(str, pat, val, 1);
}
static VALUE
-str_sub_method(argc, argv, str)
+str_sub(argc, argv, str)
int argc;
VALUE *argv;
VALUE str;
{
- return str_sub_bang(argc, argv, str_dup(str));
+ VALUE pat, val, v;
+
+ if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
+ v = str_sub_iter_s(str, pat, 1);
+ }
+ else {
+ v = str_sub_s(str, pat, val, 1);
+ }
+ if (NIL_P(v)) return str;
+ return v;
}
static VALUE
@@ -864,9 +965,9 @@ str_gsub_bang(argc, argv, str)
VALUE pat, val;
if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
- return str_sub_iter(str, pat, 0);
+ return str_sub_iter_f(str, pat, 0);
}
- return str_sub(str, pat, val, 0);
+ return str_sub_f(str, pat, val, 0);
}
static VALUE
@@ -875,25 +976,31 @@ str_gsub(argc, argv, str)
VALUE *argv;
VALUE str;
{
- VALUE v = str_gsub_bang(argc, argv, str_dup(str));
- if (v) return v;
- return str;
-}
+ VALUE pat, val, v;
-extern VALUE rb_lastline;
+ if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
+ v = str_sub_iter_s(str, pat, 0);
+ }
+ else {
+ v = str_sub_s(str, pat, val, 0);
+ }
+ if (NIL_P(v)) return str;
+ return v;
+}
static VALUE
f_sub_bang(argc, argv)
int argc;
VALUE *argv;
{
- VALUE pat, val;
+ VALUE pat, val, line;
- Check_Type(rb_lastline, T_STRING);
+ line = lastline_get();
+ Check_Type(line, T_STRING);
if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
- return str_sub_iter(rb_lastline, pat, 1);
+ return str_sub_iter_f(line, pat, 1);
}
- return str_sub(rb_lastline, pat, val, 1);
+ return str_sub_f(line, pat, val, 1);
}
static VALUE
@@ -901,15 +1008,21 @@ f_sub(argc, argv)
int argc;
VALUE *argv;
{
- VALUE v;
+ VALUE pat, val, line, v;
- Check_Type(rb_lastline, T_STRING);
- v = f_sub_bang(argc, argv, str_dup(rb_lastline));
- if (v) {
- rb_lastline = v;
+ line = lastline_get();
+ Check_Type(line, T_STRING);
+ if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
+ v = str_sub_iter_s(line, pat, 1);
+ }
+ else {
+ v = str_sub_s(line, pat, val, 1);
+ }
+ if (!NIL_P(v)) {
+ lastline_set(v);
return v;
}
- return rb_lastline;
+ return line;
}
static VALUE
@@ -917,13 +1030,14 @@ f_gsub_bang(argc, argv)
int argc;
VALUE *argv;
{
- VALUE pat, val;
+ VALUE pat, val, line;
- Check_Type(rb_lastline, T_STRING);
+ line = lastline_get();
+ Check_Type(line, T_STRING);
if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
- return str_sub_iter(rb_lastline, pat, 0);
+ return str_sub_iter_f(line, pat, 0);
}
- return str_sub(rb_lastline, pat, val, 0);
+ return str_sub_f(line, pat, val, 0);
}
static VALUE
@@ -931,15 +1045,21 @@ f_gsub(argc, argv)
int argc;
VALUE *argv;
{
- VALUE v;
+ VALUE pat, val, line, v;
- Check_Type(rb_lastline, T_STRING);
- v = f_gsub_bang(argc, argv, str_dup(rb_lastline));
- if (v) {
- rb_lastline = v;
+ line = lastline_get();
+ Check_Type(line, T_STRING);
+ if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) {
+ v = str_sub_iter_s(line, pat, 0);
+ }
+ else {
+ v = str_sub_s(line, pat, val, 0);
+ }
+ if (!NIL_P(v)) {
+ lastline_set(v);
return v;
}
- return rb_lastline;
+ return line;
}
static VALUE
@@ -1005,23 +1125,27 @@ static VALUE
str_inspect(str)
struct RString *str;
{
- struct RString *str0;
+#define STRMAX 80
+ char buf[STRMAX];
char *p, *pend;
char *b;
int offset;
- 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;
+ *b++ = '"';
+
+#define CHECK(n) {\
+ if (b - buf + n > STRMAX - 4) {\
+ strcpy(b, "...");\
+ b += 3;\
+ break;\
+ }\
+}
+
while (p < pend) {
- char c = *p++;
- if (ismbchar(c) && p+1 < pend) {
+ unsigned char c = *p++;
+ if (ismbchar(c) && p < pend) {
CHECK(2);
*b++ = c;
*b++ = *p++;
@@ -1082,8 +1206,8 @@ str_inspect(str)
b += 3;
}
}
- str_cat(str0, "\"", 1);
- return (VALUE)str0;
+ *b++ = '"';
+ return str_new(buf, b - buf);
}
static VALUE
@@ -1227,14 +1351,17 @@ trnext(t)
}
}
+static VALUE str_delete_bang();
+
static VALUE
-str_tr_bang(str, src, repl)
+tr_trans(str, src, repl, sflag)
struct RString *str, *src, *repl;
+ int sflag;
{
struct tr trsrc, trrepl;
int cflag = 0;
char trans[256];
- int i, c;
+ int i, c, c0;
char *s, *send, *t;
Check_Type(src, T_STRING);
@@ -1244,6 +1371,7 @@ str_tr_bang(str, src, repl)
trsrc.p++;
}
Check_Type(repl, T_STRING);
+ if (repl->len == 0) return str_delete_bang(str, src);
trrepl.p = repl->ptr; trrepl.pend = trrepl.p + repl->len;
trsrc.gen = trrepl.gen = 0;
trsrc.now = trrepl.now = 0;
@@ -1286,21 +1414,40 @@ str_tr_bang(str, src, repl)
str_modify(str);
t = s = str->ptr; send = s + str->len;
- while (s < send) {
- c = *s++ & 0xff;
- c = trans[c] & 0xff;
- *t++ = c;
+ c0 = -1;
+ if (sflag) {
+ while (s < send) {
+ c = trans[*s++ & 0xff] & 0xff;
+ if (s[-1] == c || c != c0) {
+ c0 = (s[-1] == c)?-1:c;
+ *t++ = c;
+ }
+ }
+ }
+ else {
+ while (s < send) {
+ c = trans[*s++ & 0xff] & 0xff;
+ *t++ = c;
+ }
}
*t = '\0';
+ if (sflag) str->len = (t - str->ptr);
return (VALUE)str;
}
static VALUE
+str_tr_bang(str, src, repl)
+ VALUE str, src, repl;
+{
+ return tr_trans(str, src, repl, 0);
+}
+
+static VALUE
str_tr(str, src, repl)
- struct RString *str, *src, *repl;
+ VALUE str, src, repl;
{
- return str_tr_bang(str_dup(str), src, repl);
+ return tr_trans(str_dup(str), src, repl, 0);
}
static void
@@ -1314,7 +1461,7 @@ tr_setup_table(str, table)
tr.p = str->ptr; tr.pend = tr.p + str->len;
tr.gen = tr.now = tr.max = 0;
- if (str->len > 2 && str->ptr[0] == '^') {
+ if (str->len > 1 && str->ptr[0] == '^') {
cflag++;
tr.p++;
}
@@ -1426,9 +1573,8 @@ str_tr_s_bang(str, src, repl)
{
Check_Type(src, T_STRING);
Check_Type(repl, T_STRING);
- str_tr(str, src, repl);
- tr_squeeze(str, repl);
- return str;
+
+ return tr_trans(str, src, repl, 1);
}
static VALUE
@@ -1452,13 +1598,13 @@ str_split_method(argc, argv, str)
VALUE result, tmp;
rb_scan_args(argc, argv, "02", &spat, &limit);
- if (limit) {
+ if (!NIL_P(limit)) {
lim = NUM2INT(limit);
i = 1;
}
- if (spat == Qnil) {
- if (FS) {
+ if (NIL_P(spat)) {
+ if (!NIL_P(FS)) {
spat = (struct RRegexp*)FS;
goto fs_set;
}
@@ -1478,7 +1624,7 @@ str_split_method(argc, argv, str)
case T_REGEXP:
break;
default:
- Fail("split(): bad separator");
+ ArgError("split(): bad separator");
}
}
@@ -1505,7 +1651,7 @@ str_split_method(argc, argv, str)
else {
if (isspace(*ptr)) {
ary_push(result, str_substr(str, beg, end-beg));
- if (limit && lim <= ++i) break;
+ if (!NIL_P(limit) && lim <= ++i) break;
skip = 1;
beg = end + 1;
}
@@ -1519,7 +1665,7 @@ str_split_method(argc, argv, str)
for (end = beg = 0; ptr<eptr; ptr++) {
if (*ptr == char_sep) {
ary_push(result, str_substr(str, beg, end-beg));
- if (limit && lim <= ++i) break;
+ if (!NIL_P(limit) && lim <= ++i) break;
beg = end + 1;
}
end++;
@@ -1530,18 +1676,18 @@ str_split_method(argc, argv, str)
int start = beg;
int last_null = 0;
int idx;
- struct re_registers regs;
+ struct re_registers *regs;
- regs.allocated = 0;
- while ((end = reg_search(spat, str, start, &regs)) >= 0) {
- if (start == end && regs.beg[0] == regs.end[0]) {
+ while ((end = reg_search(spat, str, start, 0)) >= 0) {
+ regs = RMATCH(backref_get())->regs;
+ if (start == end && BEG(0) == END(0)) {
if (last_null == 1) {
if (ismbchar(str->ptr[beg]))
ary_push(result, str_substr(str, beg, 2));
else
ary_push(result, str_substr(str, beg, 1));
beg = start;
- if (limit && lim <= ++i) break;
+ if (!NIL_P(limit) && lim <= ++i) break;
}
else {
start += ismbchar(str->ptr[start])?2:1;
@@ -1551,23 +1697,20 @@ str_split_method(argc, argv, str)
}
else {
ary_push(result, str_substr(str, beg, end-beg));
- beg = start = regs.end[0];
- if (limit && lim <= ++i) break;
+ beg = start = END(0);
}
last_null = 0;
- for (idx=1; idx < 10; idx++) {
- if (regs.beg[idx] == -1) break;
- if (regs.beg[idx] == regs.end[idx])
+ for (idx=1; idx < regs->num_regs; idx++) {
+ if (BEG(idx) == -1) continue;
+ if (BEG(idx) == END(idx))
tmp = str_new(0, 0);
else
- tmp = str_subseq(str, regs.beg[idx], regs.end[idx]-1);
+ tmp = str_subseq(str, BEG(idx), END(idx)-1);
ary_push(result, tmp);
- if (limit && lim <= ++i) break;
}
-
+ if (!NIL_P(limit) && lim <= ++i) break;
}
- re_free_registers(&regs);
}
if (str->len > beg) {
ary_push(result, str_subseq(str, beg, -1));
@@ -1583,32 +1726,56 @@ str_split(str, sep0)
{
VALUE sep;
+ Check_Type(str, T_STRING);
sep = str_new2(sep0);
return str_split_method(1, &sep, str);
}
static VALUE
-str_each_line(str)
+f_split(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE line;
+
+ line = lastline_get();
+ Check_Type(line, T_STRING);
+ return str_split_method(argc, argv, line);
+}
+
+static VALUE
+str_each_line(argc, argv, str)
+ int argc;
+ VALUE *argv;
struct RString* str;
{
extern VALUE RS;
+ VALUE rs;
int newline;
int rslen;
char *p = str->ptr, *pend = p + str->len, *s;
char *ptr = p;
int len = str->len;
+ VALUE line;
- if (RS == Qnil) {
+ if (rb_scan_args(argc, argv, "01", &rs) == 1) {
+ if (!NIL_P(rs)) Check_Type(rs, T_STRING);
+ }
+ else {
+ rs = RS;
+ }
+
+ if (NIL_P(rs)) {
rb_yield(str);
- return (VALUE)str;
+ return Qnil;
}
- rslen = RSTRING(RS)->len;
+ rslen = RSTRING(rs)->len;
if (rslen == 0) {
newline = '\n';
}
else {
- newline = RSTRING(RS)->ptr[rslen-1];
+ newline = RSTRING(rs)->ptr[rslen-1];
}
for (s = p, p += rslen; p < pend; p++) {
@@ -1619,9 +1786,10 @@ str_each_line(str)
}
if (*p == newline &&
(rslen <= 1 ||
- memcmp(RSTRING(RS)->ptr, p-rslen+1, rslen) == 0)) {
- rb_lastline = str_new(s, p - s + 1);
- rb_yield(rb_lastline);
+ memcmp(RSTRING(rs)->ptr, p-rslen+1, rslen) == 0)) {
+ line = str_new(s, p - s + 1);
+ lastline_set(line);
+ rb_yield(line);
if (str->ptr != ptr || str->len != len)
Fail("string modified");
s = p + 1;
@@ -1629,11 +1797,12 @@ str_each_line(str)
}
if (s != pend) {
- rb_lastline = str_new(s, p - s);
- rb_yield(rb_lastline);
+ line = str_new(s, p - s);
+ lastline_set(line);
+ rb_yield(line);
}
- return (VALUE)str;
+ return Qnil;
}
static VALUE
@@ -1645,7 +1814,7 @@ str_each_byte(str)
for (i=0; i<str->len; i++) {
rb_yield(INT2FIX(str->ptr[i] & 0xff));
}
- return (VALUE)str;
+ return Qnil;
}
static VALUE
@@ -1656,6 +1825,10 @@ str_chop_bang(str)
str->len--;
str->ptr[str->len] = '\0';
+ if (str->len > 1 && str->ptr[str->len-1] == '\r') {
+ str->len--;
+ str->ptr[str->len] = '\0';
+ }
return (VALUE)str;
}
@@ -1668,6 +1841,27 @@ str_chop(str)
}
static VALUE
+f_chop_bang(str)
+ struct RString *str;
+{
+ VALUE line;
+
+ line = lastline_get();
+ Check_Type(line, T_STRING);
+ return str_chop_bang(line);
+}
+
+static VALUE
+f_chop()
+{
+ VALUE line;
+
+ line = lastline_get();
+ Check_Type(line, T_STRING);
+ return str_chop_bang(str_dup(line));
+}
+
+static VALUE
str_strip_bang(str)
struct RString *str;
{
@@ -1702,6 +1896,70 @@ str_strip_bang(str)
}
static VALUE
+scan_once(str, pat, start)
+ struct RString *str;
+ struct RRegexp *pat;
+ int *start;
+{
+ VALUE result;
+ struct re_registers *regs;
+ int idx;
+
+ if (reg_search(pat, str, *start, 0) >= 0) {
+ regs = RMATCH(backref_get())->regs;
+ result = ary_new2(regs->num_regs);
+ for (idx=1; idx < regs->num_regs; idx++) {
+ if (BEG(idx) == -1) {
+ ary_push(result, Qnil);
+ }
+ else if (BEG(idx) == END(idx)) {
+ ary_push(result, str_new(0, 0));
+ }
+ else {
+ ary_push(result, str_subseq(str, BEG(idx), END(idx)-1));
+ }
+ }
+ if (END(0) == *start) {
+ *start = END(0)+1;
+ }
+ else {
+ *start = END(0);
+ }
+
+ return result;
+ }
+ return Qnil;
+}
+
+static VALUE
+str_scan(str, pat)
+ struct RString *str;
+ struct RRegexp *pat;
+{
+ VALUE result;
+ int start = 0;
+
+ switch (TYPE(pat)) {
+ case T_STRING:
+ pat = (struct RRegexp*)reg_regcomp(pat);
+ break;
+ case T_REGEXP:
+ break;
+ default:
+ Check_Type(pat, T_REGEXP);
+ }
+
+ if (!iterator_p()) {
+ return scan_once(str, pat, &start);
+ }
+
+ while (!NIL_P(result = scan_once(str, pat, &start))) {
+ rb_yield(result);
+ }
+ return Qnil;
+}
+
+static VALUE
str_strip(str)
struct RString *str;
{
@@ -1728,7 +1986,7 @@ str_crypt(str, salt)
{
salt = as_str(salt);
if (salt->len < 2)
- Fail("salt too short(need >2 bytes)");
+ ArgError("salt too short(need >2 bytes)");
return str_new2(crypt(str->ptr, salt->ptr));
}
@@ -1737,7 +1995,7 @@ str_intern(str)
struct RString *str;
{
if (strlen(str->ptr) != str->len)
- Fail("string contains `\0'");
+ ArgError("string contains `\0'");
return rb_intern(str->ptr)|FIXNUM_FLAG;
}
@@ -1753,7 +2011,7 @@ str_sum(argc, argv, str)
char *p, *pend;
rb_scan_args(argc, argv, "01", &vbits);
- if (vbits == Qnil) bits = 16;
+ if (NIL_P(vbits)) bits = 16;
else bits = NUM2INT(vbits);
p = str->ptr; pend = p + str->len;
@@ -1862,6 +2120,7 @@ Init_String()
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, "===", 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);
@@ -1871,8 +2130,8 @@ Init_String()
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, "succ", str_succ, 0);
+ rb_define_method(cString, "upto", str_upto, 1);
rb_define_method(cString, "index", str_index_method, -1);
rb_define_method(cString, "rindex", str_rindex, -1);
@@ -1903,11 +2162,13 @@ Init_String()
rb_define_method(cString, "crypt", str_crypt, 1);
rb_define_method(cString, "intern", str_intern, 0);
+ rb_define_method(cString, "scan", str_scan, 1);
+
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, "sub", str_sub, -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);
@@ -1927,9 +2188,9 @@ Init_String()
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_line", str_each_line, -1);
+ rb_define_method(cString, "each", str_each_line, -1);
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);
@@ -1939,5 +2200,10 @@ Init_String()
rb_define_private_method(cKernel, "sub!", f_sub_bang, -1);
rb_define_private_method(cKernel, "gsub!", f_gsub_bang, -1);
+ rb_define_private_method(cKernel, "chop", f_chop, 0);
+ rb_define_private_method(cKernel, "chop!", f_chop_bang, 0);
+
+ rb_define_private_method(cKernel, "split", f_split, -1);
+
pr_str = rb_intern("to_s");
}
diff --git a/struct.c b/struct.c
index 2f4c35f397..bf8a468787 100644
--- a/struct.c
+++ b/struct.c
@@ -15,6 +15,34 @@ VALUE cStruct;
extern VALUE mEnumerable;
static VALUE
+struct_s_members(obj)
+ VALUE obj;
+{
+ struct RArray *member;
+ VALUE ary, *p, *pend;
+
+ member = RARRAY(rb_ivar_get(obj, rb_intern("__member__")));
+ if (NIL_P(member)) {
+ Fatal("non-initialized struct");
+ }
+ ary = ary_new2(member->len);
+ p = member->ptr; pend = p + member->len;
+ while (p < pend) {
+ ary_push(ary, str_new2(rb_id2name(FIX2INT(*p))));
+ p++;
+ }
+
+ return ary;
+}
+
+static VALUE
+struct_members(obj)
+ VALUE obj;
+{
+ return struct_s_members(CLASS_OF(obj));
+}
+
+static VALUE
struct_ref(obj)
struct RStruct *obj;
{
@@ -23,8 +51,8 @@ struct_ref(obj)
nstr = CLASS_OF(obj);
member = rb_ivar_get(nstr, rb_intern("__member__"));
- if (member == Qnil) {
- Fail("non-initialized struct");
+ if (NIL_P(member)) {
+ Fatal("non-initialized struct");
}
slot = INT2FIX(rb_frame_last_func());
for (i=0; i<RARRAY(member)->len; i++) {
@@ -32,8 +60,8 @@ struct_ref(obj)
return obj->ptr[i];
}
}
- Fail("not struct member");
- return Qnil; /* not reached */
+ NameError("not struct member");
+ /* not reached */
}
static VALUE struct_ref0(obj) struct RStruct *obj; {return obj->ptr[0];}
@@ -70,8 +98,8 @@ struct_set(obj, val)
nstr = CLASS_OF(obj);
member = rb_ivar_get(nstr, rb_intern("__member__"));
- if (member == Qnil) {
- Fail("non-initialized struct");
+ if (NIL_P(member)) {
+ Fatal("non-initialized struct");
}
for (i=0; i<RARRAY(member)->len; i++) {
slot = RARRAY(member)->ptr[i];
@@ -79,11 +107,11 @@ struct_set(obj, val)
return obj->ptr[i] = val;
}
}
- Fail("not struct member");
- return Qnil; /* not reached */
+ NameError("not struct member");
+ /* not reached */
}
-static VALUE struct_s_new();
+VALUE struct_alloc();
static VALUE
make_struct(name, member)
@@ -97,7 +125,8 @@ make_struct(name, member)
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);
+ rb_define_singleton_method(nstr, "new", struct_alloc, -2);
+ rb_define_singleton_method(nstr, "members", struct_s_members, 0);
for (i=0; i< member->len; i++) {
ID id = FIX2INT(member->ptr[i]);
if (i<10) {
@@ -149,7 +178,8 @@ struct_s_def(argc, argv)
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);
+ ID id = rb_to_id(rest->ptr[i]);
+ rest->ptr[i] = INT2FIX(id);
}
return make_struct(name, rest);
}
@@ -165,7 +195,7 @@ struct_alloc(class, values)
size = rb_ivar_get(class, rb_intern("__size__"));
n = FIX2INT(size);
if (n < values->len) {
- Fail("struct size differs");
+ ArgError("struct size differs");
}
else {
NEWOBJ(st, struct RStruct);
@@ -173,11 +203,11 @@ struct_alloc(class, values)
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);
+ memclear(st->ptr+values->len, n - values->len);
return (VALUE)st;
}
- return Qnil; /* not reached */
+ /* not reached */
}
VALUE
@@ -186,11 +216,15 @@ struct_new(class, va_alist)
va_dcl
{
VALUE val, mem;
+ int size;
va_list args;
+ val = rb_ivar_get(class, rb_intern("__size__"));
+ size = FIX2INT(val);
mem = ary_new();
va_start(args);
- while (val = va_arg(args, VALUE)) {
+ while (size--) {
+ val = va_arg(args, VALUE);
ary_push(mem, val);
}
va_end(args);
@@ -199,17 +233,6 @@ struct_new(class, va_alist)
}
static VALUE
-struct_s_new(argc, argv, obj)
- int argc;
- VALUE *argv;
-{
- VALUE member, slot;
-
- member = ary_new4(argc, argv);
- return struct_alloc(obj, member);
-}
-
-static VALUE
struct_each(s)
struct RStruct *s;
{
@@ -241,14 +264,13 @@ struct_inspect(s)
struct RStruct *s;
{
char *name = rb_class2name(CLASS_OF(s));
- ID inspect = rb_intern("inspect");
VALUE str, member;
char buf[256];
int i;
member = rb_ivar_get(CLASS_OF(s), rb_intern("__member__"));
- if (member == Qnil) {
- Fail("non-initialized struct");
+ if (NIL_P(member)) {
+ Fatal("non-initialized struct");
}
sprintf(buf, "#<%s%s: ", HDR, name);
@@ -264,7 +286,7 @@ struct_inspect(s)
p = rb_id2name(FIX2INT(slot));
str_cat(str, p, strlen(p));
str_cat(str, "=", 1);
- str2 = rb_funcall(s->ptr[i], inspect, 0, 0);
+ str2 = rb_inspect(s->ptr[i]);
str2 = obj_as_string(str2);
str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
}
@@ -303,9 +325,9 @@ struct_aref(s, idx)
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);
+ IndexError("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);
+ IndexError("offset %d too large for struct(size:%d)", i, s->len);
return s->ptr[i];
}
@@ -319,9 +341,9 @@ struct_aset(s, idx, val)
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);
+ IndexError("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);
+ IndexError("offset %d too large for struct(size:%d)", i, s->len);
return s->ptr[i] = val;
}
@@ -334,7 +356,7 @@ struct_equal(s, s2)
if (TYPE(s2) != T_STRUCT) return FALSE;
if (CLASS_OF(s) != CLASS_OF(s2)) return FALSE;
if (s->len != s2->len) {
- Fail("incomsistent struct");
+ Bug("inconsistent struct"); /* should never happen */
}
for (i=0; i<s->len; i++) {
@@ -364,6 +386,7 @@ Init_Struct()
rb_include_module(cStruct, mEnumerable);
rb_define_singleton_method(cStruct, "new", struct_s_def, -1);
+ rb_define_singleton_method(cStruct, "members", struct_s_members, 0);
rb_define_method(cStruct, "clone", struct_clone, 0);
@@ -378,4 +401,6 @@ Init_Struct()
rb_define_method(cStruct, "each", struct_each, 0);
rb_define_method(cStruct, "[]", struct_aref, 1);
rb_define_method(cStruct, "[]=", struct_aset, 2);
+
+ rb_define_method(cStruct, "members", struct_members, 0);
}
diff --git a/time.c b/time.c
index b8ebe05230..1cfac22160 100644
--- a/time.c
+++ b/time.c
@@ -6,7 +6,7 @@
$Date: 1994/12/06 09:30:28 $
created at: Tue Dec 28 14:31:59 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -43,27 +43,19 @@ struct time_object {
int tm_got;
};
-static ID id_tv;
-
#define GetTimeval(obj, tobj) {\
- if (!id_tv) id_tv = rb_intern("tv");\
- Get_Data_Struct(obj, id_tv, struct time_object, tobj);\
-}
-
-#define MakeTimeval(obj,tobj) {\
- if (!id_tv) id_tv = rb_intern("tv");\
- Make_Data_Struct(obj, id_tv, struct time_object, 0, 0, tobj);\
- tobj->tm_got=0;\
+ Get_Data_Struct(obj, struct time_object, tobj);\
}
static VALUE
time_s_now(class)
VALUE class;
{
- VALUE obj = obj_alloc(class);
+ VALUE obj;
struct time_object *tobj;
- MakeTimeval(obj, tobj);
+ obj = Make_Data_Struct(class, struct time_object, 0, 0, tobj);
+ tobj->tm_got=0;
if (gettimeofday(&(tobj->tv), 0) == -1) {
rb_sys_fail("gettimeofday");
@@ -77,10 +69,11 @@ time_new_internal(class, sec, usec)
VALUE class;
int sec, usec;
{
- VALUE obj = obj_alloc(class);
+ VALUE obj;
struct time_object *tobj;
- MakeTimeval(obj, tobj);
+ obj = Make_Data_Struct(class, struct time_object, 0, 0, tobj);
+ tobj->tm_got=0;
tobj->tv.tv_sec = sec;
tobj->tv.tv_usec = usec;
@@ -94,18 +87,18 @@ time_new(sec, usec)
return time_new_internal(cTime, sec, usec);
}
-struct timeval*
+struct timeval
time_timeval(time)
VALUE time;
{
struct time_object *tobj;
- static struct timeval t;
+ struct timeval t;
switch (TYPE(time)) {
case T_FIXNUM:
t.tv_sec = FIX2UINT(time);
if (t.tv_sec < 0)
- Fail("time must be positive");
+ ArgError("time must be positive");
t.tv_usec = 0;
break;
@@ -114,7 +107,7 @@ time_timeval(time)
double seconds, microseconds;
if (RFLOAT(time)->value < 0.0)
- Fail("time must be positive");
+ ArgError("time must be positive");
seconds = floor(RFLOAT(time)->value);
microseconds = (RFLOAT(time)->value - seconds) * 1000000.0;
t.tv_sec = seconds;
@@ -122,26 +115,153 @@ time_timeval(time)
}
break;
+ case T_BIGNUM:
+ t.tv_sec = NUM2INT(time);
+ t.tv_usec = 0;
+ break;
+
default:
if (!obj_is_kind_of(time, cTime)) {
- Fail("Can't convert %s into Time", rb_class2name(CLASS_OF(time)));
+ TypeError("Can't convert %s into Time",
+ rb_class2name(CLASS_OF(time)));
}
GetTimeval(time, tobj);
t = tobj->tv;
break;
}
- return &t;
+ return t;
}
static VALUE
time_s_at(class, time)
VALUE class, time;
{
- struct timeval *tp;
+ struct timeval tv;
+
+ tv = time_timeval(time);
+ return time_new_internal(class, tv.tv_sec, tv.tv_usec);
+}
+
+static char *months [12] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec",
+};
+
+static void
+time_arg(argc, argv, args)
+ int argc;
+ VALUE *argv;
+ int *args;
+{
+ VALUE v[6];
+ int i;
+
+ rb_scan_args(argc, argv, "15", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
+
+ args[0] = NUM2INT(v[0]);
+ if (args[0] < 70) args[0] += 100;
+ if (args[0] > 1900) args[0] -= 1900;
+ if (v[1] == Qnil) {
+ args[1] = 0;
+ }
+ else if (TYPE(v[1]) == T_STRING) {
+ args[1] = -1;
+ for (i=0; i<12; i++) {
+ if (strcasecmp(months[i], RSTRING(v[1])->ptr) == 0) {
+ args[1] = i;
+ break;
+ }
+ }
+ }
+ else {
+ args[1] = NUM2INT(v[1]) - 1;
+ }
+ if (v[2] == Qnil) {
+ args[2] = 1;
+ }
+ else {
+ args[2] = NUM2INT(v[2]);
+ }
+ for (i=3;i<6;i++) {
+ if (v[i] == Qnil) {
+ args[i] = 0;
+ }
+ else {
+ args[i] = NUM2INT(v[i]);
+ }
+ }
+
+ /* value validation */
+ if ( args[0] < 70|| args[1] > 137
+ || args[1] < 0 || args[1] > 11
+ || args[2] < 1 || args[2] > 31
+ || args[3] < 0 || args[3] > 23
+ || args[4] < 0 || args[4] > 60
+ || args[5] < 0 || args[5] > 61)
+ ArgError("argument out of range");
+}
+
+static VALUE
+time_gm_or_local(argc, argv, gm_or_local)
+ int argc;
+ VALUE *argv;
+ int gm_or_local;
+{
+ int args[6];
+ struct timeval tv;
+ struct tm *tm;
+ time_t guess, t;
+ int diff;
+ struct tm *(*fn)();
+
+ fn = (gm_or_local) ? gmtime : localtime;
+ time_arg(argc, argv, args);
+
+ gettimeofday(&tv, 0);
+ guess = tv.tv_sec;
+
+ tm = (*fn)(&guess);
+ if (!tm) goto error;
+ t = args[0];
+ while (diff = t - tm->tm_year) {
+ guess += diff * 364 * 24 * 3600;
+ if (guess < 0) ArgError("too far future");
+ tm = (*fn)(&guess);
+ if (!tm) goto error;
+ }
+ t = args[1];
+ while (diff = t - tm->tm_mon) {
+ guess += diff * 27 * 24 * 3600;
+ tm = (*fn)(&guess);
+ if (!tm) goto error;
+ }
+ guess += (args[2] - tm->tm_mday) * 3600 * 24;
+ guess += (args[3] - tm->tm_hour) * 3600;
+ guess += (args[4] - tm->tm_min) * 60;
+ guess += args[5] - tm->tm_sec;
+
+ return time_new_internal(cTime, guess, 0);
- tp = time_timeval(time);
- return time_new_internal(class, tp->tv_sec, tp->tv_usec);
+ error:
+ ArgError("gmtime error");
+}
+static VALUE
+time_s_timegm(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ return time_gm_or_local(argc, argv, 1);
+}
+
+static VALUE
+time_s_timelocal(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ return time_gm_or_local(argc, argv, 0);
}
static VALUE
@@ -182,6 +302,25 @@ time_cmp(time1, time2)
int i;
GetTimeval(time1, tobj1);
+ switch (TYPE(time2)) {
+ case T_FIXNUM:
+ i = FIX2INT(time2);
+ if (tobj1->tv.tv_sec == i) return INT2FIX(0);
+ if (tobj1->tv.tv_sec > i) return INT2FIX(1);
+ return FIX2INT(-1);
+
+ case T_FLOAT:
+ {
+ double t;
+
+ if (tobj1->tv.tv_sec == (int)RFLOAT(time2)->value) return INT2FIX(0);
+ t = (double)tobj1->tv.tv_sec + (double)tobj1->tv.tv_usec*1e-6;
+ if (tobj1->tv.tv_sec == RFLOAT(time2)->value) return INT2FIX(0);
+ if (tobj1->tv.tv_sec > RFLOAT(time2)->value) return INT2FIX(1);
+ return FIX2INT(-1);
+ }
+ }
+
if (obj_is_instance_of(time2, cTime)) {
GetTimeval(time2, tobj2);
if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
@@ -271,6 +410,13 @@ static VALUE
time_coerce(time1, time2)
VALUE time1, time2;
{
+ if (TYPE(time2) == T_FLOAT) {
+ double d = RFLOAT(time2)->value;
+ unsigned int i = (unsigned int) d;
+
+ return time_new_internal(i, (int)(d - (double)i)*1e6);
+ }
+
return time_new(CLASS_OF(time1), NUM2INT(time2), 0);
}
@@ -282,7 +428,11 @@ time_plus(time1, time2)
int sec, usec;
GetTimeval(time1, tobj1);
- if (obj_is_instance_of(time2, cTime)) {
+ if (TYPE(time2) == T_FLOAT) {
+ sec = tobj1->tv.tv_sec + (unsigned int)RFLOAT(time2)->value;
+ usec = tobj1->tv.tv_usec + (RFLOAT(time2)->value - (double)sec)*1e6;
+ }
+ else 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;
@@ -290,10 +440,11 @@ time_plus(time1, time2)
else {
sec = tobj1->tv.tv_sec + NUM2INT(time2);
usec = tobj1->tv.tv_usec;
- if (usec >= 1000000) {
- sec++;
- usec -= 1000000;
- }
+ }
+
+ if (usec >= 1000000) { /* usec overflow */
+ sec++;
+ usec -= 1000000;
}
return time_new(sec, usec);
}
@@ -307,18 +458,28 @@ time_minus(time1, time2)
GetTimeval(time1, tobj1);
if (obj_is_instance_of(time2, cTime)) {
+ double f;
+
GetTimeval(time2, tobj2);
- sec = tobj1->tv.tv_sec - tobj2->tv.tv_sec;
- usec = tobj1->tv.tv_usec - tobj2->tv.tv_usec;
- if (usec < 0) {
- sec--;
- usec += 1000000;
- }
+ f = tobj1->tv.tv_sec - tobj2->tv.tv_sec;
+
+ f += (tobj1->tv.tv_usec - tobj2->tv.tv_usec)*1e-6;
+
+ return float_new(f);
+ }
+ else if (TYPE(time2) == T_FLOAT) {
+ sec = tobj1->tv.tv_sec - (int)RFLOAT(time2)->value;
+ usec = tobj1->tv.tv_usec - (RFLOAT(time2)->value - (double)sec)*1e6;
}
else {
sec = tobj1->tv.tv_sec - NUM2INT(time2);
usec = tobj1->tv.tv_usec;
}
+
+ if (usec < 0) { /* usec underflow */
+ sec--;
+ usec += 1000000;
+ }
return time_new(sec, usec);
}
@@ -522,13 +683,12 @@ time_s_times(obj)
#endif /* HZ */
struct tms buf;
- if (times(&buf) == -1) rb_sys_fail(Qnil);
+ if (times(&buf) == -1) rb_sys_fail(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);
+ float_new((double)buf.tms_cstime / HZ));
#else
#ifdef NT
FILETIME create, exit, kernel, user;
@@ -537,14 +697,12 @@ time_s_times(obj)
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)(kernel.dwHighDateTime*2e32+kernel.dwLowDateTime)/2e6),
+ float_new((double)(user.dwHighDateTime*2e32+user.dwLowDateTime)/2e6),
float_new((double)0),
- Qnil);
+ float_new((double)0));
#else
- Fail("can't call times");
- return Qnil;
+ rb_notimplement();
#endif
#endif
}
@@ -558,6 +716,9 @@ Init_Time()
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, "gm", time_s_timegm, -1);
+ rb_define_singleton_method(cTime, "local", time_s_timelocal, -1);
+ rb_define_singleton_method(cTime, "mktime", time_s_timelocal, -1);
rb_define_singleton_method(cTime, "times", time_s_times, 0);
@@ -596,6 +757,6 @@ Init_Time()
rb_define_method(cTime, "strftime", time_strftime, 1);
#if defined(HAVE_TIMES) || defined(NT)
- S_Tms = struct_define("Tms", "utime", "stime", "cutime", "cstime", Qnil);
+ S_Tms = struct_define("Tms", "utime", "stime", "cutime", "cstime", 0);
#endif
}
diff --git a/top.sed b/top.sed
new file mode 100644
index 0000000000..371190875a
--- /dev/null
+++ b/top.sed
@@ -0,0 +1,37 @@
+s/@srcdir@/./
+s/@CC@/gcc/
+s/@CPP@/gcc -E/
+s/@CPPFLAGS@//
+s/@YACC@/bison -y/
+s/@INSTALL@/ginstall -c/
+s/@INSTALL_PROGRAM@/${INSTALL}/
+s/@INSTALL_DATA@/${INSTALL} -m 644/
+s/@SET_MAKE@//
+s/@CFLAGS@/-g -O -I./
+s/@STATIC@//
+s/@LDFLAGS@//
+s/@LIBS@//
+s/@LIBOBJS@/crypt.o/
+s/@ALLOCA@//
+s!@prefix@!/usr/local!
+s/@exec_prefix@/${prefix}/
+s/@STRIP@/strip/
+s!/bin/rm!rm!
+s/@LDEXT@/so/
+s/@CCDLFLAGS@/-fpic/
+s!@arclib@!/usr/local/lib/ruby/i386-msdos!
+/\/dev\/null/s,/dev/null 2>&1, nul,
+/if older/s/"ruby"/"ruby.exe"/g
+/`rm -f ruby`/s//`rm -f ruby.exe`/
+/`cp miniruby ruby`/s//`cp miniruby.exe ruby.exe`/
+/^extruby:/ {
+ n;N;N;N;c\
+ cd ext\
+ ../miniruby ./extmk.rb\
+ cd ..
+}
+/^clean:;/ {
+ n;n;s!cd.*!cd ext\
+ ../miniruby ./extmk.rb clean\
+ cd ..!
+}
diff --git a/util.c b/util.c
index 19a0416794..717e0beed9 100644
--- a/util.c
+++ b/util.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Mar 10 17:22:34 JST 1995
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
diff --git a/util.h b/util.h
index 91805d99c5..570d894ccb 100644
--- a/util.h
+++ b/util.h
@@ -6,7 +6,7 @@
$Date$
created at: Thu Mar 9 11:55:53 JST 1995
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
#ifndef UTIL_H
diff --git a/variable.c b/variable.c
index 9721c1c2d4..1d6449bcab 100644
--- a/variable.c
+++ b/variable.c
@@ -10,20 +10,21 @@
#include "ruby.h"
#include "env.h"
+#include "node.h"
#include "st.h"
-st_table *rb_global_tbl;
+static st_table *rb_global_tbl;
st_table *rb_class_tbl;
#define global_tbl rb_global_tbl
#define class_tbl rb_class_tbl
-VALUE rb_const_defined();
+int rb_const_defined();
VALUE rb_const_get();
st_table *
new_idhash()
{
- return st_init_table(ST_NUMCMP, ST_NUMHASH);
+ return st_init_numtable();
}
void
@@ -39,10 +40,13 @@ rb_class2path(class)
{
VALUE path;
- while (TYPE(class) == T_ICLASS) {
+ while (TYPE(class) == T_ICLASS || FL_TEST(class, FL_SINGLETON)) {
class = (VALUE)RCLASS(class)->super;
}
path = rb_ivar_get(class, rb_intern("__classpath__"));
+ if (NIL_P(path)) {
+ return rb_class2name(class);
+ }
if (TYPE(path) != T_STRING) Bug("class path does not set properly");
return RSTRING(path)->ptr;
}
@@ -55,7 +59,6 @@ rb_class_path(class)
if (strchr(name, ':')) {
VALUE ary = str_split(str_new2(name), ":");
- ary_pop(ary);
ary = ary_reverse(ary);
return ary_join(ary, str_new2("::"));
}
@@ -70,6 +73,7 @@ rb_set_class_path(class, under, name)
VALUE str;
char *s;
+ if (cObject == under) return;
str = str_new2(name);
if (under) {
str_cat(str, ":", 1);
@@ -93,8 +97,9 @@ rb_path2class(path)
*p++;
}
if (*p == '\0') { /* pre-defined class */
- if (!st_lookup(class_tbl, rb_intern(path), &class)) {
- Fail("Undefined class -- %s", path);
+ if (!st_lookup(RCLASS(cObject)->iv_tbl, rb_intern(path), &class)
+ && !st_lookup(class_tbl, rb_intern(path), &class)) {
+ NameError("Undefined class -- %s", path);
}
return class;
}
@@ -107,14 +112,14 @@ rb_path2class(path)
*s = '\0';
id = rb_intern(name);
if (!rb_const_defined(class, id))
- Fail("%s not defined", name);
+ Fatal("%s not defined", name);
class = rb_const_get(class, id);
switch (TYPE(class)) {
case T_CLASS:
case T_MODULE:
break;
default:
- Fail("%s not a module/class");
+ Fatal("0x%x is not a class/module", class);
}
return class;
}
@@ -124,7 +129,7 @@ rb_name_class(class, id)
VALUE class;
ID id;
{
- rb_ivar_set(class, rb_intern("__classname__"), INT2FIX(id));
+ rb_ivar_set(class, rb_intern("__classname__"), id);
}
static st_table *autoload_tbl = 0;
@@ -173,16 +178,15 @@ rb_class2name(class)
case T_MODULE:
break;
default:
- Fail("0x%x is not a class/module", class);
+ Fatal("0x%x is not a class/module", class);
}
- while (FL_TEST(class, FL_SINGLE) || TYPE(class) == T_ICLASS) {
+ while (FL_TEST(class, FL_SINGLETON) || TYPE(class) == T_ICLASS) {
class = (struct RClass*)class->super;
}
name = rb_ivar_get(class, rb_intern("__classname__"));
- if (name) {
- name = FIX2INT(name);
+ if (!NIL_P(name)) {
return rb_id2name((ID)name);
}
Bug("class 0x%x not named", class);
@@ -194,6 +198,8 @@ struct trace_var {
struct trace_var *next;
};
+VALUE f_untrace_var();
+
struct global_entry {
ID id;
void *data;
@@ -241,7 +247,7 @@ static VALUE
undef_getter(id)
ID id;
{
- Warning("global var %s not initialized", rb_id2name(id));
+ Warning("global variable `%s' not initialized", rb_id2name(id));
return Qnil;
}
@@ -315,12 +321,12 @@ var_marker(var)
}
static void
-readonly_setter(id, var, val)
+readonly_setter(val, id, var)
ID id;
void *var;
VALUE val;
{
- Fail("Can't set variable %s", rb_id2name(id));
+ NameError("Can't set variable %s", rb_id2name(id));
}
static int
@@ -405,8 +411,6 @@ rb_define_virtual_variable(name, getter, setter)
rb_define_hooked_variable(name, 0, getter, setter);
}
-void rb_trace_eval();
-
void
rb_trace_eval(cmd, val)
VALUE cmd, val;
@@ -427,9 +431,12 @@ f_trace_var(argc, argv)
if (rb_scan_args(argc, argv, "11", &var, &cmd) == 1) {
cmd = f_lambda();
}
+ if (NIL_P(cmd)) {
+ return f_untrace_var(argc, argv, Qnil);
+ }
id = rb_to_id(var);
if (!st_lookup(global_tbl, id, &entry)) {
- Fail("undefined global variable %s", rb_id2name(id));
+ NameError("undefined global variable %s", rb_id2name(id));
}
trace = ALLOC(struct trace_var);
trace->next = entry->trace;
@@ -441,29 +448,52 @@ f_trace_var(argc, argv)
}
VALUE
-f_untrace_var(obj, var)
- VALUE obj, var;
+f_untrace_var(argc, argv)
+ int argc;
+ VALUE *argv;
{
+ VALUE var, cmd;
ID id;
struct global_entry *entry;
struct trace_var *trace;
- VALUE ary;
+ rb_scan_args(argc, argv, "11", &var, &cmd);
id = rb_to_id(var);
if (!st_lookup(global_tbl, id, &entry)) {
- Fail("undefined global variable %s", rb_id2name(id));
+ NameError("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;
+ if (NIL_P(cmd)) {
+ VALUE 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;
+ return ary;
+ }
+ else {
+ struct trace_var t;
+ struct trace_var *next;
+
+ t.next = entry->trace;
+ trace = &t;
+ while (trace->next) {
+ next = trace->next;
+ if (next->data == (void*)cmd) {
+ trace->next = next->next;
+ free(next);
+ entry->trace = t.next;
+ return ary_new3(1, cmd);
+ }
+ trace = next;
+ }
+ }
+ return Qnil;
}
VALUE
@@ -549,8 +579,8 @@ rb_ivar_get(obj, id)
return val;
return Qnil;
default:
- Fail("class %s can not have instance variables",
- rb_class2name(CLASS_OF(obj)));
+ Fatal("class %s can not have instance variables",
+ rb_class2name(CLASS_OF(obj)));
break;
}
Warning("instance var %s not initialized", rb_id2name(id));
@@ -571,8 +601,8 @@ rb_ivar_set(obj, id, val)
st_insert(obj->iv_tbl, id, val);
break;
default:
- Fail("class %s can not have instance variables",
- rb_class2name(CLASS_OF(obj)));
+ Fatal("class %s can not have instance variables",
+ rb_class2name(CLASS_OF(obj)));
break;
}
return val;
@@ -595,6 +625,22 @@ rb_ivar_defined(obj, id)
}
VALUE
+rb_const_get_at(class, id)
+ struct RClass *class;
+ ID id;
+{
+ VALUE value;
+
+ if (class->iv_tbl && st_lookup(class->iv_tbl, id, &value)) {
+ return value;
+ }
+ NameError("Uninitialized constant %s::%s",
+ RSTRING(rb_class_path(class))->ptr,
+ rb_id2name(id));
+ /* not reached */
+}
+
+VALUE
rb_const_get(class, id)
struct RClass *class;
ID id;
@@ -624,21 +670,32 @@ rb_const_get(class, id)
st_delete(autoload_tbl, &id, &modname);
module = str_new2(modname);
free(modname);
- f_require(Qnil, module);
+ f_require(0, 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));
+ NameError("Uninitialized constant %s::%s",
+ RSTRING(rb_class_path(class))->ptr,
+ rb_id2name(id));
else
- Fail("Uninitialized constant %s",rb_id2name(id));
+ NameError("Uninitialized constant %s",rb_id2name(id));
/* not reached */
}
-VALUE
+int
+rb_const_defined_at(class, id)
+ struct RClass *class;
+ ID id;
+{
+ if (class->iv_tbl && st_lookup(class->iv_tbl, id, 0)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
rb_const_defined(class, id)
struct RClass *class;
ID id;
@@ -656,16 +713,31 @@ rb_const_defined(class, id)
return FALSE;
}
+int
+rb_autoload_defined(id)
+ ID id;
+{
+ if (autoload_tbl && st_lookup(autoload_tbl, id, 0))
+ return TRUE;
+ return FALSE;
+}
+
void
rb_const_set(class, id, val)
struct RClass *class;
ID id;
VALUE val;
{
- if (rb_const_defined(class, id))
- Fail("already initialized constnant %s", rb_id2name(id));
+ if (!class->iv_tbl) {
+ class->iv_tbl = new_idhash();
+ }
+ else if (st_lookup(class->iv_tbl, id, 0)) {
+ NameError("already initialized constnant %s", rb_id2name(id));
+ }
+ if (!rb_autoload_defined(id) && rb_const_defined(class, id)) {
+ Warning("already initialized constnant %s", rb_id2name(id));
+ }
- if (!class->iv_tbl) class->iv_tbl = new_idhash();
st_insert(class->iv_tbl, id, val);
}
@@ -678,6 +750,16 @@ rb_define_const(class, name, val)
rb_const_set(class, rb_intern(name), val);
}
+extern VALUE cKernel;
+
+void
+rb_define_global_const(name, val)
+ char *name;
+ VALUE val;
+{
+ rb_define_const(cKernel, name, val);
+}
+
VALUE
rb_iv_get(obj, name)
VALUE obj;
@@ -698,33 +780,3 @@ rb_iv_set(obj, name, 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;
-}
diff --git a/version.c b/version.c
index dba5f6c8b5..26ef8d054f 100644
--- a/version.c
+++ b/version.c
@@ -7,7 +7,7 @@
$Date: 1995/01/12 08:54:54 $
created at: Thu Sep 30 20:08:01 JST 1993
- Copyright (C) 1993-1995 Yukihiro Matsumoto
+ Copyright (C) 1993-1996 Yukihiro Matsumoto
************************************************/
@@ -20,18 +20,18 @@ extern VALUE cKernel;
void
Init_version()
{
- rb_define_const(cKernel, "VERSION", str_new2(RUBY_VERSION));
+ rb_define_global_const("VERSION", str_new2(RUBY_VERSION));
}
void
show_version()
{
- fprintf(stderr, "ruby - version %s (%s)\n", RUBY_VERSION, VERSION_DATE);
+ fprintf(stderr, "ruby - version %s\n", RUBY_VERSION, VERSION_DATE);
}
void
show_copyright()
{
- fprintf(stderr, "ruby - Copyright (C) 1993-1995 Yukihiro Matsumoto\n");
+ fprintf(stderr, "ruby - Copyright (C) 1993-1996 Yukihiro Matsumoto\n");
exit(0);
}
diff --git a/version.h b/version.h
index f019bbdae3..3ed36e70e2 100644
--- a/version.h
+++ b/version.h
@@ -1,2 +1,2 @@
-#define RUBY_VERSION "0.95"
-#define VERSION_DATE "95/12/21"
+#define RUBY_VERSION "0.99.4-961224"
+#define VERSION_DATE "96/12/24"