summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYukihiro Matsumoto <matz@ruby-lang.org>1994-07-18 10:19:15 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2019-08-17 22:09:29 +0900
commit200e0ee2fd3c1c006c528874a88f684447215524 (patch)
treedf2252585f3000878a15123d98ccb08124306b90
version 0.49v0_49
https://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.49.tar.gz Mon Jul 18 10:19:15 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: 多重代入を処理するルールにバグがあって, 3要素以上の多重 代入に失敗していた. * eval.c(rb_eval): 多重代入で, 右辺が配列でない時には`to_a'メソッ ドで配列に変換して代入するようにした. 今までの仕様だと右辺値が第 1要素にそのまま代入されていたが, structなど配列に変換できるもの は変換した方が嬉しい気がする. * dbm.c,dict.c(delete_if): メソッド追加. * process.c(wait,waitpid): システムコールwaitpidまたはwait4がある 時はそちらを使うように. configureもそれらをチェックするように変更. * dbm.c, dict.c(clear): メソッド追加. Fri Jul 15 10:54:45 1994 Yukihiro Matsumoto (matz@ix-02) * array.c(Fary_fill,Fary_clear): メソッドを追加. * string.c(Fstr_split): $;の値が長さ1の文字列である時, これを正規 表現化しないで, 単なる文字として分割する. * string.c(Fstr_aset/Fstr_aref): インデックスが文字列の範囲外だっ た時の動作をArrayを参考に修正した. * array.c(astore,Fary_aset): 領域をreallocした後, ゼロでクリアする ように. 今まで配列にゴミが入っていた. * array.c: []/[]=でのインデックス関係を整理. 基本的に負のインデッ クスに代入しない限り例外は起きないように変更した. 必要に応じて適 当に解釈して, 必要ならば領域を拡張するように. Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02) * autoexec.c: 削除. autoload関係の機能は今後検討しよう. * dict.c: 辞書クラスの正式名称をDictに変更した. 別名としてHashを用 意した. 今までDictionaryなどと長い名前にしていたが誰も使っていな かったしね. *BACKWARD INCOMPATIBILITY* * parse.y: Dictを生成する構文を追加. こちらを{..}にした. * parse.y: 配列を生成する構文を[..]に変更した. 過去のRubyスクリプ トとの互換性が保てないが, Dictを生成する構文を導入するに当たり, perl5に合わせて(意識して), 変更する時期は今しかないと考えた. *BACKWARD INCOMPATIBILITY* * eval.c(Feval): eval()でメソッドを定義する時, 定義されるクラスを メソッドの所属するクラスにした. 今まではObjectクラスに定義されて いた. * parse.y: ローカル引数がない時のeval()で落ちていた. Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c: メソッドが存在しない時にはKernel:_undefined(id)が呼ばれ るように. しかし, rubyでは個々のメソッド毎の処理よりも, クラス 単位の処理が必要な気もするなあ. * autoexec.c: 削除. autoload関係の機能は今後検討しよう. * dict.c: 辞書クラスの正式名称をDictに変更した. 別名としてHashを用 意した. 今までDictionaryなどと長い名前にしていたが誰も使っていな かったしね. *BACKWARD INCOMPATIBILITY* * parse.y: Dictを生成する構文を追加. こちらを{..}にした. * parse.y: 配列を生成する構文を[..]に変更した. 過去のRubyスクリプ トとの互換性が保てないが, Dictを生成する構文を導入するに当たり, perl5に合わせて(意識して), 変更する時期は今しかないと考えた. *BACKWARD INCOMPATIBILITY* * eval.c(Feval): eval()でメソッドを定義する時, 定義されるクラスを メソッドの所属するクラスにした. 今まではObjectクラスに定義されて いた. * parse.y: ローカル引数がない時のeval()で落ちていた. Tue Jul 12 09:41:28 1994 Yukihiro Matsumoto (matz@ix-02) * pack.c: uuencode形式のサポート. * `-0'を`-R'に. 出力レコードセパレータをコマンドラインから指定する 方法はなくなった. どうも, 仕様がゆれるなあ. Mon Jul 11 09:51:24 1994 Yukihiro Matsumoto (matz@ix-02) * ruby.c: `-r'オプションは`-0'オプションになった. 当面は`-r'も有効 だが変更される可能性がある. `-R'は当面はそのまま. * version.c: version表示に日付を含めた. * parse.y: private methodの復活. RubyのprivateメソッドはC++におけ るprotected methodに該当するもので, `@'で始まる名前を持つ. * env.h: struct ENVIRONの定義を分離. * parse.y: `\$var', `\@var', `%var'も許すように. * variable.c(Fdefined): idも引数として受け付けるように. * parse.y: if文/unless文にキーワードthenを追加. thenなしというのは, 意外と間違いが多いので. もちろん省略できる. Sat Jul 9 02:16:04 1994 Yukihiro Matsumoto (matz@dyna) * eval.c(rb_eval): class/moduleの評価で新しいスコープを割り当てて いなかった. スコープ割り当て部分をrb_call()からrb_eval()に移した. * eval.c(rb_call): realloc()に渡される事のある, ローカル変数用の領 域をalloca()していた. たまに落ちるわけだ. * string.c(Fstr_times): 割り当てた領域を越えた部分を変更していた. Wed Jul 6 15:52:42 1994 Yukihiro Matsumoto (matz@ix-02) * socket.c: Socket -> BasicSocket, RawSocket -> Socket に改名. * string.c(ucfirst,lcfirst): 最初の1文字だけの大文字/小文字変換. * numeric.c(chr): 整数の文字列化メソッド. * inits.c, dbm.c: DBMが使えない時はクラスそのものを定義しないよう にした. 利用できないクラスはnilとすることを今後のポリシーとしよ う(いままではアクセスした時点でエラーが発生していた). autoexec() のあり方も検討が必要になりそうだ. * bignum.c(bigadd): バグ修正. Thu Jul 7 11:12:18 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c(Fload,Feval): eval_treeをクリアし忘れていた. * _inspect: オブジェクトを可読形式の文字列に変換する(主にデバッグ 出力用). Wed Jul 6 00:57:18 1994 Yukihiro Matsumoto (matz@dyna) * numeric.c, bignum.c: 整数に対する`[]'演算子. nビット目がセットさ れているかどうかを返す. Tue Jul 5 12:48:39 1994 Yukihiro Matsumoto (matz@ix-02) * io.c(Feof): 追加. コマンドラインからなる仮想ファイルについても EOFが検出できるように. * ruby.c: -l/-r/-R/-Xオプションの追加. * ruby.c: -n/-pオプションのloopの付加などをメインルーチンに移動し た. これで, オプションの解析途中で(`-c'オプションのせいで)終了な どといったことはない. * io.c(Fgets): 高速化. 凝ったことをしない方が速かった. 虚しい. Mon Jul 4 15:55:48 1994 Yukihiro Matsumoto (matz@ix-02) * Socket:getsockname/getpeername - ようやく仕様が安定した. * io.c(Fgets): eachでgetsを記述するのではなく, getsでeachを記述す るようにした. Fri Jul 1 10:35:49 1994 Yukihiro Matsumoto (matz@ix-02) * $ENV[env] = nil/$ENV.delete(env)で環境変数を削除できるようになっ た. $ENV.deleteは環境変数の以前の値を返す. * !~の定義が間違っていた. * Dict,DBM:[]= - nilの代入によって要素を削除できるようになった. こ れにともないnilはDictの要素になれなくなった. * ソースの整理. 盲腸のような使われていないコードをなくしたり, 変数 名を付け変えたりした. Fri Jul 1 00:21:29 1994 Yukihiro Matsumoto (matz@dyna) * Array:join() - 要素数0の配列に対して空文字列を返す. * RawSocket:open(),socketpair() - 文字列で指定できるドメインとタイ プをいくつか追加した. Thu Jun 30 13:51:29 1994 Yukihiro Matsumoto (matz@ix-02) * -fオプションをなくした. 昔(loadのなかった頃)の名残なので, 今となっ ては必要ないだろう. * -sオプションを追加. perlの-sオプションと同じ動きをする. * RawSocketクラスを提供する. Socketに対するシステムコールレベルの アクセスが可能になった. Thu Jun 30 00:27:19 1994 Yukihiro Matsumoto (matz@dyna) * Socket - bug fixes. * linuxではsyscall(SYS_select)が正常に動作しない. * Socket:addr,peeraddr - 配列としてsockaddrの情報を返す. Wed Jun 29 00:14:20 1994 Yukihiro Matsumoto (matz@dyna) * Socket:setopt,getopt - setsockopt(2), getsockopt(2)へのアクセス を実現. * sprintf() - rubyにはunsignedは無いので, %uを取り除いた. * sprintf() - %b, %x, %oでは2の補数表現, %B, %X, %Oでは符号付き表 現で出力するように. ここ数日でsprintf()の仕様がゆらいでいたが, これで落ち着きそうだ. Tue Jun 28 14:42:03 1994 Yukihiro Matsumoto (matz@ix-02) * Bignum:<<,>> - 2の補数をとる処理を除いた. シフト演算には関係ない 処理だった. * Bignum:^ - bug fix. 符合が反対だった. * sprintf() - 2進出力子"%b"を追加. * sprintf() - %x, %oでFixnumを出力する時, 2の補数表示を行なわない. * sprintf() - %x, %oはやはり負の数の時は`-'を出力するように. Mon Jun 27 14:56:13 1994 Yukihiro Matsumoto (matz@ix-02) * sprintf() - Bignumについても%d, %oは(2の補数表現に変換して)正の 整数を表示するようにした. * Bignumに対する論理演算の定義を修正した. 負の数は2の補数表現であ るとみなし, かつ仮想的に左側に無限に1が連続しているような演算結 果を得る. * Fixnum:<<,>> - 符合付シフトに変更. * Bignum:>> - 負の整数のシフトに対応した. * __END__, ^D, ^Zでスクリプトを終了できる. * -xオプションを追加. #! ..rubyなる行まで読み飛ばす. * -cオプションを追加. コンパイルのみを行う. Sat Jun 25 01:37:21 1994 Yukihiro Matsumoto (matz@dyna) * Fixnum:<< - 必要に応じてBignumに拡張して左シフトするように. よっ て, シフト幅が32を越えるとCやPerlとは違った値を返す. Fri Jun 24 10:01:28 1994 Yukihiro Matsumoto (matz@ix-02) * ioctl()/fcntl() - システムコールを呼び出す前にバッファの大きさを 調節するようにした. * String:toupper/tolower - 文字列を置き換えたコピーを作るのではな く, 元の文字列の内容を変更するようにした. * inplace editを実現した. perlと同じように`-i'オプションで指定する. もっとも, こちらはMS-DOSのこととか考えてないけど. * デフォルトの出力先を追加した. 今までは$stdoutに代入するしか方法 はなかった. Fri Jun 17 10:55:08 1994 Yukihiro Matsumoto (matz@ix-02) * 環境変数にアクセスする方法としてgetenv()/setenv()以外に$ENVを用 意した. $ENVは文字列-文字列の辞書であるかのように動作するEnvDict オブジェクトが代入されている(eachはassocを与える). * nilに代入するとcore dumpした. コンパイル時のチェックを強化. * Struct: struct_new()の引数をGCプロテクトする必要がある. せめてス タック領域だけでもスキャンできるようにしなければいけないんだろう か? でも, 移植性がなあ. Fri Jun 17 01:01:46 1994 Yukihiro Matsumoto (matz@dyna) * Time::asctime() - 日付のフォーマットで日が落ちていた. * Stat: StatはEtcなどと同様にStructで実現したので, Statクラスは無 くなった. Thu Jun 16 10:32:23 1994 Yukihiro Matsumoto (matz@ix-02) * bignum.c: いくつかのバグを修正した. Fixnumを渡すべきところで普通 のintを渡していた. 失敗. * big2str() - 1桁ずつbaseで割る代わりに, 4桁ずつ割算を行なうように した. これで多倍長割算の回数が1/4になる. さらに整数->数(文字)へ の変換をテーブルを用いるようにした. * rb_ivar_get_1() - すでに何らかのインスタンス変数を持つオブジェク トでは, 未定義のインスタンス変数の値が不定値になっていた. * yylex() - インスタンス変数の認識に失敗していた. attr()は正しく動 作していたので, 混用すると動作しなかった. 全部違っていたから動い ていたのね. * Object:attr() - すでにアクセスメソッドが定義されている時にはデフォ ルトのアクセスメソッドを定義しないようにした. もっともアクセスメ ソッドと同名のメソッドの区別はRubyには存在しないけど, それは仕方 がないよね. * pack.c: エンディアンをautoconfで判定するようにしたので, v/Vが使 えるようになった. またntoh?()/hton?()も自前で用意した. * Stat: st_rdevをアクセスするメソッドを追加. さらにシステムがstat 構造体にst_blksize, st_blockを持っているかをautoconfでチェックす るようにした. * ドキュメントを少し整備した. * INT2FIX()のうち, 31bit幅が保証できないものは, int2inum()に置き換 えた. Wed Jun 15 10:18:27 1994 Yukihiro Matsumoto (matz@ix-02) * sprintf() - bignumの出力の時, 出力幅を正しく計算するようにした. * str2inum() - baseが0の時, baseを自動判定するように(0xで始まる時 16 進, 0で始まる時8進). Tue Jun 14 16:08:42 1994 Yukihiro Matsumoto (matz@ix-02) * gc.c: Bignumを追加するのを忘れていた. 組み込み型を追加した時には 必ずmark()とsweep()にその型に関する処理を追加する必要がある. * bignum: 割算も動いたような気がする. アルゴリズムを理解していない ので, 自信がない. Mon Jun 13 14:36:55 1994 Yukihiro Matsumoto (matz@ix-02) * まだサポートしていないメソッドなどがあるが, 曲がりなりにもBignum が使えるようになる. これでioctlも使える. Fri Jun 10 17:26:42 1994 Yukihiro Matsumoto (matz@ix-02) * Comparable: 基礎となるメソッドを`=='と`>'から`<=>'に変更した. 今 後Comparableのサブクラスは`<=>'だけを定義する必要がある. Wed Jun 8 13:12:18 1994 Yukihiro Matsumoto (matz@ix-02) * Need_Fixnum()をほとんどなくして, NUM2INT()で直接intに変換するこ とにした. これで31bitに丸めて桁落ちをおこす問題がなくなる. Tue Jun 7 09:45:31 1994 Yukihiro Matsumoto (matz@ix-02) * ruby.h: マクロFIXABLE(n)を追加. ついでにFIXNUM周りの定義を変更し て, 移植性を高めた(つもり). * C++の予約語であるnewを削除した. しかし, もうひとつの予約語である classに関しては, 置き換える単語が思いつかないこともあってそのま まになっている. * 31bitを越えそうなINT2FIX()を関数呼び出しに変えた. 将来bignumが導 入された時には自動的にbignumを返すようにする. * readline() - 引数の`-'は標準入力を意味するようになった. * ruby.h: 右シフトが論理シフトか算術シフトかは処理系依存のようなの で, ruby.hでcppを使ってチェックするようにした. これでうまくいく と思うのだが, 手元に符合付intを論理シフトする処理系がないので確 認できない. NEWS-OSのCCは確か右シフトはいつも論理シフトだったよ うな気がするんだけど…. Mon Jun 6 10:10:22 1994 Yukihiro Matsumoto (matz@ix-02) * FIX2INT()の定義を変更した. どうして昔はうまく動かなかったんだろ うか? もしかして, 右シフトの符号拡張は処理系依存? * FIX2INT()とFIX2UINT()を使い分けるようにした. もっともfixnumは31 ビットしかないので, 本質的な解決にはならないのだが(ioctlが組み込 みたかった). * printを関数的メソッドから通常メソッドに変更. 引数が与えられない 時にはレシーバをプリントするようにした. これでprintをメッセージ 形式でも実行できるようになった. 例: ruby -e 'readlines().sort.print' 上のスクリプトは, 引数として与えられた(あるいは標準入力から読み 込まれた)文字列を各行毎にソートして表示する. * eval.c: argc,argvパターンで引数を受けるメソッドに引数が一つも与 えられない時, argvがnilになっていた(argv[0]にアクセスすると落ち てしまう). * _exit()を追加. こちらは例外処理など行なわない. * dbmクラス: クラス名称をDBM(大文字)に統一した. Sat Jun 4 00:51:04 1994 Yukihiro Matsumoto (matz@dyna) * ループ変数にも属性や配列要素を指定できるようにした. Fri Jun 3 09:49:48 1994 Yukihiro Matsumoto (matz@ix-02) * 多重代入において, 属性代入, 配列要素への代入も行なえるようにした. * Need_Fixnum(): nilを0に変換するように. * Enumerable:min, max, index, includes - 追加. min, maxは要素が `<=>'メソッドを持つことを仮定している. * Dict/Dbm:length - 要素数を返すメソッド. * Dbmクラスにto_aメソッドを追加. * Sunにおけるsortの誤動作の件, 昨日の修正でfixされた. しかし, それ でなぜ動かなかったのかは明らかではないが…. 比較関数がどんな値を 返しても指定した領域外をアクセスするのはバグではないか. * ファイルの全内容を読んで, 各行を配列として返すメソッドはpythonを 参考にして`readlines'という名前にした. それにともないgetsに対し てreadlineという別名を用意した. Fri Jun 3 00:08:38 1994 Yukihiro Matsumoto (matz@dyna) * Array:sort - 判別関数の戻り値はFixnumではなく, Intであるべきだっ た. 間違い. Sunで動作がおかしかったのはこのせいかも知れない. Thu Jun 2 11:48:37 1994 Yukihiro Matsumoto (matz@ix-02) * IO:read_all() - ストリームの最後まで入力して, 各行を要素とする配 列を返すメソッドを追加. また関数メソッド read_all()も追加した. これは引数のファイルから読み込んで各行を要素とする配列を返す. 意 味的には def read_all() ary = {} while gets() ary.push($_) end end とほぼ等価である. * String:atoiメソッドを削除. to_aメソッドからaが配列であるとの連想 を呼んで, 混乱を招かないため. 代わりにto_iメソッドを使うこと. * 配列への変換メソッドto_aを導入した. 通常のオブジェクトは自分自身 を唯一の要素とする長さ1の配列を返す. 配列は自分自身を, 辞書はキー と値のペアの配列を返す. Enumeratedをincludeしたクラスは, eachが 返す各要素を含む配列を返す. * file.c: 不定個の引数を受けとるメソッド(chmod,chown,utimes)を書き 換えて, 整理した. それに伴い, 最初に全ての引数の型チェックを行な うようにした. 型チェックに失敗すると処理を行なわずに例外を発生さ せる. * configure.in: 不必要なテストを行なわないように修正した. Tue May 31 10:41:08 1994 Yukihiro Matsumoto (matz@ix-02) * String:pack(): 2進数の文字列変換(B,b)で0と1が逆だった. * Math.c: 実数系のメソッドに引数として整数が渡された時に自動的に変 換するようにした. * toupper(), tolower(): 文字列の判定ミスで変換されていなかった. * getopt_long()の仕様によって, スクリプトへの引数がインタプリタの 引数だと解釈されていた. 引数パターン文字列の先頭に`+'を追加. * config.hを削除した. DEFINEはMakefileで与えられる. * sprintf(): "%d"に文字列が与えられた時にはアドレスではなく内容を 整数に変換するようにした. ついでに浮動小数点数も変換するように変 更した. * regexp.c: rubyの拡張正規表現(\d, \D, \s, \S)の処理で割り当てた領 域を越えてバッファに書き込んでいた. 処理前にバッファをきちんと拡 張するようにした. これで昨日問題にしていたメモリの問題は解決でき たと思う. * yylex(): ダブルクォート文字列中でダブルクォートを表現するため のバックスラッシュ表現ができなかった. Mon May 30 10:07:42 1994 Yukihiro Matsumoto (matz@ix-02) * 演算子`!'の右辺も条件式であるとした. これによって, この演算子を 再定義する人は混乱するかも知れないが, 大多数のこの演算子を使う人 は混乱を避けることができると思う. * autoconfを使って, 自動的にMakefile, config.hを生成するようにした. これで, 大抵のマシンでは`configure'を実行した後, `make'一発でコ ンパイルできると思う. * clone: サブクラスに対して用いられた場合, 元のオブジェクトと同じ クラスのインスタンスを返すように(以前はビルトインクラスの場合を 考えてなかった). * ビルトインクラスのサブクラスも作れるように, リテラルのあるクラス にもnewメソッドを追加した. * malloc()で落ちる. purifyが必要かも知れない. * re.c: rb_global_variable()の呼びだし形式の間違い. 変数へのポイン タを渡さなければいけない. * parse.y: ローカル変数の扱いに引数の評価順に依存する移植性のない 部分があった. * attr(): 属性設定のバグを直した. いつ内部仕様が変わったんだろう…? Sat May 28 23:08:18 1994 Yukihiro Matsumoto (matz@dyna) * 正規表現キャッシュの文字列一致判定をポインタ一致から内容一致に変 更した. そういえば文字列リテラルは一回毎に新しくオブジェクトが生 成されるのだった. Fri May 27 11:42:00 1994 Yukihiro Matsumoto (matz@ix-02) * 正規表現の文字コードのデフォルトを漢字非対応にした. これによって 若干の高速化が図れる. * trから文字削除(delete), 文字圧縮(squeeze)を分離した. それにとも ないtrのオプション引数はなくなった. Thu May 26 10:32:55 1994 Yukihiro Matsumoto (matz@ix-02) * スクリプト読み込みルーチンを書き直して, 通常ファイル以外のファイ ル名や空文字列がスクリプトとして与えられた場合に対応した. また, 標準入力からスクリプトを読み込む時に, 一時ファイルが/tmpに残らな いようにした. * Fixnum:id2name - IDから文字列に戻す関数. String:internの逆. * Array: 配列の範囲外の要素をアクセスした時に例外を発生させずに, nilを返すようにした. 配列は自動的に拡張される. * string:stripを追加. * -nオプションが-eオプションを複数指定した時も動作するように. * parse.yで<sys/types.h>もインクルードするようにした. * fname周りの細かいbugを修正. Wed May 26 11:45:10 1994 Yukihiro Matsumoto (matz@dyna) * 定数をキャッシュするようにした. 繰り返しが多い場合には有効のはず だが, 一度しかアクセスしない場合は遅くなるなあ. Wed May 25 00:42:24 1994 Yukihiro Matsumoto (matz@dyna) * 多重代入文(foo, bar = 1, 2)の採用. * 条件式部に文字列あるいは正規表現リテラルをおくと`=~'演算子によっ て`$_'と比較される. 更に`...'の両辺では整数リテラルが`$.'と比較 される. Mon May 23 23:27:03 1994 Yukihiro Matsumoto (matz@dyna) * &式 形式はなくなった. 代わりにkernel:apply(id, args..)を導入. * def op () ..形式の導入. opは再定義可能な演算子. * constantの代入時チェック. 既に初期化されている定数に代入した場合 は例外が発生する. * 多重代入文. Thu May 19 22:57:07 1994 Yukihiro Matsumoto (matz@dyna) * 複合文でもvoid valueのチェックを行うようにした. * untilの動作の修正(do..until型だった). Wed May 18 01:06:25 1994 Yukihiro Matsumoto (matz@dyna) * 移植に関する若干の問題を修正. * 別名の構文を「def a b」にした. * until/unless: 演算子から制御文へ. 例外を捕捉する機能はそのまま. * 選択可能な機能をconfig.hからdefines.hに移動. Fri May 13 23:20:21 1994 Yukihiro Matsumoto (matz@dyna) * -yオプションを新設. -dオプションからコンパイラのデバッグ部分を分 離した. Tue Apr 25 20:17:33 1994 Yukihiro Matsumoto (matz@dyna) * マルチバイト文字列を識別子に使えるように. 個人的には使いたくは無 いけどなあ. * `-v'フラグの状態を$verboseでアクセスできるように. * CVSの導入に伴い, バージョン管理の方法を変更. * 真面目にChangeLogをつける事にした. Tue Mar 8 10:09:25 1994 Yukihiro Matsumoto (matz at nws119) * %変数名 によるクラス定数を導入. * undef メソッド によるメソッド定義の取り消しを導入. * rb_get_method_bodyではthe_envを変更せず, rb_call()で明示的に変更 するように. これでresponds_toなどで環境が破壊されない. Mon Mar 7 17:46:15 1994 Yukihiro Matsumoto (matz at nws119) * 「&文字列」形式. 「式.文字列」型のメッセージセンドはなくなった. * 自己代入形式(+=. -=, ...) * obj.attr = expr形式の採用. Thu Feb 24 16:23:28 1994 Yukihiro Matsumoto (matz at nws119) * toint, tofloat, print_stringをそれぞれto_i, to_f, to_sに変更. * String:clone - Copy on Writeの実現. Tue Feb 22 11:11:44 1994 Yukihiro Matsumoto (matz at nws119) * re.c: マッチした文字列の保存に失敗していた. * trap: 可能ならば処理に時間のかかるシステムコール(read, wait, sigpause, select)をフックして割り込み処理の即答性を高める(DOSな どでは無理だなあ). * trap: 割り込みをその場で処理するか(迅速だが危険), 安全なタイミン グで処理するかを選択できるように. Tue Feb 17 11:11:12 1994 Yukihiro Matsumoto (matz at nws119) * trap: 割り込みハンドラ. Wed Feb 16 12:29:12 1994 Yukihiro Matsumoto (matz at nws119) * String:crypt: 暗号化ルーチン * "::"演算子の追加. a::b は {a, b}と同義. a::b::c は {a, {b, c}}と 同義(右結合). 同義とはいうものの, "::"演算子を使った方が少しだけ メモリ効率が良い. * Dir.rmdir(), File.unlink(), File.utime() -- 各システムコールへの インタフェース. * kill -- kill(2) I/F * select(): readのチェックではstdioにバッファリングされているかど うかをチェックするように. Tue Feb 15 15:08:31 1994 Yukihiro Matsumoto (matz at nws119) * file.c: statをキャッシュするように. * File:utime()を追加. * unliteralize(): フラグを破壊していた. * Bug(): coreを吐くように. * String:tr -- tr(1)互換. 引数パターンがちょっと違うけど…. Mon Feb 14 18:24:13 1994 Yukihiro Matsumoto (matz at nws119) * unless, untilが例外も偽と見なすように. * select() -- select(2) I/F * Array:pack, String:unpack: perlのpack/unpackの同等品 Tue Feb 8 17:11:10 1994 Yukihiro Matsumoto (matz at nws119) * setenv()のないシステムのためにputenv()を使ったコードも用意した. Mon Feb 7 09:52:44 1994 Yukihiro Matsumoto (matz at nws119) * 引数の一番最後に`*'を置けるようにした. これでrest引数のリストを 操作する必要が少なくなる. Fri Feb 4 18:23:26 1994 Yukihiro Matsumoto (matz at nws119) * ruby-mode.elを書き直す. ずいぶんましになったと思う. * 文字列リテラルのCopy on Writeを実現. これで文字列がリテラルであ るからといっていちいちcloneしなくても済む. Tue Feb 1 09:21:09 1994 Yukihiro Matsumoto (matz at nws119) * sub(), gsub()で, マッチした文字列を$&, $1..$9でアクセスできるよ うにした. 同時にマッチした部分文字列をコピーしておくように(元の 文字列が変更されても状態を保存するため). Mon Jan 31 15:16:58 1994 Yukihiro Matsumoto (matz at nws119) * プライベートメソッドの仕様を変更. 今までは同じクラスのメソッドか らしかアクセスできなかったが, サブクラスのメソッドからもアクセス できるようにした(C++におけるprotected メンバ関数). * メソッドサーチのアルゴリズムを改善し, 10%程度の高速化を行なった. * 高速化. Cで記述されたメソッドを呼び出す時にはsetjmpを呼ばないよ うにした. これでCメソッドを多用する場合には3倍程度高速になった. Fri Jan 28 15:44:04 1994 Yukihiro Matsumoto (matz at nws119) * sh-modeを元にruby-mode.elを作る. 演算子で終る, 2行に渡る文には対 応していないけど…. Thu Jan 27 11:35:19 1994 Yukihiro Matsumoto (matz at nws119) * freenode(): NODE_NILの解放忘れ. * 字句解析部のバグ修正(コメントの後の状態を戻し忘れ). * protect .. endのバグ修正. GC_LINKのネストが不正だった. * joinのバグ修正(使っているオブジェクトをfreeしていた). * splitのバグ修正(アルゴリズムがおかしかった). * fork()を追加. Wed Jan 26 17:09:56 1994 Yukihiro Matsumoto (matz at nws119) * ファイルテストメソッドの追加. * rb_autoexec(): クラスを初めてアクセスした時の挙動を制御できるよ うにした. これでautoloadも実現できる. これにともないメソッド unknownはなくなった. Tue Jan 25 15:51:36 1994 Yukihiro Matsumoto (matz at nws119) * Dbmクラス, Mathモジュールを作成. * -Iオプションでサーチパスに追加できるように. * サーチパスを変数$load_pathに設定できるように. * load(): ダイナミックロードを使えるようにした. Tue Jan 18 14:14:01 1994 Yukihiro Matsumoto (matz at nws119) * Comparable:"<=>" * Float,Fixnum:"**" * Array:sort Fri Jan 14 16:53:37 1994 Yukihiro Matsumoto (matz at nws119) * version 0.07 * メソッドに関するドキュメントを充実させた. * String:index(): 引数positionを増やした. Thu Jan 13 15:13:52 1994 Yukihiro Matsumoto (matz at nws119) * 未初期化の変数アクセスをなくした. * 無駄なhash tableのアロケーションを削除. * Purify'd(on Sun) * ~RE と ~STRのコンパイル時展開の抑制. * Sunへ移植. signal()の戻り値. RDataのbug修正. * parse.y: nlsルールを削除. * yylex(): 改行と符合の解析部分を変更. * missing/strftime.c: 移植用. * Time:strftime: その他のメソッドもstrftimeを利用するように. * メソッド再定義時にメソッドキャッシュをクリアする. Fri Jan 7 15:23:20 1994 Yukihiro Matsumoto (matz at nws119) * Float:coerce(): FixnumとFloat以外の引数を与えられるた時には例外 を発生するように. * Stat: stat構造体の全てのメンバに対するアクセスメソッドを用意. * 未定義のクラス/モジュールへの参照がunknownメソッドを呼び出すよう にした. * baseline - version 0.06.
-rw-r--r--C-IF135
-rw-r--r--CVS/Entries51
-rw-r--r--CVS/Repository1
-rw-r--r--ChangeLog788
-rw-r--r--FAQ65
-rw-r--r--Makefile202
-rw-r--r--Makefile.in186
-rw-r--r--ToDo6
-rw-r--r--array.c805
-rw-r--r--bignum.c1121
-rw-r--r--bring57
-rw-r--r--class.c387
-rw-r--r--compar.c99
-rwxr-xr-xconfig.status72
-rw-r--r--configure.in63
-rw-r--r--dbm.c401
-rw-r--r--defines.h36
-rw-r--r--dict.c517
-rw-r--r--dir.c257
-rw-r--r--dln.c1088
-rw-r--r--dln.h33
-rw-r--r--enum.c332
-rw-r--r--env.h40
-rw-r--r--error.c165
-rw-r--r--etc.c220
-rw-r--r--eval.c1858
-rw-r--r--file.c1059
-rw-r--r--gc.c490
-rw-r--r--ident.h25
-rw-r--r--inits.c50
-rw-r--r--io.c1221
-rw-r--r--io.h47
-rw-r--r--math.c125
-rw-r--r--methods.c145
-rw-r--r--missing.c67
-rw-r--r--missing/CVS/Entries9
-rw-r--r--missing/CVS/Repository1
-rw-r--r--missing/getopt.c662
-rw-r--r--missing/getopt.h128
-rw-r--r--missing/getopt1.c162
-rw-r--r--missing/memmove.c24
-rw-r--r--missing/mkdir.c103
-rw-r--r--missing/strerror.c19
-rw-r--r--missing/strftime.c781
-rw-r--r--missing/strstr.c73
-rw-r--r--missing/strtol.c84
-rw-r--r--missing/strtoul.c184
-rwxr-xr-xnewver.rb14
-rw-r--r--node.h216
-rw-r--r--numeric.c962
-rw-r--r--object.c458
-rw-r--r--pack.c849
-rw-r--r--parse.y2585
-rw-r--r--process.c849
-rw-r--r--random.c80
-rw-r--r--range.c149
-rw-r--r--re.c442
-rw-r--r--re.h28
-rw-r--r--regex.c3237
-rw-r--r--regex.h276
-rw-r--r--ruby.1212
-rw-r--r--ruby.c374
-rw-r--r--ruby.h298
-rw-r--r--sample/Artistic117
-rw-r--r--sample/aset.rb3
-rw-r--r--sample/attr.rb9
-rw-r--r--sample/biorhythm.rb201
-rw-r--r--sample/caller.rb15
-rw-r--r--sample/case.rb12
-rw-r--r--sample/cat.rb4
-rw-r--r--sample/cbreak.rb34
-rw-r--r--sample/clnt.rb12
-rw-r--r--sample/clone.rb12
-rw-r--r--sample/const.rb20
-rw-r--r--sample/dbm.rb6
-rw-r--r--sample/dir.rb9
-rw-r--r--sample/evaldef.rb21
-rw-r--r--sample/fib.awk5
-rw-r--r--sample/fib.pl10
-rw-r--r--sample/fib.rb8
-rw-r--r--sample/freq.rb13
-rw-r--r--sample/fullpath.pl22
-rw-r--r--sample/fullpath.rb24
-rw-r--r--sample/gctest.rb69
-rw-r--r--sample/getopts.rb111
-rwxr-xr-xsample/getopts.test31
-rw-r--r--sample/hash.rb4
-rw-r--r--sample/io.rb40
-rwxr-xr-xsample/less.rb30
-rw-r--r--sample/list.rb81
-rw-r--r--sample/list2.rb20
-rw-r--r--sample/math.rb4
-rwxr-xr-xsample/mpart.rb42
-rw-r--r--sample/newver.rb13
-rw-r--r--sample/occur.pl9
-rw-r--r--sample/occur.rb10
-rw-r--r--sample/occur2.rb14
-rw-r--r--sample/opt_s.rb8
-rw-r--r--sample/opt_x.rb8
-rw-r--r--sample/parsearg.rb69
-rw-r--r--sample/perror.rb7
-rw-r--r--sample/rcs.awk33
-rw-r--r--sample/rcs.dat17
-rw-r--r--sample/rcs.rb42
-rw-r--r--sample/reach.rb5
-rw-r--r--sample/resp.rb2
-rw-r--r--sample/samp.rb12
-rw-r--r--sample/split.rb12
-rw-r--r--sample/struct.rb4
-rw-r--r--sample/svr.rb23
-rw-r--r--sample/system.rb1
-rw-r--r--sample/t1.rb20
-rw-r--r--sample/t2.rb24
-rw-r--r--sample/test.rb5
-rw-r--r--sample/trap.pl6
-rw-r--r--sample/trap.rb3
-rw-r--r--sample/tt.rb103
-rw-r--r--socket.c738
-rw-r--r--spec3244
-rw-r--r--sprintf.c431
-rw-r--r--st.c365
-rw-r--r--st.h57
-rw-r--r--string.c1552
-rw-r--r--struct.c274
-rw-r--r--time.c562
-rw-r--r--variable.c416
-rw-r--r--version.c31
-rw-r--r--version.h2
128 files changed, 34584 insertions, 0 deletions
diff --git a/C-IF b/C-IF
new file mode 100644
index 0000000000..df4dffc3f6
--- /dev/null
+++ b/C-IF
@@ -0,0 +1,135 @@
+.\" C-IF - -*- Text -*- created at: Tue Oct 12 14:15:00 JST 1993
+
+Ruby-C 󥿡ե
+
+VALUE
+
+ Ruby֥Ȥɽ뷿. ɬפ˱ƥ㥹ȤѤ.
+
+Qnil
+
+ : nil֥
+
+Qself
+
+ ѿ: ߤself֥Ȥ. ѿͤѹϰʸ
+ selfͤΤΤѤäƤޤΤ, Ť˹Ԥʤ.
+
+VALUE rb_define_class(char *name, VALUE super)
+
+ Ruby饹.
+
+VALUE rb_define_module(char *name)
+
+ Ruby⥸塼.
+
+rb_include_module(VALUE class, VALUE module)
+
+ ⥸塼򥤥󥯥롼ɤ. classǤmodule򥤥󥯥롼ɤƤ
+ ˤϲ⤷ʤ(¿ť󥯥롼ɤζػ).
+
+void rb_define_variable(char *name, VALUE *var,
+ VALUE (*get_hook), VALUE (*set+hook)())
+
+ RubyCȤǶͭ륰Хѿ. Ruby֥Ȥؤ
+ ѿƤδؿˤäʤФʤʤ(GCݸ뤿
+ ). get_hookQnilǤʤ, ѿȤκݤget_hook˥åȤ줿ؿ
+ ƤФ. set_hookQnilǤʤˤλset_hookƤФ.
+
+ ѿ̾`$'ǻϤޤʤˤϼưŪɲä. ѿ̾Ȥrubyμ
+ ̻ҤȤƵʤʸ(㤨` ')ޤˤrubyץफ饢
+ Ǥʤʤ.
+
+void rb_global_variable(VALUE *var)
+
+ GCRubyϥʤ, Ruby֥Ȥޤѿޡ
+ .
+
+void rb_read_only_hook()
+
+ ɤ߽ФѤѿΤset_hookؿ.
+
+rb_define_method(VALUE class, char *name, VALUE (*func)(), int argc)
+
+ ᥽åɤ. argc-1λ, argc, argvͿ.
+
+rb_define_single_method(VALUE class, char *name, VALUE (*func)(), int argc)
+
+ ðۥ᥽åɤ. rb_define_method()Ʊ.
+
+ID rb_intern(char *name)
+
+ ʸбID֤.
+
+char *rb_id2name(ID id)
+
+ IDбʸ֤(ǥХå).
+
+VALUE rb_funcall(VALUE recv, ID mid, int narg, ...)
+
+ ᥽åɸƤӽФ. ʸ󤫤mid뤿ˤrb_intern()Ȥ.
+
+rb_iv_get(VALUE obj, char *name)
+
+ objΥ󥹥ѿ. @Ϥޤʤ󥹥ѿRuby
+ फ饢Ǥʤ.
+
+rb_iv_set(VALUE obj, char *name, VALUE val)
+
+ objΥ󥹥ѿval˥åȤ.
+
+rb_call_super(VALUE args)
+
+ ѡ饹Υ᥽åɤƤӽФ. argsϰꥹȤȤʤ. args
+ Qnilλϰ򤽤ΤޤްѤ.
+
+rb_iterate(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2)
+
+ func2֥åȤꤷ, func1򥤥ƥ졼ȤƸƤ. func1ˤ
+ arg1ȤϤ, func2ˤ1˥ƥ졼ȤͿ줿
+ , 2arg2Ϥ.
+
+rb_yield(VALUE val)
+
+ valͤȤƥƥ졼֥åƤӽФ.
+
+rb_resque(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2)
+
+ ؿfunc1arg1˸ƤӽФ. func1μ¹㳰ȯˤ
+ func2arg2ȤƸƤ. ͤ㳰ȯʤäfunc1
+ , 㳰ȯˤfunc2ͤǤ.
+
+rb_ensure(VALUE (*func1)(), char *arg1, VALUE (*func2)(), char *arg2)
+
+ ؿfunc1arg1ȤƼ¹Ԥ, ¹Խλ(Ȥ㳰ȯƤ)
+ func2arg2ȤƼ¹Ԥ. ͤfunc1ͤǤ(㳰ȯ
+ nil).
+
+GC_LINK
+
+ ѿGCݸԤʤ.
+
+GC_PRO(var)
+
+ ѿGCݸ. ѿݸƤʤǽ
+ Ruby֥ȤؤƤˤGC_PRO()Ȥäݸɬפ
+ . GC_PRO()ݸѿɬƤɬפ(̤
+ ΥߤäƤGC).
+
+GC_PRO2(var)
+
+ GCݸ, varnilǽ뤳ȰʳGC_PRO(var)Ʊ.
+
+GC_PRO2(var, init)
+
+ GCݸ, varinit˽뤳ȰʳGC_PRO(var)Ʊ.
+
+GC_UNLINK
+
+ GCݸλ.
+
+/*
+ * Local variables:
+ * fill-column: 70
+ * end:
+ */
diff --git a/CVS/Entries b/CVS/Entries
new file mode 100644
index 0000000000..067e16dd65
--- /dev/null
+++ b/CVS/Entries
@@ -0,0 +1,51 @@
+/autoexec.c/0.15/Wed Jun 1 23:40:54 1994 Thu Mar 17 18:49:43 1994//
+/class.c/0.22/Wed Jun 1 23:40:54 1994 Fri Mar 25 13:12:36 1994//
+/compar.c/0.15/Wed Jun 1 23:40:54 1994 Thu Mar 17 18:49:43 1994//
+/dln.h/0.14/Wed Jun 1 23:40:57 1994 Thu Mar 17 15:55:00 1994//
+/error.c/0.15/Wed Jun 1 23:40:57 1994 Thu Mar 17 18:49:43 1994//
+/etc.c/0.22/Wed Jun 1 23:40:58 1994 Fri Mar 25 13:12:36 1994//
+/ident.h/0.17/Wed Jun 1 23:41:00 1994 Wed May 25 00:56:16 1994//
+/inits.c/0.27/Wed Jun 1 23:41:00 1994 Mon Apr 25 23:30:29 1994//
+/io.h/0.14/Wed Jun 1 23:41:01 1994 Thu Mar 17 15:55:00 1994//
+/methods.c/0.20/Wed Jun 1 23:41:01 1994 Tue Mar 22 16:58:31 1994//
+/missing.c/0.29/Wed Jun 1 23:41:01 1994 Wed Jun 1 23:36:26 1994//
+/parse.y/0.35/Wed Jun 1 23:41:08 1994 Wed Jun 1 23:36:30 1994//
+/random.c/0.15/Wed Jun 1 23:41:09 1994 Thu Mar 17 18:49:43 1994//
+/range.c/0.15/Wed Jun 1 23:41:09 1994 Thu Mar 17 18:49:43 1994//
+/re.h/0.15/Wed Jun 1 23:41:10 1994 Thu May 26 00:41:30 1994//
+/regex.c/0.15/Wed Jun 1 23:41:12 1994 Wed Jun 1 23:36:35 1994//
+/regex.h/0.14/Wed Jun 1 23:41:12 1994 Thu Mar 17 15:55:00 1994//
+/st.h/0.14/Wed Jun 1 23:41:14 1994 Thu Mar 17 15:55:00 1994//
+/variable.c/0.29/Wed Jun 1 23:41:16 1994 Wed May 25 00:56:35 1994//
+/version.c/0.27/Wed Jun 1 23:41:16 1994 Mon Apr 25 23:30:35 1994//
+/Makefile/0.32/Fri Jun 3 00:15:00 1994 Fri Jun 3 00:15:00 1994//
+/Makefile.in/1.3/Fri Jun 3 00:15:01 1994 Fri Jun 3 00:15:01 1994//
+/array.c/0.29/Fri Jun 3 00:15:02 1994 Fri Jun 3 00:15:02 1994//
+/configure/1.3/Fri Jun 3 00:15:04 1994 Fri Jun 3 00:15:03 1994//
+/configure.in/1.3/Fri Jun 3 00:15:04 1994 Fri Jun 3 00:15:04 1994//
+/dbm.c/0.28/Fri Jun 3 00:15:05 1994 Fri Jun 3 00:15:05 1994//
+/defines.h/1.4/Fri Jun 3 00:15:06 1994 Fri Jun 3 00:15:06 1994//
+/dict.c/0.28/Fri Jun 3 00:15:06 1994 Fri Jun 3 00:15:06 1994//
+/dir.c/0.18/Fri Jun 3 00:15:07 1994 Fri Jun 3 00:15:07 1994//
+/dln.c/0.29/Fri Jun 3 00:15:08 1994 Fri Jun 3 00:15:08 1994//
+/enum.c/0.16/Fri Jun 3 00:15:09 1994 Fri Jun 3 00:15:08 1994//
+/eval.c/0.35/Fri Jun 3 00:15:10 1994 Fri Jun 3 00:15:09 1994//
+/file.c/0.29/Fri Jun 3 00:15:11 1994 Fri Jun 3 00:15:11 1994//
+/gc.c/0.30/Fri Jun 3 00:15:12 1994 Fri Jun 3 00:15:12 1994//
+/io.c/0.29/Fri Jun 3 00:15:13 1994 Fri Jun 3 00:15:13 1994//
+/math.c/0.28/Fri Jun 3 00:15:14 1994 Fri Jun 3 00:15:14 1994//
+/node.h/0.32/Fri Jun 3 00:15:15 1994 Fri Jun 3 00:15:14 1994//
+/numeric.c/0.19/Fri Jun 3 00:15:15 1994 Fri Jun 3 00:15:15 1994//
+/object.c/0.32/Fri Jun 3 00:15:16 1994 Fri Jun 3 00:15:16 1994//
+/pack.c/0.18/Fri Jun 3 00:15:17 1994 Fri Jun 3 00:15:17 1994//
+/process.c/0.30/Fri Jun 3 00:15:18 1994 Fri Jun 3 00:15:17 1994//
+/re.c/0.32/Fri Jun 3 00:15:19 1994 Fri Jun 3 00:15:18 1994//
+/ruby.c/0.34/Fri Jun 3 00:15:19 1994 Fri Jun 3 00:15:19 1994//
+/ruby.h/0.30/Fri Jun 3 00:15:20 1994 Fri Jun 3 00:15:20 1994//
+/socket.c/0.27/Fri Jun 3 00:15:21 1994 Fri Jun 3 00:15:21 1994//
+/sprintf.c/0.23/Fri Jun 3 00:15:22 1994 Fri Jun 3 00:15:21 1994//
+/st.c/0.15/Fri Jun 3 00:15:22 1994 Fri Jun 3 00:15:22 1994//
+/string.c/0.29/Fri Jun 3 00:15:23 1994 Fri Jun 3 00:15:23 1994//
+/struct.c/0.26/Fri Jun 3 00:15:25 1994 Fri Jun 3 00:15:24 1994//
+/time.c/0.29/Fri Jun 3 00:15:26 1994 Fri Jun 3 00:15:26 1994//
+/version.h/1.7/Fri Jun 3 00:15:27 1994 Fri Jun 3 00:15:27 1994//
diff --git a/CVS/Repository b/CVS/Repository
new file mode 100644
index 0000000000..b98482fac9
--- /dev/null
+++ b/CVS/Repository
@@ -0,0 +1 @@
+/work/cvsroot/ruby
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000000..c54fa3085c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,788 @@
+Mon Jul 18 10:19:15 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * parse.y: ¿롼˥Хä, 3ǰʾ¿
+ ˼ԤƤ.
+
+ * eval.c(rb_eval): ¿, դǤʤˤ`to_a'᥽
+ ɤѴ褦ˤ. ޤǤλͤȱͤ
+ 1ǤˤΤޤƤ, structʤѴǤ
+ Ѵ򤷤.
+
+ * dbm.c,dict.c(delete_if): ᥽åɲ.
+
+ * process.c(wait,waitpid): ƥॳwaitpidޤwait4
+ ϤȤ褦. configure⤽å褦ѹ.
+
+ * dbm.c, dict.c(clear): ᥽åɲ.
+
+Fri Jul 15 10:54:45 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * array.c(Fary_fill,Fary_clear): ᥽åɤɲ.
+
+ * string.c(Fstr_split): $;ͤĹ1ʸǤ,
+ ɽʤ, ñʤʸȤʬ䤹.
+
+ * string.c(Fstr_aset/Fstr_aref): ǥåʸϰϳ
+ ưArray򻲹ͤ˽.
+
+ * array.c(astore,Fary_aset): ΰrealloc, ǥꥢ
+ 褦. ޤ˥ߤäƤ.
+
+ * array.c: []/[]=ǤΥǥåط. ŪΥǥ
+ ʤ¤㳰ϵʤ褦ѹ. ɬפ˱Ŭ
+ ˲ᤷ, ɬפʤΰĥ褦.
+
+Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * autoexec.c: . autoloadطεǽϺ帡Ƥ褦.
+
+ * dict.c: 񥯥饹̾ΤDictѹ. ̾ȤHash
+ դ. ޤDictionaryʤɤĹ̾ˤƤïȤäƤ
+ ä. *BACKWARD INCOMPATIBILITY*
+
+ * parse.y: Dict빽ʸɲ. {..}ˤ.
+
+ * parse.y: 빽ʸ[..]ѹ. Ruby
+ ȤȤθߴݤƤʤ, Dict빽ʸƳ,
+ perl5˹碌(ռ), ѹϺʤȹͤ.
+ *BACKWARD INCOMPATIBILITY*
+
+ * eval.c(Feval): eval()ǥ᥽åɤ, 륯饹
+ ᥽åɤν°륯饹ˤ. ޤǤObject饹
+ .
+
+ * parse.y: ʤeval()Ƥ.
+
+Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * eval.c: ᥽åɤ¸ߤʤˤKernel:_undefined(id)ƤФ
+ 褦. , rubyǤϸġΥ᥽åν, 饹
+ ñ̤νɬפʵ⤹ʤ.
+
+ * autoexec.c: . autoloadطεǽϺ帡Ƥ褦.
+
+ * dict.c: 񥯥饹̾ΤDictѹ. ̾ȤHash
+ դ. ޤDictionaryʤɤĹ̾ˤƤïȤäƤ
+ ä. *BACKWARD INCOMPATIBILITY*
+
+ * parse.y: Dict빽ʸɲ. {..}ˤ.
+
+ * parse.y: 빽ʸ[..]ѹ. Ruby
+ ȤȤθߴݤƤʤ, Dict빽ʸƳ,
+ perl5˹碌(ռ), ѹϺʤȹͤ.
+ *BACKWARD INCOMPATIBILITY*
+
+ * eval.c(Feval): eval()ǥ᥽åɤ, 륯饹
+ ᥽åɤν°륯饹ˤ. ޤǤObject饹
+ .
+
+ * parse.y: ʤeval()Ƥ.
+
+Tue Jul 12 09:41:28 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * pack.c: uuencodeΥݡ.
+
+ * `-0'`-R'. ϥ쥳ɥѥ졼򥳥ޥɥ饤󤫤ꤹ
+ ˡϤʤʤä. ɤ, ͤʤ.
+
+Mon Jul 11 09:51:24 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ruby.c: `-r'ץ`-0'ץˤʤä. ̤`-r'ͭ
+ ѹǽ. `-R'̤ϤΤޤ.
+
+ * version.c: versionɽդޤ᤿.
+
+ * parse.y: private method. Rubyprivate᥽åɤC++ˤ
+ protected method˳Τ, `@'ǻϤޤ̾.
+
+ * env.h: struct ENVIRONʬΥ.
+
+ * parse.y: `\$var', `\@var', `%var'褦.
+
+ * variable.c(Fdefined): idȤƼդ褦.
+
+ * parse.y: ifʸ/unlessʸ˥thenɲ. thenʤȤΤ,
+ ճȴְ㤤¿Τ. άǤ.
+
+Sat Jul 9 02:16:04 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * eval.c(rb_eval): class/moduleɾǿפƤ
+ ʤä. ׳ʬrb_call()rb_eval()˰ܤ.
+
+ * eval.c(rb_call): realloc()ϤΤ, ѿѤ
+ alloca()Ƥ. ޤ櫓.
+
+ * string.c(Fstr_times): ƤΰۤʬѹƤ.
+
+Wed Jul 6 15:52:42 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * socket.c: Socket -> BasicSocket, RawSocket -> Socket ˲̾.
+
+ * string.c(ucfirst,lcfirst): ǽ1ʸʸ/ʸѴ.
+
+ * numeric.c(chr): ʸ󲽥᥽å.
+
+ * inits.c, dbm.c: DBMȤʤϥ饹ΤΤʤ褦
+ ˤ. ѤǤʤ饹nilȤ뤳Ȥ򺣸ΥݥꥷȤ
+ (ޤޤǤϥǥ顼ȯƤ). autoexec()
+ Τ⸡Ƥɬפˤʤꤽ.
+
+ * bignum.c(bigadd): Х.
+
+Thu Jul 7 11:12:18 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * eval.c(Fload,Feval): eval_tree򥯥ꥢ˺Ƥ.
+
+ * _inspect: ֥ȤɷʸѴ(˥ǥХå
+ ).
+
+Wed Jul 6 00:57:18 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * numeric.c, bignum.c: Ф`[]'黻. nӥåܤåȤ
+ Ƥ뤫ɤ֤.
+
+Tue Jul 5 12:48:39 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * io.c(Feof): ɲ. ޥɥ饤󤫤ʤ벾ۥեˤĤƤ
+ EOFФǤ褦.
+
+ * ruby.c: -l/-r/-R/-Xץɲ.
+
+ * ruby.c: -n/-pץloopղäʤɤᥤ롼˰ư
+ . , ץβ(`-c'ץΤ)λ
+ ɤȤäȤϤʤ.
+
+ * io.c(Fgets): ®. ŤäȤ򤷤ʤ®ä. .
+
+Mon Jul 4 15:55:48 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * Socket:getsockname/getpeername - 褦䤯ͤꤷ.
+
+ * io.c(Fgets): eachgets򵭽ҤΤǤϤʤ, getseach򵭽Ҥ
+ 褦ˤ.
+
+Fri Jul 1 10:35:49 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * $ENV[env] = nil/$ENV.delete(env)ǴĶѿǤ褦ˤʤ
+ . $ENV.deleteϴĶѿΰ֤ͤ.
+
+ * !~ְäƤ.
+
+ * Dict,DBM:[]= - nilˤäǤǤ褦ˤʤä.
+ ˤȤʤnilDictǤˤʤʤʤä.
+
+ * . IJΤ褦ʻȤƤʤɤʤ, ѿ
+ ̾դѤꤷ.
+
+Fri Jul 1 00:21:29 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Array:join() - ǿ0Фƶʸ֤.
+
+ * RawSocket:open(),socketpair() - ʸǻǤɥᥤȥ
+ פ򤤤Ĥɲä.
+
+Thu Jun 30 13:51:29 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * -fץʤ. (loadΤʤä)̾ĤʤΤ, Ȥʤ
+ Ƥɬפʤ.
+
+ * -sץɲ. perl-sץƱư򤹤.
+
+ * RawSocket饹󶡤. SocketФ륷ƥॳ٥
+ ǽˤʤä.
+
+Thu Jun 30 00:27:19 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Socket - bug fixes.
+
+ * linuxǤsyscall(SYS_select)ưʤ.
+
+ * Socket:addr,peeraddr - Ȥsockaddrξ֤.
+
+Wed Jun 29 00:14:20 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Socket:setopt,getopt - setsockopt(2), getsockopt(2)ؤΥ
+ ¸.
+
+ * sprintf() - rubyˤunsigned̵Τ, %u.
+
+ * sprintf() - %b, %x, %oǤ2ɽ, %B, %X, %OǤդɽ
+ ǽϤ褦. sprintf()λͤ餤Ǥ,
+ 夭.
+
+Tue Jun 28 14:42:03 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * Bignum:<<,>> - 2Ȥ. եȱ黻ˤϴطʤ
+ ä.
+
+ * Bignum:^ - bug fix. 礬ȿФä.
+
+ * sprintf() - 2ʽϻ"%b"ɲ.
+
+ * sprintf() - %x, %oFixnumϤ, 2ɽԤʤʤ.
+
+ * sprintf() - %x, %oϤϤολ`-'Ϥ褦.
+
+Mon Jun 27 14:56:13 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * sprintf() - BignumˤĤƤ%d, %o(2ɽѴ)
+ ɽ褦ˤ.
+
+ * BignumФ黻. ο2ɽǤ
+ Ȥߤʤ, IJŪ˺¦̵¤1Ϣ³Ƥ褦ʱ黻
+ ̤.
+
+ * Fixnum:<<,>> - եեȤѹ.
+
+ * Bignum:>> - ΥեȤб.
+
+ * __END__, ^D, ^ZǥץȤλǤ.
+
+ * -xץɲ. #! ..rubyʤԤޤɤФ.
+
+ * -cץɲ. ѥΤߤԤ.
+
+Sat Jun 25 01:37:21 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Fixnum:<< - ɬפ˱Bignum˳ĥƺեȤ褦.
+ , ե32ۤCPerlȤϰä֤ͤ.
+
+Fri Jun 24 10:01:28 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ioctl()/fcntl() - ƥॳƤӽФ˥Хåե礭
+ Ĵ᤹褦ˤ.
+
+ * String:toupper/tolower - ʸ֤ԡΤǤϤ
+ , ʸƤѹ褦ˤ.
+
+ * inplace edit¸. perlƱ褦`-i'ץǻꤹ.
+ äȤ, MS-DOSΤȤȤͤƤʤ.
+
+ * ǥեȤνɲä. ޤǤ$stdout뤷ˡ
+ Ϥʤä.
+
+Fri Jun 17 10:55:08 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * Ķѿ˥ˡȤgetenv()/setenv()ʳ$ENV
+ դ. $ENVʸ-ʸμǤ뤫Τ褦ưEnvDict
+ ֥ȤƤ(eachassocͿ).
+
+ * nilcore dump. ѥΥå򶯲.
+
+ * Struct: struct_new()ΰGCץƥȤɬפ. ƥ
+ åΰǤ⥹Ǥ褦ˤʤФʤ
+ ? Ǥ, ܿʤ.
+
+Fri Jun 17 01:01:46 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Time::asctime() - դΥեޥåȤƤ.
+
+ * Stat: StatEtcʤɤƱͤStructǼ¸Τ, Stat饹̵
+ ʤä.
+
+Thu Jun 16 10:32:23 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * bignum.c: ĤΥХ. FixnumϤ٤Ȥ
+ intϤƤ. .
+
+ * big2str() - 1夺basedz, 4夺ij任Ԥʤ褦
+ . ¿Ĺ任β1/4ˤʤ. ->(ʸ)
+ Ѵơ֥Ѥ褦ˤ.
+
+ * rb_ivar_get_1() - Ǥ˲餫Υ󥹥ѿĥ֥
+ ȤǤ, ̤Υ󥹥ѿͤͤˤʤäƤ.
+
+ * yylex() - 󥹥ѿǧ˼ԤƤ. attr()ư
+ ƤΤ, Ѥưʤä. äƤư
+ ƤΤ.
+
+ * Object:attr() - Ǥ˥᥽åɤƤˤϥǥե
+ ȤΥ᥽åɤʤ褦ˤ. äȤ⥢
+ åɤƱ̾Υ᥽åɤζ̤Rubyˤ¸ߤʤ, ϻ
+ ʤ.
+
+ * pack.c: ǥautoconfȽꤹ褦ˤΤ, v/V
+ 褦ˤʤä. ޤntoh?()/hton?()⼫Ѱդ.
+
+ * Stat: st_rdev򥢥᥽åɤɲ. ˥ƥबstat
+ ¤Τst_blksize, st_blockäƤ뤫autoconfǥå
+ 褦ˤ.
+
+ * ɥȤ򾯤.
+
+ * INT2FIX()Τ, 31bitݾڤǤʤΤ, int2inum()֤
+ .
+
+Wed Jun 15 10:18:27 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * sprintf() - bignumνϤλ, ׻褦ˤ.
+
+ * str2inum() - base0λ, baseưȽꤹ褦(0xǻϤޤ
+ 16 , 0ǻϤޤ8).
+
+Tue Jun 14 16:08:42 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * gc.c: BignumɲäΤ˺Ƥ. Ȥ߹߷ɲäˤ
+ ɬmark()sweep()ˤη˴ؤɲäɬפ.
+
+ * bignum: 任ư褦ʵ. 르ꥺ򤷤Ƥʤ
+ Τ, ʤ.
+
+Mon Jun 13 14:36:55 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ޤݡȤƤʤ᥽åɤʤɤ뤬, ʤʤˤBignum
+ Ȥ褦ˤʤ. ioctlȤ.
+
+Fri Jun 10 17:26:42 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * Comparable: äȤʤ᥽åɤ`=='`>'`<=>'ѹ.
+ ComparableΥ֥饹`<=>'ɬפ.
+
+Wed Jun 8 13:12:18 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * Need_Fixnum()ۤȤɤʤ, NUM2INT()ľintѴ뤳
+ Ȥˤ. 31bit˴ݤƷ򤪤꤬ʤʤ.
+
+Tue Jun 7 09:45:31 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ruby.h: ޥFIXABLE(n)ɲ. ĤǤFIXNUMѹ
+ , ܿ᤿(Ĥ).
+
+ * C++ͽǤnew. , ⤦ҤȤĤͽǤ
+ class˴ؤƤ, ֤ñ줬פĤʤȤ⤢äƤΤ
+ ޤˤʤäƤ.
+
+ * 31bitۤINT2FIX()ؿƤӽФѤ. bignumƳ
+ 줿ˤϼưŪbignum֤褦ˤ.
+
+ * readline() - `-'ɸϤ̣褦ˤʤä.
+
+ * ruby.h: եȤեȤѥեȤϽϰ¸Τ褦ʤ
+ , ruby.hcppȤäƥå褦ˤ. Ǥޤ
+ ȻפΤ, 긵intեȤϤʤΤdz
+ ǧǤʤ. NEWS-OSCCϳΤեȤϤĤեȤä
+ ʵɡ.
+
+Mon Jun 6 10:10:22 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * FIX2INT()ѹ. ɤΤϤޤưʤä
+ ? ⤷, եȤĥϽϰ¸?
+
+ * FIX2INT()FIX2UINT()Ȥʬ褦ˤ. äȤfixnum31
+ ӥåȤʤΤ, ܼŪʲˤϤʤʤΤ(ioctlȤ߹
+ ߤä).
+
+ * printؿŪ᥽åɤ̾᥽åɤѹ. Ϳʤ
+ ˤϥ쥷ФץȤ褦ˤ. printå
+ Ǥ¹ԤǤ褦ˤʤä. :
+
+ ruby -e 'readlines().sort.print'
+
+ ΥץȤ, ȤͿ줿(뤤ɸϤɤ
+ ޤ줿)ʸƹ˥Ȥɽ.
+
+ * eval.c: argc,argvѥǰ᥽åɤ˰ĤͿ
+ ʤ, argvnilˤʤäƤ(argv[0]˥
+ Ƥޤ).
+
+ * _exit()ɲ. 㳰ʤɹԤʤʤ.
+
+ * dbm饹: 饹̾ΤDBM(ʸ)줷.
+
+Sat Jun 4 00:51:04 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * 롼ѿˤ°ǤǤ褦ˤ.
+
+Fri Jun 3 09:49:48 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ¿ˤ, °, ǤؤԤʤ褦ˤ.
+
+ * Need_Fixnum(): nil0Ѵ褦.
+
+ * Enumerable:min, max, index, includes - ɲ. min, maxǤ
+ `<=>'᥽åɤĤȤꤷƤ.
+
+ * Dict/Dbm:length - ǿ֤᥽å.
+
+ * Dbm饹to_a᥽åɤɲ.
+
+ * Sunˤsortθưη, νfix줿. ,
+ ǤʤưʤäΤ餫ǤϤʤ. Ӵؿɤͤ
+ ֤Ƥꤷΰ賰򥢥ΤϥХǤϤʤ.
+
+ * եƤɤ, ƹԤȤ֤᥽åɤpython
+ ͤˤ`readlines'Ȥ̾ˤ. ˤȤʤgetsФ
+ readlineȤ̾Ѱդ.
+
+Fri Jun 3 00:08:38 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * Array:sort - Ƚ̴ؿͤFixnumǤϤʤ, IntǤ٤
+ . ְ㤤. SunưäΤϤΤΤʤ.
+
+Thu Jun 2 11:48:37 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * IO:read_all() - ȥ꡼κǸޤϤ, ƹԤǤȤ
+ ֤᥽åɤɲ. ޤؿ᥽å read_all()ɲä.
+ ϰΥե뤫ɤ߹dzƹԤǤȤ֤.
+ ̣Ūˤ
+
+ def read_all()
+ ary = {}
+ while gets()
+ ary.push($_)
+ end
+ end
+
+ ȤۤǤ.
+
+ * String:atoi᥽åɤ. to_a᥽åɤaǤȤϢ
+ Ƥ, 򾷤ʤ. to_i᥽åɤȤ.
+
+ * ؤѴ᥽åto_aƳ. ̾Υ֥Ȥϼʬ
+ ͣǤȤĹ1֤. ϼʬȤ, ϥ
+ ͤΥڥ֤. Enumeratedinclude饹, each
+ ֤Ǥޤ֤.
+
+ * file.c: ĤΰȤ᥽å(chmod,chown,utimes)
+ , . ȼ, ǽƤΰηåԤ
+ 褦ˤ. å˼ԤȽԤʤ鷺㳰ȯ
+ .
+
+ * configure.in: ɬפʥƥȤԤʤʤ褦˽.
+
+Tue May 31 10:41:08 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * String:pack(): 2ʿʸѴ(B,b)01դä.
+
+ * Math.c: ¿ϤΥ᥽åɤ˰ȤϤ줿˼ưŪ
+ 褦ˤ.
+
+ * toupper(), tolower(): ʸȽߥѴƤʤä.
+
+ * getopt_long()λͤˤä, ץȤؤΰ󥿥ץ꥿
+ ȲᤵƤ. ѥʸƬ`+'ɲ.
+
+ * config.h. DEFINEMakefileͿ.
+
+ * sprintf(): "%d"ʸͿ줿ˤϥɥ쥹ǤϤʤƤ
+ Ѵ褦ˤ. ĤǤưѴ褦
+ .
+
+ * regexp.c: rubyγĥɽ(\d, \D, \s, \S)νdzƤ
+ ۤƥХåե˽񤭹Ǥ. ˥Хåե򤭤ȳ
+ ĥ褦ˤ. ǺˤƤϲǤ
+ Ȼפ.
+
+ * yylex(): ֥륯ʸǥ֥륯Ȥɽ뤿
+ ΥХååɽǤʤä.
+
+Mon May 30 10:07:42 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * 黻`!'αդPǤȤ. ˤä, α黻Ҥ
+ ͤϺ𤹤뤫Τʤ, ¿Τα黻ҤȤ
+ Ϻ򤱤뤳ȤǤȻפ.
+
+ * autoconfȤä, ưŪMakefile, config.h褦ˤ.
+ , ΥޥǤ`configure'¹Ԥ, `make'ȯǥ
+ ѥǤȻפ.
+
+ * clone: ֥饹ФѤ줿, Υ֥ȤƱ
+ 饹Υ󥹥󥹤֤褦(ϥӥȥ󥯥饹ξ
+ ͤƤʤä).
+
+ * ӥȥ󥯥饹Υ֥饹褦, ƥΤ륯饹
+ ˤnew᥽åɤɲä.
+
+ * malloc(). purifyɬפΤʤ.
+
+ * re.c: rb_global_variable()θƤӤδְ㤤. ѿؤΥݥ
+ ϤʤФʤ.
+
+ * parse.y: ѿΰ˰ɾ˰¸ܿΤʤ
+ ʬä.
+
+ * attr(): °ΥХľ. ͤѤä?
+
+Sat May 28 23:08:18 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * ɽåʸȽݥ󥿰פưפ
+ . ʸƥϰ˿֥Ȥ
+ Τä.
+
+Fri May 27 11:42:00 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ɽʸɤΥǥեȤбˤ. ˤä
+ 㴳ι®ޤ.
+
+ * trʸ(delete), ʸ(squeeze)ʬΥ. ˤȤ
+ ʤtrΥץϤʤʤä.
+
+Thu May 26 10:32:55 1994 Yukihiro Matsumoto (matz@ix-02)
+
+ * ץɤ߹ߥ롼ľ, ̾եʳΥե
+ ̾ʸ󤬥ץȤȤͿ줿б. ޤ,
+ ɸϤ饹ץȤɤ߹, ե뤬/tmp˻Ĥ
+ 褦ˤ.
+
+ * Fixnum:id2name - IDʸ᤹ؿ. String:internε.
+
+ * Array: ϰϳǤ򥢥㳰ȯ,
+ nil֤褦ˤ. ϼưŪ˳ĥ.
+
+ * string:stripɲ.
+
+ * -nץ-eץʣꤷư褦.
+
+ * parse.y<sys/types.h>⥤󥯥롼ɤ褦ˤ.
+
+ * fnameκ٤bug.
+
+Wed May 26 11:45:10 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * 򥭥å夹褦ˤ. ֤¿ˤͭΤϤ
+ , ٤ʤ٤ʤʤ.
+
+Wed May 25 00:42:24 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * ¿ʸ(foo, bar = 1, 2)κ.
+
+ * Pʸ󤢤뤤ɽƥ򤪤`=~'黻Ҥˤ
+ `$_'Ӥ. `...'ξդǤƥ뤬`$.'
+ .
+
+Mon May 23 23:27:03 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * & Ϥʤʤä. kernel:apply(id, args..)Ƴ.
+
+ * def op () ..Ƴ. opϺǽʱ黻.
+
+ * constantå. ˽Ƥ
+ 㳰ȯ.
+
+ * ¿ʸ.
+
+Thu May 19 22:57:07 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * ʣʸǤvoid valueΥåԤ褦ˤ.
+
+ * untilưν(do..untilä).
+
+Wed May 18 01:06:25 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * ܿ˴ؤ㴳.
+
+ * ̾ιʸdef a bפˤ.
+
+ * until/unless: 黻Ҥʸ. 㳰ª뵡ǽϤΤޤ.
+
+ * ǽʵǽconfig.hdefines.h˰ư.
+
+Fri May 13 23:20:21 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * -yץ. -dץ󤫤饳ѥΥǥХåʬʬ
+ Υ.
+
+Tue Apr 25 20:17:33 1994 Yukihiro Matsumoto (matz@dyna)
+
+ * ޥХʸ̻Ҥ˻Ȥ褦. ĿŪˤϻȤ̵
+ ɤʤ.
+
+ * `-v'ե饰ξ֤$verboseǥǤ褦.
+
+ * CVSƳȼ, Сˡѹ.
+
+ * ܤChangeLogĤˤ.
+
+Tue Mar 8 10:09:25 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * %ѿ̾ ˤ륯饹Ƴ.
+
+ * undef ᥽å ˤ᥽åμäƳ.
+
+ * rb_get_method_bodyǤthe_envѹ, rb_call()Ūѹ
+ 褦. responds_toʤɤǴĶ˲ʤ.
+
+Mon Mar 7 17:46:15 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * &ʸ׷. ּ.ʸ׷ΥåɤϤʤʤä.
+
+ * (+=. -=, ...)
+
+ * obj.attr = exprκ.
+
+Thu Feb 24 16:23:28 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * toint, tofloat, print_string򤽤줾to_i, to_f, to_sѹ.
+
+ * String:clone - Copy on Writeμ¸.
+
+Tue Feb 22 11:11:44 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * re.c: ޥåʸ¸˼ԤƤ.
+
+ * trap: ǽʤн˻֤Τ륷ƥॳ(read, wait,
+ sigpause, select)եåƳ߽¨(DOS
+ ɤǤ̵ʤ).
+
+ * trap: ߤ򤽤ξǽ뤫(®), ʥߥ
+ ǽ뤫Ǥ褦.
+
+Tue Feb 17 11:11:12 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * trap: ߥϥɥ.
+
+Wed Feb 16 12:29:12 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * String:crypt: Ź沽롼
+
+ * "::"黻Ҥɲ. a::b {a, b}Ʊ. a::b::c {a, {b, c}}
+ Ʊ(). ƱȤϤΤ, "::"黻ҤȤä
+ Ψɤ.
+
+ * Dir.rmdir(), File.unlink(), File.utime() -- ƥƥॳؤ
+ 󥿥ե.
+
+ * kill -- kill(2) I/F
+
+ * select(): readΥåǤstdio˥Хåե󥰤Ƥ뤫
+ å褦.
+
+Tue Feb 15 15:08:31 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * file.c: stat򥭥å夹褦.
+
+ * File:utime()ɲ.
+
+ * unliteralize(): ե饰˲Ƥ.
+
+ * Bug(): coreǤ褦.
+
+ * String:tr -- tr(1)ߴ. ѥ󤬤äȰ㤦ɡ.
+
+Mon Feb 14 18:24:13 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * unless, until㳰⵶ȸʤ褦.
+
+ * select() -- select(2) I/F
+
+ * Array:pack, String:unpack: perlpack/unpackƱ
+
+Tue Feb 8 17:11:10 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * setenv()ΤʤƥΤputenv()ȤäɤѰդ.
+
+Mon Feb 7 09:52:44 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * ΰֺǸ`*'֤褦ˤ. restΥꥹȤ
+ ɬפʤʤ.
+
+Fri Feb 4 18:23:26 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * ruby-mode.elľ. ֤ޤˤʤäȻפ.
+
+ * ʸƥCopy on Write¸. ʸ󤬥ƥǤ
+ 뤫ȤäƤcloneʤƤѤ.
+
+Tue Feb 1 09:21:09 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * sub(), gsub(), ޥåʸ$&, $1..$9ǥǤ
+ ˤ. Ʊ˥ޥåʬʸ򥳥ԡƤ褦(
+ ʸѹƤ֤¸뤿).
+
+Mon Jan 31 15:16:58 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * ץ饤١ȥ᥽åɤλͤѹ. ޤǤƱ饹Υ᥽åɤ
+ 餷Ǥʤä, ֥饹Υ᥽åɤ⥢
+ Ǥ褦ˤ(C++ˤprotected дؿ).
+
+ * ᥽åɥΥ르ꥺ, 10%٤ι®Ԥʤä.
+
+ * ®. CǵҤ줿᥽åɤƤӽФˤsetjmpƤФʤ
+ ˤ. C᥽åɤ¿Ѥˤ3ٹ®ˤʤä.
+
+Fri Jan 28 15:44:04 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * sh-mode򸵤ruby-mode.el. 黻Ҥǽ, 2ԤϤʸˤ
+ Ƥʤɡ.
+
+Thu Jan 27 11:35:19 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * freenode(): NODE_NILβ˺.
+
+ * ΥХ(Ȥθξ֤ᤷ˺).
+
+ * protect .. endΥХ. GC_LINKΥͥȤä.
+
+ * joinΥХ(ȤäƤ륪֥ȤfreeƤ).
+
+ * splitΥХ(르ꥺबä).
+
+ * fork()ɲ.
+
+Wed Jan 26 17:09:56 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * եƥȥ᥽åɤɲ.
+
+ * rb_autoexec(): 饹ƥεưǤ
+ ˤ. autoload¸Ǥ. ˤȤʤ᥽å
+ unknownϤʤʤä.
+
+Tue Jan 25 15:51:36 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * Dbm饹, Math⥸塼.
+
+ * -IץǥѥɲäǤ褦.
+
+ * ѥѿ$load_pathǤ褦.
+
+ * load(): ʥߥåɤȤ褦ˤ.
+
+Tue Jan 18 14:14:01 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * Comparable:"<=>"
+
+ * Float,Fixnum:"**"
+
+ * Array:sort
+
+Fri Jan 14 16:53:37 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * version 0.07
+
+ * ᥽åɤ˴ؤɥȤ򽼼¤.
+
+ * String:index(): position䤷.
+
+Thu Jan 13 15:13:52 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * ̤ѿʤ.
+
+ * ̵̤hash tableΥ.
+
+ * Purify'd(on Sun)
+
+ * ~RE ~STRΥѥŸ.
+
+ * Sunذܿ. signal(). RDatabug.
+
+ * parse.y: nls롼.
+
+ * yylex(): Ԥβʬѹ.
+
+ * missing/strftime.c: ܿ.
+
+ * Time:strftime: ¾Υ᥽åɤstrftimeѤ褦.
+
+ * ᥽åɺ˥᥽åɥå򥯥ꥢ.
+
+Fri Jan 7 15:23:20 1994 Yukihiro Matsumoto (matz at nws119)
+
+ * Float:coerce(): FixnumFloatʳΰͿ뤿ˤ㳰
+ ȯ褦.
+
+ * Stat: stat¤ΤƤΥФФ륢᥽åɤѰ.
+
+ * ̤Υ饹/⥸塼ؤλȤunknown᥽åɤƤӽФ褦
+ ˤ.
+
+ * baseline - version 0.06.
diff --git a/FAQ b/FAQ
new file mode 100644
index 0000000000..eaa9c86056
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,65 @@
+.\" FAQ - -*- Indented-Text -*- created at: Tue Jan 4 12:48:12 JST 1994
+Ruby˴ؤFAQ(Frequently Asked Question)ޤȤ᤿ΤǤ.
+EmacsǤΥե򸫤ƤˤM-2 C-x $Ǽʬɤळ
+ȤǤޤ. C-x $Ƥɤ֤᤻ޤ.
+------------------------------
+1) Ruby̾Τͳ
+
+ RubyϲάΤǤϤʤ, ֤Ф̾Ȥä̾դޤ.
+ perl(pearl6)μܻؤ(Ruby7)Ȥ̣
+ ޤ. ͤΤǤäߤȤꤤޤޤ
+ Ƥޤ.
+------------------------------
+2) Rubyƶ?
+
+ RubyθͤC, Perl, EiffelγƸˤν˱ƶƤ
+ . ¾˱ƶȤƤtcl, AWK, bourne shell, CLU,
+ Icon, Sather, Smalltalk, Emacs Lispʤɤޤ.
+------------------------------
+3) Rubyħ?
+
+ ʲ˲վˤޤ.
+
+ + 󥿥ץ꥿Ǥ, ڤ˥ץǤ.
+ + ѿפʤ.
+ + ե䥹ȥ꡼ʤɤؤUNIXŪñˤǤ.
+ + Хʥեⰷ.
+ + Ȥ֥ȻظǤ.
+ + ðۥ᥽åɤεǽ󶡤Ƥ.
+ + ⥸塼ˤ뵡ǽζͭǤ.
+ + 㳰ǽ.
+ + GCΤǥ򵤤ˤʤƺѤ.
+ + Τ.
+ + ʣʥǡ¤ⰷ.
+ + ʥߥåɤ򥵥ݡȤ(ƥˤ).
+
+ դ˷
+
+ - ȯŸӾǵǽʤ.
+ - ǥХå(ޤ)ʤ.
+ - ٤.
+
+ ȤǤ礦.
+------------------------------
+4) RubyѿʤȤΥåȤ?
+
+ ѿɤΥפ°뤫ܤʬΤ, ץɤ
+ פ, 򤷰פȤΥåȤǤ. ޤȡΥ
+ ̤Τǽ񤭰פ, ڤˤ׸ޤ.
+------------------------------
+5) RubyPerlΰ㤤?
+
+ RubyPerlϥƥͥ줿ץȸǤȤ,
+ СϰϤ᤯, 츫ȥ饤Х˸ޤ. ΤRuby߷
+ ˤơPerlˤǤ뤳ȤϤǤ褦ˡפȤȤ, Ĥν
+ ɸǤ. , RubyοŪϤޤǤּڤʥ֥
+ ȻظפǤ, ޤ, Ruby߷פ3ɸǤ, ǽ
+ ĥη̤Ȥ, RubyPerlȤϰۤʤˤʤޤ.
+
+ RubyPerlۤɡֲǤ⤢פǤϤޤ, ۤȤɤξPerl
+ ®٤٤Ǥ. , RubyΥ֥ȻظǽPerlǤ
+ ­Ǥʤͤ˥ԡ뤹Ȼפޤ.
+------------------------------
+Local variables:
+fill-column: 70
+end:
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..a5d9bab730
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,202 @@
+# Generated automatically from Makefile.in by configure.
+# Main Makefile for GNU m4.
+# Copyright (C) 1992 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# 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.
+
+SHELL = /bin/sh
+
+#### Start of system configuration section. ####
+
+srcdir = .
+VPATH = .
+
+CC = gcc -traditional
+DBM = -fpcc-struct-return
+YACC = bison -y
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+MAKEINFO = makeinfo
+
+CFLAGS = -g
+LDFLAGS = -static $(CFLAGS)
+LIBS = -lm -ldbm
+DEFS = -DHAVE_UNISTD_H=1 -DHAVE_SYSCALL_H=1 -DHAVE_A_OUT_H=1 -DDIRENT=1 -DGETGROUPS_T=int -DRETSIGTYPE=void -DHAVE_STRTOL=1 -DHAVE_STRDUP=1 -DHAVE_KILLPG=1 -DHAVE_MKDIR=1 -DHAVE_STRFTIME=1 -DHAVE_PUTENV=1 -DHAVE_ALLOCA_H=1 -DPW_AGE=1 -DPW_COMMENT=1
+
+prefix = /usr/local
+binprefix =
+exec_prefix = $(prefix)
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+
+#### End of system configuration section. ####
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I$(srcdir)/lib $<
+
+HDRS = defines.h \
+ dln.h \
+ ident.h \
+ io.h \
+ node.h \
+ re.h \
+ regex.h \
+ ruby.h \
+ st.h \
+ version.h
+
+SRCS = array.c \
+ autoexec.c \
+ class.c \
+ compar.c \
+ dbm.c \
+ dict.c \
+ dir.c \
+ dln.c \
+ enum.c \
+ error.c \
+ etc.c \
+ eval.c \
+ file.c \
+ gc.c \
+ inits.c \
+ io.c \
+ math.c \
+ methods.c \
+ missing.c \
+ numeric.c \
+ object.c \
+ pack.c \
+ parse.y \
+ process.c \
+ random.c \
+ range.c \
+ re.c \
+ regex.c \
+ ruby.c \
+ socket.c \
+ sprintf.c \
+ st.c \
+ string.c \
+ struct.c \
+ time.c \
+ variable.c \
+ version.c
+
+OBJS = array.o \
+ autoexec.o \
+ class.o \
+ compar.o \
+ dbm.o \
+ dict.o \
+ dir.o \
+ dln.o \
+ enum.o \
+ error.o \
+ etc.o \
+ eval.o \
+ file.o \
+ gc.o \
+ inits.o \
+ io.o \
+ math.o \
+ methods.o \
+ missing.o \
+ numeric.o \
+ object.o \
+ pack.o \
+ parse.o \
+ process.o \
+ random.o \
+ range.o \
+ re.o \
+ regex.o \
+ ruby.o \
+ socket.o \
+ sprintf.o \
+ st.o \
+ string.o \
+ struct.o \
+ time.o \
+ variable.o \
+ version.o
+
+DISTFILES = README NEWS TODO THANKS COPYING INSTALL \
+ChangeLog Makefile.in configure.in \
+$(HDRS) $(SRCS) configure
+
+PROGRAM = ruby
+
+all: $(PROGRAM)
+
+$(PROGRAM): $(OBJS)
+ @echo -n "Loading $(PROGRAM) ... "
+ @rm -f $(PROGRAM)
+ @$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
+ @echo "done"
+
+install: $(PROGMAM)
+ $(INSTALL_PROGRAM) $(PROGRAM) $(bindir)/$(PROGRAM)
+
+clean:; @rm -f $(OBJS)
+
+realclean:; @rm -f $(OBJS)
+ @rm -f core ruby *~
+
+dbm.o:dbm.c
+ $(CC) -c $(DBM) $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I$(srcdir)/lib dbm.c
+
+# Prevent GNU make v3 from overflowing arg limit on SysV.
+.NOEXPORT:
+###
+array.o : array.c ruby.h defines.h
+autoexec.o : autoexec.c ruby.h defines.h
+class.o : class.c ruby.h defines.h node.h st.h
+compar.o : compar.c ruby.h defines.h
+dbm.o : dbm.c ruby.h defines.h
+dict.o : dict.c ruby.h defines.h st.h
+dir.o : dir.c ruby.h defines.h
+dln.o : dln.c defines.h dln.h
+enum.o : enum.c ruby.h defines.h
+error.o : error.c ruby.h defines.h
+etc.o : etc.c ruby.h defines.h
+eval.o : eval.c ruby.h defines.h node.h ident.h st.h
+file.o : file.c ruby.h defines.h io.h
+gc.o : gc.c ruby.h defines.h st.h
+inits.o : inits.c
+io.o : io.c ruby.h defines.h io.h
+math.o : math.c ruby.h defines.h
+methods.o : methods.c ruby.h defines.h node.h
+missing.o : missing.c ruby.h defines.h missing/memmove.c missing/strerror.c \
+ missing/strtoul.c missing/strftime.c missing/getopt.h missing/getopt.c missing/getopt1.c
+numeric.o : numeric.c ruby.h defines.h
+object.o : object.c ruby.h defines.h
+pack.o : pack.c ruby.h defines.h
+process.o : process.c ruby.h defines.h st.h
+random.o : random.c ruby.h defines.h
+range.o : range.c ruby.h defines.h
+re.o : re.c ruby.h defines.h re.h regex.h
+regex.o : regex.c regex.h
+ruby.o : ruby.c ruby.h defines.h re.h regex.h missing/getopt.h
+socket.o : socket.c ruby.h defines.h io.h
+sprintf.o : sprintf.c ruby.h defines.h
+st.o : st.c st.h
+string.o : string.c ruby.h defines.h re.h regex.h
+struct.o : struct.c ruby.h defines.h
+time.o : time.c ruby.h defines.h
+variable.o : variable.c ruby.h defines.h st.h ident.h
+version.o : version.c ruby.h defines.h \
+ version.h
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000000..33338c9e61
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,186 @@
+SHELL = /bin/sh
+
+#### Start of system configuration section. ####
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+DBM = @DBM@
+YACC = @YACC@
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+MAKEINFO = makeinfo
+
+CFLAGS = -g
+LDFLAGS = @STATIC@ $(CFLAGS)
+LIBS = @LIBS@
+DEFS = @DEFS@
+
+prefix = /usr/local
+binprefix =
+exec_prefix = $(prefix)
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+
+#### End of system configuration section. ####
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I$(srcdir)/lib $<
+
+HDRS = defines.h \
+ dln.h \
+ ident.h \
+ io.h \
+ node.h \
+ re.h \
+ regex.h \
+ ruby.h \
+ st.h \
+ version.h
+
+SRCS = array.c \
+ bignum.c \
+ class.c \
+ compar.c \
+ dbm.c \
+ dict.c \
+ dir.c \
+ dln.c \
+ enum.c \
+ error.c \
+ etc.c \
+ eval.c \
+ file.c \
+ gc.c \
+ inits.c \
+ io.c \
+ math.c \
+ methods.c \
+ missing.c \
+ numeric.c \
+ object.c \
+ pack.c \
+ parse.y \
+ process.c \
+ random.c \
+ range.c \
+ re.c \
+ regex.c \
+ ruby.c \
+ socket.c \
+ sprintf.c \
+ st.c \
+ string.c \
+ struct.c \
+ time.c \
+ variable.c \
+ version.c
+
+OBJS = array.o \
+ bignum.o \
+ class.o \
+ compar.o \
+ dbm.o \
+ dict.o \
+ dir.o \
+ dln.o \
+ enum.o \
+ error.o \
+ etc.o \
+ eval.o \
+ file.o \
+ gc.o \
+ inits.o \
+ io.o \
+ math.o \
+ methods.o \
+ missing.o \
+ numeric.o \
+ object.o \
+ pack.o \
+ parse.o \
+ process.o \
+ random.o \
+ range.o \
+ re.o \
+ regex.o \
+ ruby.o \
+ socket.o \
+ sprintf.o \
+ st.o \
+ string.o \
+ struct.o \
+ time.o \
+ variable.o \
+ version.o
+
+DISTFILES = README NEWS TODO THANKS COPYING INSTALL \
+ChangeLog Makefile.in configure.in \
+$(HDRS) $(SRCS) configure
+
+PROGRAM = ruby
+
+all: $(PROGRAM)
+
+$(PROGRAM): $(OBJS)
+ @echo -n "Loading $(PROGRAM) ... "
+ @rm -f $(PROGRAM)
+ @$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
+ @echo "done"
+
+install: $(PROGMAM)
+ $(INSTALL_PROGRAM) $(PROGRAM) $(bindir)/$(PROGRAM)
+
+clean:; @rm -f $(OBJS)
+
+realclean:; @rm -f $(OBJS)
+ @rm -f core ruby *~
+
+dbm.o:dbm.c
+ $(CC) -c $(DBM) $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I$(srcdir)/lib dbm.c
+
+# Prevent GNU make v3 from overflowing arg limit on SysV.
+.NOEXPORT:
+###
+parse.o : parse.y ruby.h defines.h env.h ident.h node.h st.h regex.h
+###
+array.o : array.c ruby.h defines.h
+bignum.o : bignum.c ruby.h defines.h
+class.o : class.c ruby.h defines.h env.h node.h st.h
+compar.o : compar.c ruby.h defines.h
+dbm.o : dbm.c ruby.h defines.h
+dict.o : dict.c ruby.h defines.h st.h
+dir.o : dir.c ruby.h defines.h
+dln.o : dln.c defines.h dln.h
+enum.o : enum.c ruby.h defines.h
+error.o : error.c ruby.h defines.h env.h
+etc.o : etc.c ruby.h defines.h
+eval.o : eval.c ruby.h defines.h env.h node.h ident.h st.h
+file.o : file.c ruby.h defines.h io.h
+gc.o : gc.c ruby.h defines.h env.h st.h
+inits.o : inits.c
+io.o : io.c ruby.h defines.h io.h
+math.o : math.c ruby.h defines.h
+methods.o : methods.c ruby.h defines.h env.h node.h
+missing.o : missing.c ruby.h defines.h missing/memmove.c missing/strerror.c \
+ missing/strtoul.c missing/strftime.c missing/strstr.c missing/getopt.h missing/getopt.c \
+ missing/getopt1.c missing/mkdir.c
+numeric.o : numeric.c ruby.h defines.h env.h
+object.o : object.c ruby.h defines.h env.h node.h st.h
+pack.o : pack.c ruby.h defines.h
+process.o : process.c ruby.h defines.h st.h
+random.o : random.c ruby.h defines.h
+range.o : range.c ruby.h defines.h
+re.o : re.c ruby.h defines.h re.h regex.h
+regex.o : regex.c regex.h
+ruby.o : ruby.c ruby.h defines.h re.h regex.h missing/getopt.h
+socket.o : socket.c ruby.h defines.h
+sprintf.o : sprintf.c ruby.h defines.h
+st.o : st.c st.h
+string.o : string.c ruby.h defines.h re.h regex.h
+struct.o : struct.c ruby.h defines.h env.h
+time.o : time.c ruby.h defines.h
+variable.o : variable.c ruby.h defines.h env.h node.h ident.h st.h
+version.o : version.c ruby.h defines.h version.h
diff --git a/ToDo b/ToDo
new file mode 100644
index 0000000000..b909816583
--- /dev/null
+++ b/ToDo
@@ -0,0 +1,6 @@
+* $0ؤpsνϤѲ褦
+* rubyѿhookμ¸
+* FUNCΥ᥽åɤɬפ
+* write debugger for ruby
+* re-write regex code for speeding
+* byte code interpretor
diff --git a/array.c b/array.c
new file mode 100644
index 0000000000..91c06a3f6f
--- /dev/null
+++ b/array.c
@@ -0,0 +1,805 @@
+/************************************************
+
+ array.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:20 $
+ created at: Fri Aug 6 09:46:12 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+VALUE C_Array;
+
+static ID eq;
+
+#define ARY_DEFAULT_SIZE 16
+
+VALUE
+ary_new2(len)
+{
+ NEWOBJ(ary, struct RArray);
+ OBJSETUP(ary, C_Array, T_ARRAY);
+
+ GC_LINK;
+ GC_PRO(ary);
+ ary->len = 0;
+ ary->capa = len;
+ ary->ptr = ALLOC_N(VALUE, len);
+ GC_UNLINK;
+
+ return (VALUE)ary;
+}
+
+VALUE
+ary_new()
+{
+ return ary_new2(ARY_DEFAULT_SIZE);
+}
+
+#include <varargs.h>
+
+VALUE
+ary_new3(n, va_alist)
+ int n;
+ va_dcl
+{
+ va_list ar;
+ struct RArray* ary;
+ int len, i;
+
+ if (n < 0) {
+ Fail("Negative number of items(%d)", n);
+ }
+ ary = (struct RArray*)ary_new2(n<ARY_DEFAULT_SIZE?ARY_DEFAULT_SIZE:n);
+
+ va_start(ar);
+ for (i=0; i<n; i++) {
+ ary->ptr[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+
+ ary->len = n;
+ return (VALUE)ary;
+}
+
+VALUE
+ary_new4(n, elts)
+ int n;
+ VALUE *elts;
+{
+ struct RArray* ary;
+
+ GC_LINK;
+ GC_PRO4(elts, n);
+ ary = (struct RArray*)ary_new2(n);
+ memcpy(ary->ptr, elts, sizeof(VALUE)*n);
+ ary->len = n;
+ GC_UNLINK;
+
+ return (VALUE)ary;
+}
+
+VALUE
+assoc_new(elm1, elm2)
+ VALUE elm1, elm2;
+{
+ struct RArray *ary;
+
+ GC_LINK;
+ GC_PRO(elm1); GC_PRO(elm2);
+ ary = (struct RArray*)ary_new2(2);
+ ary->ptr[0] = elm1;
+ ary->ptr[1] = elm2;
+ ary->len = 2;
+ GC_UNLINK;
+
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_new(class)
+ VALUE class;
+{
+ NEWOBJ(ary, struct RArray);
+ OBJSETUP(ary, class, T_ARRAY);
+
+ GC_LINK;
+ GC_PRO(ary);
+ ary->len = 0;
+ ary->capa = ARY_DEFAULT_SIZE;
+ ary->ptr = ALLOC_N(VALUE, ARY_DEFAULT_SIZE);
+ GC_UNLINK;
+
+ return (VALUE)ary;
+}
+
+static void
+astore(ary, idx, val)
+ struct RArray *ary;
+ int idx;
+ VALUE val;
+{
+ int max;
+
+ if (idx < 0) {
+ Fail("negative index for array");
+ }
+
+ max = idx + 1;
+ if (idx >= ary->capa) {
+ GC_LINK; GC_PRO(val);
+ ary->capa = max;
+ REALLOC_N(ary->ptr, VALUE, max);
+ GC_UNLINK;
+ }
+ if (idx >= ary->len) {
+ bzero(ary->ptr+ary->len, sizeof(VALUE)*(max-ary->len));
+ }
+
+ if (idx >= ary->len) {
+ ary->len = idx + 1;
+ }
+ ary->ptr[idx] = val;
+}
+
+VALUE
+Fary_push(ary, item)
+ struct RArray *ary;
+ VALUE item;
+{
+ astore(ary, ary->len, item);
+ return item;
+}
+
+static VALUE
+Fary_append(ary, item)
+ struct RArray *ary;
+ VALUE item;
+{
+ astore(ary, ary->len, item);
+ return (VALUE)ary;
+}
+
+VALUE
+Fary_pop(ary)
+ struct RArray *ary;
+{
+ if (ary->len == 0) return Qnil;
+ return ary->ptr[--ary->len];
+}
+
+VALUE
+Fary_shift(ary)
+ struct RArray *ary;
+{
+ VALUE top;
+
+ if (ary->len == 0) return Qnil;
+
+ top = ary->ptr[0];
+ ary->len--;
+
+ /* sliding items */
+ memmove(ary->ptr, ary->ptr+1, sizeof(VALUE)*(ary->len));
+
+ return top;
+}
+
+VALUE
+Fary_unshift(ary, item)
+ struct RArray *ary;
+{
+ VALUE top;
+
+ if (ary->len >= ary->capa) {
+ ary->capa+=ARY_DEFAULT_SIZE;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
+
+ /* sliding items */
+ memmove(ary->ptr+1, ary->ptr, sizeof(VALUE)*(ary->len));
+
+ ary->len++;
+ return ary->ptr[0] = item;
+}
+
+VALUE
+ary_entry(ary, offset)
+ struct RArray *ary;
+ int offset;
+{
+ if (ary->len == 0) return Qnil;
+
+ if (offset < 0) {
+ offset = ary->len + offset;
+ }
+ if (offset < 0 || ary->len <= offset) {
+ return Qnil;
+ }
+
+ return ary->ptr[offset];
+}
+
+static VALUE
+ary_subseq(ary, beg, len)
+ struct RArray *ary;
+ int beg, len;
+{
+ struct RArray *ary2;
+ VALUE *ptr;
+
+ if (beg < 0) {
+ beg = ary->len + beg;
+ if (beg < 0) beg = 0;
+ }
+ if (len < 0) {
+ Fail("negative length for sub-array(size: %d)", ary->len);
+ }
+ if (len == 0) {
+ return ary_new();
+ }
+ if (beg + len > ary->len) {
+ len = ary->len - beg;
+ }
+
+ ary2 = (struct RArray*)ary_new2(len);
+ memmove(ary2->ptr, ary->ptr+beg, sizeof(VALUE)*len);
+ ary2->len = len;
+
+ return (VALUE)ary2;
+}
+
+extern VALUE C_Range;
+
+static void
+range_beg_end(range, begp, lenp, len)
+ VALUE range;
+ int *begp, *lenp;
+ int len;
+{
+ int beg, end;
+
+ beg = rb_iv_get(range, "start"); beg = NUM2INT(beg);
+ end = rb_iv_get(range, "end"); end = NUM2INT(end);
+ if (beg < 0) {
+ beg = len + beg;
+ if (beg < 0) beg = 0;
+ }
+ if (end < 0) {
+ end = len + end;
+ if (end < 0) end = 0;
+ }
+ if (beg > end) {
+ int tmp;
+
+ if (verbose) {
+ Warning("start %d is bigger than end %d", beg, end);
+ }
+ tmp = beg; beg = end; end = tmp;
+ }
+ *begp = beg; *lenp = end - beg + 1;
+}
+
+static VALUE
+Fary_aref(ary, args)
+ struct RArray *ary;
+ VALUE args;
+{
+ VALUE arg1, arg2;
+
+ if (rb_scan_args(args, "11", &arg1, &arg2) == 2) {
+ int beg, len;
+
+ beg = NUM2INT(arg1);
+ len = NUM2INT(arg2);
+ if (len <= 0) {
+ return ary_new();
+ }
+ return ary_subseq(ary, beg, len);
+ }
+
+ /* check if idx is Range */
+ if (obj_is_kind_of(arg1, C_Range)) {
+ int beg, len;
+
+ range_beg_end(arg1, &beg, &len, ary->len);
+ return ary_subseq(ary, beg, len);
+ }
+
+ return ary_entry(ary, NUM2INT(arg1));
+}
+
+static VALUE
+Fary_aset(ary, args)
+ struct RArray *ary;
+ VALUE args;
+{
+ VALUE arg1, arg2;
+ struct RArray *arg3;
+ int offset;
+
+ if (rb_scan_args(args, "21", &arg1, &arg2, &arg3) == 3) {
+ int beg, len;
+
+ beg = NUM2INT(arg1);
+ Check_Type(arg3, T_ARRAY);
+ 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);
+ }
+ bzero(ary->ptr+ary->len, sizeof(VALUE)*(beg-ary->len));
+ memcpy(ary->ptr+beg, arg3->ptr, sizeof(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,
+ sizeof(VALUE)*(ary->len-(beg+len)));
+ memmove(ary->ptr+beg, arg3->ptr, sizeof(VALUE)*arg3->len);
+ ary->len = alen;
+ }
+ return (VALUE)arg3;
+ }
+
+ /* check if idx is Range */
+ if (obj_is_kind_of(arg1, C_Range)) {
+ int beg, len;
+
+ Check_Type(arg2, T_ARRAY);
+ range_beg_end(arg1, &beg, &len, ary->len);
+ if (ary->len < beg) {
+ len = beg + RARRAY(arg2)->len;
+ if (len >= ary->capa) {
+ ary->capa=len;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
+ bzero(ary->ptr+ary->len, sizeof(VALUE)*(beg-ary->len));
+ memcpy(ary->ptr+beg, RARRAY(arg2)->ptr,
+ sizeof(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,
+ sizeof(VALUE)*(ary->len-(beg+len)));
+ memmove(ary->ptr+beg, RARRAY(arg2)->ptr,
+ sizeof(VALUE)*RARRAY(arg2)->len);
+ ary->len = alen;
+ }
+ return arg2;
+ }
+
+ offset = NUM2INT(arg1);
+ if (offset < 0) {
+ offset = ary->len + offset;
+ }
+ astore(ary, offset, arg2);
+ return arg2;
+}
+
+static VALUE
+Fary_each(ary)
+ struct RArray *ary;
+{
+ int i;
+
+ if (iterator_p()) {
+ for (i=0; i<ary->len; i++) {
+ rb_yield(ary->ptr[i]);
+ }
+ }
+ else {
+ return (VALUE)ary;
+ }
+}
+
+static VALUE
+Fary_length(ary)
+ struct RArray *ary;
+{
+ return INT2FIX(ary->len);
+}
+
+static VALUE
+Fary_clone(ary)
+ struct RArray *ary;
+{
+ VALUE ary2 = ary_new2(ary->len);
+
+ CLONESETUP(ary2, ary);
+ memcpy(RARRAY(ary2)->ptr, ary->ptr, sizeof(VALUE)*ary->len);
+ RARRAY(ary2)->len = ary->len;
+ return ary2;
+}
+
+extern VALUE OFS;
+
+VALUE
+ary_join(ary, sep)
+ struct RArray *ary;
+ struct RString *sep;
+{
+ int i;
+ VALUE result, tmp;
+ if (ary->len == 0) return str_new(0, 0);
+
+ if (TYPE(ary->ptr[0]) == T_STRING)
+ result = Fstr_clone(ary->ptr[0]);
+ else
+ result = obj_as_string(ary->ptr[0]);
+
+ GC_LINK;
+ GC_PRO(result);
+ GC_PRO2(tmp);
+
+ for (i=1; i<ary->len; i++) {
+ int need_free = 1;
+ tmp = ary->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ need_free = 0;
+ break;
+ case T_ARRAY:
+ tmp = ary_join(tmp, sep);
+ break;
+ default:
+ tmp = obj_as_string(tmp);
+ }
+ if (sep) str_cat(result, sep->ptr, sep->len);
+ str_cat(result, RSTRING(tmp)->ptr, RSTRING(tmp)->len);
+ if (need_free == 1) obj_free(tmp);
+ }
+
+ GC_UNLINK;
+
+ return result;
+}
+
+static VALUE
+Fary_join(ary, args)
+ struct RArray *ary;
+ VALUE args;
+{
+ VALUE sep;
+
+ rb_scan_args(args, "01", &sep);
+ if (sep == Qnil) sep = OFS;
+
+ if (sep != Qnil)
+ Check_Type(sep, T_STRING);
+
+ return ary_join(ary, sep);
+}
+
+VALUE
+Fary_to_s(ary)
+ VALUE ary;
+{
+ VALUE str = ary_join(ary, OFS);
+ if (str == Qnil) return str_new(0, 0);
+ return str;
+}
+
+static VALUE
+Fary_inspect(ary)
+ struct RArray *ary;
+{
+ int i, len;
+ VALUE str;
+ char *p;
+
+ ary = (struct RArray*)Fary_clone(ary);
+ GC_LINK;
+ GC_PRO(ary);
+
+ len = ary->len;
+ for (i=0; i<len; i++) {
+ ary->ptr[i] = rb_funcall(ary->ptr[i], rb_intern("_inspect"), 0, Qnil);
+ }
+
+ GC_PRO3(str, str_new2(", "));
+ str = ary_join(ary, str);
+ if (str == Qnil) return str_new2("[]");
+ len = RSTRING(str)->len;
+ str_grow(str, len+2);
+ p = RSTRING(str)->ptr;
+ memmove(p+1, p, len);
+ p[0] = '[';
+ p[len+1] = ']';
+
+ GC_UNLINK;
+
+ return str;
+}
+
+static VALUE
+Fary_to_a(ary)
+ VALUE ary;
+{
+ return ary;
+}
+
+static VALUE
+Fary_reverse(ary)
+ struct RArray *ary;
+{
+ VALUE ary2 = ary_new2(ary->len);
+ int i, j;
+
+ for (i=ary->len-1, j=0; i >=0; i--, j++) {
+ RARRAY(ary2)->ptr[j] = ary->ptr[i];
+ }
+ RARRAY(ary2)->len = ary->len;
+
+ return ary2;
+}
+
+static ID cmp;
+
+static int
+sort_1(a, b)
+ VALUE *a, *b;
+{
+ VALUE retval = rb_yield(assoc_new(*a, *b));
+ return NUM2INT(retval);
+}
+
+static int
+sort_2(a, b)
+ VALUE *a, *b;
+{
+ VALUE retval = rb_funcall(*a, cmp, 1, *b);
+ return NUM2INT(retval);
+}
+
+VALUE
+Fary_sort(ary)
+ struct RArray *ary;
+{
+ qsort(ary->ptr, ary->len, sizeof(VALUE), iterator_p()?sort_1:sort_2);
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_delete(ary, item)
+ struct RArray *ary;
+ VALUE item;
+{
+ int i1, i2;
+
+ for (i1 = i2 = 0; i1 < ary->len; i1++) {
+ if (rb_funcall(ary->ptr[i1], eq, 1, item)) continue;
+ if (i1 != i2) {
+ ary->ptr[i2] = ary->ptr[i1];
+ }
+ i2++;
+ }
+ ary->len = i2;
+
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_delete_if(ary)
+ struct RArray *ary;
+{
+ int i1, i2;
+
+ for (i1 = i2 = 0; i1 < ary->len; i1++) {
+ if (rb_yield(ary->ptr[i1])) continue;
+ if (i1 != i2) {
+ ary->ptr[i2] = ary->ptr[i1];
+ }
+ i2++;
+ }
+ ary->len = i2;
+
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_clear(ary)
+ struct RArray *ary;
+{
+ ary->len = 0;
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_fill(ary, args)
+ struct RArray *ary;
+ VALUE args;
+{
+ VALUE item, arg1, arg2;
+ int beg, len, end;
+ VALUE *p, *pend;
+
+ rb_scan_args(args, "12", &item, &arg1, &arg2);
+ if (arg2 == Qnil && obj_is_kind_of(arg1, C_Range)) {
+ range_beg_end(arg1, &beg, &len, ary->len);
+ }
+ else {
+ beg = NUM2INT(arg1);
+ if (beg < 0) {
+ beg = ary->len + beg;
+ if (beg < 0) beg = 0;
+ }
+ if (arg2) {
+ len = NUM2INT(arg2);
+ }
+ else {
+ len = ary->len - beg;
+ }
+ }
+ end = beg + len;
+ if (end > ary->len) {
+ if (end >= ary->capa) {
+ ary->capa=end;
+ REALLOC_N(ary->ptr, VALUE, ary->capa);
+ }
+ if (beg > ary->len) {
+ bzero(ary->ptr+ary->len, sizeof(VALUE)*(end-ary->len));
+ }
+ ary->len = end;
+ }
+ p = ary->ptr + beg; pend = p + len;
+
+ while (p < pend) {
+ *p++ = item;
+ }
+ return (VALUE)ary;
+}
+
+static VALUE
+Fary_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, x->len*sizeof(VALUE));
+ memcpy(z->ptr+x->len, y->ptr, y->len*sizeof(VALUE));
+ z->len = x->len + RARRAY(y)->len;
+ break;
+
+ default:
+ GC_LINK;
+ GC_PRO3(z, (struct RArray*)Fary_clone(x));
+ Fary_push(z, y);
+ GC_UNLINK;
+ break;
+ }
+ return (VALUE)z;
+}
+
+static VALUE
+Fary_times(ary, times)
+ struct RArray *ary;
+ VALUE times;
+{
+ struct RArray *ary2;
+ int i, len;
+
+ len = NUM2INT(times) * ary->len;
+ ary2 = (struct RArray*)ary_new2(len);
+ ary2->len = len;
+
+ for (i=0; i<len; i+=ary->len) {
+ memcpy(ary2->ptr+i, ary->ptr, ary->len*sizeof(VALUE));
+ }
+
+ return (VALUE)ary2;
+}
+
+VALUE
+Fary_assoc(ary, key)
+ struct RArray *ary;
+ VALUE key;
+{
+ VALUE *p, *pend;
+
+ p = ary->ptr; pend = p + ary->len;
+ while (p < pend) {
+ if (TYPE(*p) == T_ARRAY
+ && RARRAY(*p)->len == 2
+ && rb_funcall(RARRAY(*p)->ptr[0], eq, 1, key))
+ return *p;
+ }
+ return Qnil;
+}
+
+VALUE
+Fary_rassoc(ary, value)
+ struct RArray *ary;
+ VALUE value;
+{
+ VALUE *p, *pend;
+
+ p = ary->ptr; pend = p + ary->len;
+ while (p < pend) {
+ if (TYPE(*p) == T_ARRAY
+ && RARRAY(*p)->len == 2
+ && rb_funcall(RARRAY(*p)->ptr[1], eq, 1, value))
+ return *p;
+ }
+ return Qnil;
+}
+
+extern VALUE C_Kernel;
+extern VALUE M_Enumerable;
+
+Init_Array()
+{
+ C_Array = rb_define_class("Array", C_Object);
+ rb_include_module(C_Array, M_Enumerable);
+
+ rb_define_single_method(C_Array, "new", Fary_new, 0);
+ rb_define_method(C_Array, "to_s", Fary_to_s, 0);
+ rb_define_method(C_Array, "_inspect", Fary_inspect, 0);
+ rb_define_method(C_Array, "to_a", Fary_to_a, 0);
+
+ rb_define_method(C_Array, "[]", Fary_aref, -2);
+ rb_define_method(C_Array, "[]=", Fary_aset, -2);
+ rb_define_method(C_Array, "<<", Fary_append, 1);
+ rb_define_method(C_Array, "push", Fary_push, 1);
+ rb_define_method(C_Array, "pop", Fary_pop, 0);
+ rb_define_method(C_Array, "shift", Fary_shift, 0);
+ rb_define_method(C_Array, "unshift", Fary_unshift, 1);
+ rb_define_method(C_Array, "each", Fary_each, 0);
+ rb_define_method(C_Array, "length", Fary_length, 0);
+ rb_define_method(C_Array, "clone", Fary_clone, 0);
+ rb_define_method(C_Array, "join", Fary_join, -2);
+ rb_define_method(C_Array, "reverse", Fary_reverse, 0);
+ rb_define_method(C_Array, "sort", Fary_sort, 0);
+ rb_define_method(C_Array, "delete", Fary_delete, 1);
+ rb_define_method(C_Array, "delete_if", Fary_delete_if, 0);
+ rb_define_method(C_Array, "clear", Fary_clear, 0);
+ rb_define_method(C_Array, "fill", Fary_fill, -2);
+
+ rb_define_method(C_Array, "assoc", Fary_assoc, 1);
+ rb_define_method(C_Array, "rassoc", Fary_rassoc, 1);
+
+ rb_define_method(C_Array, "+", Fary_plus, 1);
+ rb_define_method(C_Array, "*", Fary_times, 1);
+
+ cmp = rb_intern("<=>");
+ eq = rb_intern("==");
+
+ rb_define_method(C_Kernel, "::", assoc_new, 1);
+}
diff --git a/bignum.c b/bignum.c
new file mode 100644
index 0000000000..ce124a6362
--- /dev/null
+++ b/bignum.c
@@ -0,0 +1,1121 @@
+/************************************************
+
+ bignum.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:21 $
+ created at: Fri Jun 10 00:48:55 JST 1994
+
+************************************************/
+
+#include "ruby.h"
+#include <ctype.h>
+
+extern VALUE C_Integer;
+VALUE C_Bignum;
+
+#define BDIGITS(x) RBIGNUM(x)->digits
+#define BITSPERDIG (sizeof(USHORT)*CHAR_BIT)
+#define BIGRAD (1L << BITSPERDIG)
+#define DIGSPERLONG ((UINT)(sizeof(long)/sizeof(USHORT)))
+#define BIGUP(x) ((unsigned long)(x) << BITSPERDIG)
+#define BIGDN(x) ((x) >> BITSPERDIG)
+#define BIGLO(x) ((x) & (BIGRAD-1))
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+static VALUE
+bignew_1(class, len, sign)
+ VALUE class;
+ UINT len;
+ char sign;
+{
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, C_Bignum, T_BIGNUM);
+ big->sign = sign;
+ big->len = len;
+ BDIGITS(big) = ALLOC_N(USHORT, len);
+
+ return (VALUE)big;
+}
+
+#define bignew(len,sign) bignew_1(C_Bignum,len,sign)
+
+static VALUE
+Fbig_new(class, y)
+ VALUE class;
+ struct RBignum *y;
+{
+ Check_Type(y, T_BIGNUM);
+ return bignew_1(class, y->len, y->sign);
+}
+
+VALUE
+Fbig_clone(x)
+ struct RBignum *x;
+{
+ VALUE z = bignew_1(CLASS_OF(x), x->len, x->sign);
+
+ bcopy(BDIGITS(x), BDIGITS(z), x->len*sizeof(USHORT));
+ return (VALUE)z;
+}
+
+void
+big_2comp(x) /* get 2's complement */
+ struct RBignum *x;
+{
+ UINT i = x->len;
+ USHORT *ds = BDIGITS(x);
+ long num;
+
+ while (i--) ds[i] = ~ds[i];
+ i = 0; num = 1;
+ do {
+ num += (long)ds[i];
+ ds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ } while (i < x->len);
+}
+
+VALUE
+bignorm(x)
+ struct RBignum *x;
+{
+ UINT len = x->len;
+ USHORT *ds = BDIGITS(x);
+
+ while (len-- && !ds[len]) ;
+ x->len = ++len;
+ if (len*sizeof(USHORT) <= sizeof(VALUE)) {
+ long num = 0;
+ while (len--) {
+ num = BIGUP(num) + ds[len];
+ }
+ if (x->sign) {
+ if (POSFIXABLE(num)) return INT2FIX(num);
+ }
+ else if (NEGFIXABLE(-num)) return INT2FIX(-num);
+ }
+ return (VALUE)x;
+}
+
+VALUE
+uint2big(n)
+ UINT n;
+{
+ UINT i = 0;
+ USHORT *digits;
+ struct RBignum *big;
+
+ i = 0;
+ big = (struct RBignum*)bignew(DIGSPERLONG, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLONG) {
+ digits[i++] = BIGLO(n);
+ n = BIGDN(n);
+ }
+
+ i = DIGSPERLONG;
+ while (i-- && !digits[i]) ;
+ big->len = i+1;
+ return (VALUE)big;
+}
+
+VALUE
+int2big(n)
+ int n;
+{
+ int neg = 0;
+ struct RBignum *big;
+
+ if (n < 0) {
+ n = -n;
+ neg = 1;
+ }
+ big = (struct RBignum*)uint2big(n);
+ if (neg) {
+ big->sign = FALSE;
+ }
+ return (VALUE)big;
+}
+
+VALUE
+uint2inum(n)
+ UINT n;
+{
+ if (POSFIXABLE(n)) return INT2FIX(n);
+ return uint2big(n);
+}
+
+VALUE
+int2inum(n)
+ int n;
+{
+ if (FIXABLE(n)) return INT2FIX(n);
+ return int2big(n);
+}
+
+VALUE
+str2inum(str, base)
+ char *str;
+ int base;
+{
+ char sign = 1, c;
+ unsigned long num;
+ UINT len, blen = 1, i;
+ VALUE z;
+ USHORT *zds;
+
+ while (isspace(*str)) str++;
+ if (*str == '-') {
+ str++;
+ sign = 0;
+ }
+ if (base == 0) {
+ if (*str == '0') {
+ str++;
+ if (*str == 'x' || *str == 'X') {
+ str++;
+ base = 16;
+ }
+ else {
+ base = 8;
+ }
+ if (*str == '\0') return INT2FIX(0);
+ }
+ else {
+ base = 10;
+ }
+ }
+ len = strlen(str);
+ if (base == 8) {
+ len = 3*len*sizeof(char);
+ }
+ else { /* base == 10 or 16 */
+ len = 4*len*sizeof(char);
+ }
+
+ if (len <= (sizeof(VALUE)*CHAR_BIT)) {
+ int result = strtoul(str, Qnil, base);
+
+ if (!sign) result = -result;
+ if (FIXABLE(result)) return INT2FIX(result);
+ return int2big(result);
+ }
+ len = (len/(sizeof(USHORT)*CHAR_BIT))+1;
+
+ z = bignew(len, sign);
+ zds = BDIGITS(z);
+ for (i=len;i--;) zds[i]=0;
+ while (c = *str++) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c = c - '0';
+ break;
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ c = c - 'a' + 10;
+ break;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ c = c - 'A' + 10;
+ break;
+ default:
+ c = base;
+ break;
+ }
+ if (c >= base) break;
+ i = 0;
+ num = c;
+ for (;;) {
+ while (i<blen) {
+ num += zds[i]*base;
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ if (num) {
+ blen++;
+ continue;
+ }
+ break;
+ }
+ }
+ return bignorm(z);
+}
+
+static char hexmap[] = "0123456789abcdef";
+VALUE
+big2str(x, base)
+ struct RBignum *x;
+ int base;
+{
+ VALUE t;
+ USHORT *ds;
+ UINT i, j, hbase;
+ VALUE ss;
+ char *s, c;
+
+ if (FIXNUM_P(x)) {
+ return fix2str(x, base);
+ }
+ i = x->len;
+ if (x->len == 0) return str_new2("0");
+ if (base == 10) {
+ j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i*241L)/800+2;
+ hbase = 10000;
+ }
+ else if (base == 16) {
+ j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i)/4+2;
+ hbase = 0x10000;
+ }
+ else if (base == 8) {
+ j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i)+2;
+ hbase = 010000;
+ }
+ else if (base == 2) {
+ j = (sizeof(USHORT)*CHAR_BIT*i)+2;
+ hbase = 020;
+ }
+ else {
+ Fail("bignum cannot treat base %d", base);
+ }
+
+ GC_LINK;
+ GC_PRO3(t, Fbig_clone(x));
+ ds = BDIGITS(t);
+ GC_PRO3(ss, str_new(0, j));
+ s = RSTRING(ss)->ptr;
+
+ s[0] = x->sign ? '+' : '-';
+ while (i && j) {
+ int k = i;
+ unsigned long num = 0;
+ while (k--) {
+ num = BIGUP(num) + ds[k];
+ ds[k] = num / hbase;
+ num %= hbase;
+ }
+ if (ds[i-1] == 0) i--;
+ k = 4;
+ while (k--) {
+ c = num % base;
+ s[--j] = hexmap[(int)c];
+ num /= base;
+ if (i == 0 && num == 0) break;
+ }
+ }
+ while (s[j] == '0') j++;
+ RSTRING(ss)->len -= x->sign?j:j-1;
+ memmove(x->sign?s:s+1, s+j, RSTRING(ss)->len);
+ s[RSTRING(ss)->len] = '\0';
+ GC_UNLINK;
+ return ss;
+}
+
+static VALUE
+Fbig_to_s(x)
+ struct RBignum *x;
+{
+ return big2str(x, 10);
+}
+
+int
+big2int(x)
+ struct RBignum *x;
+{
+ unsigned long num;
+ UINT len = x->len;
+ USHORT *ds;
+
+ if (len > sizeof(long)/sizeof(USHORT))
+ Fail("Bignum too big to convert into fixnum");
+ ds = BDIGITS(x);
+ num = 0;
+ while (len--) {
+ num = BIGUP(num);
+ num += ds[len];
+ }
+ if (!x->sign) return -num;
+ return num;
+}
+
+VALUE
+Fbig_to_i(x)
+ VALUE x;
+{
+ int v = big2int(x);
+
+ if (FIXABLE(v)) {
+ return INT2FIX(v);
+ }
+ return x;
+}
+
+VALUE
+dbl2big(d)
+ double d;
+{
+ UINT i = 0;
+ long c;
+ USHORT *digits;
+ VALUE z;
+ double u = (d < 0)?-d:d;
+
+ while (0 != floor(u)) {
+ u /= BIGRAD;
+ i++;
+ }
+ GC_LINK;
+ GC_PRO3(z, bignew(i, d>=0));
+ digits = BDIGITS(z);
+ while (i--) {
+ u *= BIGRAD;
+ c = floor(u);
+ u -= c;
+ digits[i] = c;
+ }
+ GC_UNLINK;
+ return bignorm(z);
+}
+
+double
+big2dbl(x)
+ struct RBignum *x;
+{
+ double d = 0.0;
+ UINT i = x->len;
+ USHORT *ds = BDIGITS(x);
+
+ while (i--) d = ds[i] + BIGRAD*d;
+ if (!x->sign) d = -d;
+ return d;
+}
+
+VALUE
+Fbig_to_f(x)
+ VALUE x;
+{
+ return float_new(big2dbl(x));
+}
+
+static VALUE
+Fbig_uminus(x)
+ struct RBignum *x;
+{
+ VALUE z = Fbig_clone(x);
+
+ RBIGNUM(z)->sign = !x->sign;
+
+ return bignorm(z);
+}
+
+static VALUE
+bigadd(x, y, sign)
+ struct RBignum *x, *y;
+ char sign;
+{
+ struct RBignum *z;
+ USHORT *zds;
+ long num;
+ UINT i, len;
+
+ len = MAX(x->len, y->len) + 1;
+ z = (struct RBignum*)bignew(len, sign==y->sign);
+ zds = BDIGITS(z);
+
+ i = len;
+ while (i--) zds[i] = 0;
+ i = y->len;
+ while (i--) zds[i] = BDIGITS(y)[i];
+
+ GC_LINK;
+ GC_PRO(z);
+ i = 0; num = 0;
+ if (x->sign == z->sign) {
+ 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) {
+ num += zds[i];
+ if (num < 0) {
+ zds[i++] = num + BIGRAD;
+ num = -1;
+ }
+ else {
+ zds[i++] = BIGLO(num);
+ num = 0;
+ }
+ }
+ }
+ GC_UNLINK;
+ return bignorm(z);
+}
+
+VALUE
+Fbig_plus(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(x, T_BIGNUM);
+ }
+ z = bigadd(x, y, 1);
+ GC_UNLINK;
+ return z;
+}
+
+VALUE
+Fbig_minus(x, y)
+ VALUE x, y;
+{
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+ x = bigadd(x, y, 0);
+ GC_UNLINK;
+
+ return x;
+}
+
+VALUE
+Fbig_mul(x, y)
+ struct RBignum *x, *y;
+{
+ UINT i = 0, j;
+ unsigned long n = 0;
+ VALUE z;
+ USHORT *zds;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = (struct RBignum*)int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+
+ j = x->len + y->len + 1;
+ z = bignew(j, x->sign==y->sign);
+ zds = BDIGITS(z);
+ while (j--) zds[j] = 0;
+ do {
+ j = 0;
+ if (BDIGITS(x)[i]) {
+ do {
+ n += zds[i + j] + ((unsigned long)BDIGITS(x)[i]*BDIGITS(y)[j]);
+ zds[i + j++] = BIGLO(n);
+ n = BIGDN(n);
+ } while (j < y->len);
+ if (n) {
+ zds[i + j] = n;
+ n = 0;
+ }
+ }
+ } while (++i < x->len);
+ GC_UNLINK;
+
+ return bignorm(z);
+}
+
+static void
+bigdivmod(x, y, div, mod)
+ struct RBignum *x, *y;
+ VALUE *div, *mod;
+{
+ UINT nx = x->len, ny = y->len, i, j;
+ VALUE z;
+ USHORT *xds, *yds, *zds, *tds;
+ unsigned long t2;
+ long num;
+ USHORT dd, q;
+
+ yds = BDIGITS(y);
+ if (ny == 0 && yds[0] == 0) Fail("divided by 0");
+ if (nx < ny) {
+ if (div) *div = INT2FIX(0);
+ if (mod) *mod = bignorm(x);
+ return;
+ }
+ xds = BDIGITS(x);
+ if (ny == 1) {
+ dd = yds[0];
+ GC_LINK;
+ GC_PRO3(z, Fbig_clone(x));
+ zds = BDIGITS(z);
+ t2 = 0; i = nx;
+ while(i--) {
+ t2 = BIGUP(t2) + zds[i];
+ zds[i] = t2 / dd;
+ t2 %= dd;
+ }
+ if (div) *div = bignorm(z);
+ if (mod) {
+ if (!y->sign) t2 = -t2;
+ *mod = FIX2INT(t2);
+ }
+ GC_UNLINK;
+ return;
+ }
+ GC_LINK;
+ GC_PRO3(z, bignew(nx==ny?nx+2:nx+1, x->sign==y->sign));
+ zds = BDIGITS(z);
+ if (nx==ny) zds[nx+1] = 0;
+ while (!yds[ny-1]) ny--;
+ if ((dd = BIGRAD/(yds[ny-1]+1)) != 1) {
+ GC_PRO3(y, (struct RBignum*)Fbig_clone(y));
+ tds = BDIGITS(y);
+ j = 0;
+ num = 0;
+ while (j<ny) {
+ num += (unsigned long)yds[j]*dd;
+ tds[j++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ yds = tds;
+ j = 0;
+ num = 0;
+ while (j<nx) {
+ num += (unsigned long)xds[j]*dd;
+ zds[j++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ zds[j] = num;
+ }
+ else {
+ zds[nx] = 0;
+ j = nx;
+ while (j--) zds[j] = xds[j];
+ }
+ j = nx==ny?nx+1:nx;
+ do {
+ if (zds[j] == yds[ny-1]) q = BIGRAD-1;
+ else q = (BIGUP(zds[j]) + zds[j-1])/yds[ny-1];
+ if (q) {
+ i = 0; num = 0; t2 = 0;
+ do { /* multiply and subtract */
+ t2 += (unsigned long)yds[i] * q;
+ num += zds[j - ny + i] - BIGLO(t2);
+ if (num < 0) {
+ zds[j - ny + i] = num + BIGRAD;
+ num = -1;
+ }
+ else {
+ zds[j - ny + i] = num;
+ num = 0;
+ }
+ t2 = BIGDN(t2);
+ } while (++i < ny);
+ num += zds[j - ny + i] - t2; /* borrow from high digit; don't update */
+ while (num) { /* "add back" required */
+ i = 0; num = 0; q--;
+ do {
+ num += (long) zds[j - ny + i] + yds[i];
+ zds[j - ny + i] = BIGLO(num);
+ num = BIGDN(num);
+ } while (++i < ny);
+ num--;
+ }
+ }
+ zds[j] = q;
+ } while (--j >= ny);
+ if (div) { /* move quotient down in z */
+ *div = Fbig_clone(z);
+ zds = BDIGITS(*div);
+ j = (nx==ny ? nx+2 : nx+1) - ny;
+ for (i = 0;i < j;i++) zds[i] = zds[i+ny];
+ RBIGNUM(*div)->len = i;
+ *div = bignorm(*div);
+ }
+ if (mod) { /* just normalize remainder */
+ *mod = Fbig_clone(z);
+ if (dd) {
+ zds = BDIGITS(*mod);
+ t2 = 0; i = ny;
+ while(i--) {
+ t2 = BIGUP(t2) + zds[i];
+ zds[i] = t2 / dd;
+ t2 %= dd;
+ }
+ }
+ RBIGNUM(*mod)->len = ny;
+ RBIGNUM(*mod)->sign = y->sign;
+ *mod = bignorm(*mod);
+ }
+ GC_UNLINK;
+}
+
+static VALUE
+Fbig_div(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+ bigdivmod(x, y, &z, Qnil);
+ GC_UNLINK;
+ return z;
+}
+
+static VALUE
+Fbig_mod(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+ bigdivmod(x, y, Qnil, &z);
+ GC_UNLINK;
+ return z;
+}
+
+static VALUE
+Fbig_divmod(x, y)
+ VALUE x, y;
+{
+ VALUE div, mod;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+ bigdivmod(x, y, &div, &mod);
+ GC_UNLINK;
+
+ return assoc_new(div, mod);;
+}
+
+static VALUE
+Fbig_pow(x, y)
+ VALUE x, y;
+{
+ extern double pow();
+ double d1, d2;
+
+ GC_LINK;
+ GC_PRO(y);
+ if (FIXNUM_P(y)) y = int2big(FIX2INT(y));
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+
+ d1 = big2dbl(x);
+ d2 = big2dbl(y);
+ d1 = pow(d1, d2);
+ GC_UNLINK;
+
+ return dbl2big(d1);
+}
+
+VALUE
+Fbig_and(x, y)
+ struct RBignum *x, *y;
+{
+ VALUE z;
+ USHORT *ds1, *ds2, *zds;
+ UINT i, l1, l2;
+ char sign;
+
+ if (FIXNUM_P(y)) {
+ y = (struct RBignum*)int2big(FIX2INT(y));
+ }
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+
+ GC_LINK;
+ GC_PRO(y);
+ if (!y->sign) {
+ y = (struct RBignum*)Fbig_clone(y);
+ big_2comp(y);
+ }
+ if (!x->sign) {
+ GC_PRO3(x, (struct RBignum*)Fbig_clone(x));
+ big_2comp(x);
+ }
+ if (x->len > y->len) {
+ l1 = y->len;
+ l2 = x->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = y->sign;
+ }
+ else {
+ l1 = x->len;
+ l2 = y->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = x->sign;
+ }
+ z = bignew(l2, x->sign && y->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] & ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?0:ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) big_2comp(z);
+ GC_UNLINK;
+ return bignorm(z);
+}
+
+VALUE
+Fbig_or(x, y)
+ struct RBignum *x, *y;
+{
+ VALUE z;
+ USHORT *ds1, *ds2, *zds;
+ UINT i, l1, l2;
+ char sign;
+
+ if (FIXNUM_P(y)) {
+ y = (struct RBignum*)int2big(FIX2INT(y));
+ }
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+
+ GC_LINK;
+ GC_PRO(y);
+ if (!y->sign) {
+ y = (struct RBignum*)Fbig_clone(y);
+ big_2comp(y);
+ }
+ if (!x->sign) {
+ GC_PRO3(x, (struct RBignum*)Fbig_clone(x));
+ big_2comp(x);
+ }
+ if (x->len > y->len) {
+ l1 = y->len;
+ l2 = x->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = y->sign;
+ }
+ else {
+ l1 = x->len;
+ l2 = y->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = x->sign;
+ }
+ z = bignew(l2, x->sign || y->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] | ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:(BIGRAD-1);
+ }
+ if (!RBIGNUM(z)->sign) big_2comp(z);
+ GC_UNLINK;
+ return bignorm(z);
+}
+
+VALUE
+Fbig_xor(x, y)
+ struct RBignum *x, *y;
+{
+ VALUE z;
+ USHORT *ds1, *ds2, *zds;
+ UINT i, l1, l2;
+ char sign;
+
+ if (FIXNUM_P(y)) {
+ y = (struct RBignum*)int2big(FIX2INT(y));
+ }
+ else {
+ Check_Type(y, T_BIGNUM);
+ }
+
+ GC_LINK;
+ GC_PRO(y);
+ if (!y->sign) {
+ y = (struct RBignum*)Fbig_clone(y);
+ big_2comp(y);
+ }
+ if (!x->sign) {
+ GC_PRO3(x, (struct RBignum*)Fbig_clone(x));
+ big_2comp(x);
+ }
+ if (x->len > y->len) {
+ l1 = y->len;
+ l2 = x->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = y->sign;
+ }
+ else {
+ l1 = x->len;
+ l2 = y->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = x->sign;
+ }
+ x->sign = x->sign?1:0;
+ y->sign = y->sign?1:0;
+ z = bignew(l2, !(x->sign ^ y->sign));
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] ^ ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:~ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) big_2comp(z);
+ GC_UNLINK;
+ return bignorm(z);
+}
+
+static VALUE
+Fbig_neg(x)
+ struct RBignum *x;
+{
+ VALUE z = Fbig_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 Fbig_rshift();
+
+VALUE
+Fbig_lshift(x, y)
+ struct RBignum *x;
+ VALUE y;
+{
+ USHORT *xds, *zds;
+ UINT shift = NUM2INT(y);
+ UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT);
+ UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT);
+ VALUE z;
+ unsigned long num = 0;
+ UINT len, i;
+
+ if (shift < 0) return Fbig_rshift(x, INT2FIX(-shift));
+ xds = BDIGITS(x);
+ len = x->len;
+ z = bignew(len+s1+1, x->sign);
+ zds = BDIGITS(z);
+ for (i=0; i<s1; i++) {
+ *zds++ = 0;
+ }
+ for (i=0; i<len; i++) {
+ num = num | *xds++<<s2;
+ *zds++ = BIGLO(num);
+ num = BIGDN(num);
+ }
+ *zds = BIGLO(num);
+ return bignorm(z);
+}
+
+static VALUE
+Fbig_rshift(x, y)
+ struct RBignum *x;
+ VALUE y;
+{
+ USHORT *xds, *zds;
+ UINT shift = NUM2INT(y);
+ UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT);
+ UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT);
+ VALUE z;
+ unsigned long num = 0;
+ UINT i = x->len, j;
+
+ if (shift < 0) return Fbig_lshift(x, INT2FIX(-shift));
+ if (s1 > x->len) {
+ if (x->sign)
+ return INT2FIX(0);
+ else
+ return INT2FIX(-1);
+ }
+ xds = BDIGITS(x);
+ i = x->len; j = i - s1;
+ z = bignew(j, x->sign);
+ zds = BDIGITS(z);
+ while (i--, j--) {
+ num = (num | xds[i]) >> s2;
+ zds[j] = BIGLO(num);
+ num = BIGUP(xds[i]);
+ }
+ return bignorm(z);
+}
+
+static VALUE
+Fbig_aref(x, y)
+ struct RBignum *x;
+ VALUE y;
+{
+ USHORT *xds;
+ int shift = NUM2INT(y);
+ UINT s1, s2;
+
+ if (shift < 0) return INT2FIX(0);
+ s1 = shift/(sizeof(USHORT)*CHAR_BIT);
+ s2 = shift%(sizeof(USHORT)*CHAR_BIT);
+
+ if (!x->sign) {
+ if (s1 >= x->len) return INT2FIX(1);
+ x = (struct RBignum*)Fbig_clone(x);
+ big_2comp(x);
+ }
+ else {
+ if (s1 >= x->len) return INT2FIX(0);
+ }
+ xds = BDIGITS(x);
+ if (xds[s1] & (1<<s2))
+ return INT2FIX(1);
+ return INT2FIX(0);
+}
+
+static VALUE
+Fbig_cmp(x, y)
+ struct RBignum *x, *y;
+{
+ int xlen = x->len;
+
+ Check_Type(x, 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
+Fbig_hash(x)
+ struct RBignum *x;
+{
+ int i, len, key;
+ USHORT *digits;
+
+ key = 0; digits = BDIGITS(x);
+ for (i=0,len=x->len; i<x->len; i++) {
+ key ^= *digits++;
+ }
+ return INT2FIX(key);
+}
+
+static VALUE
+Fbig_coerce(x, y)
+ struct RBignum *x;
+ VALUE y;
+{
+ if (FIXNUM_P(y)) {
+ return int2big(FIX2INT(y));
+ }
+ else {
+ Fail("can't coerce %s to Bignum", rb_class2name(CLASS_OF(y)));
+ }
+ /* not reached */
+ return Qnil;
+}
+
+static VALUE
+Fbig_abs(x)
+ struct RBignum *x;
+{
+ if (!x->sign) {
+ x = (struct RBignum*)Fbig_clone(x);
+ x->sign = 1;
+ }
+ return (VALUE)x;
+}
+
+Init_Bignum()
+{
+ C_Bignum = rb_define_class("Bignum", C_Integer);
+ rb_define_single_method(C_Bignum, "new", Fbig_new, 1);
+ rb_define_method(C_Bignum, "clone", Fbig_clone, 0);
+ rb_define_method(C_Bignum, "to_s", Fbig_to_s, 0);
+ rb_define_method(C_Bignum, "coerce", Fbig_coerce, 1);
+ rb_define_method(C_Bignum, "-@", Fbig_uminus, 0);
+ rb_define_method(C_Bignum, "+", Fbig_plus, 1);
+ rb_define_method(C_Bignum, "-", Fbig_minus, 1);
+ rb_define_method(C_Bignum, "*", Fbig_mul, 1);
+ rb_define_method(C_Bignum, "/", Fbig_div, 1);
+ rb_define_method(C_Bignum, "%", Fbig_mod, 1);
+ rb_define_method(C_Bignum, "divmod", Fbig_divmod, 1);
+ rb_define_method(C_Bignum, "**", Fbig_pow, 1);
+ rb_define_method(C_Bignum, "&", Fbig_and, 1);
+ rb_define_method(C_Bignum, "|", Fbig_or, 1);
+ rb_define_method(C_Bignum, "^", Fbig_xor, 1);
+ rb_define_method(C_Bignum, "~", Fbig_neg, 0);
+ rb_define_method(C_Bignum, "<<", Fbig_lshift, 1);
+ rb_define_method(C_Bignum, ">>", Fbig_rshift, 1);
+ rb_define_method(C_Bignum, "[]", Fbig_aref, 1);
+
+ rb_define_method(C_Bignum, "<=>", Fbig_cmp, 1);
+ rb_define_method(C_Bignum, "hash", Fbig_hash, 0);
+ rb_define_method(C_Bignum, "to_i", Fbig_to_i, 0);
+ rb_define_method(C_Bignum, "to_f", Fbig_to_f, 0);
+ rb_define_method(C_Bignum, "abs_f", Fbig_abs, 0);
+}
diff --git a/bring b/bring
new file mode 100644
index 0000000000..771cd04542
--- /dev/null
+++ b/bring
@@ -0,0 +1,57 @@
+#! /usr/bin/bash
+
+function fdeject() {
+ if type eject > /dev/null 2>&1; then
+ eject
+ fi
+}
+
+function copyfiles() {
+ for d in . missing sample; do
+ if [ ! -d $1/$d ];then mkdir $1/$d; fi
+ for i in $d/*;do
+ case $i in
+ */ruby|*.o|*~|*.sav|*.bak|*.orig|*/core|"#"*);;
+ */Change*|*/config.status|*/Makefile);;
+ *)
+ if [ -f $i ]; then
+ if [ $i -nt $1/$i -o ! -f $1/$i ];then
+ echo copying $i
+ cp -p $i $1/$i
+ fi
+ fi;;
+ esac
+ done
+ done
+}
+
+if [ ! -d exchange ]; then mkdir exchange; fi
+
+if [ "$1" = "in" ]; then
+
+ cd exchange
+
+ mread ruby.tgz ruby.tgz
+ fdeject
+ tar zxf ruby.tgz
+ rm -f ruby.tgz
+ cd ruby
+
+ cp ChangeLog ../../Changes
+ copyfiles ../..
+
+else
+# bring out
+ if [ ! -d exchange/ruby ]; then mkdir exchange/ruby; fi
+
+ cp -p ChangeLog exchange/ruby
+ copyfiles exchange/ruby
+
+ cd exchange
+ (cd ruby; make realclean)
+
+ tar zcf ruby.tgz ruby
+ mwrite ruby.tgz ruby.tgz
+ fdeject
+ rm -f ruby.tgz
+fi
diff --git a/class.c b/class.c
new file mode 100644
index 0000000000..406c0f139d
--- /dev/null
+++ b/class.c
@@ -0,0 +1,387 @@
+/************************************************
+
+ class.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Tue Aug 10 15:05:44 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "st.h"
+
+struct st_table *new_idhash();
+
+extern VALUE C_Class;
+extern VALUE C_Module;
+extern VALUE C_Method;
+
+VALUE
+class_new(super)
+ struct RClass *super;
+{
+ NEWOBJ(cls, struct RClass);
+ OBJSETUP(cls, C_Class, T_CLASS);
+
+ cls->super = super;
+ cls->m_tbl = new_idhash();
+ cls->c_tbl = Qnil;
+
+ return (VALUE)cls;
+}
+
+VALUE
+single_class_new(super)
+ struct RClass *super;
+{
+ struct RClass *cls = (struct RClass*)class_new(super);
+
+ FL_SET(cls, FL_SINGLE);
+
+ return (VALUE)cls;
+}
+
+VALUE
+single_class_clone(class)
+ struct RClass *class;
+{
+ if (!FL_TEST(class, FL_SINGLE))
+ return (VALUE)class;
+ else {
+ /* copy single(unnamed) class */
+ NEWOBJ(cls, struct RClass);
+ CLONESETUP(cls, class);
+
+ cls->super = class->super;
+ cls->m_tbl = st_copy(class->m_tbl);
+ cls->c_tbl = Qnil;
+ FL_SET(cls, FL_SINGLE);
+ return (VALUE)cls;
+ }
+}
+
+VALUE
+rb_define_class_id(id, super)
+ ID id;
+ struct RBasic *super;
+{
+ struct RClass *cls = (struct RClass*)class_new(super);
+
+ rb_name_class(cls, id);
+
+ /* make metaclass */
+ RBASIC(cls)->class = single_class_new(super?super->class:C_Class);
+ literalize(RBASIC(cls)->class);
+
+ return (VALUE)cls;
+}
+
+VALUE
+rb_define_class(name, super)
+ char *name;
+ VALUE super;
+{
+ return rb_define_class_id(rb_intern(name), super);
+}
+
+VALUE
+module_new()
+{
+ NEWOBJ(mdl, struct RClass);
+ OBJSETUP(mdl, C_Module, T_MODULE);
+
+ mdl->super = Qnil;
+ mdl->m_tbl = new_idhash();
+ mdl->c_tbl = Qnil;
+
+ return (VALUE)mdl;
+}
+
+VALUE
+rb_define_module_id(id)
+ ID id;
+{
+ struct RClass *mdl = (struct RClass*)module_new();
+
+ rb_name_class(mdl, id);
+ return (VALUE)mdl;
+}
+
+VALUE
+rb_define_module(name)
+ char *name;
+{
+ return rb_define_module_id(rb_intern(name));
+}
+
+static struct RClass *
+include_class_new(module, super)
+ struct RClass *module, *super;
+{
+ struct RClass *p;
+
+ NEWOBJ(cls, struct RClass);
+ OBJSETUP(cls, C_Class, T_ICLASS);
+
+ cls->m_tbl = module->m_tbl;
+ cls->c_tbl = module->c_tbl;
+ cls->super = super;
+
+ return cls;
+}
+
+void
+rb_include_module(class, module)
+ struct RClass *class, *module;
+{
+ struct RClass *p;
+
+ Check_Type(module, T_MODULE);
+
+ while (module) {
+ /* ignore if module included already in superclasses */
+ for (p = class->super; p; p = p->super) {
+ if (BUILTIN_TYPE(p) == T_ICLASS && p->m_tbl == module->m_tbl)
+ goto ignore_module;
+ }
+
+ class->super = include_class_new(module, class->super);
+ class = class->super;
+ ignore_module:
+ module = module->super;
+ }
+ rb_clear_cache2(class);
+}
+
+void
+rb_add_method(class, mid, node, scope)
+ struct RClass *class;
+ ID mid;
+ NODE *node;
+ enum mth_scope scope;
+{
+ struct RMethod *body;
+ NEWOBJ(mth, struct RMethod);
+ OBJSETUP(mth, C_Method, T_METHOD);
+
+ if (class == Qnil) class = (struct RClass*)C_Object;
+ if (st_lookup(class->m_tbl, mid, &body)) {
+ if (verbose) {
+ Warning("redefine %s", rb_id2name(mid));
+ }
+ unliteralize(body);
+ rb_clear_cache(body);
+ }
+ mth->node = node;
+ if (BUILTIN_TYPE(class) == T_MODULE)
+ mth->origin = Qnil;
+ else
+ mth->origin = class;
+ mth->id = mid;
+ mth->scope = scope;
+ literalize(mth);
+ st_insert(class->m_tbl, mid, mth);
+}
+
+void
+rb_define_method(class, name, func, argc)
+ struct RClass *class;
+ char *name;
+ VALUE (*func)();
+ int argc;
+{
+ NODE *temp = NEW_CFUNC(func, argc);
+
+ rb_add_method(class, rb_intern(name), temp, MTH_METHOD);
+}
+
+void
+rb_define_func(class, name, func, argc)
+ struct RClass *class;
+ char *name;
+ VALUE (*func)();
+ int argc;
+{
+ NODE *temp = NEW_CFUNC(func, argc);
+
+ rb_add_method(class, rb_intern(name), temp, MTH_FUNC);
+}
+
+void
+rb_undef_method(class, name)
+ struct RClass *class;
+ char *name;
+{
+ rb_add_method(class, rb_intern(name), Qnil, MTH_UNDEF);
+}
+
+VALUE
+rb_single_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 single method for built-in classes");
+ break;
+ }
+
+ if (FL_TEST(obj->class, FL_SINGLE)) {
+ return (VALUE)obj->class;
+ }
+ return obj->class = single_class_new(obj->class);
+}
+
+void
+rb_define_single_method(obj, name, func, argc)
+ VALUE obj;
+ char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_method(rb_single_class(obj), name, func, argc, MTH_METHOD);
+}
+
+void
+rb_define_mfunc(class, name, func, argc)
+ struct RClass *class;
+ char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_func(class, name, func, argc);
+ rb_define_single_method(class, name, func, argc);
+}
+
+void
+rb_define_alias(class, name1, name2)
+ struct RClass *class;
+ char *name1, *name2;
+{
+ rb_alias(class, rb_intern(name1), rb_intern(name2));
+}
+
+void
+rb_define_attr(class, name, pub)
+ struct RClass *class;
+ char *name;
+ int pub;
+{
+ char *buf;
+ ID attr, attreq, attriv;
+
+ attr = rb_intern(name);
+ buf = (char*)alloca(strlen(name) + 2);
+ sprintf(buf, "%s=", name);
+ attreq = rb_intern(buf);
+ sprintf(buf, "@%s", name);
+ attriv = rb_intern(buf);
+ if (rb_get_method_body(class, attr, 0, MTH_METHOD) == Qnil) {
+ rb_add_method(class, attr, NEW_IVAR(attriv), MTH_METHOD);
+ }
+ if (pub && rb_get_method_body(class, attreq, 0, MTH_METHOD) == Qnil) {
+ rb_add_method(class, attreq, NEW_ATTRSET(attriv), MTH_METHOD);
+ }
+}
+
+void
+rb_define_single_attr(obj, name, pub)
+ VALUE obj;
+ char *name;
+ int pub;
+{
+ rb_define_attr(rb_single_class(obj), name, pub);
+}
+
+#include <varargs.h>
+#include <ctype.h>
+
+int
+rb_scan_args(args, fmt, va_alist)
+ VALUE args;
+ char *fmt;
+ va_dcl
+{
+ int n, i, len;
+ char *p = fmt;
+ VALUE *var;
+ va_list vargs;
+
+ if (NIL_P(args)) {
+ len = 0;
+ }
+ else {
+ Check_Type(args, T_ARRAY);
+ len = RARRAY(args)->len;
+ }
+
+ va_start(vargs);
+
+ if (*p == '*') {
+ var = va_arg(vargs, VALUE*);
+ *var = args;
+ return len;
+ }
+
+ if (isdigit(*p)) {
+ n = *p - '0';
+ if (n > len)
+ Fail("Wrong number of arguments for %s",
+ rb_id2name(the_env->last_func));
+ for (i=0; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ *var = ary_entry(args, i);
+ }
+ p++;
+ }
+ else {
+ goto error;
+ }
+
+ if (isdigit(*p)) {
+ n = i + *p - '0';
+ for (; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ if (len > i) {
+ *var = ary_entry(args, i);
+ }
+ else {
+ *var = Qnil;
+ }
+ }
+ p++;
+ }
+
+ if(*p == '*') {
+ var = va_arg(vargs, VALUE*);
+ if (len > i) {
+ *var = ary_new4(RARRAY(args)->len-i, RARRAY(args)->ptr+i);
+ }
+ else {
+ *var = ary_new();
+ }
+ }
+ else if (*p == '\0') {
+ if (len > i) {
+ Fail("Wrong # of arguments(%d for %d)", len, i);
+ }
+ }
+ else {
+ goto error;
+ }
+
+ va_end(vargs);
+ return len;
+
+ error:
+ Fail("bad scan arg format: %s", fmt);
+}
diff --git a/compar.c b/compar.c
new file mode 100644
index 0000000000..84b2583ce5
--- /dev/null
+++ b/compar.c
@@ -0,0 +1,99 @@
+/************************************************
+
+ compar.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Thu Aug 26 14:39:48 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+VALUE M_Comparable;
+
+static ID cmp;
+
+static VALUE
+Fcmp_eq(this, other)
+ VALUE this, other;
+{
+ VALUE c = rb_funcall(this, cmp, 1, other);
+ int t = NUM2INT(c);
+
+ if (t == 0) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Fcmp_gt(this, other)
+ VALUE this, other;
+{
+ VALUE c = rb_funcall(this, cmp, 1, other);
+ int t = NUM2INT(c);
+
+ if (t > 0) return other;
+ return FALSE;
+}
+
+static VALUE
+Fcmp_ge(this, other)
+ VALUE this, other;
+{
+ VALUE c = rb_funcall(this, cmp, 1, other);
+ int t = NUM2INT(c);
+
+ if (t >= 0) return other;
+ return FALSE;
+}
+
+static VALUE
+Fcmp_lt(this, other)
+ VALUE this, other;
+{
+ VALUE c = rb_funcall(this, cmp, 1, other);
+ int t = NUM2INT(c);
+
+ if (t < 0) return other;
+ return FALSE;
+}
+
+static VALUE
+Fcmp_le(this, other)
+ VALUE this, other;
+{
+ VALUE c = rb_funcall(this, cmp, 1, other);
+ int t = NUM2INT(c);
+
+ if (t <= 0) return other;
+ return FALSE;
+}
+
+static VALUE
+Fcmp_between(this, min, max)
+ VALUE this, min, max;
+{
+ VALUE c = rb_funcall(this, cmp, 1, min);
+ int t = NUM2INT(c);
+ if (t < 0) return FALSE;
+
+ c = rb_funcall(this, cmp, 1, min);
+ t = NUM2INT(c);
+ if (t > 0) return FALSE;
+ return TRUE;
+}
+
+Init_Comparable()
+{
+ M_Comparable = rb_define_module("Comparable");
+ rb_define_method(M_Comparable, "==", Fcmp_eq, 1);
+ rb_define_method(M_Comparable, ">", Fcmp_gt, 1);
+ rb_define_method(M_Comparable, ">=", Fcmp_ge, 1);
+ rb_define_method(M_Comparable, "<", Fcmp_lt, 1);
+ rb_define_method(M_Comparable, "<=", Fcmp_le, 1);
+ rb_define_method(M_Comparable, "between", Fcmp_between, 2);
+
+ cmp = rb_intern("<=>");
+}
diff --git a/config.status b/config.status
new file mode 100755
index 0000000000..7bdc7b26ed
--- /dev/null
+++ b/config.status
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host dyna:
+#
+# ./configure
+
+for arg
+do
+ case "$arg" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo running ${CONFIG_SHELL-/bin/sh} ./configure
+ exec ${CONFIG_SHELL-/bin/sh} ./configure ;;
+ *) echo "Usage: config.status --recheck" 2>&1; exit 1 ;;
+ esac
+done
+
+trap 'rm -f Makefile; exit 1' 1 3 15
+PROGS='ruby'
+CC='gcc'
+CPP='gcc -E'
+DBM='-fpcc-struct-return'
+STATIC=''
+YACC='bison -y'
+INSTALL='/usr/bin/install -c'
+INSTALL_PROGRAM='$(INSTALL)'
+INSTALL_DATA='$(INSTALL) -m 644'
+ALLOCA=''
+LIBS=' -lm -ldbm'
+srcdir='.'
+DEFS=' -DHAVE_UNISTD_H=1 -DHAVE_SYSCALL_H=1 -DHAVE_A_OUT_H=1 -DDIRENT=1 -DGETGROUPS_T=gid_t -DRETSIGTYPE=void -DHAVE_GETOPT_LONG=1 -DHAVE_MEMMOVE=1 -DHAVE_STRERROR=1 -DHAVE_STRTOL=1 -DHAVE_STRTOUL=1 -DHAVE_STRDUP=1 -DHAVE_SETENV=1 -DHAVE_KILLPG=1 -DHAVE_MKDIR=1 -DHAVE_STRFTIME=1 -DHAVE_ALLOCA_H=1'
+prefix=''
+exec_prefix=''
+prsub=''
+extrasub=''
+
+top_srcdir=$srcdir
+
+CONFIG_FILES=${CONFIG_FILES-"Makefile"}
+for file in .. ${CONFIG_FILES}; do if test "x$file" != x..; then
+ srcdir=$top_srcdir
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ dir=`echo $file|sed 's%/[^/][^/]*$%%'`
+ if test "$dir" != "$file"; then
+ test "$top_srcdir" != . && srcdir=$top_srcdir/$dir
+ test ! -d $dir && mkdir $dir
+ fi
+ echo creating $file
+ rm -f $file
+ echo "# Generated automatically from `echo $file|sed 's|.*/||'`.in by configure." > $file
+ sed -e "
+$prsub
+$extrasub
+s%@PROGS@%$PROGS%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@DBM@%$DBM%g
+s%@STATIC@%$STATIC%g
+s%@YACC@%$YACC%g
+s%@INSTALL@%$INSTALL%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@ALLOCA@%$ALLOCA%g
+s%@LIBS@%$LIBS%g
+s%@srcdir@%$srcdir%g
+s%@DEFS@%$DEFS%
+" $top_srcdir/${file}.in >> $file
+fi; done
+
+
+exit 0
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000000..0acb17eae5
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,63 @@
+
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(ruby.h)
+PROGS="ruby"
+AC_SUBST(PROGS)dnl
+AC_PROG_CC
+AC_GCC_TRADITIONAL
+if test $GCC -eq 1 ; then
+ DBM=-fpcc-struct-return
+fi
+AC_SUBST(DBM)dnl
+if test "$HOSTTYPE" = sparc; then
+ if test $GCC -eq 1 ; then
+ STATIC=-static
+ else
+ STATIC=-Bstatic
+ fi
+else
+ STATIC=
+fi
+AC_SUBST(STATIC)dnl
+AC_PROG_YACC
+AC_PROG_INSTALL
+AC_HAVE_HEADERS(unistd.h syscall.h a.out.h)
+AC_DIR_HEADER
+AC_GETGROUPS_T
+AC_RETSIGTYPE
+AC_HAVE_FUNCS(getopt_long memmove strerror strtoul strdup strstr)
+AC_HAVE_FUNCS(setenv fmod killpg mkdir strftime socket random)
+AC_HAVE_FUNCS(wait4 waitpid)
+if echo $DEFS | grep "HAVE_SETENV" 2>&1 > /dev/null; then
+ :
+else
+ AC_HAVE_FUNCS(putenv)
+fi
+if echo $DEFS | grep "HAVE_STRFTIME" 2>&1 > /dev/null; then
+ :
+else
+ AC_TIMEZONE
+ AC_COMPILE_CHECK([daylight], [],
+ [extern int daylight; daylight;], AC_DEFINE(HAVE_DAYLIGHT))
+fi
+AC_ALLOCA
+AC_WORDS_BIGENDIAN
+AC_ST_BLKSIZE
+AC_ST_BLOCKS
+AC_ST_RDEV
+AC_COMPILE_CHECK([pw_change in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_change;], AC_DEFINE(PW_CHANGE))
+AC_COMPILE_CHECK([pw_quota in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_quota;], AC_DEFINE(PW_QUOTA))
+AC_COMPILE_CHECK([pw_age in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_age;], AC_DEFINE(PW_AGE))
+AC_COMPILE_CHECK([pw_class in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_class;], AC_DEFINE(PW_CLASSS))
+AC_COMPILE_CHECK([pw_comment in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_comment;], AC_DEFINE(PW_COMMENT))
+AC_COMPILE_CHECK([pw_expire in struct passwd], [#include <pwd.h>],
+[struct passwd pw; pw.pw_expire;], AC_DEFINE(PW_EXPIRE))
+AC_HAVE_LIBRARY(m, [LIBS="$LIBS -lm"])
+AC_HAVE_LIBRARY(dbm, [LIBS="$LIBS -ldbm"])
+AC_HAVE_LIBRARY(socket, [LIBS="$LIBS -lsocket"])
+AC_OUTPUT(Makefile)
diff --git a/dbm.c b/dbm.c
new file mode 100644
index 0000000000..f3946f7163
--- /dev/null
+++ b/dbm.c
@@ -0,0 +1,401 @@
+/************************************************
+
+ dbm.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Mon Jan 24 15:59:52 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+#ifdef USE_DBM
+
+#include <ndbm.h>
+#include <sys/file.h>
+#include <errno.h>
+
+VALUE C_DBM;
+
+extern VALUE M_Enumerable;
+
+static void
+closeddbm()
+{
+ Fail("closed DBM file");
+}
+
+#define GetDBM(obj, dbmp) {\
+ DBM **_dbm;\
+ Get_Data_Struct(obj, "dbm", DBM*, _dbm);\
+ dbmp = *_dbm;\
+ if (dbmp == Qnil) closeddbm();\
+}
+
+static void
+free_dbm(dbmp)
+ DBM **dbmp;
+{
+ if (*dbmp) dbm_close(*dbmp);
+}
+
+#define MakeDBM(obj, dp) {\
+ DBM **_dbm;\
+ Make_Data_Struct(obj,"dbm",DBM*,Qnil,free_dbm,_dbm);\
+ *_dbm=dp;\
+}
+
+static VALUE
+Fdbm_open(class, args)
+ VALUE class, args;
+{
+ VALUE file, vmode;
+ DBM *dbm, **dbm2;
+ int mode;
+ VALUE obj;
+
+ if (rb_scan_args(args, "11", &file, &vmode) == 1) {
+ mode = 0666; /* default value */
+ }
+ else if (NIL_P(vmode)) {
+ mode = -1; /* return nil if DB not exist */
+ }
+ else {
+ mode = NUM2INT(vmode);
+ }
+ Check_Type(file, T_STRING);
+
+ dbm = Qnil;
+ if (mode >= 0)
+ dbm = dbm_open(RSTRING(file)->ptr, O_RDWR|O_CREAT, mode);
+ if (!dbm)
+ dbm = dbm_open(RSTRING(file)->ptr, O_RDWR, mode);
+ if (!dbm)
+ dbm = dbm_open(RSTRING(file)->ptr, O_RDONLY, mode);
+
+ if (!dbm) {
+ if (mode == -1) return Qnil;
+ rb_sys_fail(RSTRING(file)->ptr);
+ }
+
+ GC_LINK;
+ GC_PRO3(obj, obj_alloc(class));
+ MakeDBM(obj, dbm);
+ GC_UNLINK;
+
+ return obj;
+}
+
+static VALUE
+Fdbm_close(obj)
+ VALUE obj;
+{
+ DBM **dbmp;
+
+ Get_Data_Struct(obj, "dbm", DBM*, dbmp);
+ if (*dbmp == Qnil) Fail("already closed DBM file");
+ dbm_close(*dbmp);
+ *dbmp = Qnil;
+
+ return Qnil;
+}
+
+static VALUE
+Fdbm_fetch(obj, keystr)
+ VALUE obj, keystr;
+{
+ datum key, value;
+ DBM *dbm;
+
+ Check_Type(keystr, T_STRING);
+ key.dptr = RSTRING(keystr)->ptr;
+ key.dsize = RSTRING(keystr)->len;
+
+ GetDBM(obj, dbm);
+ value = dbm_fetch(dbm, key);
+ if (value.dptr == Qnil) {
+ return Qnil;
+ }
+ return str_new(value.dptr, value.dsize);
+}
+
+static VALUE
+Fdbm_delete(obj, keystr)
+ VALUE obj, keystr;
+{
+ datum key;
+ DBM *dbm;
+
+ Check_Type(keystr, T_STRING);
+ key.dptr = RSTRING(keystr)->ptr;
+ key.dsize = RSTRING(keystr)->len;
+
+ GetDBM(obj, dbm);
+ if (dbm_delete(dbm, key)) {
+ Fail("DBM delete failed");
+ }
+ return obj;
+}
+
+static VALUE
+Fdbm_delete_if(obj)
+ VALUE obj;
+{
+ datum key, val;
+ DBM *dbm;
+ VALUE keystr, valstr;
+
+ GetDBM(obj, dbm);
+ GC_LINK;
+ GC_PRO2(keystr); GC_PRO2(valstr);
+ 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");
+ }
+ }
+ GC_UNLINK;
+ return obj;
+}
+
+static VALUE
+Fdbm_clear(obj)
+ VALUE obj;
+{
+ datum key;
+ DBM *dbm;
+
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ if (dbm_delete(dbm, key)) {
+ Fail("DBM delete failed");
+ }
+ }
+ return obj;
+}
+
+static VALUE
+Fdbm_store(obj, keystr, valstr)
+ VALUE obj, keystr, valstr;
+{
+ datum key, val;
+ DBM *dbm;
+
+ if (valstr == Qnil) {
+ Fdbm_delete(obj, keystr);
+ return Qnil;
+ }
+
+ Check_Type(keystr, T_STRING);
+ key.dptr = RSTRING(keystr)->ptr;
+ key.dsize = RSTRING(keystr)->len;
+ Check_Type(valstr, T_STRING);
+ val.dptr = RSTRING(valstr)->ptr;
+ val.dsize = RSTRING(valstr)->len;
+
+ GetDBM(obj, dbm);
+ if (dbm_store(dbm, key, val, DBM_REPLACE)) {
+ dbm_clearerr(dbm);
+ if (errno == EPERM) rb_sys_fail(Qnil);
+ Fail("DBM store failed");
+ }
+ return valstr;
+}
+
+static VALUE
+Fdbm_length(obj)
+ VALUE obj;
+{
+ datum key;
+ DBM *dbm;
+ int i = 0;
+
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ i++;
+ }
+ return INT2FIX(i);
+}
+
+static VALUE
+Fdbm_each(obj)
+ VALUE obj;
+{
+ datum key, val;
+ DBM *dbm;
+
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ val = dbm_fetch(dbm, key);
+ rb_yield(str_new(val.dptr, val.dsize));
+ }
+ return obj;
+}
+
+static VALUE
+Fdbm_each_key(obj)
+ VALUE obj;
+{
+ datum key;
+ DBM *dbm;
+
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ rb_yield(str_new(key.dptr, key.dsize));
+ }
+ return obj;
+}
+
+static VALUE
+Fdbm_each_pair(obj)
+ VALUE obj;
+{
+ datum key, val;
+ DBM *dbm;
+ VALUE keystr, valstr;
+
+ GetDBM(obj, dbm);
+ GC_LINK;
+ GC_PRO2(keystr); GC_PRO2(valstr);
+
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ val = dbm_fetch(dbm, key);
+ keystr = str_new(key.dptr, key.dsize);
+ valstr = str_new(val.dptr, val.dsize);
+ rb_yield(assoc_new(keystr, valstr));
+ }
+ GC_UNLINK;
+
+ return obj;
+}
+
+static VALUE
+Fdbm_keys(obj)
+ VALUE obj;
+{
+ datum key;
+ DBM *dbm;
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ Fary_push(ary, str_new(key.dptr, key.dsize));
+ }
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Fdbm_values(obj)
+ VALUE obj;
+{
+ datum key, val;
+ DBM *dbm;
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ val = dbm_fetch(dbm, key);
+ Fary_push(ary, str_new(val.dptr, val.dsize));
+ }
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Fdbm_has_key(obj, keystr)
+ VALUE obj, keystr;
+{
+ datum key, val;
+ DBM *dbm;
+
+ Check_Type(keystr, T_STRING);
+ key.dptr = RSTRING(keystr)->ptr;
+ key.dsize = RSTRING(keystr)->len;
+
+ GetDBM(obj, dbm);
+ val = dbm_fetch(dbm, key);
+ if (val.dptr) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Fdbm_has_value(obj, valstr)
+ VALUE obj, valstr;
+{
+ datum key, val;
+ DBM *dbm;
+
+ Check_Type(valstr, T_STRING);
+ val.dptr = RSTRING(valstr)->ptr;
+ val.dsize = RSTRING(valstr)->len;
+
+ GetDBM(obj, dbm);
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ val = dbm_fetch(dbm, key);
+ if (val.dsize == RSTRING(valstr)->len &&
+ memcmp(val.dptr, RSTRING(valstr)->ptr, val.dsize) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+Fdbm_to_a(obj)
+ VALUE obj;
+{
+ datum key, val;
+ DBM *dbm;
+ VALUE ary;
+
+ GetDBM(obj, dbm);
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
+ val = dbm_fetch(dbm, key);
+ Fary_push(ary, assoc_new(str_new(key.dptr, key.dsize),
+ str_new(val.dptr, val.dsize)));
+ }
+
+ GC_UNLINK;
+ return ary;
+}
+
+Init_DBM()
+{
+ C_DBM = rb_define_class("DBM", C_Object);
+ rb_include_module(C_DBM, M_Enumerable);
+
+ rb_define_single_method(C_DBM, "open", Fdbm_open, -2);
+ rb_define_method(C_DBM, "close", Fdbm_close, 0);
+ rb_define_method(C_DBM, "[]", Fdbm_fetch, 1);
+ rb_define_method(C_DBM, "[]=", Fdbm_store, 2);
+ rb_define_method(C_DBM, "length", Fdbm_length, 0);
+ rb_define_method(C_DBM, "each", Fdbm_each, 0);
+ rb_define_method(C_DBM, "each_value", Fdbm_each, 0);
+ rb_define_method(C_DBM, "each_key", Fdbm_each_key, 0);
+ rb_define_method(C_DBM, "each_pair", Fdbm_each_pair, 0);
+ rb_define_method(C_DBM, "keys", Fdbm_keys, 0);
+ rb_define_method(C_DBM, "values", Fdbm_values, 0);
+ rb_define_method(C_DBM, "delete", Fdbm_delete, 1);
+ rb_define_method(C_DBM, "delete_if", Fdbm_delete_if, 0);
+ rb_define_method(C_DBM, "clear", Fdbm_clear, 0);
+ rb_define_method(C_DBM, "includes", Fdbm_has_key, 1);
+ rb_define_method(C_DBM, "has_key", Fdbm_has_key, 1);
+ rb_define_method(C_DBM, "has_value", Fdbm_has_value, 1);
+
+ rb_define_method(C_DBM, "to_a", Fdbm_to_a, 0);
+}
+#endif /* USE_DBM */
diff --git a/defines.h b/defines.h
new file mode 100644
index 0000000000..b65433468b
--- /dev/null
+++ b/defines.h
@@ -0,0 +1,36 @@
+/************************************************
+
+ defines.h -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Wed May 18 00:21:44 JST 1994
+
+************************************************/
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#define RUBY
+
+/* #include "config.h" */
+
+/* define USE_DLN to load object file(.o). */
+#ifdef HAVE_A_OUT_H
+
+#undef USE_DLN
+#ifdef USE_DLN
+#define LIBC_NAME "libc.a"
+#define DLN_DEFAULT_PATH "/lib:/usr/lib:."
+#endif
+
+#endif
+
+/* define USE_DBM to use dbm class. */
+#define USE_DBM
+
+#ifdef HAVE_SYSCALL_H
+/* define SAFE_SIGHANDLE to override syscall for trap. */
+#define SAFE_SIGHANDLE
+#endif
+
+#endif
diff --git a/dict.c b/dict.c
new file mode 100644
index 0000000000..909a85ae81
--- /dev/null
+++ b/dict.c
@@ -0,0 +1,517 @@
+/************************************************
+
+ dict.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Mon Nov 22 18:51:18 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "st.h"
+
+VALUE C_Dict, C_EnvDict;
+static ID hash, eq;
+VALUE Fgetenv(), Fsetenv();
+
+static VALUE
+rb_cmp(a, b)
+ VALUE a, b;
+{
+ return rb_funcall(a, eq, 1, b)?0:1;
+}
+
+static VALUE
+rb_hash(a, mod)
+ VALUE a;
+ int mod;
+{
+ return rb_funcall(a, hash, 0) % mod;
+}
+
+#define ASSOC_KEY(a) RARRAY(a)->ptr[0]
+#define ASSOC_VAL(a) RARRAY(a)->ptr[1]
+
+VALUE
+Fdic_new(class)
+ VALUE class;
+{
+ int i, max;
+ NEWOBJ(dic, struct RDict);
+ OBJSETUP(dic, class, T_DICT);
+
+ GC_LINK;
+ GC_PRO(dic);
+
+ dic->tbl = st_init_table(rb_cmp, rb_hash);
+ GC_UNLINK;
+
+ return (VALUE)dic;
+}
+
+static VALUE
+Fdic_clone(dic)
+ struct RDict *dic;
+{
+ NEWOBJ(dic2, struct RDict);
+ CLONESETUP(dic2, dic);
+
+ GC_LINK;
+ GC_PRO(dic2);
+
+ dic2->tbl = (st_table*)st_copy(dic->tbl);
+
+ GC_UNLINK;
+
+ return (VALUE)dic2;
+}
+
+static VALUE
+Fdic_aref(dic, key)
+ struct RDict *dic;
+ VALUE key;
+{
+ VALUE val = Qnil;
+
+ if (!st_lookup(dic->tbl, key, &val)) {
+ return Qnil;
+ }
+ return val;
+}
+
+static VALUE
+Fdic_delete(dic, key)
+ struct RDict *dic;
+ VALUE key;
+{
+ VALUE val;
+
+ if (st_delete(dic->tbl, &key, &val))
+ return val;
+ return Qnil;
+}
+
+static int
+dic_delete_if(key, value)
+ VALUE key, value;
+{
+ if (rb_yield(assoc_new(key, value)))
+ return ST_DELETE;
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_delete_if(dic)
+ struct RDict *dic;
+{
+ st_foreach(dic->tbl, dic_delete_if, Qnil);
+
+ return (VALUE)dic;
+}
+
+static
+dic_clear(key, value)
+ VALUE key, value;
+{
+ return ST_DELETE;
+}
+
+static VALUE
+Fdic_clear(dic)
+ struct RDict *dic;
+{
+ st_foreach(dic->tbl, dic_clear, Qnil);
+
+ return (VALUE)dic;
+}
+
+VALUE
+Fdic_aset(dic, key, val)
+ struct RDict *dic;
+ VALUE key, val;
+{
+ if (val == Qnil) {
+ Fdic_delete(dic, key);
+ return Qnil;
+ }
+ st_insert(dic->tbl, key, val);
+ return val;
+}
+
+static VALUE
+Fdic_length(dic)
+ struct RDict *dic;
+{
+ return INT2FIX(dic->tbl->num_entries);
+}
+
+static
+dic_each(key, value)
+ VALUE key, value;
+{
+ rb_yield(value);
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_each(dic)
+ struct RDict *dic;
+{
+ st_foreach(dic->tbl, dic_each);
+ return (VALUE)dic;
+}
+
+static
+dic_each_key(key, value)
+ VALUE key, value;
+{
+ rb_yield(key);
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_each_key(dic)
+ struct RDict *dic;
+{
+ st_foreach(dic->tbl, dic_each_key);
+ return (VALUE)dic;
+}
+
+static
+dic_each_pair(key, value)
+ VALUE key, value;
+{
+ rb_yield(assoc_new(key, value));
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_each_pair(dic)
+ struct RDict *dic;
+{
+ st_foreach(dic->tbl, dic_each_pair);
+ return (VALUE)dic;
+}
+
+static
+dic_to_a(key, value, ary)
+ VALUE key, value, ary;
+{
+ Fary_push(ary, assoc_new(key, value));
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_to_a(dic)
+ struct RDict *dic;
+{
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ st_foreach(dic->tbl, dic_to_a, ary);
+ GC_UNLINK;
+
+ return ary;
+}
+
+static
+dic_inspect(key, value, str)
+ VALUE key, value;
+ struct RString *str;
+{
+ VALUE str2;
+ ID inspect = rb_intern("_inspect");
+
+ if (str->len > 1) {
+ str_cat(str, ", ", 2);
+ }
+ GC_LINK;
+ GC_PRO3(str2, rb_funcall(key, inspect, 0, Qnil));
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ str_cat(str, "=>", 2);
+ str2 = rb_funcall(value, inspect, 0, Qnil);
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ GC_UNLINK;
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_inspect(dic)
+ struct RDict *dic;
+{
+ VALUE str;
+
+ GC_LINK;
+ GC_PRO3(str, str_new2("{"));
+ st_foreach(dic->tbl, dic_inspect, str);
+ str_cat(str, "}", 1);
+ GC_UNLINK;
+
+ return str;
+}
+
+static VALUE
+Fdic_to_s(dic)
+ VALUE dic;
+{
+ VALUE str;
+
+ GC_LINK;
+ GC_PRO(dic);
+ dic = Fdic_to_a(dic);
+ str = Fary_to_s(dic);
+ GC_UNLINK;
+
+ return str;
+}
+
+static
+dic_keys(key, value, ary)
+ VALUE key, value, ary;
+{
+ Fary_push(ary, key);
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_keys(dic)
+ struct RDict *dic;
+{
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ st_foreach(dic->tbl, dic_keys, ary);
+ GC_UNLINK;
+ return ary;
+}
+
+static
+dic_values(key, value, ary)
+ VALUE key, value, ary;
+{
+ Fary_push(ary, key);
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_values(dic)
+ struct RDict *dic;
+{
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ st_foreach(dic->tbl, dic_values, ary);
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Fdic_has_key(dic, key)
+ struct RDict *dic;
+ VALUE key;
+{
+ VALUE val;
+
+ if (st_lookup(dic->tbl, key, &val))
+ return TRUE;
+ return FALSE;
+}
+
+static VALUE value_found;
+
+static
+dic_search_value(key, value, arg)
+ VALUE key, value, arg;
+{
+ if (rb_funcall(value, eq, 1, arg)) {
+ value_found = TRUE;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fdic_has_value(dic, val)
+ struct RDict *dic;
+ VALUE val;
+{
+ value_found = FALSE;
+ st_foreach(dic->tbl, dic_search_value, val);
+ return value_found;
+}
+
+char *index();
+extern VALUE rb_readonly_hook();
+
+extern char **environ;
+
+static VALUE
+Fenv_each(dic)
+ VALUE dic;
+{
+ char **env;
+
+ env = environ;
+ while (*env) {
+ VALUE var, val;
+ char *s = index(*env, '=');
+
+ GC_LINK;
+ GC_PRO3(var, str_new(*env, s-*env));
+ GC_PRO3(val, str_new2(s+1));
+ rb_yield(assoc_new(var, val));
+ GC_UNLINK;
+ env++;
+ }
+ return dic;
+}
+
+static VALUE
+Fenv_delete(obj, name)
+ VALUE obj;
+ struct RString *name;
+{
+ int i, len;
+ char *nam, *val = Qnil;
+
+ Check_Type(name, T_STRING);
+ nam = name->ptr;
+ len = strlen(nam);
+ for(i=0; environ[i]; i++) {
+ if (strncmp(environ[i], nam, len) == 0 && environ[i][len] == '=') {
+ val = environ[i]+len+1;
+ break;
+ }
+ }
+ while (environ[i]) {
+ environ[i] = environ[i+1];
+ i++;
+ }
+ if (val) {
+ return str_new2(val);
+ }
+ return Qnil;
+}
+
+VALUE
+Fgetenv(obj, name)
+ VALUE obj;
+ struct RString *name;
+{
+ extern char *getenv();
+ char *env;
+
+ Check_Type(name, T_STRING);
+
+ if (strlen(name->ptr) != name->len)
+ Fail("Bad environment name");
+
+ env = getenv(name->ptr);
+ if (env) {
+ return str_new2(env);
+ }
+ return Qnil;
+}
+
+VALUE
+Fsetenv(obj, name, value)
+ VALUE obj;
+ struct RString *name, *value;
+{
+ Check_Type(name, T_STRING);
+ if (value == Qnil) {
+ Fenv_delete(obj, name);
+ return Qnil;
+ }
+
+ Check_Type(value, T_STRING);
+
+ if (strlen(name->ptr) != name->len)
+ Fail("Bad environment name");
+ if (strlen(value->ptr) != value->len)
+ Fail("Bad environment value");
+
+#ifdef HAVE_SETENV
+ if (setenv(name->ptr, value->ptr, 1) == 0) return TRUE;
+#else
+#ifdef HAVE_PUTENV
+ {
+ char *str;
+ int len;
+
+ str = ALLOC_N(char, name->len + value->len + 2);
+ sprintf("%s=%s", name->ptr, value->ptr);
+ if (putenv(str) == 0) return TRUE;
+ }
+#else
+ Fail("setenv is not supported on this system");
+#endif
+#endif
+
+ Fail("setenv failed");
+ return FALSE; /* not reached */
+}
+
+Init_Dict()
+{
+ extern VALUE C_Kernel;
+ extern VALUE M_Enumerable;
+ static VALUE envtbl;
+
+ hash = rb_intern("hash");
+ eq = rb_intern("==");
+
+ C_Dict = rb_define_class("Dict", C_Object);
+ rb_name_class(C_Dict, rb_intern("Hash")); /* alias */
+
+ rb_include_module(C_Dict, M_Enumerable);
+
+ rb_define_single_method(C_Dict, "new", Fdic_new, 0);
+
+ rb_define_method(C_Dict,"clone", Fdic_clone, 0);
+
+ rb_define_method(C_Dict,"to_a", Fdic_to_a, 0);
+ rb_define_method(C_Dict,"to_s", Fdic_to_s, 0);
+ rb_define_method(C_Dict,"_inspect", Fdic_inspect, 0);
+
+ rb_define_method(C_Dict,"[]", Fdic_aref, 1);
+ rb_define_method(C_Dict,"[]=", Fdic_aset, 2);
+ rb_define_method(C_Dict,"length", Fdic_length, 0);
+ rb_define_method(C_Dict,"each", Fdic_each, 0);
+ rb_define_method(C_Dict,"each_value", Fdic_each, 0);
+ rb_define_method(C_Dict,"each_key", Fdic_each_key, 0);
+ rb_define_method(C_Dict,"each_pair", Fdic_each_pair, 0);
+
+ rb_define_method(C_Dict,"keys", Fdic_keys, 0);
+ rb_define_method(C_Dict,"values", Fdic_values, 0);
+
+ rb_define_method(C_Dict,"delete", Fdic_delete, 1);
+ rb_define_method(C_Dict,"delete_if", Fdic_delete_if, 0);
+ rb_define_method(C_Dict,"clear", Fdic_clear, 0);
+
+ rb_define_method(C_Dict,"includes", Fdic_has_key, 1);
+ rb_define_method(C_Dict,"has_key", Fdic_has_key, 1);
+ rb_define_method(C_Dict,"has_value", Fdic_has_value, 1);
+
+
+ C_EnvDict = rb_define_class("EnvDict", C_Object);
+
+ rb_include_module(C_EnvDict, M_Enumerable);
+
+ rb_define_method(C_EnvDict,"[]", Fgetenv, 1);
+ rb_define_method(C_EnvDict,"[]=", Fsetenv, 2);
+ rb_define_method(C_EnvDict,"each", Fenv_each, 0);
+ rb_define_method(C_EnvDict,"delete", Fenv_delete, 1);
+ envtbl = obj_alloc(C_EnvDict);
+ rb_define_variable("$ENV", &envtbl, Qnil, rb_readonly_hook);
+
+ rb_define_func(C_Kernel, "getenv", Fgetenv, 1);
+ rb_define_func(C_Kernel, "setenv", Fsetenv, 2);
+}
diff --git a/dir.c b/dir.c
new file mode 100644
index 0000000000..bf62e302de
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,257 @@
+/************************************************
+
+ dir.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Wed Jan 5 09:51:01 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/param.h>
+
+/* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */
+#if defined(DIRENT) || defined(_POSIX_VERSION)
+#include <dirent.h>
+#define NLENGTH(dirent) (strlen((dirent)->d_name))
+#else /* not (DIRENT or _POSIX_VERSION) */
+#define dirent direct
+#define NLENGTH(dirent) ((dirent)->d_namlen)
+#ifdef SYSNDIR
+#include <sys/ndir.h>
+#endif /* SYSNDIR */
+#ifdef SYSDIR
+#include <sys/dir.h>
+#endif /* SYSDIR */
+#ifdef NDIR
+#include <ndir.h>
+#endif /* NDIR */
+#endif /* not (DIRENT or _POSIX_VERSION) */
+
+static VALUE C_Dir;
+
+static void
+free_dir(dir)
+ DIR **dir;
+{
+ if (dir) closedir(*dir);
+}
+
+static VALUE
+Fdir_open(dir_class, dirname)
+ VALUE dir_class;
+ struct RString *dirname;
+{
+ VALUE obj;
+ DIR *dirp, **d;
+
+ Check_Type(dirname, T_STRING);
+
+ dirp = opendir(dirname->ptr);
+ if (dirp == NULL) Fail("Can't open directory %s", dirname->ptr);
+
+ GC_LINK;
+ GC_PRO3(obj, obj_alloc(dir_class));
+ Make_Data_Struct(obj, "dir", DIR*, Qnil, free_dir, d);
+ *d = dirp;
+ /* use memcpy(d, dirp, sizeof(DIR)) if needed.*/
+ GC_UNLINK;
+
+ return obj;
+}
+
+static void
+closeddir()
+{
+ Fail("closed directory");
+}
+
+#define GetDIR(obj, dirp) {\
+ DIR **_dp;\
+ Get_Data_Struct(obj, "dir", DIR*, _dp);\
+ dirp = *_dp;\
+ if (dirp == NULL) closeddir();\
+}
+
+static VALUE
+Fdir_each(dir)
+ VALUE dir;
+{
+ extern VALUE rb_lastline;
+ DIR *dirp;
+ struct dirent *dp;
+
+ GetDIR(dir, dirp);
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ rb_lastline = str_new(dp->d_name, NLENGTH(dp));
+ rb_yield(rb_lastline);
+ }
+ return dir;
+}
+
+static VALUE
+Fdir_tell(dir)
+ VALUE dir;
+{
+ DIR *dirp;
+ int pos;
+
+ GetDIR(dir, dirp);
+ pos = telldir(dirp);
+ return int2inum(pos);
+}
+
+static VALUE
+Fdir_seek(dir, pos)
+ VALUE dir, pos;
+{
+ DIR *dirp;
+
+ GetDIR(dir, dirp);
+ seekdir(dirp, NUM2INT(pos));
+ return dir;
+}
+
+static VALUE
+Fdir_rewind(dir)
+ VALUE dir;
+{
+ DIR *dirp;
+
+ GetDIR(dir, dirp);
+ rewinddir(dirp);
+ return dir;
+}
+
+static VALUE
+Fdir_close(dir)
+ VALUE dir;
+{
+ DIR **dirpp;
+
+ Get_Data_Struct(dir, "dir", DIR*, dirpp);
+ if (*dirpp == NULL) Fail("already closed directory");
+ closedir(*dirpp);
+ *dirpp = NULL;
+
+ return Qnil;
+}
+
+char *getenv();
+
+static VALUE
+Fdir_chdir(obj, args)
+ VALUE obj, args;
+{
+ VALUE path;
+ char *dist = "";
+
+ rb_scan_args(args, "01", args, &path);
+ if (path) {
+ Check_Type(path, T_STRING);
+ dist = RSTRING(path)->ptr;
+ }
+ else {
+ dist = getenv("HOME");
+ if (!dist) {
+ dist = getenv("LOGDIR");
+ }
+ }
+
+ if (chdir(dist) < 0)
+ rb_sys_fail(Qnil);
+
+ return Qnil;
+}
+
+static VALUE
+Fdir_getwd(dir)
+ VALUE dir;
+{
+ extern char *getwd();
+ char path[MAXPATHLEN];
+
+ if (getwd(path) == 0) Fail(path);
+
+ return str_new2(path);
+}
+
+static VALUE
+Fdir_chroot(dir, path)
+ VALUE dir, path;
+{
+ Check_Type(path, T_STRING);
+
+ if (chroot(RSTRING(path)->ptr) == -1)
+ rb_sys_fail(Qnil);
+
+ return Qnil;
+}
+
+static VALUE
+Fdir_mkdir(obj, args)
+ VALUE obj, args;
+{
+ VALUE path, vmode;
+ int mode;
+
+ if (rb_scan_args(args, "11", &path, &vmode) == 2) {
+ mode = NUM2INT(vmode);
+ }
+ else {
+ mode = 0777;
+ }
+
+ Check_Type(path, T_STRING);
+ if (mkdir(RSTRING(path)->ptr, mode) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+
+ return Qnil;
+}
+
+static VALUE
+Fdir_rmdir(obj, dir)
+ VALUE obj;
+ struct RString *dir;
+{
+ Check_Type(dir, T_STRING);
+ if (rmdir(dir->ptr) < 0)
+ rb_sys_fail(dir->ptr);
+
+ return TRUE;
+}
+
+Init_Dir()
+{
+ extern VALUE M_Enumerable;
+
+ C_Dir = rb_define_class("Directory", C_Object);
+ rb_name_class(C_Dir, rb_intern("Dir")); /* alias */
+
+ rb_include_module(C_Dir, M_Enumerable);
+
+ rb_define_single_method(C_Dir, "open", Fdir_open, 1);
+
+ rb_define_method(C_Dir,"each", Fdir_each, 0);
+ rb_define_method(C_Dir,"rewind", Fdir_rewind, 0);
+ rb_define_method(C_Dir,"tell", Fdir_tell, 0);
+ rb_define_method(C_Dir,"seek", Fdir_seek, 1);
+ rb_define_method(C_Dir,"close", Fdir_close, 0);
+
+ rb_define_single_method(C_Dir,"chdir", Fdir_chdir, -2);
+ rb_define_single_method(C_Dir,"getwd", Fdir_getwd, 0);
+ rb_define_alias(C_Dir, "pwd", "getwd");
+ rb_define_single_method(C_Dir,"chroot", Fdir_chroot, 1);
+ rb_define_single_method(C_Dir,"mkdir", Fdir_mkdir, -2);
+ rb_define_single_method(C_Dir,"rmdir", Fdir_rmdir, 1);
+ rb_define_alias(C_Dir, "delete", "rmdir");
+ rb_define_alias(C_Dir, "unlink", "rmdir");
+}
diff --git a/dln.c b/dln.c
new file mode 100644
index 0000000000..766297e2ae
--- /dev/null
+++ b/dln.c
@@ -0,0 +1,1088 @@
+/************************************************
+
+ dln.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Tue Jan 18 17:05:06 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include "defines.h"
+#include "dln.h"
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+char *strdup();
+
+extern int errno;
+int dln_errno;
+
+static int dln_init_p = 0;
+
+#include <sys/stat.h>
+
+static char fbuf[MAXPATHLEN];
+static char *dln_find_1();
+char *getenv();
+char *index();
+int strcmp();
+
+char *
+dln_find_exe(fname, path)
+ char *fname;
+ char *path;
+{
+ if (!path) path = getenv("PATH");
+ if (!path) path = "/usr/local/bin:/usr/ucb:/usr/bin:/bin:.";
+ return dln_find_1(fname, path, 1);
+}
+
+char *
+dln_find_file(fname, path)
+ char *fname;
+ char *path;
+{
+ if (!path) path = ".";
+ return dln_find_1(fname, path, 0);
+}
+
+static char *
+dln_find_1(fname, path, exe_flag)
+ char *fname;
+ char *path;
+ int exe_flag; /* non 0 if looking for executable. */
+{
+ register char *dp;
+ register char *ep;
+ register char *bp;
+ struct stat st;
+
+ if (fname[0] == '/') return fname;
+
+ for (dp = path;; dp = ++ep)
+ {
+ register int l;
+ int i;
+ int fspace;
+
+ /* extract a component */
+ ep = index(dp, ':');
+ if (ep == NULL)
+ ep = dp+strlen(dp);
+
+ /* find the length of that component */
+ l = ep - dp;
+ bp = fbuf;
+ fspace = sizeof fbuf - 2;
+ if (l > 0)
+ {
+ /*
+ ** If the length of the component is zero length,
+ ** start from the current directory. If the
+ ** component begins with "~", start from the
+ ** user's $HOME environment variable. Otherwise
+ ** take the path literally.
+ */
+
+ if (*dp == '~' && (l == 1 || dp[1] == '/'))
+ {
+ char *home;
+
+ home = getenv("HOME");
+ if (home != NULL)
+ {
+ i = strlen(home);
+ if ((fspace -= i) < 0)
+ goto toolong;
+ memcpy(bp, home, i);
+ bp += i;
+ }
+ dp++;
+ l--;
+ }
+ if (l > 0)
+ {
+ if ((fspace -= l) < 0)
+ goto toolong;
+ memcpy(bp, dp, l);
+ bp += l;
+ }
+
+ /* add a "/" between directory and filename */
+ if (ep[-1] != '/')
+ *bp++ = '/';
+ }
+
+ /* now append the file name */
+ i = strlen(fname);
+ if ((fspace -= i) < 0)
+ {
+ toolong:
+ fprintf(stderr, "openpath: pathname too long (ignored)\n");
+ *bp = '\0';
+ fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
+ fprintf(stderr, "\tFile \"%s\"\n", fname);
+ continue;
+ }
+ memcpy(bp, fname, i + 1);
+
+ if (stat(fbuf, &st) == 0) {
+ if (exe_flag == 0) return fbuf;
+ /* looking for executable */
+#ifdef RUBY
+ if (eaccess(fbuf, X_OK) == 0) return fbuf;
+#else
+ {
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ if (uid == st.st_uid &&
+ (st.st_mode & S_IEXEC) ||
+ gid == st.st_gid &&
+ (st.st_mode & (S_IEXEC>>3)) ||
+ st.st_mode & (S_IEXEC>>6)) {
+ return fbuf;
+ }
+ }
+#endif
+ }
+ /* if not, and no other alternatives, life is bleak */
+ if (*ep == '\0') {
+ dln_errno = DLN_ENOENT;
+ return NULL;
+ }
+
+ /* otherwise try the next component in the search path */
+ }
+}
+
+#ifdef USE_DLN
+
+#include "st.h"
+#include <ar.h>
+#include <a.out.h>
+#ifndef N_COMM
+# define N_COMM 0x12
+#endif
+
+#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC)
+
+static st_table *sym_tbl;
+static st_table *undef_tbl;
+
+static int
+dln_load_header(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ int size;
+
+ lseek(fd, disp, 0);
+ size = read(fd, hdrp, sizeof(*hdrp));
+ if (size == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (size != sizeof(*hdrp) || N_BADMAG(*hdrp)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ return 0;
+}
+
+#if defined(sun) && defined(sparc)
+/* Sparc (Sun 4) macros */
+# undef relocation_info
+# define relocation_info reloc_info_sparc
+# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type])
+# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type])
+# define R_LENGTH(r) (reloc_r_length[(r)->r_type])
+static int reloc_r_rightshift[] = {
+ 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
+};
+static int reloc_r_bitsize[] = {
+ 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
+};
+static int reloc_r_length[] = {
+ 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+};
+# define R_PCREL(r) \
+ ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
+# define R_SYMBOL(r) ((r)->r_index)
+#else
+# define R_LENGTH(r) ((r)->r_length)
+# define R_PCREL(r) ((r)->r_pcrel)
+# define R_SYMBOL(r) ((r)->r_symbolnum)
+#endif
+
+static struct relocation_info *
+dln_load_reloc(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct relocation_info * reloc;
+ int size;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0);
+
+ size = hdrp->a_trsize + hdrp->a_drsize;
+ reloc = (struct relocation_info*)xmalloc(size);
+ if (reloc == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ if (read(fd, reloc, size) != size) {
+ dln_errno = errno;
+ free(reloc);
+ return NULL;
+ }
+
+ return reloc;
+}
+
+static struct nlist *
+dln_load_sym(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct nlist * buffer;
+ struct nlist * sym;
+ struct nlist * end;
+ long displ;
+ int size;
+ st_table *tbl;
+
+ lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0);
+ if (read(fd, &size, sizeof(int)) != sizeof(int)) {
+ goto err_noexec;
+ }
+
+ buffer = (struct nlist*)xmalloc(hdrp->a_syms + size);
+ if (buffer == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ lseek(fd, disp + N_SYMOFF(*hdrp), 0);
+ if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) {
+ free(buffer);
+ goto err_noexec;
+ }
+
+ sym = buffer;
+ end = sym + hdrp->a_syms / sizeof(struct nlist);
+ displ = (long)buffer + (long)(hdrp->a_syms);
+
+ while (sym < end) {
+ sym->n_un.n_name = (char*)sym->n_un.n_strx + displ;
+ sym++;
+ }
+ return buffer;
+
+ err_noexec:
+ dln_errno = DLN_ENOEXEC;
+ return NULL;
+}
+
+static st_table *
+dln_sym_hash(hdrp, syms)
+ struct exec *hdrp;
+ struct nlist *syms;
+{
+ st_table *tbl;
+ struct nlist *sym = syms;
+ struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist));
+
+ tbl = st_init_table(strcmp, st_strhash);
+ if (tbl == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ while (sym < end) {
+ st_insert(tbl, sym->n_un.n_name, sym);
+ sym++;
+ }
+ return tbl;
+}
+
+int
+dln_init(prog)
+ char *prog;
+{
+ char *file;
+ int fd, size;
+ struct exec hdr;
+ struct nlist *syms;
+
+ if (dln_init_p == 1) return;
+
+ file = dln_find_exe(prog, NULL);
+ if (file == NULL) return -1;
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ dln_errno = errno;
+ return -1;
+ }
+
+ if (dln_load_header(fd, &hdr, 0) == -1) return -1;
+ syms = dln_load_sym(fd, &hdr, 0);
+ if (syms == NULL) {
+ close(fd);
+ return -1;
+ }
+ sym_tbl = dln_sym_hash(&hdr, syms);
+ if (sym_tbl == NULL) { /* file may be start with #! */
+ char c = '\0';
+ char buf[MAXPATHLEN];
+ char *p;
+
+ free(syms);
+ lseek(fd, 0L, 0);
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '#') goto err_noexec;
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '!') goto err_noexec;
+
+ p = buf;
+ /* skip forwading spaces */
+ while (read(fd, &c, 1) == 1) {
+ if (c == '\n') goto err_noexec;
+ if (c != '\t' && c != ' ') {
+ *p++ = c;
+ break;
+ }
+ }
+ /* read in command name */
+ while (read(fd, p, 1) == 1) {
+ if (*p == '\n' || *p == '\t' || *p == ' ') break;
+ p++;
+ }
+ *p = '\0';
+ printf("%s\n", buf);
+
+ return dln_init(buf);
+ }
+ dln_init_p = 1;
+ undef_tbl = st_init_table(strcmp, st_strhash);
+ close(fd);
+ return 0;
+
+ err_noexec:
+ close(fd);
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+}
+
+long
+dln_load_text_data(fd, hdrp, bss, disp)
+ int fd;
+ struct exec *hdrp;
+ int bss;
+ long disp;
+{
+ int size;
+ unsigned char* addr;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp), 0);
+ size = hdrp->a_text + hdrp->a_data;
+
+ if (bss == -1) size += hdrp->a_bss;
+ else if (bss > 1) size += bss;
+
+ addr = (unsigned char*)xmalloc(size);
+ if (addr == NULL) {
+ dln_errno = errno;
+ return 0;
+ }
+
+ if (read(fd, addr, size) != size) {
+ dln_errno = errno;
+ free(addr);
+ return 0;
+ }
+
+ if (bss == -1) {
+ bzero(addr + hdrp->a_text + hdrp->a_data, hdrp->a_bss);
+ }
+ else if (bss > 0) {
+ bzero(addr + hdrp->a_text + hdrp->a_data, bss );
+ }
+
+ return (long)addr;
+}
+
+static int
+undef_print(key, value, arg)
+ char *key;
+{
+ fprintf(stderr, " %s\n", key);
+ return ST_CONTINUE;
+}
+
+static
+dln_undefined()
+{
+ fprintf(stderr, "dln: Calling undefined function\n");
+ fprintf(stderr, " Undefined symbols:\n");
+ st_foreach(undef_tbl, undef_print, NULL);
+#ifdef RUBY
+ rb_exit(1);
+#else
+ exit(1);
+#endif
+}
+
+struct undef {
+ char *name;
+ struct relocation_info reloc;
+ long base;
+ char *addr;
+ union {
+ char c;
+ short s;
+ long l;
+ } u;
+};
+
+static st_table *reloc_tbl = NULL;
+static void
+link_undef(name, base, reloc)
+ char *name;
+ long base;
+ struct relocation_info *reloc;
+{
+ static int u_no = 0;
+ struct undef *obj;
+ char *addr = (char*)(reloc->r_address + base);
+
+ obj = (struct undef*)xmalloc(sizeof(struct undef));
+ obj->name = strdup(name);
+ obj->reloc = *reloc;
+ obj->base = base;
+ switch (R_LENGTH(reloc)) {
+ case 0: /* byte */
+ obj->u.c = *addr;
+ break;
+ case 1: /* word */
+ obj->u.s = *(short*)addr;
+ break;
+ case 2: /* long */
+ obj->u.l = *(long*)addr;
+ break;
+ }
+ if (reloc_tbl == NULL) {
+ reloc_tbl = st_init_table(ST_NUMCMP, ST_NUMHASH);
+ }
+ st_insert(reloc_tbl, u_no++, obj);
+}
+
+struct reloc_arg {
+ char *name;
+ long value;
+};
+
+static int
+reloc_undef(no, undef, arg)
+ int no;
+ struct undef *undef;
+ struct reloc_arg *arg;
+{
+ int datum;
+ char *address;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE;
+ address = (char*)(undef->base + undef->reloc.r_address);
+ datum = arg->value;
+
+ if (R_PCREL(&(undef->reloc))) datum -= undef->base;
+#if defined(sun) && defined(sparc)
+ datum += undef->reloc.r_addend;
+ datum >>= R_RIGHTSHIFT(&(undef->reloc));
+ mask = 1 << R_BITSIZE(&(undef->reloc)) - 1;
+ mask |= mask -1;
+ datum &= mask;
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0:
+ *address = undef->u.c;
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address = undef->u.s;
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address = undef->u.l;
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0: /* byte */
+ *address = undef->u.c + datum;
+ break;
+ case 1: /* word */
+ *(short *)address = undef->u.s + datum;
+ break;
+ case 2: /* long */
+ *(long *)address = undef->u.l + datum;
+ break;
+ }
+#endif
+ free(undef->name);
+ free(undef);
+ return ST_DELETE;
+}
+
+static int
+unlink_undef(name, value)
+ char *name;
+ long value;
+{
+ struct reloc_arg arg;
+
+ arg.name = name;
+ arg.value = value;
+ st_foreach(reloc_tbl, reloc_undef, &arg);
+}
+
+static int dln_load_1(fd, disp, need_init)
+ int fd;
+ long disp;
+ int need_init;
+{
+ static char *libc = LIBC_NAME;
+ struct exec hdr;
+ struct relocation_info *reloc = NULL;
+ long block = 0;
+ long new_common = 0; /* Length of new common */
+ struct nlist *syms = NULL;
+ struct nlist *sym;
+ struct nlist *end;
+ int init_p = 0;
+
+ if (dln_load_header(fd, &hdr, disp) == -1) return -1;
+ if (INVALID_OBJECT(hdr)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ reloc = dln_load_reloc(fd, &hdr, disp);
+ if (reloc == NULL) return -1;
+ syms = dln_load_sym(fd, &hdr, disp);
+ if (syms == NULL) return -1;
+
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ struct nlist *old_sym;
+ int value = sym->n_value;
+
+ if (sym->n_type == (N_UNDF | N_EXT)) {
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) {
+ old_sym = NULL;
+ }
+
+ if (value) {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ int rnd =
+ value >= sizeof(double) ? sizeof(double) - 1
+ : value >= sizeof(long) ? sizeof(long) - 1
+ : sizeof(short) - 1;
+
+ sym->n_type = N_COMM;
+ new_common += rnd;
+ new_common &= ~(long)rnd;
+ sym->n_value = new_common;
+ new_common += value;
+ }
+ }
+ else {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ sym->n_value = (long)dln_undefined;
+ st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL);
+ }
+ }
+ }
+ sym++;
+ }
+
+ block = dln_load_text_data(fd, &hdr, hdr.a_bss + new_common, disp);
+ if (block == 0) goto err_exit;
+
+ sym = syms;
+ while (sym < end) {
+ struct nlist *new_sym;
+ char *key;
+
+ switch (sym->n_type) {
+ case N_COMM:
+ sym->n_value += hdr.a_text + hdr.a_data;
+ case N_TEXT|N_EXT:
+ case N_DATA|N_EXT:
+ sym->n_value += block;
+
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0
+ && new_sym->n_value != (long)dln_undefined) {
+ dln_errno = DLN_ECONFL;
+ goto err_exit;
+ }
+
+ key = sym->n_un.n_name;
+ if (st_delete(undef_tbl, &key, NULL) != 0) {
+ unlink_undef(key, sym->n_value);
+ free(key);
+ }
+
+ new_sym = (struct nlist*)xmalloc(sizeof(struct nlist));
+ *new_sym = *sym;
+ new_sym->n_un.n_name = strdup(sym->n_un.n_name);
+ st_insert(sym_tbl, new_sym->n_un.n_name, new_sym);
+ }
+ sym++;
+ }
+
+ /*
+ * First comes the text-relocation
+ */
+ {
+ struct relocation_info * rel = reloc;
+ struct relocation_info * rel_beg = reloc +
+ (hdr.a_trsize/sizeof(struct relocation_info));
+ struct relocation_info * rel_end = reloc +
+ (hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info);
+
+ while (rel < rel_end) {
+ char *address = (char*)(rel->r_address + block);
+ long datum = 0;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if(rel >= rel_beg)
+ address += hdr.a_text;
+
+ if (rel->r_extern) { /* Look it up in symbol-table */
+ sym = &(syms[R_SYMBOL(rel)]);
+ switch (sym->n_type) {
+ case N_EXT|N_UNDF:
+ link_undef(sym->n_un.n_name, block, rel);
+ case N_EXT|N_COMM:
+ case N_COMM:
+ datum = sym->n_value;
+ break;
+ default:
+ goto err_exit;
+ }
+ } /* end.. look it up */
+ else { /* is static */
+ switch (R_SYMBOL(rel) & N_TYPE) {
+ case N_TEXT:
+ case N_DATA:
+ datum = block;
+ break;
+ case N_BSS:
+ datum = block + new_common;
+ break;
+ case N_ABS:
+ break;
+ }
+ } /* end .. is static */
+ if (R_PCREL(rel)) datum -= block;
+
+#if defined(sun) && defined(sparc)
+ datum += rel->r_addend;
+ datum >>= R_RIGHTSHIFT(rel);
+ mask = 1 << R_BITSIZE(rel) - 1;
+ mask |= mask -1;
+ datum &= mask;
+
+ switch (R_LENGTH(rel)) {
+ case 0:
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(rel)) {
+ case 0: /* byte */
+ if (datum < -128 || datum > 127) goto err_exit;
+ *address += datum;
+ break;
+ case 1: /* word */
+ *(short *)address += datum;
+ break;
+ case 2: /* long */
+ *(long *)address += datum;
+ break;
+ }
+#endif
+ rel++;
+ }
+ }
+
+ if (need_init) {
+ if (undef_tbl->num_entries > 0) {
+ if (dln_load_lib(libc) == -1) goto err_exit;
+ }
+
+ sym = syms;
+ while (sym < end) {
+ char *name = sym->n_un.n_name;
+ if (name[0] == '_' && sym->n_value >= block
+ && ((bcmp (name, "_Init_", 6) == 0
+ || bcmp (name, "_init_", 6) == 0) && name[6] != '_')) {
+ init_p = 1;
+ ((int (*)())sym->n_value)();
+ }
+ sym++;
+ }
+ }
+ free(reloc);
+ free(syms);
+ if (need_init) {
+ if (init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+ if (undef_tbl->num_entries > 0) {
+ if (dln_load_lib(libc) == -1) goto err_exit;
+ if (undef_tbl->num_entries > 0) {
+ dln_errno = DLN_EUNDEF;
+ return -1;
+ }
+ }
+ }
+ return 0;
+
+ err_exit:
+ if (syms) free(syms);
+ if (reloc) free(reloc);
+ if (block) free(block);
+ return -1;
+}
+
+int
+dln_load(file)
+ char *file;
+{
+ int fd;
+ int result;
+
+ if (dln_init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ result = dln_load_1(fd, 0, 1);
+ close(fd);
+
+ return result;
+}
+
+struct symdef {
+ int str_index;
+ int lib_offset;
+};
+
+static int target_offset;
+static int
+search_undef(key, value, lib_tbl)
+ char *key;
+ int value;
+ st_table *lib_tbl;
+{
+ static char *last = "";
+ int offset;
+
+ if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE;
+ if (strcmp(last, key) != 0) {
+ last = key;
+ target_offset = offset;
+ }
+ return ST_STOP;
+}
+
+char *dln_library_path = DLN_DEFAULT_PATH;
+
+int
+dln_load_lib(lib)
+ char *lib;
+{
+ char *path, *file;
+ char armagic[SARMAG];
+ int fd, size;
+ struct ar_hdr ahdr;
+ st_table *lib_tbl = NULL;
+ int *data, nsym;
+ struct symdef *base;
+ char *name_base;
+
+ if (dln_init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+
+ if (undef_tbl->num_entries == 0) return 0;
+ dln_errno = DLN_EBADLIB;
+
+ /* library search path: */
+ /* look for environment variable DLN_LIBRARY_PATH first. */
+ /* then variable dln_library_path. */
+ /* if path is still NULL, use "." for path. */
+ path = getenv("DLN_LIBRARY_PATH");
+ if (path == NULL) path = dln_library_path;
+
+ file = dln_find_file(lib, path);
+ fd = open(file, O_RDONLY);
+ if (fd == -1) goto syserr;
+ size = read(fd, armagic, SARMAG);
+ if (fd == -1) goto syserr;
+
+ if (size != SARMAG) {
+ dln_errno = DLN_ENOTLIB;
+ goto badlib;
+ }
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+
+ if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0 && ahdr.ar_name[9] == ' ') {
+ /* make hash table from __.SYMDEF */
+
+ lib_tbl = st_init_table(strcmp, st_strhash);
+ data = (int*)xmalloc(size);
+ if (data == NULL) goto syserr;
+ size = read(fd, data, size);
+ nsym = *data / sizeof(struct symdef);
+ base = (struct symdef*)(data + 1);
+ name_base = (char*)(base + nsym) + sizeof(int);
+ while (nsym > 0) {
+ char *name = name_base + base->str_index;
+
+ st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr));
+ nsym--;
+ base++;
+ }
+ for (;;) {
+ target_offset = -1;
+ st_foreach(undef_tbl, search_undef, lib_tbl);
+ if (target_offset == -1) break;
+ if (dln_load_1(fd, target_offset, 0) == -1) {
+ st_free_table(lib_tbl);
+ free(data);
+ goto badlib;
+ }
+ if (undef_tbl->num_entries == 0) break;
+ }
+ free(data);
+ st_free_table(lib_tbl);
+ }
+ else {
+ /* linear library, need to scan (FUTURE) */
+
+ for (;;) {
+ int offset = SARMAG;
+ int found = 0;
+ struct exec hdr;
+ struct nlist *syms, *sym, *end;
+
+ while (undef_tbl->num_entries > 0) {
+ found = 0;
+ lseek(fd, offset, 0);
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size == 0) break;
+ if (size != sizeof(ahdr)
+ || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+ offset += sizeof(ahdr);
+ if (dln_load_header(fd, &hdr, offset) == -1)
+ goto badlib;
+ syms = dln_load_sym(fd, &hdr, offset);
+ if (syms == NULL) goto badlib;
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ if (sym->n_type == N_EXT|N_TEXT
+ && st_lookup(undef_tbl, sym->n_un.n_name, NULL)) {
+ break;
+ }
+ sym++;
+ }
+ if (sym < end) {
+ found++;
+ free(syms);
+ if (dln_load_1(fd, offset, 0) == -1) {
+ goto badlib;
+ }
+ }
+ offset += size;
+ if (offset & 1) offset++;
+ }
+ if (found) break;
+ }
+ }
+ close(fd);
+ return 0;
+
+ syserr:
+ dln_errno = errno;
+ badlib:
+ if (fd >= 0) close(fd);
+ return -1;
+}
+
+char *
+dln_strerror()
+{
+ char *strerror();
+
+ switch (dln_errno) {
+ case DLN_ECONFL:
+ return "Symbol name conflict";
+ case DLN_ENOINIT:
+ return "No inititalizer given";
+ case DLN_EUNDEF:
+ return "Unresolved symbols";
+ case DLN_ENOTLIB:
+ return "Not a library file";
+ case DLN_EBADLIB:
+ return "Malformed library file";
+ case DLN_EINIT:
+ return "Not initialized";
+ default:
+ return strerror(dln_errno);
+ }
+}
+
+dln_perror(str)
+ char *str;
+{
+ fprintf(stderr, "%s: %s\n", str, dln_strerror());
+}
+
+void*
+dln_get_sym(name)
+ char *name;
+{
+ struct nlist *sym;
+
+ if (st_lookup(sym_tbl, name, &sym))
+ return (void*)sym->n_value;
+ return NULL;
+}
+
+#ifdef TEST
+xmalloc(size)
+ int size;
+{
+ return malloc(size);
+}
+
+xcalloc(size, n)
+ int size, n;
+{
+ return calloc(size, n);
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (dln_init(argv[0]) == -1) {
+ dln_perror("dln_init");
+ exit(1);
+ }
+
+ while (argc > 1) {
+ printf("obj: %s\n", argv[1]);
+ if (dln_load(argv[1]) == -1) {
+ dln_perror("dln_load");
+ exit(1);
+ }
+ argc--;
+ argv++;
+ }
+ if (dln_load_lib("libdln.a") == -1) {
+ dln_perror("dln_init");
+ exit(1);
+ }
+
+ if (dln_get_sym("_foo"))
+ printf("_foo defined\n");
+ else
+ printf("_foo undefined\n");
+}
+#endif /* TEST */
+
+#else /* USE_DLN */
+
+int
+dln_init(file)
+ char *file;
+{
+ return 0;
+}
+
+int
+dln_load(file)
+ char *file;
+{
+ return 0;
+}
+
+int
+dln_load_lib(file)
+ char *file;
+{
+ return 0;
+}
+
+#endif /* USE_DLN */
diff --git a/dln.h b/dln.h
new file mode 100644
index 0000000000..0589954e73
--- /dev/null
+++ b/dln.h
@@ -0,0 +1,33 @@
+/************************************************
+
+ dln.h -
+
+ $Author: matz $
+ $Revision: 1.1.1.1 $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Wed Jan 19 16:53:09 JST 1994
+
+************************************************/
+#ifndef DLN_H
+#define DLN_H
+
+#include <sys/errno.h>
+
+int dln_init();
+int dln_load();
+int dln_load_lib();
+
+extern int dln_errno;
+
+#define DLN_ENOENT ENOENT /* No such file or directory */
+#define DLN_ENOEXEC ENOEXEC /* Exec format error */
+#define DLN_ECONFL 101 /* Symbol name conflict */
+#define DLN_ENOINIT 102 /* No inititalizer given */
+#define DLN_EUNDEF 103 /* Undefine symbol remains */
+#define DLN_ENOTLIB 104 /* Not a library file */
+#define DLN_EBADLIB 105 /* Malformed library file */
+#define DLN_EINIT 106 /* Not initialized */
+
+char *dln_strerror();
+
+#endif
diff --git a/enum.c b/enum.c
new file mode 100644
index 0000000000..bf04757cfc
--- /dev/null
+++ b/enum.c
@@ -0,0 +1,332 @@
+/************************************************
+
+ enum.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Fri Oct 1 15:15:19 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+VALUE M_Enumerable;
+static ID id_each, id_match, id_equal, id_cmp;
+
+void
+rb_each(obj)
+ VALUE obj;
+{
+ rb_funcall(obj, id_each, 0, Qnil);
+}
+
+static void
+enum_grep(i, arg)
+ VALUE i, *arg;
+{
+ if (rb_funcall(arg[0], id_match, 1, i)) {
+ Fary_push(arg[1], i);
+ }
+}
+
+static void
+enum_grep2(i, pat)
+ VALUE i, pat;
+{
+ if (rb_funcall(pat, id_match, 1, i)) {
+ rb_yield(i);
+ }
+}
+
+static VALUE
+Fenum_grep(obj, pat)
+ VALUE obj;
+{
+ if (iterator_p()) {
+ rb_iterate(rb_each, obj, enum_grep2, pat);
+ return obj;
+ }
+ else {
+ VALUE tmp, arg[2];
+
+ arg[0] = pat; arg[1] = tmp = ary_new();
+ GC_LINK;
+ GC_PRO(tmp);
+
+ rb_iterate(rb_each, obj, enum_grep, arg);
+
+ GC_UNLINK;
+ return tmp;
+ }
+}
+
+static void
+enum_find(i, foundp)
+ VALUE i;
+ int *foundp;
+{
+ if (rb_yield(i)) {
+ *foundp = TRUE;
+ rb_break();
+ }
+}
+
+static VALUE
+Fenum_find(obj)
+ VALUE obj;
+{
+ int enum_found;
+
+ enum_found = FALSE;
+ rb_iterate(rb_each, obj, enum_find, &enum_found);
+ return enum_found;
+}
+
+static void
+enum_find_all(i, tmp)
+ VALUE i;
+{
+ if (rb_yield(i)) {
+ Fary_push(tmp, i);
+ }
+}
+
+static VALUE
+Fenum_find_all(obj)
+ VALUE obj;
+{
+ VALUE tmp;
+
+ GC_LINK;
+ GC_PRO3(tmp, ary_new());
+
+ rb_iterate(rb_each, obj, enum_find_all, Qnil);
+
+ GC_UNLINK;
+ return tmp;
+}
+
+static void
+enum_collect(i, tmp)
+ VALUE i;
+{
+ VALUE retval;
+
+ GC_LINK;
+ GC_PRO3(retval, rb_yield(i));
+
+ if (retval) {
+ Fary_push(tmp, retval);
+ }
+
+ GC_UNLINK;
+}
+
+static VALUE
+Fenum_collect(obj)
+ VALUE obj;
+{
+ VALUE tmp;
+
+ GC_LINK;
+ GC_PRO3(tmp, ary_new());
+
+ rb_iterate(rb_each, obj, enum_collect, tmp);
+
+ GC_UNLINK;
+ return tmp;
+}
+
+static void
+enum_reverse(i, tmp)
+ VALUE i, tmp;
+{
+ Fary_unshift(tmp, i);
+}
+
+static VALUE
+Fenum_reverse(obj)
+ VALUE obj;
+{
+ VALUE tmp;
+
+ GC_LINK;
+ GC_PRO3(tmp, ary_new());
+
+ rb_iterate(rb_each, obj, enum_reverse, tmp);
+
+ GC_UNLINK;
+ return tmp;
+}
+
+static void
+enum_all(i, ary)
+ VALUE i, ary;
+{
+ Fary_push(ary, i);
+}
+
+static VALUE
+Fenum_to_a(obj)
+ VALUE obj;
+{
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ rb_iterate(rb_each, obj, enum_all, ary);
+ GC_UNLINK;
+
+ return ary;
+}
+
+static VALUE
+Fenum_sort(obj)
+ VALUE obj;
+{
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(ary, Fenum_to_a(obj));
+ Fary_sort(ary);
+ GC_UNLINK;
+ return ary;
+}
+
+static void
+enum_min(i, min)
+ VALUE i, *min;
+{
+ VALUE cmp;
+
+ if (*min == Qnil)
+ *min = i;
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, *min);
+ if (FIX2INT(cmp) < 0)
+ *min = i;
+ }
+}
+
+static VALUE
+Fenum_min(obj)
+ VALUE obj;
+{
+ VALUE min;
+
+ GC_LINK;
+ GC_PRO2(min);
+ rb_iterate(rb_each, obj, enum_min, &min);
+ GC_UNLINK;
+ return min;
+}
+
+static void
+enum_max(i, max)
+ VALUE i, *max;
+{
+ VALUE cmp;
+
+ if (*max == Qnil)
+ *max = i;
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, *max);
+ if (FIX2INT(cmp) > 0)
+ *max = i;
+ }
+}
+
+static VALUE
+Fenum_max(obj)
+ VALUE obj;
+{
+ VALUE max;
+
+ GC_LINK;
+ GC_PRO2(max);
+ rb_iterate(rb_each, obj, enum_max, &max);
+ GC_UNLINK;
+ return max;
+}
+
+struct i_v_pair {
+ int i;
+ VALUE v;
+ int found;
+};
+
+static void
+enum_index(item, iv)
+ VALUE item;
+ struct i_v_pair *iv;
+{
+ if (rb_funcall(item, id_equal, 1, iv->v)) {
+ iv->found = 1;
+ rb_break();
+ }
+ else {
+ iv->i++;
+ }
+}
+
+static VALUE
+Fenum_index(obj, val)
+ VALUE obj;
+{
+ struct i_v_pair iv;
+
+ iv.i = 0;
+ iv.v = val;
+ iv.found = 0;
+ rb_iterate(rb_each, obj, enum_index, &iv);
+ if (iv.found) return INT2FIX(iv.i);
+ return Qnil; /* not found */
+}
+
+static void
+enum_includes(item, iv)
+ VALUE item;
+ struct i_v_pair *iv;
+{
+ if (rb_funcall(item, id_equal, 1, iv->v)) {
+ iv->i = 1;
+ rb_break();
+ }
+}
+
+static VALUE
+Fenum_includes(obj, val)
+ VALUE obj;
+{
+ struct i_v_pair iv;
+
+ iv.i = 0;
+ iv.v = val;
+ rb_iterate(rb_each, obj, enum_includes, &iv);
+ if (iv.i) return TRUE;
+ return FALSE;
+}
+
+Init_Enumerable()
+{
+ M_Enumerable = rb_define_module("Enumerable");
+
+ rb_define_method(M_Enumerable,"to_a", Fenum_to_a, 0);
+
+ rb_define_method(M_Enumerable,"grep", Fenum_grep, 1);
+ rb_define_method(M_Enumerable,"find", Fenum_find, 0);
+ rb_define_method(M_Enumerable,"find_all", Fenum_find_all, 0);
+ rb_define_method(M_Enumerable,"collect", Fenum_collect, 0);
+ rb_define_method(M_Enumerable,"reverse", Fenum_reverse, 0);
+ rb_define_method(M_Enumerable,"min", Fenum_min, 0);
+ rb_define_method(M_Enumerable,"max", Fenum_max, 0);
+ rb_define_method(M_Enumerable,"index", Fenum_index, 1);
+ rb_define_method(M_Enumerable,"includes", Fenum_includes, 1);
+
+ id_each = rb_intern("each");
+ id_match = rb_intern("=~");
+ id_equal = rb_intern("==");
+ id_cmp = rb_intern("<=>");
+}
diff --git a/env.h b/env.h
new file mode 100644
index 0000000000..09ff31d13f
--- /dev/null
+++ b/env.h
@@ -0,0 +1,40 @@
+/************************************************
+
+ env.h -
+
+ $Author$
+ $Revision$
+ $Date$
+ created at: Mon Jul 11 11:53:03 JST 1994
+
+************************************************/
+#ifndef ENV_H
+#define ENV_H
+
+extern struct ENVIRON {
+ VALUE self;
+ int argc;
+ VALUE *argv;
+ struct RClass *current_module;
+ struct RClass *last_class;
+ char *file;
+ int line;
+ ID last_func;
+ ID *local_tbl;
+ VALUE *local_vars;
+ int in_eval;
+ struct BLOCK *block;
+ int iterator;
+ int flags;
+ struct ENVIRON *prev;
+} *the_env;
+
+#define ITERATOR_P() (the_env->iterator > 0 && the_env->iterator < 3)
+#define Qself the_env->self
+#define the_class the_env->current_module
+
+#define DURING_ITERATE 1
+#define DURING_RESQUE 2
+#define DURING_CALL 4
+
+#endif /* ENV_H */
diff --git a/error.c b/error.c
new file mode 100644
index 0000000000..001f93df44
--- /dev/null
+++ b/error.c
@@ -0,0 +1,165 @@
+/************************************************
+
+ error.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Mon Aug 9 16:11:34 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include <stdio.h>
+#include <varargs.h>
+
+extern char *sourcefile;
+extern int sourceline;
+
+int nerrs;
+
+static void
+err_sprintf(buf, fmt, args)
+ char *buf, *fmt;
+ va_list 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;
+{
+ extern errstr;
+ char buf[BUFSIZ];
+
+ err_sprintf(buf, fmt, args);
+ if (the_env->in_eval) {
+ if (errstr == Qnil) {
+ errstr = str_new2(buf);
+ }
+ else {
+ str_cat(errstr, buf, strlen(buf));
+ }
+ }
+ else {
+ fputs(buf, stderr);
+ }
+}
+
+yyerror(msg)
+ char *msg;
+{
+ Error("%s", msg);
+}
+
+Error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ va_list args;
+
+ va_start(args);
+ err_print(fmt, args);
+ va_end(args);
+ nerrs++;
+}
+
+Warning(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ sprintf(buf, "warning: %s", fmt);
+
+ va_start(args);
+ err_print(buf, args);
+ va_end(args);
+}
+
+Fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ va_list args;
+
+ va_start(args);
+ err_print(fmt, args);
+ va_end(args);
+ rb_exit(1);
+}
+
+Bug(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ sprintf(buf, "[BUG] %s", fmt);
+
+ va_start(args);
+ err_print(buf, args);
+ va_end(args);
+ abort();
+}
+
+Fail(fmt, va_alist)
+ char *fmt;
+ va_dcl
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_start(args);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+
+ rb_fail(str_new2(buf));
+}
+
+rb_sys_fail(mesg)
+ char *mesg;
+{
+ char buf[BUFSIZ];
+ extern int errno;
+
+ if (mesg == Qnil)
+ sprintf(buf, "%s.\n", strerror(errno));
+ else
+ sprintf(buf, "%s - %s.\n", strerror(errno), mesg);
+
+ errno = 0;
+ rb_fail(str_new2(buf));
+}
+
+static char *builtin_types[] = {
+ "Nil",
+ "Object",
+ "Class",
+ "iClass",
+ "Module",
+ "Float",
+ "String",
+ "Regexp",
+ "Array",
+ "Fixnum",
+ "Dictionary",
+ "Data",
+};
+
+WrongType(x, t)
+ VALUE x;
+ int t;
+{
+ Fail("wrong argument type %s (expected %s)",
+ rb_class2name(CLASS_OF(x)), builtin_types[t]);
+}
diff --git a/etc.c b/etc.c
new file mode 100644
index 0000000000..311f6eb137
--- /dev/null
+++ b/etc.c
@@ -0,0 +1,220 @@
+/************************************************
+
+ etc.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Tue Mar 22 18:39:19 JST 1994
+
+************************************************/
+
+#include "ruby.h"
+#include <pwd.h>
+#include <grp.h>
+
+char *getlogin();
+
+static VALUE
+Fetc_getlogin(obj)
+ VALUE obj;
+{
+ char *login = getlogin();
+
+ if (login)
+ return str_new2(getlogin());
+ return Qnil;
+}
+
+static VALUE
+setup_passwd(pwd)
+ struct passwd *pwd;
+{
+ VALUE pw, name, passwd, gecos, dir, shell;
+#ifdef PW_CLASS
+ VALUE class;
+#endif
+#ifdef PW_COMMENT
+ VALUE comment;
+#endif
+
+ if (pwd == Qnil) rb_sys_fail("/etc/passwd");
+ GC_LINK;
+ GC_PRO3(pw, str_new2(pwd->pw_name));
+ GC_PRO3(passwd, str_new2(pwd->pw_passwd));
+ GC_PRO3(gecos, str_new2(pwd->pw_gecos));
+ GC_PRO3(dir, str_new2(pwd->pw_dir));
+ GC_PRO3(shell, str_new2(pwd->pw_shell));
+#ifdef PW_CLASS
+ GC_PRO3(class, str_new2(pwd->pw_class));
+#endif
+#ifdef PW_COMMENT
+ GC_PRO3(comment, str_new2(pwd->pw_comment));
+#endif
+
+ pw = struct_new("passwd",
+ "name", name,
+ "passwd", passwd,
+ "uid", INT2FIX(pwd->pw_uid),
+ "gid", INT2FIX(pwd->pw_gid),
+ "gecos", gecos,
+ "dir", dir,
+ "shell", shell,
+#ifdef PW_CHANGE
+ "change", INT2FIX(pwd->pw_change),
+#endif
+#ifdef PW_QUOTA
+ "quota", INT2FIX(pwd->pw_quota),
+#endif
+#ifdef PW_AGE
+ "age", INT2FIX(pwd->pw_age),
+#endif
+#ifdef PW_CLASS
+ "class", class,
+#endif
+#ifdef PW_COMMENT
+ "comment", comment,
+#endif
+#ifdef PW_EXPIRE
+ "expire", INT2FIX(pwd->pw_expire),
+#endif
+ Qnil);
+ GC_UNLINK;
+
+ return pw;
+}
+
+static VALUE
+Fetc_getpwuid(obj, args)
+ VALUE obj, args;
+{
+ VALUE id;
+ int uid;
+ struct passwd *pwd;
+
+ if (rb_scan_args(args, "01", &id) == 1) {
+ uid = NUM2INT(id);
+ }
+ else {
+ uid = getuid();
+ }
+ pwd = getpwuid(uid);
+ if (pwd == Qnil) Fail("can't find user for %d", uid);
+ return setup_passwd(pwd);
+}
+
+static VALUE
+Fetc_getpwnam(obj, nam)
+ VALUE obj, nam;
+{
+ struct passwd *pwd;
+
+ Check_Type(nam, T_STRING);
+ pwd = getpwnam(RSTRING(nam)->ptr);
+ if (pwd == Qnil) Fail("can't find user for %s", RSTRING(nam)->ptr);
+ return setup_passwd(pwd);
+}
+
+static VALUE
+Fetc_passwd(obj)
+ VALUE obj;
+{
+ struct passwd *pw;
+
+ if (iterator_p()) {
+ setpwent();
+ while (pw = getpwent()) {
+ rb_yield(setup_passwd(pw));
+ }
+ endpwent();
+ return obj;
+ }
+ pw = getpwent();
+ if (pw == Qnil) Fail("can't fetch next -- /etc/passwd");
+ return setup_passwd(pw);
+}
+
+static VALUE
+setup_group(grp)
+ struct group *grp;
+{
+ VALUE mem, obj, name, passwd;
+ char **tbl;
+
+ GC_LINK;
+ GC_PRO3(mem, ary_new());
+ tbl = grp->gr_mem;
+ while (*tbl) {
+ Fary_push(mem, str_new2(*tbl));
+ tbl++;
+ }
+ GC_PRO3(name, str_new2(grp->gr_name));
+ GC_PRO3(passwd, str_new2(grp->gr_passwd));
+ obj = struct_new("group",
+ "name", name,
+ "passwd", passwd,
+ "gid", INT2FIX(grp->gr_gid),
+ "mem", mem,
+ Qnil);
+ GC_UNLINK;
+
+ return obj;
+}
+
+static VALUE
+Fetc_getgrgid(obj, id)
+ VALUE obj, id;
+{
+ int gid;
+ struct group *grp;
+
+ gid = NUM2INT(id);
+ grp = getgrgid(gid);
+ if (grp == Qnil) Fail("can't find group for %d", gid);
+ return setup_group(grp);
+}
+
+static VALUE
+Fetc_getgrnam(obj, nam)
+ VALUE obj, nam;
+{
+ struct group *grp;
+
+ Check_Type(nam, T_STRING);
+ grp = getgrnam(RSTRING(nam)->ptr);
+ if (grp == Qnil) Fail("can't find group for %s", RSTRING(nam)->ptr);
+ return setup_group(grp);
+}
+
+static VALUE
+Fetc_group(obj)
+ VALUE obj;
+{
+ struct group *grp;
+
+ if (iterator_p()) {
+ setgrent();
+ while (grp = getgrent()) {
+ rb_yield(setup_group(grp));
+ }
+ endgrent();
+ return obj;
+ }
+ return setup_group(getgrent());
+}
+
+VALUE M_Etc;
+
+Init_Etc()
+{
+ M_Etc = rb_define_module("Etc");
+
+ rb_define_mfunc(M_Etc, "getlogin", Fetc_getlogin, 0);
+
+ rb_define_mfunc(M_Etc, "getpwuid", Fetc_getpwuid, -2);
+ rb_define_mfunc(M_Etc, "getpwnam", Fetc_getpwnam, 1);
+ rb_define_mfunc(M_Etc, "passwd", Fetc_passwd, 0);
+
+ rb_define_mfunc(M_Etc, "getgrgid", Fetc_getgrgid, 1);
+ rb_define_mfunc(M_Etc, "getgrnam", Fetc_getgrnam, 1);
+ rb_define_mfunc(M_Etc, "group", Fetc_group, 0);
+}
diff --git a/eval.c b/eval.c
new file mode 100644
index 0000000000..f4d6d545ae
--- /dev/null
+++ b/eval.c
@@ -0,0 +1,1858 @@
+/************************************************
+
+ eval.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:23 $
+ created at: Thu Jun 10 14:22:17 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "ident.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include "st.h"
+
+static ID match, each;
+VALUE errstr, errat;
+extern NODE *eval_tree;
+
+struct ENVIRON *the_env, *top_env;
+
+#define PUSH_ENV() {\
+ struct ENVIRON _this;\
+ if (the_env) _this = *the_env; else bzero(&_this, sizeof(_this));\
+ _this.prev = the_env;\
+ _this.flags = 0;\
+ the_env = &_this;\
+
+#define POP_ENV() the_env = the_env->prev; }
+
+struct BLOCK {
+ NODE *var;
+ NODE *body;
+ struct ENVIRON env;
+ int level;
+};
+
+#define SET_BLOCK(b,node) (b.level=tag_level,b.var=node->nd_var,\
+ b.body=node->nd_body,b.env=*the_env,\
+ the_env->block= &b)
+
+static int tag_level, target_level;
+static struct tag {
+ int level;
+ jmp_buf buf;
+ struct gc_list *gclist;
+ struct ENVIRON *env;
+} *prot_tag;
+
+#define PUSH_TAG() {\
+ struct tag _this;\
+ struct tag *_oldtag = prot_tag;\
+ &_oldtag;\
+ _this.level= ++tag_level;\
+ _this.gclist= GC_List;\
+ _this.env= the_env;\
+ prot_tag = &_this;\
+
+#define POP_TAG() \
+ tag_level--;\
+ prot_tag = _oldtag;\
+}
+
+#define EXEC_TAG() (setjmp(prot_tag->buf))
+#define JUMP_TAG(val) {\
+ the_env = prot_tag->env;\
+ GC_List = prot_tag->gclist;\
+ longjmp(prot_tag->buf,(val));\
+}
+
+#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 IN_BLOCK 0x08
+
+static VALUE rb_eval();
+VALUE Feval();
+
+VALUE Argv;
+static VALUE rb_call();
+VALUE rb_apply();
+
+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()
+{
+ if (errat) {
+ fwrite(RSTRING(errat)->ptr, 1, RSTRING(errat)->len, stderr);
+ if (last_func) {
+ fprintf(stderr, ":in method `%s': ", rb_id2name(last_func));
+ }
+ else {
+ fprintf(stderr, ": ");
+ }
+ }
+
+ if (errstr) {
+ fwrite(RSTRING(errstr)->ptr, 1, RSTRING(errstr)->len, stderr);
+ }
+ else {
+ fprintf(stderr, "unhandled failure.\n");
+ }
+ rb_trap_exit();
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int state;
+
+ PUSH_ENV();
+ top_env = the_env;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_main(argc, argv);
+ }
+ POP_TAG();
+
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETURN:
+ Fatal("unexpected return");
+ break;
+ case TAG_CONTINUE:
+ Fatal("unexpected continue");
+ break;
+ case TAG_BREAK:
+ Fatal("unexpected break");
+ break;
+ case TAG_REDO:
+ Fatal("unexpected redo");
+ break;
+ case TAG_RETRY:
+ Fatal("retry outside of protect clause");
+ break;
+ case TAG_FAIL:
+ PUSH_TAG()
+ error_print();
+ POP_TAG();
+ break;
+ case TAG_EXIT:
+ rb_trap_exit();
+ exit(FIX2UINT(last_val));
+ break;
+ default:
+ Bug("Unknown longjmp status %d", state);
+ break;
+ }
+ POP_ENV();
+ exit(0);
+}
+
+VALUE rb_readonly_hook();
+
+VALUE Progname;
+
+static VALUE
+Eval()
+{
+ int state;
+ NODE *tree;
+
+ if (match == Qnil) match = rb_intern("=~");
+ if (each == Qnil) each = rb_intern("each");
+
+ tree = eval_tree;
+ eval_tree = Qnil;
+
+ return rb_eval(tree);
+}
+
+VALUE
+TopLevel(script, argc, argv)
+ char *script;
+ int argc;
+ char **argv;
+{
+ int i;
+
+ the_class = (struct RClass*)C_Object;
+
+ rb_define_variable("$!", &errstr, Qnil, Qnil);
+ errat = Qnil; /* clear for execution */
+
+ Progname = str_new2(script);
+ rb_define_variable("$0", &Progname, Qnil, Qnil);
+
+ rb_define_variable("$ARGV", &Argv, Qnil, Qnil);
+ rb_define_variable("$*", &Argv, Qnil, Qnil);
+ Argv = ary_new2(argc);
+ for (i=0; i < argc; i++) {
+ Fary_push(Argv, str_new2(argv[i]));
+ }
+ return Eval();
+}
+
+void
+rb_trap_eval(cmd)
+ VALUE cmd;
+{
+ PUSH_ENV();
+ the_env->self = top_env->self;
+ the_env->current_module = top_env->current_module;
+ the_env->local_vars = top_env->local_vars;
+ the_class = (struct RClass*)C_Object;
+
+ Feval(Qself, cmd);
+ POP_ENV();
+}
+
+static int
+setup_arg_1(node, args)
+ NODE *node;
+ VALUE *args;
+{
+ int argc;
+
+ if (node->type == NODE_ARRAY) {
+ for (argc=0; node; node=node->nd_next) argc++;
+ argc++;
+ }
+ else {
+ *args = rb_eval(node);
+ if (TYPE(*args) != T_ARRAY)
+ Fail("*`argument' must be array");
+ argc = RARRAY(*args)->len + 1;
+ }
+ return argc;
+}
+
+static void
+setup_arg_2(node, args, argc, argv)
+ NODE *node;
+ VALUE args;
+ int argc;
+ VALUE *argv;
+{
+ int i;
+
+ bzero(argv, sizeof(VALUE)*argc);
+ if (node->type == NODE_ARRAY) {
+ for (i=1;node;node=node->nd_next) {
+ argv[i++] = rb_eval(node->nd_head);
+ }
+ }
+ else {
+ for (i=1;i<argc;i++) {
+ argv[i] = RARRAY(args)->ptr[i-1];
+ }
+ }
+}
+
+#define SETUP_ARGS {\
+ VALUE args;\
+ GC_LINK;\
+ GC_PRO2(args);\
+ argc = setup_arg_1(node->nd_args, &args);\
+ argv = (VALUE*)alloca(sizeof(VALUE)*argc);\
+ GC_PRO4(argv, argc);\
+ setup_arg_2(node->nd_args, args, argc, argv);\
+ GC_UNLINK;\
+}
+
+static VALUE
+rb_eval(node)
+ register NODE *node;
+{
+ int state;
+ int go_out = 0;
+ VALUE result;
+
+ &go_out;
+ again:
+ if (node == Qnil) return Qnil;
+
+ sourceline = node->line;
+ sourcefile = node->src;
+
+#ifdef SAFE_SIGHANDLE
+ {
+ extern int trap_pending;
+
+ if (trap_pending) {
+ rb_trap_exec();
+ }
+ }
+#endif
+
+ switch (node->type) {
+ case NODE_BLOCK:
+ while (node->nd_next) {
+ rb_eval(node->nd_head);
+ node = node->nd_next;
+ }
+ node = node->nd_head;
+ goto again;
+
+ case NODE_SELF:
+ return Qself;
+
+ case NODE_NIL:
+ return Qnil;
+
+ case NODE_IF:
+ if (rb_eval(node->nd_cond)) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ if (node) goto again;
+ return Qnil;
+
+ case NODE_UNLESS:
+ {
+ VALUE res;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ res = rb_eval(node->nd_cond);
+ }
+ POP_TAG();
+ if (state == 0)
+ ;
+ else if (state == TAG_FAIL) {
+ res = Qnil;
+ }
+ else {
+ JUMP_TAG(state);
+ }
+
+ if (res == Qnil) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ if (node) goto again;
+ return res;
+ }
+
+ case NODE_CASE:
+ {
+ VALUE val;
+
+ GC_LINK;
+ GC_PRO3(val, rb_eval(node->nd_head));
+
+ node = node->nd_body;
+ while (node) {
+ if (node->type == NODE_WHEN) {
+ NODE *tag = node->nd_head;
+
+ while (tag) {
+ if (rb_funcall(rb_eval(tag->nd_head), match, 1, val)){
+ result = rb_eval(node->nd_body);
+ goto exit_case;
+ }
+ tag = tag->nd_next;
+ }
+ }
+ else {
+ result = rb_eval(node);
+ goto exit_case;
+ }
+ node = node->nd_next;
+ }
+ exit_case:
+ GC_UNLINK;
+ }
+ return result;
+
+ case NODE_WHILE:
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ case 0:
+ while_cont:
+ while (rb_eval(node->nd_cond)) {
+ while_redo:
+ rb_eval(node->nd_body);
+ }
+ break;
+ case TAG_REDO:
+ goto while_redo;
+ case TAG_CONTINUE:
+ goto while_cont;
+ default:
+ go_out++;
+ case TAG_BREAK:
+ break;
+ }
+ while_out:
+ POP_TAG();
+ if (go_out) JUMP_TAG(state);
+ return Qnil;
+
+ case NODE_UNTIL:
+ for (;;) {
+ VALUE res;
+
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ case 0:
+ res = rb_eval(node->nd_cond);
+ break;
+
+ case TAG_FAIL:
+ res = Qnil;
+ break;
+
+ default:
+ go_out++;
+ }
+ POP_TAG();
+ if (go_out) JUMP_TAG(state);
+ if (res) return res;
+
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ case 0:
+ until_redo:
+ rb_eval(node->nd_body);
+ break;
+ case TAG_REDO:
+ goto until_redo;
+ case TAG_CONTINUE:
+ break;
+ case TAG_BREAK:
+ goto until_break;
+ default:
+ go_out++;
+ }
+ POP_TAG();
+ if (go_out) JUMP_TAG(state);
+ }
+ until_break:
+ break;
+
+ case NODE_DO:
+ case NODE_FOR:
+ {
+ struct BLOCK block;
+
+ PUSH_ENV();
+ SET_BLOCK(block, node);
+ PUSH_TAG();
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ if (node->type == NODE_DO) {
+ the_env->iterator = 1;
+ rb_eval(node->nd_iter);
+ }
+ else {
+ VALUE recv;
+
+ GC_LINK;
+ GC_PRO2(recv);
+ recv = rb_eval(node->nd_iter);
+ the_env->iterator = 1;
+ result = rb_call(CLASS_OF(recv), recv, each, 1, Qnil,
+ MTH_METHOD);
+ GC_UNLINK;
+ }
+ }
+ POP_TAG();
+ POP_ENV();
+ switch (state) {
+ case 0:
+ break;
+ case IN_BLOCK|TAG_BREAK:
+ if (target_level != tag_level) {
+ JUMP_TAG(state);
+ }
+ result = Qnil;
+ break;
+ case IN_BLOCK|TAG_RETURN:
+ if (target_level == tag_level) {
+ state = TAG_RETURN;
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ return result;
+
+ case NODE_YIELD:
+ {
+ VALUE val;
+
+ GC_LINK;
+ GC_PRO3(val, rb_eval(node->nd_stts));
+ result = rb_yield(val);
+ GC_UNLINK;
+ }
+ return result;
+
+ case NODE_PROT:
+ GC_LINK;
+ GC_PRO2(result);
+
+ PUSH_TAG();
+
+ switch (state = EXEC_TAG()) {
+ case 0:
+ retry_entry:
+ result = rb_eval(node->nd_head);
+ break;
+
+ case TAG_FAIL:
+ if (node->nd_resq) {
+ if (node->nd_resq == (NODE*)1) {
+ state = 0;
+ }
+ else {
+ PUSH_TAG();
+ state = EXEC_TAG();
+ if (state == 0) result = rb_eval(node->nd_resq);
+ POP_TAG();
+ if (state == TAG_RETRY) {
+ goto retry_entry;
+ }
+ }
+ if (state == 0) {
+ errstr = errat = Qnil;
+ last_func = 0;
+ }
+ }
+ break;
+ }
+ POP_TAG();
+
+ /* ensure clause */
+ rb_eval(node->nd_ensr);
+ GC_UNLINK;
+
+ if (state != 0) {
+ JUMP_TAG(state);
+ }
+ return result;
+
+ case NODE_AND:
+ if ((result = rb_eval(node->nd_1st)) == Qnil) return result;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_OR:
+ if ((result = rb_eval(node->nd_1st)) != Qnil) return result;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_DOT3:
+ if (node->nd_state == 0) {
+ if (rb_eval(node->nd_beg)) {
+ node->nd_state = 1;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else {
+ if (rb_eval(node->nd_end)) {
+ node->nd_state = 0;
+ }
+ return TRUE;
+ }
+ break;
+
+ case NODE_BREAK:
+ JUMP_TAG(TAG_BREAK);
+ break;
+
+ case NODE_CONTINUE:
+ JUMP_TAG(TAG_CONTINUE);
+ 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);
+ break;
+
+ case NODE_CALL:
+ case NODE_CALL2:
+ {
+ VALUE recv, *argv;
+ int argc, last_iter;
+ enum mth_scope scope;
+
+ last_iter = the_env->iterator;
+ the_env->iterator = 0; /* recv & args are not iter. */
+ recv = node->nd_recv?rb_eval(node->nd_recv):Qself;
+ if (node->nd_args) {
+ SETUP_ARGS;
+ }
+ else {
+ argc = 1;
+ argv = &recv;
+ }
+ the_env->iterator = last_iter; /* restore iter. level */
+
+ scope = node->nd_recv?MTH_METHOD:MTH_FUNC;
+ return rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope);
+ }
+ break;
+
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ {
+ int last_iter;
+ int i, argc;
+ VALUE *argv;
+
+ last_iter = the_env->iterator; /* recv & args are not iter. */
+ the_env->iterator = 0;
+
+ if (node->nd_args) {
+ SETUP_ARGS;
+ }
+ else if (node->type == NODE_ZSUPER) {
+ argc = the_env->argc;
+ argv = the_env->argv;
+ }
+ else {
+ argc = 1;
+ argv = Qnil;
+ }
+
+ /* restore iter. level */
+ switch (last_iter) {
+ case 1: /* SUPER called as iter. */
+ case 2: /* iter. called SUPER */
+ the_env->iterator = 1;
+ break;
+ default: /* otherwise SUPER is not iter. */
+ break;
+ }
+
+ result = rb_call(the_env->last_class->super, Qself,
+ the_env->last_func, argc, argv, Qnil, MTH_FUNC);
+ the_env->iterator = last_iter;
+ }
+ return result;
+
+ case NODE_SCOPE:
+ {
+ VALUE result;
+
+ PUSH_ENV();
+ PUSH_TAG();
+ if (node->nd_cnt > 0) {
+ the_env->local_vars = ALLOC_N(VALUE, node->nd_cnt);
+ bzero(the_env->local_vars, sizeof(VALUE)*node->nd_cnt);
+ the_env->local_tbl = node->nd_tbl;
+ }
+ else {
+ the_env->local_vars = Qnil;
+ the_env->local_tbl = Qnil;
+ }
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(node->nd_body);
+ }
+ POP_TAG();
+ if (the_env->local_vars) free(the_env->local_vars);
+ POP_ENV();
+ if (state != 0) JUMP_TAG(state);
+
+ return result;
+ }
+
+ case NODE_MASGN:
+ {
+ VALUE val = rb_eval(node->nd_value);
+ NODE *list = node->nd_head;
+ int i, len;
+
+ GC_LINK;
+ GC_PRO(val);
+ if (TYPE(val) != T_ARRAY) {
+ val = rb_funcall(val, rb_intern("to_a"), 0, Qnil);
+ if (TYPE(val) != T_ARRAY) {
+ Bug("to_a did not return Array");
+ }
+ }
+ len = RARRAY(val)->len;
+ for (i=0; list && i<len; i++) {
+ asign(list->nd_head, RARRAY(val)->ptr[i]);
+ list = list->nd_next;
+ }
+ while (list) {
+ asign(list->nd_head, Qnil);
+ list = list->nd_next;
+ }
+ GC_UNLINK;
+ return val;
+ }
+
+ case NODE_LASGN:
+ if (the_env->local_vars == Qnil)
+ Bug("unexpected local variable asignment");
+ return the_env->local_vars[node->nd_cnt] = rb_eval(node->nd_value);
+
+ case NODE_GASGN:
+ {
+ VALUE val;
+
+ GC_LINK; GC_PRO3(val, rb_eval(node->nd_value));
+ rb_gvar_set(node->nd_entry, val);
+ GC_UNLINK;
+ return val;
+ }
+ case NODE_IASGN:
+ {
+ VALUE val;
+
+ GC_LINK; GC_PRO3(val, rb_eval(node->nd_value));
+ rb_ivar_set(node->nd_vid, val);
+ GC_UNLINK;
+ return val;
+ }
+ case NODE_CASGN:
+ {
+ VALUE val;
+
+ GC_LINK; GC_PRO3(val, rb_eval(node->nd_value));
+ rb_const_set(node->nd_vid, val);
+ GC_UNLINK;
+ return val;
+ }
+ break;
+
+ case NODE_LVAR:
+ if (the_env->local_vars == Qnil)
+ Bug("unexpected local variable");
+ return the_env->local_vars[node->nd_cnt];
+
+ case NODE_GVAR:
+ return rb_gvar_get(node->nd_entry);
+ case NODE_IVAR:
+ return rb_ivar_get(node->nd_vid);
+ case NODE_MVAR:
+ return rb_mvar_get(node->nd_vid);
+
+ case NODE_CVAR:
+ {
+ VALUE val = rb_const_get(node->nd_vid);
+ node->type = NODE_CONST;
+ node->nd_cval = val;
+ return val;
+ }
+
+ case NODE_CONST:
+ return node->nd_cval;
+
+ case NODE_HASH:
+ {
+ extern VALUE C_Dict;
+ extern VALUE Fdic_new();
+ NODE *list;
+
+ VALUE hash = Fdic_new(C_Dict);
+ VALUE key, val;
+
+ GC_LINK;
+ GC_PRO(hash); GC_PRO2(key); GC_PRO2(val);
+ list = node->nd_head;
+ while (list) {
+ key = rb_eval(list->nd_head);
+ list = list->nd_next;
+ if (list == Qnil)
+ Bug("odd number list for hash");
+ val = rb_eval(list->nd_head);
+ list = list->nd_next;
+ Fdic_aset(hash, key, val);
+ }
+ GC_UNLINK;
+ return hash;
+ }
+ break;
+
+ case NODE_ZARRAY: /* zero length list */
+ return ary_new();
+
+ case NODE_ARRAY:
+ {
+ VALUE ary;
+ int i;
+ NODE *list;
+
+ GC_LINK;
+ for (i=0, list=node; list; list=list->nd_next) i++;
+ GC_PRO3(ary, ary_new2(i));
+ for (i=0;node;node=node->nd_next) {
+ RARRAY(ary)->ptr[i++] = rb_eval(node->nd_head);
+ RARRAY(ary)->len = i;
+ }
+ GC_UNLINK;
+
+ return ary;
+ }
+ break;
+
+ case NODE_STR:
+ return str_new3(node->nd_lit);
+
+ case NODE_LIT:
+ return node->nd_lit;
+
+ case NODE_ATTRSET:
+ if (the_env->argc != 2)
+ Fail("Wrong # of arguments(%d for 1)", the_env->argc - 1);
+ return rb_ivar_set(node->nd_vid, the_env->argv[1]);
+
+ case NODE_ARGS:
+ {
+ NODE *local;
+ int i, len;
+
+ local = node->nd_frml;
+ for (i=0; local; local=local->nd_next,i++)
+ ;
+
+ len = the_env->argc - 1;
+ if (i > len || (node->nd_rest == -1 && i < len))
+ Fail("Wrong # of arguments(%d for %d)", len, i);
+
+ local = node->nd_frml;
+ if (the_env->local_vars == Qnil)
+ Bug("unexpected local variable asignment");
+
+ for (i=1;local;i++) {
+ the_env->local_vars[(int)local->nd_head] = the_env->argv[i];
+ local = local->nd_next;
+ }
+ if (node->nd_rest >= 0) {
+ if (the_env->argc == 1)
+ the_env->local_vars[node->nd_rest] = ary_new();
+ else
+ the_env->local_vars[node->nd_rest] =
+ ary_new4(the_env->argc-i, the_env->argv+i);
+ }
+ }
+ return Qnil;
+
+ case NODE_DEFN:
+ {
+ rb_add_method(the_class,node->nd_mid,node->nd_defn,node->nd_scope);
+ }
+ return Qnil;
+
+ case NODE_DEFS:
+ {
+ VALUE recv = rb_eval(node->nd_recv);
+
+ if (recv == Qnil) {
+ Fail("Can't define method \"%s\" for nil",
+ rb_id2name(node->nd_mid));
+ }
+ rb_add_method(rb_single_class(recv),
+ node->nd_mid, node->nd_defn, MTH_METHOD);
+ }
+ return Qnil;
+
+ case NODE_UNDEF:
+ {
+ rb_add_method(the_class, node->nd_mid, Qnil, MTH_UNDEF);
+ }
+ return Qnil;
+
+ case NODE_ALIAS:
+ {
+ rb_alias(the_class, node->nd_new, node->nd_old);
+ }
+ return Qnil;
+
+ case NODE_CLASS:
+ {
+ VALUE super, class;
+
+ if (node->nd_super) {
+ super = rb_id2class(node->nd_super);
+ if (super == Qnil) {
+ Fail("undefined superclass %s",
+ rb_id2name(node->nd_super));
+ }
+ }
+ else {
+ super = C_Object;
+ }
+ if (class = rb_id2class(node->nd_cname)) {
+ if (verbose) {
+ Warning("redefine class %s", rb_id2name(node->nd_cname));
+ }
+ unliteralize(class);
+ }
+
+ PUSH_ENV();
+ the_class = (struct RClass*)
+ rb_define_class_id(node->nd_cname, super);
+ Qself = (VALUE)the_class;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval(node->nd_body);
+ }
+ POP_TAG();
+ POP_ENV();
+ if (state) JUMP_TAG(state);
+ }
+ return Qnil;
+
+ case NODE_MODULE:
+ {
+ VALUE module;
+
+ if (module = rb_id2class(node->nd_cname)) {
+ if (verbose) {
+ Warning("redefine module %s", rb_id2name(node->nd_cname));
+ }
+ unliteralize(module);
+ }
+
+ PUSH_ENV();
+ the_class = (struct RClass*)rb_define_module_id(node->nd_cname);
+ Qself = (VALUE)the_class;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval(node->nd_body);
+ }
+ POP_TAG();
+ POP_ENV();
+ if (state) JUMP_TAG(state);
+ }
+ return Qnil;
+
+ case NODE_INC:
+ {
+ struct RClass *module;
+
+ module = (struct RClass*)rb_id2class(node->nd_modl);
+ if (module == Qnil) {
+ Fail("undefined module %s", rb_id2name(node->nd_modl));
+ }
+ rb_include_module(the_class, module);
+ }
+ return Qnil;
+
+ default:
+ Bug("unknown node type %d", node->type);
+ }
+ return Qnil; /* not reached */
+}
+
+VALUE
+obj_responds_to(obj, msg)
+ VALUE obj;
+ struct RString *msg;
+{
+ ID id;
+
+ if (FIXNUM_P(msg)) {
+ id = FIX2INT(msg);
+ }
+ else {
+ Check_Type(msg, T_STRING);
+ id = rb_intern(msg->ptr);
+ }
+
+ if (rb_get_method_body(CLASS_OF(obj), id, 0, MTH_FUNC)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+rb_exit(status)
+ int status;
+{
+ last_val = INT2FIX(status);
+ JUMP_TAG(TAG_EXIT);
+}
+
+VALUE
+Fexit(obj, args)
+ VALUE obj, args;
+{
+ VALUE status;
+
+ if (rb_scan_args(args, "01", &status) == 1) {
+ Need_Fixnum(status);
+ }
+ else {
+ status = INT2FIX(0);
+ }
+ last_val = status;
+ JUMP_TAG(TAG_EXIT);
+
+ return Qnil; /* not reached */
+}
+
+void
+rb_break()
+{
+ if (the_env->flags & DURING_ITERATE) {
+ JUMP_TAG(TAG_BREAK);
+ }
+ else {
+ Fatal("unexpected break");
+ }
+}
+
+void
+rb_redo()
+{
+ if (the_env->flags & DURING_ITERATE) {
+ JUMP_TAG(TAG_REDO);
+ }
+ else {
+ Fatal("unexpected redo");
+ }
+}
+
+void
+rb_retry()
+{
+ if (the_env->flags & DURING_RESQUE) {
+ JUMP_TAG(TAG_RETRY);
+ }
+ else {
+ Fatal("unexpected retry");
+ }
+}
+
+void
+rb_fail(mesg)
+ VALUE mesg;
+{
+ char buf[BUFSIZ];
+
+ if (errat == Qnil || sourcefile) {
+ if (the_env->last_func) {
+ last_func = the_env->last_func;
+ }
+ sprintf(buf, "%s:%d", sourcefile, sourceline);
+ errat = str_new2(buf);
+ }
+
+ if (mesg) {
+ if (RSTRING(mesg)->ptr[RSTRING(mesg)->len - 1] == '\n') {
+ errstr = mesg;
+ }
+ else {
+ errstr = Fstr_clone(mesg);
+ str_cat(errstr, "\n", 1);
+ }
+ }
+
+ if (prot_tag->level == 0) error_print();
+ JUMP_TAG(TAG_FAIL);
+}
+
+VALUE
+Ffail(self, args)
+ VALUE self, args;
+{
+ VALUE mesg;
+
+ rb_scan_args(args, "01", &mesg);
+
+ if (mesg) Check_Type(mesg, T_STRING);
+ rb_fail(mesg);
+
+ return Qnil; /* not reached */
+}
+
+iterator_p()
+{
+ return ITERATOR_P();
+}
+
+VALUE
+rb_yield(val)
+ VALUE val;
+{
+ struct BLOCK *block;
+ int state;
+ int go_out;
+ VALUE result;
+ int cnt;
+
+ &go_out;
+ block = the_env->block;
+ if (!ITERATOR_P()) {
+ Fail("yield called out of iterator");
+ }
+
+ PUSH_ENV();
+ block->env.prev = the_env->prev;
+ the_env = &(block->env);
+ the_env->flags = the_env->prev->flags;
+ if (block->var) {
+ asign(block->var, val);
+ }
+
+ go_out = 0;
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ retry:
+ case 0:
+ if (block->body->type == NODE_CFUNC) {
+ the_env->flags |= DURING_ITERATE;
+ result = (*block->body->nd_cfnc)(val, block->body->nd_argc);
+ }
+ else {
+ result = rb_eval(block->body);
+ }
+ break;
+ case TAG_RETRY:
+ goto retry;
+ case TAG_CONTINUE:
+ break;
+ case TAG_BREAK:
+ case TAG_RETURN:
+ target_level = block->level;
+ state = IN_BLOCK|state;
+ default:
+ go_out++;
+ break;
+ }
+ POP_TAG();
+ POP_ENV();
+ if (go_out) JUMP_TAG(state);
+
+ return result;
+}
+
+static void
+asign(lhs, val)
+ NODE *lhs;
+ VALUE val;
+{
+ switch (lhs->type) {
+ case NODE_GASGN:
+ rb_gvar_set(lhs->nd_entry, val);
+ break;
+
+ case NODE_IASGN:
+ rb_ivar_set(lhs->nd_vid, val);
+ break;
+
+ case NODE_LASGN:
+ if (the_env->local_vars == Qnil)
+ Bug("unexpected iterator variable asignment");
+ the_env->local_vars[lhs->nd_cnt] = val;
+ break;
+
+ case NODE_CASGN:
+ rb_const_set(lhs->nd_vid, val);
+ break;
+
+ case NODE_CALL:
+ {
+ VALUE recv;
+ GC_LINK;
+ GC_PRO3(recv, rb_eval(lhs->nd_recv));
+ if (lhs->nd_args->nd_head == Qnil) {
+ /* attr set */
+ rb_funcall(recv, lhs->nd_mid, 1, val);
+ }
+ else {
+ /* array set */
+ VALUE args;
+
+ GC_PRO3(args, rb_eval(lhs->nd_args));
+ RARRAY(args)->ptr[RARRAY(args)->len-1] = val;
+ rb_apply(recv, lhs->nd_mid, args);
+ }
+ GC_UNLINK;
+ }
+ break;
+
+ default:
+ Bug("bug in iterator variable asignment");
+ break;
+ }
+}
+
+VALUE
+rb_iterate(it_proc, data1, bl_proc, data2)
+ VALUE (*it_proc)(), (*bl_proc)();
+ char *data1, *data2;
+{
+ int state;
+ VALUE retval;
+ NODE *node = NEW_CFUNC(bl_proc, data2);
+ struct BLOCK block;
+
+ PUSH_ENV();
+ block.level = tag_level;
+ block.var = Qnil;
+ block.body = node;
+ block.env = *the_env;
+ the_env->block = &block;
+ PUSH_TAG();
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ the_env->iterator = 1;
+ retval = (*it_proc)(data1);
+ }
+ POP_TAG();
+ POP_ENV();
+
+ freenode(node);
+
+ switch (state) {
+ case 0:
+ break;
+ case IN_BLOCK|TAG_BREAK:
+ if (target_level != tag_level) {
+ JUMP_TAG(state);
+ }
+ retval = Qnil;
+ break;
+ case IN_BLOCK|TAG_RETURN:
+ if (target_level == tag_level) {
+ state = TAG_RETURN;
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ }
+
+ return retval;
+}
+
+VALUE
+rb_resque(b_proc, data1, r_proc, data2)
+ VALUE (*b_proc)(), (*r_proc)();
+ char *data1, *data2;
+{
+ int state;
+ int go_out;
+ VALUE result;
+
+ &go_out;
+ go_out = 0;
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ case 0:
+ retry_entry:
+ result = (*b_proc)(data1);
+ break;
+
+ case TAG_FAIL:
+ if (r_proc) {
+ PUSH_TAG();
+ state = EXEC_TAG();
+ if (state == 0) {
+ the_env->flags |= DURING_RESQUE;
+ result = (*r_proc)(data2);
+ }
+ POP_TAG();
+ switch (state) {
+ case TAG_RETRY:
+ goto retry_entry;
+ case 0:
+ break;
+ default:
+ go_out++;
+ break;
+ }
+ }
+ if (state == 0) {
+ errstr = errat = Qnil;
+ }
+ break;
+
+ default:
+ break;
+ }
+ POP_TAG();
+ if (go_out) JUMP_TAG(state);
+
+ return result;
+}
+
+VALUE
+rb_ensure(b_proc, data1, e_proc, data2)
+ VALUE (*b_proc)(), (*e_proc)();
+ char *data1, *data2;
+{
+ int state;
+ VALUE result;
+
+ GC_LINK;
+ GC_PRO2(result);
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*b_proc)(data1);
+ }
+ POP_TAG();
+
+ (*e_proc)(data2);
+ if (state != 0) {
+ JUMP_TAG(state);
+ }
+ GC_UNLINK;
+ return result;
+}
+
+struct st_table *new_idhash();
+
+static void
+rb_undefined(obj, id)
+ VALUE obj, id;
+{
+ VALUE desc = obj_as_string(obj);
+
+ if (RSTRING(desc)->len > 160) {
+ desc = Fkrn_to_s(obj);
+ }
+ Fail("undefined method `%s' for \"%s\"(%s)",
+ rb_id2name(NUM2INT(id)),
+ RSTRING(desc)->ptr,
+ rb_class2name(CLASS_OF(obj)));
+}
+
+static VALUE
+rb_call(class, recv, mid, argc, argv, scope)
+ struct RClass *class;
+ VALUE recv, *argv;
+ int argc;
+ ID mid;
+ enum mth_scope scope;
+{
+ int state;
+ int go_out = 0;
+ int c = argc - 1;
+ NODE *body;
+ VALUE result;
+
+ &go_out;
+ PUSH_ENV();
+ the_env->flags |= DURING_CALL;
+ the_env->argc = argc;
+ the_env->argv = argv;
+ Qself = recv;
+ if (argv) argv[0] = recv;
+ if (the_env->iterator != 0) the_env->iterator++;
+
+ if ((body = rb_get_method_body(class, mid, 1, scope)) == Qnil) {
+ rb_undefined(recv, mid);
+ }
+
+ if (body->type == NODE_CFUNC) {
+ int len = body->nd_argc;
+
+ if (len >= 0 && c != len) {
+ Fail("Wrong # of arguments for(%d for %d)", c, body->nd_argc);
+ }
+
+ if (len == -2) {
+ result = (*body->nd_cfnc)(recv, ary_new4(argc-1, argv+1));
+ }
+ else if (len == -1) {
+ result = (*body->nd_cfnc)(argc, argv);
+ }
+ else if (len >= 0) {
+ switch (c) {
+ case 0:
+ result = (*body->nd_cfnc)(recv);
+ break;
+ case 1:
+ result = (*body->nd_cfnc)(recv, argv[1]);
+ break;
+ case 2:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2]);
+ break;
+ case 3:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3]);
+ break;
+ case 4:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4]);
+ break;
+ case 5:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5]);
+ break;
+ case 6:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6]);
+ break;
+ case 7:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7]);
+ break;
+ case 8:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8]);
+ break;
+ case 9:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9]);
+ break;
+ case 10:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10]);
+ break;
+ case 11:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10], argv[11]);
+ break;
+ case 12:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10], argv[11], argv[12]);
+ break;
+ case 13:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10], argv[11], argv[12],
+ argv[13]);
+ break;
+ case 14:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10], argv[11], argv[12],
+ argv[13], argv[14]);
+ break;
+ case 15:
+ result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3],
+ argv[4], argv[5], argv[6],
+ argv[7], argv[8], argv[9],
+ argv[7], argv[8], argv[9],
+ argv[10], argv[11], argv[12],
+ argv[13], argv[14], argv[15]);
+ break;
+ default:
+ Fail("too many arguments(%d)", len);
+ break;
+ }
+ }
+ else {
+ Bug("bad argc(%d) specified for `%s(%s)'",
+ len, rb_class2name(class), rb_id2name(mid));
+ }
+ }
+ else {
+ the_env->file = sourcefile;
+ the_env->line = sourceline;
+ the_env->local_vars = Qnil;
+ the_env->local_tbl = Qnil;
+
+ PUSH_TAG();
+ switch (state = EXEC_TAG()) {
+ case 0:
+ result = rb_eval(body);
+ break;
+ case TAG_CONTINUE:
+ Fatal("unexpected continue");
+ break;
+ case TAG_BREAK:
+ Fatal("unexpected break");
+ break;
+ case TAG_REDO:
+ Fatal("unexpected redo");
+ break;
+ case TAG_RETRY:
+ Fatal("retry outside of protect clause");
+ break;
+ case TAG_RETURN:
+ result = last_val;
+ break;
+ default:
+ go_out++;
+ }
+ POP_TAG();
+ }
+
+ POP_ENV();
+
+ if (go_out) JUMP_TAG(state);
+
+ return result;
+}
+
+VALUE
+rb_apply(recv, mid, args)
+ VALUE recv, args;
+ ID mid;
+{
+ VALUE *argv;
+ int argc, i;
+
+ argc = RARRAY(args)->len + 1;
+ argv = (VALUE*)alloca(sizeof(VALUE)*argc);
+ for (i=1;i<argc;i++) {
+ argv[i] = RARRAY(args)->ptr[i-1];
+ }
+ argv[0] = Qnil;
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, MTH_FUNC);
+}
+
+VALUE
+Fapply(recv, args)
+ VALUE recv, args;
+{
+ VALUE vid, rest;
+ ID mid;
+
+ rb_scan_args(args, "1*", &vid, &rest);
+ if (TYPE(vid) == T_STRING) {
+ mid = rb_intern(RSTRING(vid)->ptr);
+ }
+ else {
+ mid = NUM2INT(vid);
+ }
+ return rb_apply(recv, mid, rest);
+}
+
+#include <varargs.h>
+
+VALUE
+rb_funcall(recv, mid, n, va_alist)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_dcl
+{
+ va_list ar;
+ int argc;
+ VALUE *argv;
+
+ if (n > 0) {
+ int i;
+
+ argc = n + 1;
+ argv = (VALUE*)alloca(sizeof(VALUE)*argc);
+
+ va_start(ar);
+ for (i=1;i<argc;i++) {
+ argv[i] = va_arg(ar, VALUE);
+ }
+ argv[0] = Qnil;
+ va_end(ar);
+ }
+ else {
+ argc = 1;
+ argv = Qnil;
+ }
+
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, MTH_FUNC);
+}
+
+VALUE
+Fcaller(obj, args)
+ VALUE obj, args;
+{
+ VALUE level, file, res, ary;
+ int lev;
+ struct ENVIRON *e;
+
+ rb_scan_args(args, "01", &level);
+ if (level == Qnil) {
+ lev = 1;
+ }
+ else {
+ lev = FIX2UINT(level);
+ }
+ if (lev < 0) Fail("negative level: %d", lev);
+
+ e = the_env;
+
+ while (lev > 0) {
+ e = e->prev;
+ if (e == Qnil) Fail("no caller");
+ if (!(e->flags & DURING_CALL)) continue;
+ lev--;
+ }
+ if (e->file == Qnil) Fail("initial frame");
+
+ GC_LINK;
+ GC_PRO3(file, str_new2(e->file));
+ GC_PRO3(ary, e->argv?ary_new4(e->argc, e->argv):ary_new3(1, Qself));
+ res = ary_new3(4, file, INT2FIX(e->line),
+ str_new2(rb_id2name(e->last_func)), ary);
+ GC_UNLINK;
+
+ return res;
+}
+
+int in_eval = 0;
+extern int nerrs;
+
+VALUE
+Feval(obj, src)
+ VALUE obj;
+ struct RString *src;
+{
+ VALUE result;
+ int state;
+ NODE *node;
+ char *oldsrc = sourcefile;
+
+ Check_Type(src, T_STRING);
+ PUSH_TAG();
+ PUSH_ENV();
+ the_env->in_eval = 1;
+ node = eval_tree;
+
+ if (the_env->prev) {
+ the_class = the_env->prev->current_module;
+ }
+ else {
+ the_class = (struct RClass*)C_Object;
+ }
+
+ if ((state = EXEC_TAG()) == 0) {
+ lex_setsrc("(eval)", src->ptr, src->len);
+ eval_tree = Qnil;
+ yyparse();
+ sourcefile = oldsrc;
+ if (nerrs == 0)
+ result = Eval();
+ freenode(eval_tree);
+ }
+ eval_tree = node;
+ POP_ENV();
+ POP_TAG();
+ if (state) printf("exception in eval()\n");
+ if (state) JUMP_TAG(state);
+
+ if (nerrs > 0) {
+ VALUE mesg;
+
+ GC_LINK;
+ GC_PRO3(mesg, errstr);
+ nerrs = 0;
+ errstr = str_new2("syntax error in eval():\n");
+ str_cat(errstr, RSTRING(mesg)->ptr, RSTRING(mesg)->len);
+ rb_fail(errstr);
+ GC_UNLINK;
+ }
+
+ return result;
+}
+
+VALUE rb_load_path;
+
+char *dln_find_file();
+
+static char*
+find_file(file)
+ char *file;
+{
+ extern VALUE rb_load_path;
+ VALUE sep, vpath;
+ char *path, *found;
+
+ if (file[0] == '/') return file;
+
+ GC_LINK;
+ GC_PRO2(sep); GC_PRO2(vpath);
+
+ if (rb_load_path) {
+ Check_Type(rb_load_path, T_ARRAY);
+ sep = str_new2(":");
+ vpath = ary_join(rb_load_path, sep);
+ path = RSTRING(vpath)->ptr;
+ obj_free(sep);
+ sep = Qnil;
+ }
+ else {
+ path = Qnil;
+ }
+
+ found = dln_find_file(file, path);
+ if (found == Qnil) Fail("No such file to load -- %s", file);
+
+ if (vpath) obj_free(vpath);
+ GC_UNLINK;
+
+ return found;
+}
+
+VALUE
+Fload(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ extern VALUE TopSelf;
+ int state;
+ VALUE result;
+ NODE *node;
+ char *file;
+
+ Check_Type(fname, T_STRING);
+ file = find_file(fname->ptr);
+
+#ifdef USE_DLN
+ {
+ static int rb_dln_init = 0;
+ extern char *rb_dln_argv0;
+ int len = strlen(file);
+
+ if (len > 2 && file[len-1] == 'o' && file[len-2] == '.') {
+ if (rb_dln_init == 0 && dln_init(rb_dln_argv0) == -1) {
+ Fail("%s: %s", rb_dln_argv0, dln_strerror());
+ }
+
+ if (dln_load(file) == -1)
+ Fail(dln_strerror());
+
+ return TRUE;
+ }
+ }
+#endif
+
+ PUSH_TAG();
+ PUSH_ENV();
+ the_class = (struct RClass*)C_Object;
+ Qself = TopSelf;
+ the_env->in_eval = 1;
+ node = eval_tree;
+ state = EXEC_TAG();
+ if (state == 0) {
+ eval_tree = Qnil;
+ rb_load_file(file);
+ if (nerrs == 0) {
+ result = Eval();
+ }
+ freenode(eval_tree);
+ }
+ eval_tree = node;
+ POP_ENV();
+ POP_TAG();
+ if (nerrs > 0) {
+ rb_fail(errstr);
+ }
+ if (state) JUMP_TAG(state);
+
+ return TRUE;
+}
+
+static VALUE rb_loadfiles;
+
+Frequire(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ char *file;
+ VALUE *p, *pend;
+
+ Check_Type(fname, T_STRING);
+ file = find_file(fname->ptr);
+
+ p = RARRAY(rb_loadfiles)->ptr;
+ pend = p+ RARRAY(rb_loadfiles)->len;
+ while (p < pend) {
+ Check_Type(*p, T_STRING);
+ if (strcmp(RSTRING(*p)->ptr, file) == 0) return FALSE;
+ }
+ Fary_push(rb_loadfiles, str_new2(file));
+
+ Fload(obj, fname);
+ return TRUE;
+}
+
+char *getenv();
+char *index();
+
+#ifndef RUBY_LIB
+#define RUBY_LIB "/usr/local/lib/ruby:."
+#endif
+
+#define RUBY_LIB_SEP ':'
+
+static void
+addpath(path)
+ char *path;
+{
+ char *p, *s;
+
+ if (path == Qnil) return;
+
+ p = s = path;
+ while (*p) {
+ while (*p == RUBY_LIB_SEP) p++;
+ if (s = index(p, RUBY_LIB_SEP)) {
+ Fary_push(rb_load_path, str_new(p, (int)(s-p)));
+ p = s + 1;
+ }
+ else {
+ Fary_push(rb_load_path, str_new2(p));
+ break;
+ }
+ }
+}
+
+Init_load()
+{
+ extern VALUE C_Kernel;
+ extern VALUE rb_check_str();
+ char *path;
+
+ rb_define_variable("$LOAD_PATH", &rb_load_path, Qnil, rb_check_str);
+ rb_load_path = ary_new();
+ rb_define_variable("$LOAD_FILES", &rb_load_path, Qnil, rb_readonly_hook);
+ rb_loadfiles = ary_new();
+ addpath(getenv("RUBYLIB"));
+ addpath(RUBY_LIB);
+
+ rb_define_func(C_Kernel, "load", Fload, 1);
+ rb_define_func(C_Kernel, "require", Frequire, 1);
+}
diff --git a/file.c b/file.c
new file mode 100644
index 0000000000..7001a2f559
--- /dev/null
+++ b/file.c
@@ -0,0 +1,1059 @@
+
+/************************************************
+
+ file.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:26 $
+ created at: Mon Nov 15 12:24:34 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "io.h"
+#include <sys/time.h>
+#include <sys/param.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+char *strdup();
+
+extern VALUE C_IO;
+VALUE C_File;
+
+VALUE time_new();
+
+VALUE
+file_open(fname, mode)
+ char *fname, *mode;
+{
+ VALUE port;
+ OpenFile *fptr;
+
+ GC_LINK;
+ GC_PRO3(port, obj_alloc(C_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->path = strdup(fname);
+
+ GC_UNLINK;
+
+ return port;
+}
+
+static VALUE
+Ffile_tell(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ long pos;
+
+ GetOpenFile(obj, fptr);
+
+ pos = ftell(fptr->f);
+ if (ferror(fptr->f) != 0) rb_sys_fail(Qnil);
+
+ return int2inum(pos);
+}
+
+static VALUE
+Ffile_seek(obj, offset, ptrname)
+ VALUE obj, offset, ptrname;
+{
+ OpenFile *fptr;
+ long pos;
+
+ Check_Type(ptrname, T_FIXNUM);
+
+ GetOpenFile(obj, fptr);
+
+ pos = fseek(fptr->f, NUM2INT(offset), NUM2INT(ptrname));
+ if (pos != 0) rb_sys_fail(Qnil);
+
+ return obj;
+}
+
+static VALUE
+Ffile_rewind(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ if (fseek(fptr->f, 0L, 0) != 0) rb_sys_fail(Qnil);
+
+ return obj;
+}
+
+static VALUE
+Ffile_eof(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ if (feof(fptr->f) == 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_path(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ return str_new2(fptr->path);
+}
+
+static VALUE
+Ffile_isatty(obj)
+ VALUE obj;
+{
+ return FALSE;
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+static VALUE
+stat_new(st)
+ struct stat *st;
+{
+ VALUE obj, data, atime, mtime, ctime, stat;
+
+ if (st == Qnil) Bug("stat_new() called with nil");
+
+ GC_LINK;
+ GC_PRO3(atime, time_new(st->st_atime, 0));
+ GC_PRO3(mtime, time_new(st->st_mtime, 0));
+ GC_PRO3(ctime, time_new(st->st_ctime, 0));
+
+ stat = struct_new("stat",
+ "dev", INT2FIX((int)st->st_dev),
+ "ino", INT2FIX((int)st->st_ino),
+ "mode", INT2FIX((int)st->st_mode),
+ "nlink", INT2FIX((int)st->st_nlink),
+ "uid", INT2FIX((int)st->st_uid),
+ "gid", INT2FIX((int)st->st_gid),
+#ifdef HAVE_ST_RDEV
+ "rdev", INT2FIX((int)st->st_rdev),
+#else
+ "rdev", INT2FIX(0),
+#endif
+ "size", INT2FIX((int)st->st_size),
+#ifdef HAVE_ST_BLKSIZE
+ "blksize", INT2FIX((int)st->st_blksize),
+#else
+ "blksize", INT2FIX(0),
+#endif
+#ifdef HAVE_ST_BLOCKS
+ "blocks", INT2FIX((int)st->st_blocks),
+#else
+ "blocks", INT2FIX(0),
+#endif
+ "atime", atime,
+ "mtime", mtime,
+ "ctime", ctime,
+ Qnil);
+ GC_UNLINK;
+
+ return stat;
+}
+
+static char lastpath[MAXPATHLEN];
+static struct stat laststat;
+
+cache_stat(path, st)
+ char *path;
+ struct stat *st;
+{
+ if (strcmp(lastpath, path) == 0) {
+ *st = laststat;
+ return 0;
+ }
+ if (stat(path, st) == -1)
+ return -1;
+ strcpy(lastpath, path);
+ laststat = *st;
+
+ return 0;
+}
+
+static VALUE
+Ffile_stat(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) == -1) {
+ rb_sys_fail(fname->ptr);
+ }
+ return stat_new(&st);
+}
+
+static VALUE
+Ffile_stat2(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+}
+
+static VALUE
+Ffile_lstat(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (lstat(fname->ptr, &st) == -1) {
+ rb_sys_fail(fname->ptr);
+ }
+ return stat_new(&st);
+}
+
+static VALUE
+Ffile_lstat2(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (lstat(fptr->path, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+}
+
+#define HAS_GETGROUPS
+
+static int
+group_member(gid)
+ GETGROUPS_T gid;
+{
+ GETGROUPS_T egid;
+
+ if (getgid() == gid || getegid() == gid)
+ return TRUE;
+
+#ifdef HAS_GETGROUPS
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+ {
+ GETGROUPS_T gary[NGROUPS];
+ int anum;
+
+ anum = getgroups(NGROUPS, gary);
+ while (--anum >= 0)
+ if (gary[anum] == gid)
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+#ifndef S_IXUGO
+# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif
+
+int
+eaccess(path, mode)
+ char *path;
+ int mode;
+{
+ extern int group_member ();
+ struct stat st;
+ static int euid = -1;
+
+ if (cache_stat(path, &st) < 0) return (-1);
+
+ if (euid == -1)
+ euid = geteuid ();
+
+ if (euid == 0)
+ {
+ /* Root can read or write any file. */
+ if (mode != X_OK)
+ return 0;
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return 0;
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (group_member (st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode) return 0;
+
+ return -1;
+}
+
+static VALUE
+Ffile_d(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISDIR(st.st_mode)) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Ffile_p(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifdef S_IFIFO
+# ifndef S_ISFIFO
+# define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
+# endif
+
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISFIFO(st.st_mode)) return TRUE;
+
+#endif
+ return FALSE;
+}
+
+static VALUE
+Ffile_l(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifndef S_ISLNK
+# ifdef _S_ISLNK
+# define S_ISLNK(m) _S_ISLNK(m)
+# else
+# ifdef _S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
+# else
+# ifdef S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISLNK
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISLNK(st.st_mode)) return TRUE;
+
+#endif
+ return FALSE;
+}
+
+Ffile_S(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifndef S_ISSOCK
+# ifdef _S_ISSOCK
+# define S_ISSOCK(m) _S_ISSOCK(m)
+# else
+# ifdef _S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
+# else
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISSOCK
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISSOCK(st.st_mode)) return TRUE;
+
+#endif
+ return FALSE;
+}
+
+static VALUE
+Ffile_b(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifndef S_ISBLK
+# ifdef S_IFBLK
+# define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
+# endif
+#endif
+
+#ifdef S_ISBLK
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISBLK(st.st_mode)) return TRUE;
+
+#endif
+ return FALSE;
+}
+
+static VALUE
+Ffile_c(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+#ifndef S_ISCHR
+# define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
+#endif
+
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISBLK(st.st_mode)) return TRUE;
+
+ return FALSE;
+}
+
+static VALUE
+Ffile_e(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_r(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (eaccess(fname->ptr, R_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_R(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (access(fname->ptr, R_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_w(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (eaccess(fname->ptr, W_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_W(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (access(fname->ptr, W_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_x(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (eaccess(fname->ptr, X_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_X(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+ if (access(fname->ptr, X_OK) < 0) return FALSE;
+ return TRUE;
+}
+
+static VALUE
+Ffile_f(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (S_ISREG(st.st_mode)) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Ffile_z(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (st.st_size == 0) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Ffile_s(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (st.st_size == 0) return FALSE;
+ return int2inum(st.st_size);
+}
+
+static VALUE
+Ffile_owned(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (st.st_uid == geteuid()) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Ffile_grpowned(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) return FALSE;
+ if (st.st_gid == getegid()) return TRUE;
+ return FALSE;
+}
+
+#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
+static VALUE
+check3rdbyte(file, mode)
+ char *file;
+ int mode;
+{
+ struct stat st;
+
+ if (cache_stat(file, &st) < 0) return FALSE;
+ if (st.st_mode & mode) return TRUE;
+ return FALSE;
+}
+#endif
+
+static VALUE
+Ffile_suid(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+#ifdef S_ISUID
+ return check3rdbyte(fname->ptr, S_ISUID);
+#else
+ return FALSE;
+#endif
+}
+
+static VALUE
+Ffile_sgid(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+#ifdef S_ISGID
+ return check3rdbyte(fname->ptr, S_ISGID);
+#else
+ return FALSE;
+#endif
+}
+
+static VALUE
+Ffile_sticky(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ Check_Type(fname, T_STRING);
+#ifdef S_ISVTX
+ return check3rdbyte(fname->ptr, S_ISVTX);
+#else
+ return FALSE;
+#endif
+}
+
+static VALUE
+Ffile_atime(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ return time_new(st.st_atime, 0);
+}
+
+static VALUE
+Ffile_atime2(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return time_new(st.st_atime, 0);
+}
+
+static VALUE
+Ffile_mtime(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ return time_new(st.st_mtime, 0);
+}
+
+static VALUE
+Ffile_mtime2(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return time_new(st.st_mtime, 0);
+}
+
+static VALUE
+Ffile_ctime(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ struct stat st;
+
+ Check_Type(fname, T_STRING);
+ if (cache_stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr);
+ return time_new(st.st_ctime, 0);
+}
+
+static VALUE
+Ffile_ctime2(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return time_new(st.st_ctime, 0);
+}
+
+static VALUE
+Ffile_chmod(obj, args)
+ VALUE obj, args;
+{
+ VALUE vmode;
+ VALUE rest;
+ int mode, i, len;
+ VALUE path;
+
+ rb_scan_args(args, "1*", &vmode, &rest);
+ mode = NUM2INT(vmode);
+
+ len = RARRAY(rest)->len;
+ for (i=0; i<len; i++) {
+ path = RARRAY(rest)->ptr[i];
+ Check_Type(path, T_STRING);
+ if (chmod(RSTRING(path)->ptr, mode) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+
+ return Qnil;
+}
+
+static VALUE
+Ffile_chmod2(obj, vmode)
+ VALUE obj, vmode;
+{
+ OpenFile *fptr;
+ int mode;
+
+ mode = NUM2INT(vmode);
+
+ GetOpenFile(obj, fptr);
+ if (fchmod(fileno(fptr->f), mode) == -1)
+ rb_sys_fail(fptr->path);
+
+ return obj;
+}
+
+static VALUE
+Ffile_chown(obj, args)
+ VALUE obj, args;
+{
+ VALUE o, g, rest;
+ int owner, group;
+ int i, len;
+
+ len = rb_scan_args(args, "2*", &o, &g, &rest);
+ if (o == Qnil) {
+ owner = -1;
+ }
+ else {
+ owner = NUM2INT(o);
+ }
+ if (g == Qnil) {
+ group = -1;
+ }
+ else {
+ group = NUM2INT(g);
+ }
+
+ len -= 2;
+ for (i=0; i<len; i++) {
+ Check_Type(RARRAY(rest)->ptr[i], T_STRING);
+ }
+
+ for (i=0; i<len; i++) {
+ if (chown(RSTRING(RARRAY(rest)->ptr[i])->ptr, owner, group) < 0)
+ rb_sys_fail(RSTRING(RARRAY(rest)->ptr[i])->ptr);
+ }
+
+ return INT2FIX(i);
+}
+
+Ffile_chown2(obj, owner, group)
+ VALUE obj, owner, group;
+{
+ OpenFile *fptr;
+ int mode;
+
+ GetOpenFile(obj, fptr);
+ if (fchown(fileno(fptr->f), NUM2INT(owner), NUM2INT(group)) == -1)
+ rb_sys_fail(fptr->path);
+
+ return obj;
+}
+
+struct timeval *time_timeval();
+
+static VALUE
+Ffile_utime(obj, args)
+ VALUE obj, args;
+{
+ VALUE atime, mtime, rest;
+ struct timeval tvp[2];
+ int i, len;
+
+ len = rb_scan_args(args, "2*", &atime, &mtime, &rest);
+
+ tvp[0] = *time_timeval(atime);
+ tvp[1] = *time_timeval(mtime);
+
+ len -= 2;
+ for (i=0; i<len; i++) {
+ Check_Type(RARRAY(rest)->ptr[i], T_STRING);
+ }
+
+ for (i=0; i<len; i++) {
+ if (utimes(RSTRING(RARRAY(rest)->ptr[i])->ptr, tvp) < 0)
+ rb_sys_fail(RSTRING(RARRAY(rest)->ptr[i])->ptr);
+ }
+
+ return INT2FIX(i);
+}
+
+static VALUE
+Ffile_link(obj, from, to)
+ VALUE obj;
+ struct RString *from, *to;
+{
+ Check_Type(from, T_STRING);
+ Check_Type(to, T_STRING);
+
+ if (link(from->ptr, to->ptr) < 0)
+ rb_sys_fail(from->ptr);
+ return TRUE;
+}
+
+static VALUE
+Ffile_symlink(obj, from, to)
+ VALUE obj;
+ struct RString *from, *to;
+{
+ Check_Type(from, T_STRING);
+ Check_Type(to, T_STRING);
+
+ if (symlink(from->ptr, to->ptr) < 0)
+ rb_sys_fail(from->ptr);
+ return TRUE;
+}
+
+Ffile_readlink(obj, path)
+ VALUE obj;
+ struct RString *path;
+{
+ char buf[MAXPATHLEN];
+ int cc;
+
+ Check_Type(path, T_STRING);
+
+ if ((cc = readlink(path->ptr, buf, MAXPATHLEN)) < 0)
+ rb_sys_fail(path->ptr);
+
+ return str_new(buf, cc);
+}
+
+static VALUE
+Ffile_unlink(obj, args)
+ VALUE obj;
+ struct RArray *args;
+{
+ int i, len;
+
+ len = args->len;
+ for (i=0; i<len; i++) {
+ Check_Type(args->ptr[i], T_STRING);
+ }
+ for (i=0; i<len; i++) {
+ if (unlink(RSTRING(args->ptr[i])->ptr) < 0)
+ rb_sys_fail(RSTRING(args->ptr[i])->ptr);
+ }
+
+ return INT2FIX(i);
+}
+
+static VALUE
+Ffile_rename(obj, from, to)
+ VALUE obj;
+ struct RString *from, *to;
+{
+ Check_Type(from, T_STRING);
+ Check_Type(to, T_STRING);
+
+ if (rename(from->ptr, to->ptr) == -1)
+ rb_sys_fail(from->ptr);
+
+ return TRUE;
+}
+
+static VALUE
+Ffile_umask(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE mask;
+ int omask;
+
+ if (argc == 1) {
+ int omask = umask(0);
+ umask(omask);
+ }
+ else if (argc == 2) {
+ omask = umask(NUM2INT(argv[1]));
+ }
+ else {
+ Fail("wrong # of argument");
+ }
+ return INT2FIX(omask);
+}
+
+static VALUE
+Ffile_truncate(obj, path, len)
+ VALUE obj, len;
+ struct RString *path;
+{
+ Check_Type(path, T_STRING);
+
+ if (truncate(path->ptr, NUM2INT(len)) < 0)
+ rb_sys_fail(path->ptr);
+ return TRUE;
+}
+
+static VALUE
+Ffile_truncate2(obj, len)
+ VALUE obj, len;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ Fail("not opened for writing");
+ }
+ if (ftruncate(fileno(fptr->f), NUM2INT(len)) < 0)
+ rb_sys_fail(fptr->path);
+ return TRUE;
+}
+
+static VALUE
+Ffile_fcntl(obj, req, arg)
+ VALUE obj, req;
+ struct RString *arg;
+{
+ io_ctl(obj, req, arg, 0);
+ return obj;
+}
+
+Init_File()
+{
+ C_File = rb_define_class("File", C_IO);
+
+ rb_define_single_method(C_File, "stat", Ffile_stat, 1);
+ rb_define_single_method(C_File, "lstat", Ffile_lstat, 1);
+
+ rb_define_single_method(C_File, "d", Ffile_d, 1);
+ rb_define_single_method(C_File, "isdirectory", Ffile_d, 1);
+ rb_define_single_method(C_File, "a", Ffile_e, 1);
+ rb_define_single_method(C_File, "e", Ffile_e, 1);
+ rb_define_single_method(C_File, "exists", Ffile_e, 1);
+ rb_define_single_method(C_File, "r", Ffile_r, 1);
+ rb_define_single_method(C_File, "readable", Ffile_r, 1);
+ rb_define_single_method(C_File, "R", Ffile_R, 1);
+ rb_define_single_method(C_File, "w", Ffile_w, 1);
+ rb_define_single_method(C_File, "writable", Ffile_w, 1);
+ rb_define_single_method(C_File, "W", Ffile_W, 1);
+ rb_define_single_method(C_File, "x", Ffile_x, 1);
+ rb_define_single_method(C_File, "executable", Ffile_x, 1);
+ rb_define_single_method(C_File, "X", Ffile_X, 1);
+ rb_define_single_method(C_File, "f", Ffile_f, 1);
+ rb_define_single_method(C_File, "isfile", Ffile_f, 1);
+ rb_define_single_method(C_File, "z", Ffile_z, 1);
+ rb_define_single_method(C_File, "s", Ffile_s, 1);
+ rb_define_single_method(C_File, "size", Ffile_s, 1);
+ rb_define_single_method(C_File, "O", Ffile_owned, 1);
+ rb_define_single_method(C_File, "owned", Ffile_owned, 1);
+ rb_define_single_method(C_File, "G", Ffile_grpowned, 1);
+
+ rb_define_single_method(C_File, "p", Ffile_p, 1);
+ rb_define_single_method(C_File, "ispipe", Ffile_p, 1);
+ rb_define_single_method(C_File, "l", Ffile_l, 1);
+ rb_define_single_method(C_File, "issymlink", Ffile_l, 1);
+ rb_define_single_method(C_File, "S", Ffile_S, 1);
+ rb_define_single_method(C_File, "issocket", Ffile_S, 1);
+
+ rb_define_single_method(C_File, "b", Ffile_b, 1);
+ rb_define_single_method(C_File, "c", Ffile_c, 1);
+
+ rb_define_single_method(C_File, "u", Ffile_suid, 1);
+ rb_define_single_method(C_File, "setuid", Ffile_suid, 1);
+ rb_define_single_method(C_File, "g", Ffile_sgid, 1);
+ rb_define_single_method(C_File, "setgid", Ffile_sgid, 1);
+ rb_define_single_method(C_File, "k", Ffile_sticky, 1);
+
+ rb_define_single_method(C_File, "atime", Ffile_atime, 1);
+ rb_define_single_method(C_File, "mtime", Ffile_mtime, 1);
+ rb_define_single_method(C_File, "ctime", Ffile_ctime, 1);
+
+ rb_define_single_method(C_File, "utime", Ffile_utime, -1);
+ rb_define_single_method(C_File, "chmod", Ffile_chmod, -2);
+ rb_define_single_method(C_File, "chown", Ffile_chown, -1);
+
+ rb_define_single_method(C_File, "link", Ffile_link, 2);
+ rb_define_single_method(C_File, "symlink", Ffile_symlink, 2);
+ rb_define_single_method(C_File, "readlink", Ffile_readlink, 1);
+
+ rb_define_single_method(C_File, "unlink", Ffile_unlink, -1);
+ rb_define_single_method(C_File, "delete", Ffile_unlink, -1);
+ rb_define_single_method(C_File, "rename", Ffile_rename, 2);
+ rb_define_single_method(C_File, "umask", Ffile_umask, -1);
+ rb_define_single_method(C_File, "truncate", Ffile_truncate, 2);
+
+ rb_define_method(C_File, "stat", Ffile_stat2, 0);
+ rb_define_method(C_File, "lstat", Ffile_lstat2, 0);
+
+ rb_define_method(C_File, "atime", Ffile_atime2, 0);
+ rb_define_method(C_File, "mtime", Ffile_mtime2, 0);
+ rb_define_method(C_File, "ctime", Ffile_ctime2, 0);
+
+ rb_define_method(C_File, "chmod", Ffile_chmod2, 1);
+ rb_define_method(C_File, "chown", Ffile_chown2, 2);
+ rb_define_method(C_File, "truncate", Ffile_truncate2, 1);
+
+ rb_define_method(C_File, "tell", Ffile_tell, 0);
+ rb_define_method(C_File, "seek", Ffile_seek, 2);
+ rb_define_method(C_File, "rewind", Ffile_rewind, 0);
+ rb_define_method(C_File, "isatty", Ffile_isatty, 0);
+ rb_define_method(C_File, "eof", Ffile_eof, 0);
+
+ rb_define_method(C_IO, "fcntl", Ffile_fcntl, 2);
+
+ rb_define_method(C_File, "path", Ffile_path, 0);
+}
diff --git a/gc.c b/gc.c
new file mode 100644
index 0000000000..5f30206f09
--- /dev/null
+++ b/gc.c
@@ -0,0 +1,490 @@
+/************************************************
+
+ gc.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:27 $
+ created at: Tue Oct 5 09:44:46 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "st.h"
+#include <stdio.h>
+
+void *malloc();
+void *calloc();
+void *realloc();
+
+struct gc_list *GC_List = Qnil;
+static struct gc_list *Global_List = Qnil;
+static unsigned long bytes_alloc = 0, gc_threshold = 1000000;
+
+static mark_tbl();
+
+void *
+xmalloc(size)
+ unsigned long size;
+{
+ void *mem;
+
+ bytes_alloc += size;
+ if (size == 0) size = 1;
+ mem = malloc(size);
+ if (mem == Qnil) {
+ gc();
+ bytes_alloc += size;
+ mem = malloc(size);
+ if (mem == Qnil)
+ Fatal("failed to allocate memory");
+ }
+
+ return mem;
+}
+
+void *
+xcalloc(n, size)
+ unsigned long n, size;
+{
+ void *mem;
+
+ mem = xmalloc(n * size);
+ bzero(mem, n * size);
+
+ return mem;
+}
+
+void *
+xrealloc(ptr, size)
+ void *ptr;
+ unsigned long size;
+{
+ void *mem;
+
+ mem = realloc(ptr, size);
+ if (mem == Qnil) {
+ gc();
+ mem = realloc(ptr, size);
+ if (mem == Qnil)
+ Fatal("failed to allocate memory(realloc)");
+ }
+
+ return mem;
+}
+
+void
+rb_global_variable(var)
+ VALUE *var;
+{
+ struct gc_list *tmp;
+
+ tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list));
+ tmp->next = Global_List;
+ tmp->varptr = var;
+ tmp->n = 1;
+ Global_List = tmp;
+}
+
+static struct RBasic *object_list = Qnil;
+static struct RBasic *literal_list = Qnil;
+static unsigned long fl_current = FL_MARK;
+static unsigned long fl_old = 0L;
+
+static int dont_gc;
+
+VALUE
+Fgc_enable()
+{
+ int old = dont_gc;
+
+ dont_gc = Qnil;
+ return old;
+}
+
+VALUE
+Fgc_disable()
+{
+ int old = dont_gc;
+
+ dont_gc = TRUE;
+ return old;
+}
+
+VALUE
+Fgc_threshold(obj)
+ VALUE obj;
+{
+ return INT2FIX(gc_threshold);
+}
+
+VALUE
+Fgc_set_threshold(obj, val)
+ VALUE obj, val;
+{
+ int old = gc_threshold;
+
+ gc_threshold = NUM2INT(val);
+ return INT2FIX(old);
+}
+
+#include <sys/types.h>
+#include <sys/times.h>
+
+static Fgc_begin()
+{
+ return Qnil;
+}
+
+static Fgc_end()
+{
+ return Qnil;
+}
+
+VALUE M_GC;
+
+static ID start_hook, end_hook;
+
+struct RBasic *
+newobj(size)
+ unsigned long size;
+{
+ struct RBasic *obj = Qnil;
+
+ if (bytes_alloc + size > gc_threshold) {
+ gc();
+ }
+ obj = (struct RBasic*)xmalloc(size);
+ obj->next = object_list;
+ object_list = obj;
+ obj->flags = fl_current;
+ obj->iv_tbl = Qnil;
+
+ return obj;
+}
+
+literalize(obj)
+ struct RBasic *obj;
+{
+ struct RBasic *ptr = object_list;
+
+ if (NIL_P(obj) || FIXNUM_P(obj)) return;
+
+ FL_SET(obj, FL_LITERAL);
+ if (ptr == obj) {
+ object_list = ptr->next;
+ obj->next = literal_list;
+ literal_list = obj;
+
+ return;
+ }
+
+ while (ptr && ptr->next) {
+ if (ptr->next == obj) {
+ ptr->next = obj->next;
+ obj->next = literal_list;
+ literal_list = obj;
+
+ return;
+ }
+ ptr = ptr->next;
+ }
+ Bug("0x%x is not a object.", obj);
+}
+
+void
+unliteralize(obj)
+ struct RBasic *obj;
+{
+ struct RBasic *ptr = literal_list;
+
+ if (NIL_P(obj) || FIXNUM_P(obj)) return;
+
+ if (!FL_TEST(obj, FL_LITERAL)) return;
+ FL_UNSET(obj, FL_LITERAL);
+
+ if (ptr == obj) {
+ literal_list = ptr->next;
+ goto unlit;
+ }
+
+ while (ptr->next) {
+ if (ptr->next == obj) {
+ ptr->next = obj->next;
+ }
+ ptr = ptr->next;
+ goto unlit;
+ }
+ Bug("0x%x is not a literal object.", obj);
+
+ unlit:
+ obj->next = object_list;
+ object_list = obj;
+ obj->flags &= ~FL_MARK;
+ obj->flags |= fl_current;
+ return;
+}
+
+extern st_table *rb_global_tbl;
+extern st_table *rb_class_tbl;
+
+gc()
+{
+ struct gc_list *list;
+ struct ENVIRON *env;
+ int i, max;
+
+ rb_funcall(M_GC, start_hook, 0, Qnil);
+
+ if (dont_gc) return;
+ dont_gc++;
+ fl_old = fl_current;
+ fl_current = ~fl_current & FL_MARK;
+
+ /* mark env stack */
+ for (env = the_env; env; env = env->prev) {
+ mark(env->self);
+ for (i=1, max=env->argc; i<max; i++) {
+ mark(env->argv[i]);
+ }
+ if (env->local_vars) {
+ for (i=0, max=env->local_tbl[0]; i<max; i++)
+ mark(env->local_vars[i]);
+ }
+ }
+
+ /* mark protected C variables */
+ for (list=GC_List; list; list=list->next) {
+ VALUE *v = list->varptr;
+ for (i=0, max = list->n; i<max; i++) {
+ mark(*v);
+ v++;
+ }
+ }
+
+ /* mark protected global variables */
+ for (list = Global_List; list; list = list->next) {
+ mark(*list->varptr);
+ }
+
+ mark_global_tbl();
+ mark_tbl(rb_class_tbl);
+
+ mark_trap_list();
+
+ sweep();
+ bytes_alloc = 0;
+ dont_gc--;
+
+ rb_funcall(M_GC, end_hook, 0, Qnil);
+}
+
+static
+mark_entry(key, value)
+ ID key;
+ VALUE value;
+{
+ mark(value);
+ return ST_CONTINUE;
+}
+
+static
+mark_tbl(tbl)
+ st_table *tbl;
+{
+ st_foreach(tbl, mark_entry, 0);
+}
+
+static
+mark_dicentry(key, value)
+ ID key;
+ VALUE value;
+{
+ mark(key);
+ mark(value);
+ return ST_CONTINUE;
+}
+
+static
+mark_dict(tbl)
+ st_table *tbl;
+{
+ st_foreach(tbl, mark_dicentry, 0);
+}
+
+mark(obj)
+ register struct RBasic *obj;
+{
+ if (obj == Qnil) return;
+ if (FIXNUM_P(obj)) return;
+ if ((obj->flags & FL_MARK) == fl_current) return;
+
+ obj->flags &= ~FL_MARK;
+ obj->flags |= fl_current;
+
+ switch (obj->flags & T_MASK) {
+ case T_NIL:
+ case T_FIXNUM:
+ Bug("mark() called for broken object");
+ break;
+ }
+
+ if (obj->iv_tbl) mark_tbl(obj->iv_tbl);
+ switch (obj->flags & T_MASK) {
+ case T_OBJECT:
+ mark(obj->class);
+ break;
+ case T_ICLASS:
+ mark(RCLASS(obj)->super);
+ if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl);
+ mark_tbl(RCLASS(obj)->m_tbl);
+ break;
+ case T_CLASS:
+ mark(RCLASS(obj)->super);
+ case T_MODULE:
+ if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl);
+ mark_tbl(RCLASS(obj)->m_tbl);
+ mark(RBASIC(obj)->class);
+ break;
+ case T_ARRAY:
+ {
+ int i, len = RARRAY(obj)->len;
+ VALUE *ptr = RARRAY(obj)->ptr;
+
+ for (i=0; i < len; i++)
+ mark(ptr[i]);
+ }
+ break;
+ case T_DICT:
+ mark_dict(RDICT(obj)->tbl);
+ break;
+ case T_STRING:
+ if (RSTRING(obj)->orig) mark(RSTRING(obj)->orig);
+ break;
+ case T_DATA:
+ if (RDATA(obj)->dmark) (*RDATA(obj)->dmark)(DATA_PTR(obj));
+ break;
+ case T_REGEXP:
+ case T_FLOAT:
+ case T_METHOD:
+ case T_BIGNUM:
+ break;
+ case T_STRUCT:
+ {
+ int i, len = RSTRUCT(obj)->len;
+ struct kv_pair *ptr = RSTRUCT(obj)->tbl;
+
+ for (i=0; i < len; i++)
+ mark(ptr[i].value);
+ }
+ break;
+ default:
+ Bug("mark(): unknown data type %d", obj->flags & T_MASK);
+ }
+}
+
+sweep()
+{
+ register struct RBasic *link = object_list;
+ register struct RBasic *next;
+
+ if (link && (link->flags & FL_MARK) == fl_old) {
+ object_list = object_list->next;
+ obj_free(link);
+ link = object_list;
+ }
+
+ while (link && link->next) {
+ if ((link->next->flags & FL_MARK) == fl_old) {
+ next = link->next->next;
+ obj_free(link->next);
+ link->next = next;
+ continue;
+ }
+ link = link->next;
+ }
+}
+
+static
+freemethod(key, body)
+ ID key;
+ char *body;
+{
+ freenode(body);
+ return ST_CONTINUE;
+}
+
+obj_free(obj)
+ struct RBasic *obj;
+{
+ switch (obj->flags & T_MASK) {
+ case T_NIL:
+ case T_FIXNUM:
+ Bug("obj_free() called for broken object");
+ break;
+ }
+
+ if (obj->iv_tbl) st_free_table(obj->iv_tbl);
+ switch (obj->flags & T_MASK) {
+ case T_OBJECT:
+ break;
+ case T_MODULE:
+ case T_CLASS:
+ st_foreach(RCLASS(obj)->m_tbl, freemethod);
+ st_free_table(RCLASS(obj)->m_tbl);
+ if (RCLASS(obj)->c_tbl)
+ st_free_table(RCLASS(obj)->c_tbl);
+ break;
+ case T_STRING:
+ if (RSTRING(obj)->orig == Qnil) free(RSTRING(obj)->ptr);
+ break;
+ case T_ARRAY:
+ free(RARRAY(obj)->ptr);
+ break;
+ case T_DICT:
+ st_free_table(RDICT(obj)->tbl);
+ break;
+ case T_REGEXP:
+ reg_free(RREGEXP(obj)->ptr);
+ free(RREGEXP(obj)->str);
+ break;
+ case T_DATA:
+ if (RDATA(obj)->dfree) (*RDATA(obj)->dfree)(DATA_PTR(obj));
+ break;
+ case T_ICLASS:
+ /* iClass shares table with the module */
+ case T_FLOAT:
+ break;
+ case T_METHOD:
+ freenode(RMETHOD(obj)->node);
+ break;
+ case T_STRUCT:
+ free(RSTRUCT(obj)->name);
+ free(RSTRUCT(obj)->tbl);
+ break;
+ case T_BIGNUM:
+ free(RBIGNUM(obj)->digits);
+ break;
+ default:
+ Bug("sweep(): unknown data type %d", obj->flags & T_MASK);
+ }
+ free(obj);
+}
+
+Init_GC()
+{
+ M_GC = rb_define_module("GC");
+ rb_define_single_method(M_GC, "start", gc, 0);
+ rb_define_single_method(M_GC, "enable", Fgc_enable, 0);
+ rb_define_single_method(M_GC, "disable", Fgc_disable, 0);
+ rb_define_single_method(M_GC, "threshold", Fgc_threshold, 0);
+ rb_define_single_method(M_GC, "threshold=", Fgc_set_threshold, 1);
+ rb_define_single_method(M_GC, "start_hook", Fgc_begin, 0);
+ rb_define_single_method(M_GC, "end_hook", Fgc_end, 0);
+ rb_define_func(M_GC, "garbage_collect", gc, 0);
+
+ start_hook = rb_intern("start_hook");
+ end_hook = rb_intern("end_hook");
+}
diff --git a/ident.h b/ident.h
new file mode 100644
index 0000000000..8047c272ca
--- /dev/null
+++ b/ident.h
@@ -0,0 +1,25 @@
+/************************************************
+
+ ident.h -
+
+ $Author: matz $
+ $Revision: 1.1.1.1 $
+ $Date: 1994/06/17 14:23:49 $
+ created at: Mon Jan 31 16:23:19 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#ifndef IDENT_H
+#define IDENT_H
+
+#define ID_SCOPE_MASK 0x07
+#define ID_LOCAL 0x00
+#define ID_ATTRSET 0x04
+#define ID_INSTANCE 0x02
+#define ID_GLOBAL 0x03
+#define ID_CONST 0x06
+#define ID_VARMASK 0x02
+
+#endif
diff --git a/inits.c b/inits.c
new file mode 100644
index 0000000000..58d686d912
--- /dev/null
+++ b/inits.c
@@ -0,0 +1,50 @@
+/************************************************
+
+ inits.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Tue Dec 28 16:01:58 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+rb_call_inits()
+{
+ Init_sym();
+ Init_var_tables();
+ Init_Object();
+ Init_GC();
+ Init_Comparable();
+ Init_Enumerable();
+ Init_Numeric();
+ Init_Bignum();
+ Init_Array();
+ Init_Dict();
+ Init_Struct();
+ Init_String();
+ Init_Regexp();
+ Init_pack();
+ Init_Range();
+ Init_IO();
+ Init_Dir();
+ Init_Time();
+ Init_Random();
+ Init_process();
+ Init_Etc();
+ Init_load();
+ Init_Math();
+#ifdef USE_DBM
+ Init_DBM();
+#endif
+#ifdef HAVE_SOCKET
+ Init_Socket();
+#endif
+ /* new Inits comes between here.. */
+
+ /* .. and here. */
+ Init_version();
+}
diff --git a/io.c b/io.c
new file mode 100644
index 0000000000..b3ed236474
--- /dev/null
+++ b/io.c
@@ -0,0 +1,1221 @@
+/************************************************
+
+ io.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:29 $
+ created at: Fri Oct 15 18:08:59 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "io.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+VALUE rb_ad_string();
+
+VALUE C_IO;
+extern VALUE C_File;
+
+VALUE rb_stdin, rb_stdout, rb_stderr, rb_defout;
+
+VALUE FS, OFS;
+VALUE RS, ORS;
+
+ID id_write;
+
+extern char *inplace;
+
+/* writing functions */
+static VALUE
+Fio_write(obj, str)
+ VALUE obj;
+ struct RString *str;
+{
+ OpenFile *fptr;
+ FILE *f;
+ VALUE out;
+ int n;
+
+ if (TYPE(str) != T_STRING)
+ str = (struct RString*)obj_as_string(str);
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ Fail("not opened for writing");
+ }
+
+ f = (fptr->f2) ? fptr->f2 : fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ if (str->len == 0) return INT2FIX(0);
+
+ n = fwrite(str->ptr, sizeof(char), str->len, f);
+ if (n == 0 || ferror(f)) {
+ rb_sys_fail(fptr->path);
+ }
+ if (fptr->mode & FMODE_SYNC) {
+ fflush(f);
+ }
+
+ return INT2FIX(n);
+}
+
+static VALUE
+Fio_puts(obj, str)
+ VALUE obj, str;
+{
+ Fio_write(obj, str);
+ return obj;
+}
+
+static VALUE
+Fio_flush(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ FILE *f;
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ Fail("not opend for writing");
+ }
+ f = (fptr->f2) ? fptr->f2 : fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ if (fflush(f) == EOF) rb_sys_fail(Qnil);
+
+ return obj;
+}
+
+static VALUE
+Fio_eof(obj)
+ VALUE obj;
+{
+ 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
+
+ ch = getc(fptr->f);
+ if (ch != EOF) {
+ (void)ungetc(ch, fptr->f);
+ return FALSE;
+ }
+#ifdef STDSTDIO
+ if (fptr->f->_cnt < -1)
+ fptr->f->_cnt = -1;
+#endif
+ return TRUE;
+}
+
+static VALUE
+Fio_sync(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ return (fptr->mode & FMODE_SYNC) ? TRUE : FALSE;
+}
+
+static VALUE
+Fio_set_sync(obj, mode)
+ VALUE obj, mode;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ if (mode) {
+ fptr->mode |= FMODE_SYNC;
+ }
+ else {
+ fptr->mode &= ~FMODE_SYNC;
+ }
+ return mode;
+}
+
+static VALUE
+Fio_fileno(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ int f;
+
+ GetOpenFile(obj, fptr);
+ f = fileno(fptr->f);
+ return INT2FIX(f);
+}
+
+/* reading functions */
+static VALUE
+read_all(port)
+ VALUE port;
+{
+ OpenFile *fptr;
+ VALUE str;
+ char buf[BUFSIZ];
+ int n;
+
+ GetOpenFile(port, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Fail("not opend for reading");
+ }
+ if (fptr->f == NULL) Fail("closed stream");
+
+ GC_LINK;
+ GC_PRO3(str, str_new(0, 0));
+
+ for (;;) {
+ n = fread(buf, 1, BUFSIZ, fptr->f);
+ if (n == 0) {
+ if (feof(fptr->f)) break;
+ rb_sys_fail(Qnil);
+ }
+ str_cat(str, buf, n);
+ }
+
+ GC_UNLINK;
+ return str;
+}
+
+static VALUE
+Fio_read(obj, args)
+ VALUE obj, args;
+{
+ OpenFile *fptr;
+ int n, lgt;
+ VALUE len, str;
+
+ if (rb_scan_args(args, "01", &len) == 0) {
+ return read_all(obj);
+ }
+
+ lgt = NUM2INT(len);
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Fail("not opend for reading");
+ }
+ if (fptr->f == NULL) Fail("closed stream");
+
+ str = str_new(0, lgt);
+
+ n = fread(RSTRING(str)->ptr, 1, RSTRING(str)->len, fptr->f);
+ if (n == 0) {
+ if (feof(fptr->f)) return Qnil;
+ rb_sys_fail(Qnil);
+ }
+
+ RSTRING(str)->len = n;
+ RSTRING(str)->ptr[n] = '\0';
+
+ return str;
+}
+
+static void
+io_gets(str)
+ VALUE str;
+{
+ rb_break();
+}
+
+void rb_each();
+
+VALUE rb_lastline;
+static VALUE lineno;
+
+static VALUE
+Fio_gets(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ FILE *f;
+ struct RString *str;
+ int c, newline;
+ int rslen;
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Fail("not opend for reading");
+ }
+ f = fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ GC_LINK;
+ GC_PRO2(str);
+
+ if (RS) {
+ rslen = RSTRING(RS)->len;
+ if (rslen == 0) {
+ newline = '\n';
+ }
+ else {
+ newline = RSTRING(RS)->ptr[rslen-1];
+ }
+ }
+ else {
+ newline = 0777; /* non matching char */
+ rslen = 1;
+ }
+
+ if (rslen == 0 && c == '\n') {
+ do {
+ c = getc(f);
+ if (c != '\n') {
+ ungetc(c,f);
+ break;
+ }
+ } while (c != EOF);
+ }
+
+ {
+ char buf[8192];
+ char *bp, *bpe = buf + sizeof buf - 3;
+ char *ptr;
+ int append = 0;
+
+ again:
+ bp = buf;
+ while ((c = getc(f)) != EOF && (*bp++ = c) != newline && bp < bpe)
+ ;
+
+ if (c == EOF && !append && bp == buf) {
+ str = Qnil;
+ goto return_gets;
+ }
+
+ if (append)
+ str_cat(str, buf, bp - buf);
+ 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)
+ )
+ )
+ )
+ ) {
+ append = 1;
+ goto again;
+ }
+ }
+
+ return_gets:
+ if (rslen == 0 && c == '\n') {
+ while (c != EOF) {
+ c = getc(f);
+ if (c != '\n') {
+ ungetc(c, f);
+ break;
+ }
+ }
+ }
+
+ GC_UNLINK;
+
+ if (str) {
+ fptr->lineno++;
+ lineno = INT2FIX(fptr->lineno);
+ return rb_lastline = (VALUE)str;
+ }
+ return Qnil;
+}
+
+static VALUE
+Fio_each(obj)
+ VALUE obj;
+{
+ VALUE str;
+
+ GC_PRO2(str);
+ while (str = Fio_gets(obj)) {
+ rb_yield(str);
+ }
+ return Qnil;
+}
+
+static VALUE
+Fio_each_byte(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ FILE *f;
+ int c;
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Fail("not opend for reading");
+ }
+ f = fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ while ((c = getc(f)) != EOF) {
+ rb_yield(INT2FIX(c & 0xff));
+ }
+ if (ferror(f) != 0) rb_sys_fail(Qnil);
+ return obj;
+}
+
+static VALUE
+Fio_getc(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ FILE *f;
+ int c;
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ Fail("not opend for reading");
+ }
+ f = fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ c = getc(f);
+ if (c == EOF) {
+ if (ferror(f) != 0) rb_sys_fail(Qnil);
+ return Qnil;
+ }
+ return INT2FIX(c & 0xff);
+}
+
+static VALUE
+Fio_isatty(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ if (fptr->f == NULL) Fail("closed stream");
+ if (isatty(fileno(fptr->f)) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static VALUE
+Fio_close(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+
+ if (fptr->f2 != NULL) {
+ fclose(fptr->f2);
+ }
+ if (fptr->f != NULL) {
+ fclose(fptr->f);
+ }
+ fptr->f = fptr->f2 = NULL;
+ if (fptr->pid) {
+ rb_syswait(fptr->pid);
+ fptr->pid = 0;
+ }
+ return Qnil;
+}
+
+static VALUE
+Fio_syswrite(obj, str)
+ VALUE obj, str;
+{
+ OpenFile *fptr;
+ FILE *f;
+ int n;
+
+ if (TYPE(str) != T_STRING)
+ str = obj_as_string(str);
+
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ Fail("not opend for writing");
+ }
+ f = (fptr->f2) ? fptr->f2 : fptr->f;
+ if (f == NULL) Fail("closed stream");
+
+ n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len);
+
+ if (n == -1) rb_sys_fail(Qnil);
+
+ return INT2FIX(n);
+}
+
+static VALUE
+Fio_sysread(obj, len)
+ VALUE obj, 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");
+
+ str = str_new(0, ilen);
+
+ n = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len);
+
+ if (n == -1) rb_sys_fail(Qnil);
+ if (n == 0) return Qnil; /* EOF */
+
+ RSTRING(str)->len = n;
+ RSTRING(str)->ptr[n] = '\0';
+ return str;
+}
+
+void
+io_free_OpenFile(fptr)
+ OpenFile *fptr;
+{
+ if (fptr->f != NULL) {
+ fclose(fptr->f);
+ }
+ if (fptr->f2 != NULL) {
+ fclose(fptr->f2);
+ }
+ if (fptr->path) {
+ free(fptr->path);
+ }
+ if (fptr->pid) {
+ rb_syswait(fptr->pid);
+ }
+}
+
+static VALUE
+Fio_binmode(obj)
+ VALUE obj;
+{
+#ifdef MSDOS
+ OpenFile *fptr;
+
+ GetOpenFile(obj, fptr);
+ if (setmode(fileno(fptr), O_BINARY) == -1)
+ rb_sys_fail(Qnil);
+#endif
+ return obj;
+}
+
+VALUE obj_alloc();
+
+io_mode_flags(mode)
+ char *mode;
+{
+ int flags = 0;
+
+ switch (mode[0]) {
+ case 'r':
+ flags |= FMODE_READABLE;
+ break;
+ case 'w':
+ flags |= FMODE_WRITABLE;
+ break;
+ case 'a':
+ flags |= FMODE_WRITABLE;
+ break;
+ default:
+ Fail("illegal access mode");
+ }
+ if (mode[1] == '+') {
+ flags |= FMODE_READABLE | FMODE_WRITABLE;
+ }
+
+ return flags;
+}
+
+FILE *
+rb_fdopen(fd, mode)
+ int fd;
+ char *mode;
+{
+ FILE *f;
+
+ f = fdopen(fd, mode);
+ if (f == NULL) {
+ if (errno = EMFILE) {
+ gc();
+ f = fdopen(fd, mode);
+ }
+ if (f == NULL) {
+ rb_sys_fail(Qnil);
+ }
+ }
+ return f;
+}
+
+static VALUE
+pipe_open(pname, mode)
+ char *pname, *mode;
+{
+ VALUE port;
+ OpenFile *fptr;
+
+ int pid, pr[2], pw[2];
+ int doexec;
+
+ GC_LINK;
+ GC_PRO3(port, obj_alloc(C_IO));
+
+ MakeOpenFile(port, fptr);
+ fptr->mode = io_mode_flags(mode);
+
+ if ((fptr->mode & FMODE_READABLE) && pipe(pr) == -1 ||
+ (fptr->mode & FMODE_WRITABLE) && pipe(pw) == -1)
+ rb_sys_fail(Qnil);
+
+ doexec = (strcmp("-", pname) != 0);
+ if (!doexec) {
+ fflush(stdin); /* is it really needed? */
+ fflush(stdout);
+ fflush(stderr);
+ }
+
+ retry:
+ switch (pid = (doexec?vfork():fork())) {
+ case 0: /* child */
+ if (fptr->mode & FMODE_READABLE) {
+ close(pr[0]);
+ dup2(pr[1], 1);
+ close(pr[1]);
+ }
+ if (fptr->mode & FMODE_WRITABLE) {
+ close(pw[1]);
+ dup2(pw[0], 0);
+ close(pw[0]);
+ }
+
+ if (doexec) {
+ rb_proc_exec(pname);
+ _exit(127);
+ }
+ return Qnil;
+
+ case -1: /* fork failed */
+ if (errno == EAGAIN) {
+ sleep(5);
+ goto retry;
+ }
+ break;
+
+ default: /* parent */
+ if (fptr->mode & FMODE_READABLE) close(pr[1]);
+ if (fptr->mode & FMODE_WRITABLE) close(pw[0]);
+ }
+ if (pid == -1) {
+ close(pr[0]); close(pw[1]);
+ rb_sys_fail(Qnil);
+ }
+
+ fptr->pid = pid;
+ if (fptr->mode & FMODE_READABLE) fptr->f = rb_fdopen(pr[0], "r");
+ if (fptr->mode & FMODE_WRITABLE) fptr->f2 = rb_fdopen(pw[1], "w");
+
+ GC_UNLINK;
+
+ return port;
+}
+
+static VALUE
+Fopen(self, args)
+ VALUE self, args;
+{
+ char *mode;
+ VALUE port;
+ int pipe = 0;
+ VALUE pname, pmode;
+
+ rb_scan_args(args, "11", &pname, &pmode);
+ Check_Type(pname, T_STRING);
+ if (pmode == Qnil) {
+ mode = "r";
+ }
+ else {
+ Check_Type(pmode, T_STRING);
+ if (RSTRING(pmode)->len == 0 || RSTRING(pmode)->len > 2)
+ Fail("illegal access mode");
+ mode = RSTRING(pmode)->ptr;
+ }
+
+ if (RSTRING(pname)->ptr[0] == '|') {
+ port = pipe_open(RSTRING(pname)->ptr+1, mode);
+ }
+ else {
+ port = file_open(RSTRING(pname)->ptr, mode);
+ }
+
+ return port;
+}
+
+static VALUE
+Fprintf(argc, argv)
+ int argc;
+ VALUE argv[];
+{
+ VALUE out, str;
+
+ if (argc == 1) return Qnil;
+ if (TYPE(argv[1]) == T_STRING) {
+ out = rb_defout;
+ }
+ else if (rb_get_method_body(CLASS_OF(argv[1]), id_write, 0, MTH_FUNC)) {
+ out = argv[1];
+ argv++;
+ argc--;
+ }
+ else {
+ Fail("output must responds to `write'");
+ }
+
+ GC_LINK;
+ GC_PRO3(str, Fsprintf(argc, argv));
+
+ rb_funcall(out, id_write, 1, str);
+
+ GC_UNLINK;
+
+ return Qnil;
+}
+
+static void
+obj_print(obj)
+ VALUE obj;
+{
+ int i;
+
+ Fio_write(rb_defout, obj);
+}
+
+static VALUE
+Fprint(argc, argv)
+ int argc;
+ VALUE argv[];
+{
+ int i;
+
+ /* if no argument given, print recv */
+ if (argc == 1) {
+ obj_print(argv[0]);
+ }
+ else {
+ for (i=1; i<argc; i++) {
+ obj_print(argv[i]);
+ if (OFS && i>1) {
+ obj_print(OFS);
+ }
+ }
+ }
+ if (ORS) {
+ obj_print(ORS);
+ }
+
+ return Qnil;
+}
+
+static VALUE
+prep_stdio(f, mode)
+ FILE *f;
+ int mode;
+{
+ VALUE obj = obj_alloc(C_IO);
+ OpenFile *fp;
+
+ GC_LINK;
+ GC_PRO(obj);
+ MakeOpenFile(obj, fp);
+ fp->f = f;
+ fp->mode = mode;
+ GC_UNLINK;
+
+ return obj;
+}
+
+static VALUE filename = Qnil, file = Qnil;
+static int gets_lineno;
+static int init_p = 0, next_p = 0;
+
+static int
+next_argv()
+{
+ extern VALUE Argv;
+ char *fn;
+
+ if (init_p == 0) {
+ if (RARRAY(Argv)->len > 0) {
+ next_p = 1;
+ }
+ else {
+ next_p = -1;
+ file = rb_stdin;
+ }
+ init_p = 1;
+ gets_lineno = 0;
+ }
+
+ retry:
+ if (next_p == 1) {
+ next_p = 0;
+ if (RARRAY(Argv)->len > 0) {
+ filename = Fary_shift(Argv);
+ fn = RSTRING(filename)->ptr;
+ if (RSTRING(filename)->len == 1 && fn[0] == '-') {
+ file = rb_stdin;
+ if (inplace) {
+ rb_defout = rb_stdout;
+ }
+ }
+ else {
+ FILE *fr = fopen(fn, "r");
+
+ if (inplace) {
+ struct stat st, st2;
+ VALUE str;
+ FILE *fw;
+
+ if (!*inplace) {
+ Fatal("Can't do inplace edit without backup");
+ }
+ if (rb_defout != rb_stdout) {
+ Fio_close(rb_defout);
+ }
+ fstat(fileno(fr), &st);
+ str = str_new2(fn);
+ str_cat(str, inplace, strlen(inplace));
+ 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;
+ }
+ obj_free(str);
+ fw = fopen(fn, "w");
+ 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);
+ }
+ rb_defout = prep_stdio(fw, FMODE_WRITABLE);
+ }
+ file = prep_stdio(fr, FMODE_READABLE);
+ }
+ }
+ else {
+ init_p = 0;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static VALUE
+Fgets(obj)
+ VALUE obj;
+{
+ VALUE line;
+
+ retry:
+ if (!next_argv()) return Qnil;
+ line = Fio_gets(file);
+ if (line == Qnil && next_p != -1) {
+ Fio_close(file);
+ next_p = 1;
+ goto retry;
+ }
+
+ gets_lineno++;
+ lineno = INT2FIX(gets_lineno);
+
+ return line;
+}
+
+static VALUE
+Feof(obj)
+ VALUE obj;
+{
+ if (init_p == 0 && !next_argv())
+ return TRUE;
+ if (Fio_eof(file)) {
+ next_p = 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+Fgetc(obj)
+ VALUE obj;
+{
+ return Fio_getc(rb_stdin);
+}
+
+static VALUE
+Freadlines(obj)
+ VALUE obj;
+{
+ VALUE line, ary;
+
+ GC_LINK;
+ GC_PRO2(line);
+ GC_PRO3(ary, ary_new());
+
+ while (line = Fgets(obj)) {
+ Fary_push(ary, line);
+ }
+
+ GC_UNLINK;
+ return ary;
+}
+
+VALUE
+rb_check_str(val, id)
+ VALUE val;
+ ID id;
+{
+ if (val == Qnil) return TRUE;
+ if (TYPE(val) != T_STRING) {
+ Fail("value of %s must be String", rb_id2name(id));
+ }
+ return TRUE;
+}
+
+static VALUE
+Fsystem2(obj, str)
+ VALUE obj;
+ struct RString *str;
+{
+ VALUE port, result;
+ OpenFile *fptr;
+ int mask;
+
+ Check_Type(str, T_STRING);
+ GC_LINK;
+ GC_PRO3(port, pipe_open(str->ptr, "r"));
+ GC_PRO2(result);
+
+ result = read_all(port);
+
+ GetOpenFile(port, fptr);
+ rb_syswait(fptr->pid);
+ fptr->pid = 0;
+
+ obj_free(port);
+ GC_UNLINK;
+
+ return result;
+}
+
+struct timeval *time_timeval();
+
+#ifdef __linux__
+# define READ_PENDING(fp) ((fp)->_gptr < (fp)->_egptr > 0)
+#else
+# ifdef __SLBF
+# define READ_PENDING(fp) ((fp)->_r > 0)
+# else
+# define READ_PENDING(fp) ((fp)->_cnt != 0)
+# endif
+#endif
+
+static VALUE
+Fselect(obj, args)
+ VALUE obj, args;
+{
+ VALUE read, write, except, timeout, res, list;
+ fd_set rset, wset, eset, pset;
+ fd_set *rp, *wp, *ep;
+ struct timeval time, *tp, timerec;
+ OpenFile *fptr;
+ int i, max = 0, n;
+ int interrupt = 0;
+
+ rb_scan_args(args, "13", &read, &write, &except, &timeout);
+ if (timeout) {
+ tp = time_timeval(timeout);
+ }
+ else {
+ tp = NULL;
+ }
+
+ FD_ZERO(&pset);
+ if (read) {
+ int pending = 0;
+
+ Check_Type(read, T_ARRAY);
+ rp = &rset;
+ FD_ZERO(rp);
+ for (i=0; i<RARRAY(read)->len; i++) {
+ GetOpenFile(RARRAY(read)->ptr[i], fptr);
+ FD_SET(fileno(fptr->f), rp);
+ if (READ_PENDING(fptr->f)) { /* check for buffered data */
+ pending++;
+ FD_SET(fileno(fptr->f), &pset);
+ }
+ 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;
+ tp = &timerec;
+ }
+ }
+ else
+ rp = NULL;
+
+ if (write) {
+ Check_Type(write, T_ARRAY);
+ wp = &wset;
+ FD_ZERO(wp);
+ for (i=0; i<RARRAY(write)->len; i++) {
+ GetOpenFile(RARRAY(write)->ptr[i], fptr);
+ FD_SET(fileno(fptr->f), wp);
+ if (max > fileno(fptr->f)) max = fileno(fptr->f);
+ if (fptr->f2) {
+ FD_SET(fileno(fptr->f2), wp);
+ if (max < fileno(fptr->f2)) max = fileno(fptr->f2);
+ }
+ }
+ }
+ else
+ wp = NULL;
+
+ if (except) {
+ Check_Type(except, T_ARRAY);
+ ep = &eset;
+ FD_ZERO(ep);
+ for (i=0; i<RARRAY(except)->len; i++) {
+ GetOpenFile(RARRAY(except)->ptr[i], fptr);
+ FD_SET(fileno(fptr->f), ep);
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
+ if (fptr->f2) {
+ FD_SET(fileno(fptr->f2), ep);
+ if (max > fileno(fptr->f2)) max = fileno(fptr->f2);
+ }
+ }
+ }
+ else
+ ep = NULL;
+
+ max++;
+
+ retry:
+ if ((n = select(max, rp, wp, ep, tp)) < 0) {
+ if (errno == EINTR) {
+ if (tp == NULL) goto retry;
+ interrupt = 1;
+ }
+ rb_sys_fail(Qnil);
+ }
+ if (n == 0) return Qnil;
+
+ GC_LINK;
+ GC_PRO3(res, ary_new2(3));
+ RARRAY(res)->ptr[0] = rp?ary_new():Qnil;
+ RARRAY(res)->len++;
+ RARRAY(res)->ptr[1] = wp?ary_new():Qnil;
+ RARRAY(res)->len++;
+ RARRAY(res)->ptr[2] = ep?ary_new():Qnil;
+ RARRAY(res)->len++;
+
+ if (interrupt == 0) {
+
+ if (rp) {
+ list = RARRAY(res)->ptr[0];
+ for (i=0; i< RARRAY(read)->len; i++) {
+ GetOpenFile(RARRAY(read)->ptr[i], fptr);
+ if (FD_ISSET(fileno(fptr->f), rp)
+ || FD_ISSET(fileno(fptr->f), &pset)) {
+ Fary_push(list, RARRAY(read)->ptr[i]);
+ }
+ }
+ }
+
+ if (wp) {
+ list = RARRAY(res)->ptr[1];
+ for (i=0; i< RARRAY(write)->len; i++) {
+ GetOpenFile(RARRAY(write)->ptr[i], fptr);
+ if (FD_ISSET(fileno(fptr->f), rp)) {
+ Fary_push(list, RARRAY(write)->ptr[i]);
+ }
+ else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), rp)) {
+ Fary_push(list, RARRAY(write)->ptr[i]);
+ }
+ }
+ }
+
+ if (ep) {
+ list = RARRAY(res)->ptr[2];
+ for (i=0; i< RARRAY(except)->len; i++) {
+ GetOpenFile(RARRAY(except)->ptr[i], fptr);
+ if (FD_ISSET(fileno(fptr->f), rp)) {
+ Fary_push(list, RARRAY(except)->ptr[i]);
+ }
+ else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), rp)) {
+ Fary_push(list, RARRAY(except)->ptr[i]);
+ }
+ }
+ }
+ }
+
+ GC_UNLINK;
+ return res;
+}
+
+void
+io_ctl(obj, req, arg, io_p)
+ VALUE obj, req;
+ struct RString *arg;
+ int io_p;
+{
+ int cmd = NUM2INT(req);
+ OpenFile *fptr;
+ int len, fd;
+
+ GetOpenFile(obj, fptr);
+
+#ifdef IOCPARM_MASK
+#ifndef IOCPARM_LEN
+#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
+#endif
+#endif
+#ifdef IOCPARM_LEN
+ len = IOCPARM_LEN(cmd); /* on BSDish systes we're safe */
+#else
+ len = 256; /* otherwise guess at what's safe */
+#endif
+
+ Check_Type(arg, T_STRING);
+ str_modify(arg);
+
+ if (arg->len < len) {
+ str_grow(arg, len+1);
+ }
+ arg->ptr[len] = 17;
+ fd = fileno(fptr->f);
+ if (io_p?ioctl(fd, cmd, arg->ptr):fcntl(fd, cmd, arg->ptr)<0) {
+ rb_sys_fail(fptr->path);
+ }
+ if (arg->ptr[len] != 17) {
+ Fail("Return value overflowed string");
+ }
+}
+
+static VALUE
+Fio_ioctl(obj, req, arg)
+ VALUE obj, req;
+ struct RString *arg;
+{
+ io_ctl(obj, req, arg, 1);
+ return obj;
+}
+
+static VALUE
+Fio_defget(obj)
+ VALUE obj;
+{
+ return rb_defout;
+}
+
+static VALUE
+Fio_defset(obj, val)
+ VALUE obj, val;
+{
+ return rb_defout = val;
+}
+
+extern VALUE M_Enumerable;
+VALUE rb_readonly_hook();
+
+Init_IO()
+{
+ extern VALUE C_Kernel;
+
+ rb_define_func(C_Kernel, "open", Fopen, -2);
+ rb_define_func(C_Kernel, "printf", Fprintf, -1);
+ rb_define_method(C_Kernel, "print", Fprint, -1);
+ rb_define_func(C_Kernel, "gets", Fgets, 0);
+ rb_define_func(C_Kernel, "eof", Feof, 0);
+ rb_define_alias(C_Kernel,"readline", "gets");
+ rb_define_func(C_Kernel, "getc", Fgetc, 0);
+ rb_define_func(C_Kernel, "system2", Fsystem2, 1);
+ rb_define_func(C_Kernel, "select", Fselect, -2);
+
+ rb_define_func(C_Kernel, "readlines", Freadlines, 0);
+
+ C_IO = rb_define_class("IO", C_Object);
+ rb_include_module(C_IO, M_Enumerable);
+
+ rb_define_variable("$;", &FS, Qnil, rb_check_str);
+ rb_define_variable("$,", &OFS, Qnil, rb_check_str);
+
+ RS = str_new2("\n");
+ rb_define_variable("$/", &RS, Qnil, rb_check_str);
+ rb_define_variable("$\\", &ORS, Qnil, rb_check_str);
+
+ rb_define_variable("$FILENAME", &filename, Qnil, rb_readonly_hook);
+ rb_global_variable(&file);
+
+ rb_define_variable("$.", &lineno, Qnil, Qnil);
+ rb_define_variable("$_", &rb_lastline, Qnil, Qnil);
+
+ rb_define_method(C_IO, "each", Fio_each, 0);
+ rb_define_method(C_IO, "each_byte", Fio_each_byte, 0);
+
+ rb_define_method(C_IO, "syswrite", Fio_syswrite, 1);
+ rb_define_method(C_IO, "sysread", Fio_sysread, 1);
+
+ rb_define_method(C_IO, "fileno", Fio_fileno, 0);
+ rb_define_method(C_IO, "sync", Fio_sync, 0);
+ rb_define_method(C_IO, "sync=", Fio_set_sync, 1);
+
+ rb_define_alias(C_IO, "readlines", "to_a");
+
+ rb_define_method(C_IO, "read", Fio_read, -2);
+ rb_define_method(C_IO, "write", Fio_write, 1);
+ rb_define_method(C_IO, "gets", Fio_gets, 0);
+ rb_define_alias(C_IO, "readlines", "gets");
+ rb_define_method(C_IO, "getc", Fio_getc, 0);
+ rb_define_method(C_IO, "puts", Fio_puts, 1);
+ rb_define_method(C_IO, "<<", Fio_puts, 1);
+ rb_define_method(C_IO, "flush", Fio_flush, 0);
+ rb_define_method(C_IO, "eof", Fio_eof, 0);
+
+ rb_define_method(C_IO, "close", Fio_close, 0);
+
+ rb_define_method(C_IO, "isatty", Fio_isatty, 0);
+ rb_define_method(C_IO, "binmode", Fio_binmode, 0);
+
+ rb_define_method(C_IO, "ioctl", Fio_ioctl, 2);
+
+ rb_stdin = prep_stdio(stdin, FMODE_READABLE);
+ rb_define_variable("$stdin", &rb_stdin, Qnil, rb_readonly_hook);
+ rb_stdout = prep_stdio(stdout, FMODE_WRITABLE);
+ rb_define_variable("$stdout", &rb_stdout, Qnil, rb_readonly_hook);
+ rb_stderr = prep_stdio(stderr, FMODE_WRITABLE);
+ rb_define_variable("$stderr", &rb_stderr, Qnil, rb_readonly_hook);
+ rb_defout = rb_stdout;
+ rb_global_variable(&rb_defout);
+ rb_define_single_method(C_IO, "default", Fio_defget, 0);
+ rb_define_single_method(C_IO, "default=", Fio_defset, 1);
+
+ id_write = rb_intern("write");
+ Init_File();
+}
diff --git a/io.h b/io.h
new file mode 100644
index 0000000000..f325de3c3c
--- /dev/null
+++ b/io.h
@@ -0,0 +1,47 @@
+/************************************************
+
+ io.h -
+
+ $Author: matz $
+ $Revision: 1.1.1.1 $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Fri Nov 12 16:47:09 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#ifndef IO_H
+#define IO_H
+
+#include <stdio.h>
+#include <errno.h>
+
+typedef struct {
+ FILE *f; /* stdio ptr for read/write */
+ FILE *f2; /* additional ptr for rw pipes */
+ int mode; /* mode flags */
+ int pid; /* child's pid (for pipes) */
+ int lineno; /* number of lines read */
+ char *path; /* pathname for file */
+} OpenFile;
+
+#define FMODE_READABLE 1
+#define FMODE_WRITABLE 2
+#define FMODE_READWRITE 3
+#define FMODE_SYNC 4
+
+#define GetOpenFile(obj,fp) Get_Data_Struct(obj, "fd", OpenFile, fp)
+
+void io_free_OpenFile();
+
+#define MakeOpenFile(obj, fp) {\
+ Make_Data_Struct(obj, "fd", OpenFile, Qnil, io_free_OpenFile, fp);\
+ fp->f = fp->f2 = NULL;\
+ fp->mode = 0;\
+ fp->pid = 0;\
+ fp->lineno = 0;\
+ fp->path = NULL;\
+}
+
+#endif
diff --git a/math.c b/math.c
new file mode 100644
index 0000000000..b6fbb063bc
--- /dev/null
+++ b/math.c
@@ -0,0 +1,125 @@
+/************************************************
+
+ math.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Tue Jan 25 14:12:56 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include <math.h>
+
+VALUE M_Math;
+VALUE float_new();
+
+#define Need_Float(x) \
+if (FIXNUM_P(x)) {\
+ (x) = (struct RFloat*)float_new((double)FIX2INT(x));\
+} else {\
+ Check_Type(x, T_FLOAT);\
+}
+
+#define Need_Float2(x,y) {\
+ GC_LINK;\
+ GC_PRO(x);\
+ Need_Float(x);\
+ Need_Float(y);\
+ GC_UNLINK;\
+}
+
+static VALUE
+Fmath_atan2(obj, x, y)
+ VALUE obj;
+ struct RFloat *x, *y;
+{
+ Need_Float2(x, y);
+ return float_new(atan2(x->value, x->value));
+}
+
+static VALUE
+Fmath_cos(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+
+ return float_new(cos(x->value));
+}
+
+static VALUE
+Fmath_sin(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+
+ return float_new(sin(x->value));
+}
+
+static VALUE
+Fmath_tan(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+
+ return float_new(tan(x->value));
+}
+
+static VALUE
+Fmath_exp(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+ return float_new(exp(x->value));
+}
+
+static VALUE
+Fmath_log(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+ return float_new(log(x->value));
+}
+
+static VALUE
+Fmath_log10(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+ return float_new(log10(x->value));
+}
+
+static VALUE
+Fmath_sqrt(obj, x)
+ VALUE obj;
+ struct RFloat *x;
+{
+ Need_Float(x);
+ return float_new(log10(x->value));
+
+ if (x->value < 0.0) Fail("square root for negative number");
+ return float_new(sqrt(x->value));
+}
+
+Init_Math()
+{
+ M_Math = rb_define_module("Math");
+
+ rb_define_mfunc(M_Math, "atan2", Fmath_atan2, 2);
+ rb_define_mfunc(M_Math, "cos", Fmath_cos, 1);
+ rb_define_mfunc(M_Math, "sin", Fmath_sin, 1);
+ rb_define_mfunc(M_Math, "tan", Fmath_tan, 1);
+
+ rb_define_mfunc(M_Math, "exp", Fmath_exp, 1);
+ rb_define_mfunc(M_Math, "log", Fmath_log, 1);
+ rb_define_mfunc(M_Math, "log10", Fmath_log10, 1);
+ rb_define_mfunc(M_Math, "sqrt", Fmath_sqrt, 1);
+}
diff --git a/methods.c b/methods.c
new file mode 100644
index 0000000000..d861323444
--- /dev/null
+++ b/methods.c
@@ -0,0 +1,145 @@
+/************************************************
+
+ methods.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Fri Oct 1 17:25:07 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "ident.h"
+#include "env.h"
+#include "node.h"
+
+#define CACHE_SIZE 577
+#if 0
+#define EXPR1(c,m) (((int)(c)*(m))>>0)
+#else
+#define EXPR1(c,m) ((int)(c)^(m))
+#endif
+
+#define TRIAL 3
+
+struct hash_entry { /* method hash table. */
+ ID mid; /* method's id */
+ struct RClass *class; /* receiver's class */
+ struct RClass *origin; /* where method defined */
+ struct RMethod *method;
+ enum mth_scope scope;
+};
+
+static struct hash_entry cache[CACHE_SIZE];
+
+static struct RMethod*
+search_method(class, id, origin)
+ struct RClass *class, **origin;
+ ID id;
+{
+ struct RMethod *body;
+ NODE *list;
+
+ while (!st_lookup(class->m_tbl, id, &body)) {
+ class = class->super;
+ if (class == Qnil) return Qnil;
+ }
+
+ if (body->origin)
+ *origin = body->origin;
+ else
+ *origin = class;
+ return body;
+}
+
+NODE*
+rb_get_method_body(class, id, envset, scope)
+ struct RClass *class;
+ ID id;
+ int envset;
+ enum mth_scope scope;
+{
+ int pos, i;
+ int cscope;
+ struct RMethod *method;
+
+ /* is it in the method cache? */
+ pos = EXPR1(class, id) % CACHE_SIZE;
+ if (cache[pos].class != class || cache[pos].mid != id) {
+ /* not in the cache */
+ struct RMethod *body;
+ struct RClass *origin;
+
+ if ((body = search_method(class, id, &origin)) == Qnil) {
+ return Qnil;
+ }
+ /* store in cache */
+ cache[pos].mid = id;
+ cache[pos].class = class;
+ cache[pos].origin = origin;
+ cache[pos].method = body;
+ cache[pos].scope = body->scope;
+ }
+
+ cscope = cache[pos].scope;
+ method = cache[pos].method;
+ if (cscope == MTH_UNDEF) return Qnil;
+ if (cscope == MTH_FUNC && scope == MTH_METHOD) return Qnil;
+ if (envset) {
+ the_env->last_func = method->id;
+ the_env->last_class = cache[pos].origin;
+ }
+ return method->node;
+}
+
+void
+rb_alias(class, name, def)
+ struct RClass *class;
+ ID name, def;
+{
+ struct RMethod *body;
+
+ if (st_lookup(class->m_tbl, name, &body)) {
+ if (verbose) {
+ Warning("redefine %s", rb_id2name(name));
+ }
+ unliteralize(body);
+ }
+ body = search_method(class, def, &body);
+ st_insert(class->m_tbl, name, body);
+}
+
+void
+rb_clear_cache(body)
+ struct RMethod *body;
+{
+ int i;
+
+ for (i = 0; i< CACHE_SIZE; i++ ) {
+ if (cache[i].method == body) {
+ cache[i].class = Qnil;
+ cache[i].mid = Qnil;
+ }
+ }
+}
+
+void
+rb_clear_cache2(class)
+ struct RClass *class;
+{
+
+ class = class->super;
+ while (class) {
+ int i;
+
+ for (i = 0; i< CACHE_SIZE; i++ ) {
+ if (cache[i].origin == class) {
+ cache[i].class = Qnil;
+ cache[i].mid = Qnil;
+ }
+ }
+ class = class->super;
+ }
+}
diff --git a/missing.c b/missing.c
new file mode 100644
index 0000000000..ef2651ee94
--- /dev/null
+++ b/missing.c
@@ -0,0 +1,67 @@
+/*
+ * Do all necessary includes here, so that we don't have to worry about
+ * overlapping includes in the files in missing.d.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "ruby.h"
+
+#ifndef __STDC__
+#define const
+#endif /* !__STDC__ */
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+
+#ifndef HAVE_MEMMOVE
+#include "missing/memmove.c"
+#endif
+
+#ifndef HAVE_STRERROR
+#include "missing/strerror.c"
+#endif
+
+#ifndef HAVE_STRTOUL
+#include "missing/strtoul.c"
+#endif
+
+#ifndef HAVE_STRFTIME
+#include "missing/strftime.c"
+#endif
+
+#ifndef HAVE_STRSTR
+#include "missing/strstr.c"
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+#include "missing/getopt.h"
+#include "missing/getopt.c"
+#include "missing/getopt1.c"
+#endif
+
+#ifndef HAVE_MKDIR
+#include "missing/mkdir.c"
+#endif
+
+#ifndef HAVE_STRDUP
+char *
+strdup(str)
+ char *str;
+{
+ extern char *xmalloc();
+ char *tmp;
+ int len = strlen(str) + 1;
+
+ tmp = xmalloc(len);
+ if (tmp == NULL) return NULL;
+ bcopy(str, tmp, len);
+
+ return tmp;
+}
+#endif
diff --git a/missing/CVS/Entries b/missing/CVS/Entries
new file mode 100644
index 0000000000..2c02d6e353
--- /dev/null
+++ b/missing/CVS/Entries
@@ -0,0 +1,9 @@
+/getopt.c/0.26/Wed Jun 1 23:41:18 1994 Mon Apr 18 12:30:25 1994//
+/getopt.h/0.26/Wed Jun 1 23:41:18 1994 Mon Apr 18 12:30:25 1994//
+/getopt1.c/0.26/Wed Jun 1 23:41:18 1994 Mon Apr 18 12:30:25 1994//
+/memmove.c/0.26/Wed Jun 1 23:41:18 1994 Mon Apr 18 12:30:25 1994//
+/mkdir.c/1.1/Wed Jun 1 23:41:18 1994 Wed Jun 1 23:38:35 1994//
+/strerror.c/0.26/Wed Jun 1 23:41:18 1994 Mon Apr 18 12:30:25 1994//
+/strftime.c/0.26/Wed Jun 1 23:41:19 1994 Mon Apr 18 12:30:25 1994//
+/strtol.c/0.26/Wed Jun 1 23:41:19 1994 Mon Apr 18 12:30:25 1994//
+/strtoul.c/0.26/Wed Jun 1 23:41:19 1994 Mon Apr 18 12:30:25 1994//
diff --git a/missing/CVS/Repository b/missing/CVS/Repository
new file mode 100644
index 0000000000..a7ac69cf13
--- /dev/null
+++ b/missing/CVS/Repository
@@ -0,0 +1 @@
+/work/cvsroot/ruby/missing
diff --git a/missing/getopt.c b/missing/getopt.c
new file mode 100644
index 0000000000..bbf345c33c
--- /dev/null
+++ b/missing/getopt.c
@@ -0,0 +1,662 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef GAWK
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#include <string.h>
+#endif /* GNU C library. */
+
+
+#ifndef __STDC__
+#define const
+#endif
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+#define GETOPT_COMPAT
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (string, chr)
+ char *string;
+ int chr;
+{
+ while (*string)
+ {
+ if (*string == chr)
+ return string;
+ string++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ char *from, *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) malloc (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy (&argv[first_nonopt], temp, nonopts_size);
+ my_bcopy (&argv[last_nonopt], &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
+
+ free(temp);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound = 0;
+ extern int strncmp();
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+ }
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/missing/getopt.h b/missing/getopt.h
new file mode 100644
index 0000000000..de027434f7
--- /dev/null
+++ b/missing/getopt.h
@@ -0,0 +1,128 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+enum _argtype
+{
+ no_argument,
+ required_argument,
+ optional_argument
+};
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/missing/getopt1.c b/missing/getopt1.c
new file mode 100644
index 0000000000..eb06338ab3
--- /dev/null
+++ b/missing/getopt1.c
@@ -0,0 +1,162 @@
+/* Getopt for GNU.
+ Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifdef LIBC
+/* For when compiled as part of the GNU C library. */
+#include <ansidecl.h>
+#endif
+
+#ifndef RUBY
+#include "getopt.h"
+#endif
+
+#ifndef __STDC__
+#define const
+#endif
+
+#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) || defined (LIBC)
+#include <stdlib.h>
+#else /* STDC_HEADERS or __GNU_LIBRARY__ */
+char *getenv ();
+#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
+
+#if !defined (NULL)
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/missing/memmove.c b/missing/memmove.c
new file mode 100644
index 0000000000..09e64702b6
--- /dev/null
+++ b/missing/memmove.c
@@ -0,0 +1,24 @@
+/*
+ * memmove --- move memories.
+ *
+ * We supply this routine for those systems that aren't standard yet.
+ */
+
+char *
+memmove (dst, src, n)
+ char *dst, *src;
+ int n;
+{
+ char *ret = dst;
+
+ if (src < dst) {
+ src += n;
+ dst += n;
+ while (n--)
+ *--dst = *--src;
+ }
+ else if (dst < src)
+ while (n--)
+ *dst++ = *src++;
+ return ret;
+}
diff --git a/missing/mkdir.c b/missing/mkdir.c
new file mode 100644
index 0000000000..5225e586d9
--- /dev/null
+++ b/missing/mkdir.c
@@ -0,0 +1,103 @@
+/*
+ * Written by Robert Rother, Mariah Corporation, August 1985.
+ *
+ * If you want it, it's yours. All I ask in return is that if you
+ * figure out how to do this in a Bourne Shell script you send me
+ * a copy.
+ * sdcsvax!rmr or rmr@uscd
+ *
+ * Severely hacked over by John Gilmore to make a 4.2BSD compatible
+ * subroutine. 11Mar86; hoptoad!gnu
+ *
+ * Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
+ * subroutine didn't return EEXIST. It does now.
+ */
+
+#include <sys/stat.h>
+/*
+ * Make a directory.
+ */
+int
+mkdir (dpath, dmode)
+ char *dpath;
+ int dmode;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) == 0)
+ {
+ errno = EEXIST; /* Stat worked, so it already exists */
+ return -1;
+ }
+
+ /* If stat fails for a reason other than non-existence, return error */
+ if (errno != ENOENT)
+ return -1;
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ /*
+ * Cheap hack to set mode of new directory. Since this
+ * child process is going away anyway, we zap its umask.
+ * FIXME, this won't suffice to set SUID, SGID, etc. on this
+ * directory. Does anybody care?
+ */
+ status = umask (0); /* Get current umask */
+ status = umask (status | (0777 & ~dmode)); /* Set for mkdir */
+ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
+
+int
+rmdir (dpath)
+ char *dpath;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) != 0)
+ {
+ /* Stat just set errno. We don't have to */
+ return -1;
+ }
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
diff --git a/missing/strerror.c b/missing/strerror.c
new file mode 100644
index 0000000000..44013b3892
--- /dev/null
+++ b/missing/strerror.c
@@ -0,0 +1,19 @@
+/*
+ * strerror.c --- Map an integer error number into a printable string.
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+static char msg[50];
+
+char *
+strerror(error)
+ int error;
+{
+ if ((error <= sys_nerr) && (error > 0)) {
+ return sys_errlist[error];
+ }
+ sprintf (msg, "Unknown error (%d)", error);
+ return msg;
+}
diff --git a/missing/strftime.c b/missing/strftime.c
new file mode 100644
index 0000000000..36a325aa51
--- /dev/null
+++ b/missing/strftime.c
@@ -0,0 +1,781 @@
+/*
+ * strftime.c
+ *
+ * Public-domain implementation of ANSI C library routine.
+ *
+ * It's written in old-style C for maximal portability.
+ * However, since I'm used to prototypes, I've included them too.
+ *
+ * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
+ * 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 complete POSIX semantics, add POSIX_SEMANTICS.
+ *
+ * The code for %c, %x, and %X is my best guess as to what's "appropriate".
+ * This version ignores LOCALE information.
+ * It also doesn't worry about multi-byte characters.
+ * So there.
+ *
+ * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
+ * code are included if GAWK is defined.
+ *
+ * Arnold Robbins
+ * January, February, March, 1991
+ * Updated March, April 1992
+ * Updated April, 1993
+ * Updated February, 1994
+ * Updated May, 1994
+ *
+ * Fixes from ado@elsie.nci.nih.gov
+ * February 1991, May 1992
+ * Fixes from Tor Lillqvist tml@tik.vtt.fi
+ * May, 1993
+ * Further fixes from ado@elsie.nci.nih.gov
+ * February 1994
+ */
+
+#ifndef RUBY
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#endif
+#if defined(TM_IN_SYS_TIME) || ! defined(RUBY)
+#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 */
+#endif
+
+#if defined(POSIX2_DATE)
+#if ! defined(SYSV_EXT)
+#define SYSV_EXT 1
+#endif
+#if ! defined(SUNOS_EXT)
+#define SUNOS_EXT 1
+#endif
+#endif
+
+#if defined(POSIX2_DATE)
+#define adddecl(stuff) stuff
+#else
+#define adddecl(stuff)
+#endif
+
+#undef strchr /* avoid AIX weirdness */
+
+#ifndef __STDC__
+#define const /**/
+extern void *malloc();
+extern void *realloc();
+extern void tzset();
+extern char *strchr();
+extern char *getenv();
+static int weeknumber();
+adddecl(static int iso8601wknum();)
+#else
+extern void *malloc(unsigned count);
+extern void *realloc(void *ptr, unsigned count);
+extern void tzset(void);
+extern char *strchr(const char *str, int ch);
+extern char *getenv(const char *v);
+static int weeknumber(const struct tm *timeptr, int firstweekday);
+adddecl(static int iso8601wknum(const struct tm *timeptr);)
+#endif
+
+#ifdef __GNUC__
+#define inline __inline__
+#else
+#define inline /**/
+#endif
+
+#define range(low, item, hi) max(low, min(item, hi))
+
+#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
+extern char *tzname[2];
+extern int daylight;
+#endif
+
+/* min --- return minimum of two numbers */
+
+#ifndef __STDC__
+static inline int
+min(a, b)
+int a, b;
+#else
+static inline int
+min(int a, int b)
+#endif
+{
+ return (a < b ? a : b);
+}
+
+/* max --- return maximum of two numbers */
+
+#ifndef __STDC__
+static inline int
+max(a, b)
+int a, b;
+#else
+static inline int
+max(int a, int b)
+#endif
+{
+ return (a > b ? a : b);
+}
+
+/* strftime --- produce formatted time */
+
+#ifndef __STDC__
+size_t
+strftime(s, maxsize, format, timeptr)
+char *s;
+size_t maxsize;
+const char *format;
+const struct tm *timeptr;
+#else
+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;
+#ifdef POSIX_SEMANTICS
+ 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;
+#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;
+
+#ifndef POSIX_SEMANTICS
+ 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);
+ }
+ }
+ 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;
+
+#ifdef SYSV_EXT
+ 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 */
+#ifdef HAVE_TZNAME
+#ifdef HAVE_DAYLIGHT
+ i = (daylight && timeptr->tm_isdst); /* 0 or 1 */
+#else
+ i = timeptr->tm_isdst;
+#endif
+ strcpy(tbuf, tzname[i]);
+#else
+#ifdef HAVE_TM_ZONE
+ strcpy(tbuf, timeptr->tm_zone);
+#else
+ gettimeofday(& tv, & zone);
+ strcpy(tbuf, timezone(zone.tz_minuteswest,
+ timeptr->tm_isdst));
+#endif
+#endif
+ 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;
+#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;
+#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;
+#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(RUBY) && 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;
+#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;
+ }
+ }
+out:
+ if (s < endp && *format == '\0') {
+ *s = '\0';
+ return (s - start);
+ } else
+ return 0;
+}
+
+/* isleap --- is a year a leap year? */
+
+#ifndef __STDC__
+static int
+isleap(year)
+int year;
+#else
+static int
+isleap(int year)
+#endif
+{
+ return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+}
+
+
+#ifdef POSIX2_DATE
+/* iso8601wknum --- compute week number according to ISO 8601 */
+
+#ifndef __STDC__
+static int
+iso8601wknum(timeptr)
+const struct tm *timeptr;
+#else
+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) {
+#ifdef USE_BROKEN_XPG4
+ /* 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);
+#endif
+ }
+ break;
+ }
+ return weeknum;
+}
+#endif
+
+/* weeknumber --- figure how many weeks into the year */
+
+/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
+
+#ifndef __STDC__
+static int
+weeknumber(timeptr, firstweekday)
+const struct tm *timeptr;
+int firstweekday;
+#else
+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;
+}
+
+#if 0
+/* ADR --- I'm loathe to mess with ado's code ... */
+
+Date: Wed, 24 Apr 91 20:54:08 MDT
+From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
+To: arnold@audiofax.com
+
+Hi Arnold,
+in a process of fixing of strftime() in libraries on Atari ST I grabbed
+some pieces of code from your own strftime. When doing that it came
+to mind that your weeknumber() function compiles a little bit nicer
+in the following form:
+/*
+ * firstweekday is 0 if starting in Sunday, non-zero if in Monday
+ */
+{
+ return (timeptr->tm_yday - timeptr->tm_wday +
+ (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
+}
+How nicer it depends on a compiler, of course, but always a tiny bit.
+
+ Cheers,
+ Michal
+ ntomczak@vm.ucs.ualberta.ca
+#endif
+
+#ifdef TEST_STRFTIME
+
+/*
+ * NAME:
+ * tst
+ *
+ * SYNOPSIS:
+ * tst
+ *
+ * DESCRIPTION:
+ * "tst" is a test driver for the function "strftime".
+ *
+ * OPTIONS:
+ * None.
+ *
+ * AUTHOR:
+ * Karl Vogel
+ * Control Data Systems, Inc.
+ * vogelke@c-17igp.wpafb.af.mil
+ *
+ * BUGS:
+ * None noticed yet.
+ *
+ * COMPILE:
+ * cc -o tst -DTEST_STRFTIME strftime.c
+ */
+
+/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
+
+#ifndef NULL
+#include <stdio.h>
+#endif
+#include <sys/time.h>
+#include <string.h>
+
+#define MAXTIME 132
+
+/*
+ * Array of time formats.
+ */
+
+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
+};
+
+/* main routine. */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ long time();
+
+ char *next;
+ char string[MAXTIME];
+
+ int k;
+ int length;
+
+ struct tm *tm;
+
+ long clock;
+
+ /* Call the function. */
+
+ clock = time((long *) 0);
+ tm = localtime(&clock);
+
+ for (k = 0; next = array[k]; k++) {
+ length = strftime(string, MAXTIME, next, tm);
+ printf("%s\n", string);
+ }
+
+ exit(0);
+}
+#endif /* TEST_STRFTIME */
diff --git a/missing/strstr.c b/missing/strstr.c
new file mode 100644
index 0000000000..ff28ebffd6
--- /dev/null
+++ b/missing/strstr.c
@@ -0,0 +1,73 @@
+/*
+ * strstr.c --
+ *
+ * Source code for the "strstr" library routine.
+ *
+ * Copyright 1988-1991 Regents of the University of California
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appears in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Header: /work/cvsroot/ruby/missing/strstr.c,v 1.1 1994/06/27 15:49:21 matz Exp $ SPRITE (Berkeley)";
+#endif /* not lint */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strstr --
+ *
+ * Locate the first instance of a substring in a string.
+ *
+ * Results:
+ * If string contains substring, the return value is the
+ * location of the first matching instance of substring
+ * in string. If string doesn't contain substring, the
+ * return value is 0. Matching is done on an exact
+ * character-for-character basis with no wildcards or special
+ * characters.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+strstr(string, substring)
+ register char *string; /* String to search. */
+ char *substring; /* Substring to try to find in string. */
+{
+ register char *a, *b;
+
+ /* First scan quickly through the two strings looking for a
+ * single-character match. When it's found, then compare the
+ * rest of the substring.
+ */
+
+ b = substring;
+ if (*b == 0) {
+ return string;
+ }
+ for ( ; *string != 0; string += 1) {
+ if (*string != *b) {
+ continue;
+ }
+ a = string;
+ while (1) {
+ if (*b == 0) {
+ return string;
+ }
+ if (*a++ != *b++) {
+ break;
+ }
+ }
+ b = substring;
+ }
+ return (char *) 0;
+}
diff --git a/missing/strtol.c b/missing/strtol.c
new file mode 100644
index 0000000000..4941f43b91
--- /dev/null
+++ b/missing/strtol.c
@@ -0,0 +1,84 @@
+/*
+ * strtol.c --
+ *
+ * Source code for the "strtol" library procedure.
+ *
+ * Copyright 1988 Regents of the University of California
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#include <ctype.h>
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strtol --
+ *
+ * Convert an ASCII string into an integer.
+ *
+ * Results:
+ * The return value is the integer equivalent of string. If endPtr
+ * is non-NULL, then *endPtr is filled in with the character
+ * after the last one that was part of the integer. If string
+ * doesn't contain a valid integer value, then zero is returned
+ * and *endPtr is set to string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+long int
+strtol(string, endPtr, base)
+ char *string; /* String of ASCII digits, possibly
+ * preceded by white space. For bases
+ * greater than 10, either lower- or
+ * upper-case digits may be used.
+ */
+ char **endPtr; /* Where to store address of terminating
+ * character, or NULL. */
+ int base; /* Base for conversion. Must be less
+ * than 37. If 0, then the base is chosen
+ * from the leading characters of string:
+ * "0x" means hex, "0" means octal, anything
+ * else means decimal.
+ */
+{
+ register char *p;
+ int result;
+
+ /*
+ * Skip any leading blanks.
+ */
+
+ p = string;
+ while (isspace(*p)) {
+ p += 1;
+ }
+
+ /*
+ * Check for a sign.
+ */
+
+ if (*p == '-') {
+ p += 1;
+ result = -(strtoul(p, endPtr, base));
+ } else {
+ if (*p == '+') {
+ p += 1;
+ }
+ result = strtoul(p, endPtr, base);
+ }
+ if ((result == 0) && (endPtr != 0) && (*endPtr == p)) {
+ *endPtr = string;
+ }
+ return result;
+}
diff --git a/missing/strtoul.c b/missing/strtoul.c
new file mode 100644
index 0000000000..f16f2ad9cf
--- /dev/null
+++ b/missing/strtoul.c
@@ -0,0 +1,184 @@
+/*
+ * strtoul.c --
+ *
+ * Source code for the "strtoul" library procedure.
+ *
+ * Copyright 1988 Regents of the University of California
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#include <ctype.h>
+
+/*
+ * The table below is used to convert from ASCII digits to a
+ * numerical equivalent. It maps from '0' through 'z' to integers
+ * (100 for non-digit characters).
+ */
+
+static char cvtIn[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */
+ 100, 100, 100, 100, 100, 100, 100, /* punctuation */
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' - 'Z' */
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35,
+ 100, 100, 100, 100, 100, 100, /* punctuation */
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'a' - 'z' */
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strtoul --
+ *
+ * Convert an ASCII string into an integer.
+ *
+ * Results:
+ * The return value is the integer equivalent of string. If endPtr
+ * is non-NULL, then *endPtr is filled in with the character
+ * after the last one that was part of the integer. If string
+ * doesn't contain a valid integer value, then zero is returned
+ * and *endPtr is set to string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+unsigned long int
+strtoul(string, endPtr, base)
+ char *string; /* String of ASCII digits, possibly
+ * preceded by white space. For bases
+ * greater than 10, either lower- or
+ * upper-case digits may be used.
+ */
+ char **endPtr; /* Where to store address of terminating
+ * character, or NULL. */
+ int base; /* Base for conversion. Must be less
+ * than 37. If 0, then the base is chosen
+ * from the leading characters of string:
+ * "0x" means hex, "0" means octal, anything
+ * else means decimal.
+ */
+{
+ register char *p;
+ register unsigned long int result = 0;
+ register unsigned digit;
+ int anyDigits = 0;
+
+ /*
+ * Skip any leading blanks.
+ */
+
+ p = string;
+ while (isspace(*p)) {
+ p += 1;
+ }
+
+ /*
+ * If no base was provided, pick one from the leading characters
+ * of the string.
+ */
+
+ if (base == 0)
+ {
+ if (*p == '0') {
+ p += 1;
+ if (*p == 'x') {
+ p += 1;
+ base = 16;
+ } else {
+
+ /*
+ * Must set anyDigits here, otherwise "0" produces a
+ * "no digits" error.
+ */
+
+ anyDigits = 1;
+ base = 8;
+ }
+ }
+ else base = 10;
+ } else if (base == 16) {
+
+ /*
+ * Skip a leading "0x" from hex numbers.
+ */
+
+ if ((p[0] == '0') && (p[1] == 'x')) {
+ p += 2;
+ }
+ }
+
+ /*
+ * Sorry this code is so messy, but speed seems important. Do
+ * different things for base 8, 10, 16, and other.
+ */
+
+ if (base == 8) {
+ for ( ; ; p += 1) {
+ digit = *p - '0';
+ if (digit > 7) {
+ break;
+ }
+ result = (result << 3) + digit;
+ anyDigits = 1;
+ }
+ } else if (base == 10) {
+ for ( ; ; p += 1) {
+ digit = *p - '0';
+ if (digit > 9) {
+ break;
+ }
+ result = (10*result) + digit;
+ anyDigits = 1;
+ }
+ } else if (base == 16) {
+ for ( ; ; p += 1) {
+ digit = *p - '0';
+ if (digit > ('z' - '0')) {
+ break;
+ }
+ digit = cvtIn[digit];
+ if (digit > 15) {
+ break;
+ }
+ result = (result << 4) + digit;
+ anyDigits = 1;
+ }
+ } else {
+ for ( ; ; p += 1) {
+ digit = *p - '0';
+ if (digit > ('z' - '0')) {
+ break;
+ }
+ digit = cvtIn[digit];
+ if (digit >= base) {
+ break;
+ }
+ result = result*base + digit;
+ anyDigits = 1;
+ }
+ }
+
+ /*
+ * See if there were any digits at all.
+ */
+
+ if (!anyDigits) {
+ p = string;
+ }
+
+ if (endPtr != 0) {
+ *endPtr = p;
+ }
+
+ return result;
+}
diff --git a/newver.rb b/newver.rb
new file mode 100755
index 0000000000..48ff38954b
--- /dev/null
+++ b/newver.rb
@@ -0,0 +1,14 @@
+#! ./ruby
+f = open("version.h", "r")
+f.gets()
+f.close
+
+if $_ =~ /"(\d+)\.(\d+)"/;
+ f = open("version.h", "w")
+ i = $2.to_i + 1
+ date = Time.now.strftime("%d %b %y")
+ printf("ruby version %d.%0d (%s)\n", $1, i, date)
+ printf(f, "#define RUBY_VERSION \"%d.%0d\"\n", $1, i)
+ printf(f, "#define VERSION_DATE \"%s\"\n", date)
+ f.close
+end
diff --git a/node.h b/node.h
new file mode 100644
index 0000000000..2e4c134214
--- /dev/null
+++ b/node.h
@@ -0,0 +1,216 @@
+/************************************************
+
+ node.h -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Fri May 28 15:14:02 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#ifndef NODE_H
+#define NODE_H
+
+enum node_type {
+ NODE_CFUNC,
+ NODE_SCOPE,
+ NODE_BLOCK,
+ NODE_IF,
+ NODE_CASE,
+ NODE_WHEN,
+ NODE_UNLESS,
+ NODE_WHILE,
+ NODE_UNTIL,
+ NODE_DO,
+ NODE_FOR,
+ NODE_PROT,
+ NODE_AND,
+ NODE_OR,
+ NODE_MASGN,
+ NODE_LASGN,
+ NODE_GASGN,
+ NODE_IASGN,
+ NODE_CASGN,
+ NODE_CALL,
+ NODE_CALL2,
+ NODE_SUPER,
+ NODE_ZSUPER,
+ NODE_ARRAY,
+ NODE_ZARRAY,
+ NODE_HASH,
+ NODE_REDO,
+ NODE_BREAK,
+ NODE_CONTINUE,
+ NODE_RETURN,
+ NODE_RETRY,
+ NODE_YIELD,
+ NODE_LVAR,
+ NODE_GVAR,
+ NODE_IVAR,
+ NODE_MVAR,
+ NODE_CVAR,
+ NODE_CONST,
+ NODE_LIT,
+ NODE_STR,
+ NODE_ARGS,
+ NODE_DEFN,
+ NODE_DEFS,
+ NODE_ALIAS,
+ NODE_UNDEF,
+ NODE_CLASS,
+ NODE_MODULE,
+ NODE_INC,
+ NODE_DOT3,
+ NODE_ATTRSET,
+ NODE_SELF,
+ NODE_NIL,
+};
+
+typedef struct node {
+ enum node_type type;
+ char *src;
+ unsigned int line;
+ union {
+ struct node *node;
+ ID id;
+ VALUE value;
+ VALUE (*cfunc)();
+ ID *tbl;
+ enum mth_scope scope;
+ } u1;
+ union {
+ struct node *node;
+ ID id;
+ int argc;
+ } u2;
+ union {
+ struct node *node;
+ ID id;
+ int state;
+ struct global_entry *entry;
+ int cnt;
+ VALUE value;
+ } u3;
+} NODE;
+
+#define nd_head u1.node
+#define nd_last u2.node
+#define nd_next u3.node
+
+#define nd_cond u1.node
+#define nd_body u2.node
+#define nd_else u3.node
+#define nd_break u3.state
+
+#define nd_resq u2.node
+#define nd_ensr u3.node
+
+#define nd_1st u1.node
+#define nd_2nd u2.node
+
+#define nd_stts u1.node
+
+#define nd_entry u3.entry
+#define nd_vid u1.id
+#define nd_cflag u2.id
+#define nd_cval u3.value
+
+#define nd_cnt u3.cnt
+#define nd_tbl u1.tbl
+
+#define nd_var u1.node
+#define nd_ibdy u2.node
+#define nd_iter u3.node
+
+#define nd_value u2.node
+
+#define nd_lit u1.value
+
+#define nd_frml u1.node
+#define nd_rest u2.argc
+
+#define nd_recv u1.node
+#define nd_mid u2.id
+#define nd_args u3.node
+
+#define nd_scope u1.scope
+#define nd_defn u3.node
+
+#define nd_new u1.id
+#define nd_old u2.id
+
+#define nd_cfnc u1.cfunc
+#define nd_argc u2.argc
+
+#define nd_cname u1.id
+#define nd_super u3.id
+
+#define nd_modl u1.id
+
+#define nd_beg u1.node
+#define nd_end u2.node
+#define nd_state u3.state
+
+#define nd_rval u3.node
+
+#define NEW_DEFN(i,d,m) newnode(NODE_DEFN,m,i,d)
+#define NEW_DEFS(r,i,d) newnode(NODE_DEFS,r,i,d)
+#define NEW_CFUNC(f,c) newnode(NODE_CFUNC,f,c,Qnil)
+#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_BLOCK(a) newnode(NODE_BLOCK,a,Qnil,Qnil)
+#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_CASE(h,b) newnode(NODE_CASE,h,b,Qnil)
+#define NEW_WHEN(c,t,e) newnode(NODE_WHEN,c,t,e)
+#define NEW_WHILE(c,b) newnode(NODE_WHILE,c,b,Qnil)
+#define NEW_UNTIL(c,b) newnode(NODE_UNTIL,c,b,Qnil)
+#define NEW_FOR(v,i,b) newnode(NODE_FOR,v,b,i)
+#define NEW_DO(v,i,b) newnode(NODE_DO,v,b,i)
+#define NEW_PROT(b,ex,en) newnode(NODE_PROT,b,ex,en)
+#define NEW_REDO() newnode(NODE_REDO,Qnil,Qnil,Qnil)
+#define NEW_BREAK() newnode(NODE_BREAK,Qnil,Qnil,Qnil)
+#define NEW_CONT() newnode(NODE_CONTINUE,Qnil,Qnil,Qnil)
+#define NEW_RETRY() newnode(NODE_RETRY,Qnil,Qnil,Qnil)
+#define NEW_RET(s) newnode(NODE_RETURN,s,Qnil,Qnil)
+#define NEW_YIELD(a) newnode(NODE_YIELD,a,Qnil,Qnil)
+#define NEW_LIST(a) NEW_ARRAY(a)
+#define NEW_ARRAY(a) newnode(NODE_ARRAY,a,Qnil,Qnil)
+#define NEW_ZARRAY() newnode(NODE_ZARRAY,Qnil,Qnil,Qnil)
+#define NEW_HASH(a) newnode(NODE_HASH,a,Qnil,Qnil)
+#define NEW_AND(a,b) newnode(NODE_AND,a,b,Qnil)
+#define NEW_OR(a,b) newnode(NODE_OR,a,b,Qnil)
+#define NEW_MASGN(l,val) newnode(NODE_MASGN,l,val,Qnil)
+#define NEW_GASGN(v,val) newnode(NODE_GASGN,v,val,rb_global_entry(v))
+#define NEW_LASGN(v,val) newnode(NODE_LASGN,v,val,local_cnt(v))
+#define NEW_IASGN(v,val) newnode(NODE_IASGN,v,val,Qnil)
+#define NEW_CASGN(v,val) newnode(NODE_CASGN,v,val,Qnil)
+#define NEW_GVAR(v) newnode(NODE_GVAR,v,Qnil,rb_global_entry(v))
+#define NEW_LVAR(v) newnode(NODE_LVAR,v,Qnil,local_cnt(v))
+#define NEW_IVAR(v) newnode(NODE_IVAR,v,Qnil,Qnil)
+#define NEW_MVAR(v) newnode(NODE_MVAR,v,Qnil,Qnil)
+#define NEW_CVAR(v) newnode(NODE_CVAR,v,Qnil,Qnil)
+#define NEW_LIT(l) newnode(NODE_LIT,l,Qnil,Qnil)
+#define NEW_STR(s) newnode(NODE_STR,s,Qnil,Qnil)
+#define NEW_CALL(r,m,a) newnode(NODE_CALL,r,m,a)
+#define NEW_CALL2(r,m,a) newnode(NODE_CALL2,r,m,a)
+#define NEW_SUPER(a) newnode(NODE_SUPER,Qnil,Qnil,a)
+#define NEW_ZSUPER() newnode(NODE_ZSUPER,Qnil,Qnil,Qnil)
+#define NEW_ARGS(f,r) newnode(NODE_ARGS,f,r,Qnil)
+#define NEW_ALIAS(n,o) newnode(NODE_ALIAS,n,o,Qnil)
+#define NEW_UNDEF(i) newnode(NODE_UNDEF,Qnil,i,Qnil)
+#define NEW_CLASS(n,b,s) newnode(NODE_CLASS,n,NEW_SCOPE(b),s)
+#define NEW_MODULE(n,b) newnode(NODE_MODULE,n,NEW_SCOPE(b),Qnil)
+#define NEW_INC(m) newnode(NODE_INC,m,Qnil,Qnil)
+#define NEW_DOT3(b,e) newnode(NODE_DOT3,b,e,0)
+#define NEW_ATTRSET(a) newnode(NODE_ATTRSET,a,Qnil,Qnil)
+#define NEW_SELF() newnode(NODE_SELF,Qnil,Qnil,Qnil)
+#define NEW_NIL() newnode(NODE_NIL,Qnil,Qnil,Qnil)
+
+NODE *newnode();
+NODE *rb_get_method_body();
+void freenode();
+
+#endif
diff --git a/numeric.c b/numeric.c
new file mode 100644
index 0000000000..34d82134b4
--- /dev/null
+++ b/numeric.c
@@ -0,0 +1,962 @@
+/************************************************
+
+ numeric.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:32 $
+ created at: Fri Aug 13 18:33:09 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include <math.h>
+
+static ID coerce;
+static ID to_i;
+
+VALUE C_Numeric;
+VALUE C_Float;
+VALUE C_Integer;
+VALUE C_Fixnum;
+
+extern VALUE C_Range;
+double big2dbl();
+
+static
+num_coerce_bin(this, other)
+ VALUE this, other;
+{
+ return rb_funcall(rb_funcall(other, coerce, 1, this),
+ the_env->last_func, 1, other);
+}
+
+static VALUE
+Fnum_uplus(num)
+ VALUE num;
+{
+ return num;
+}
+
+static VALUE
+Fnum_uminus(num)
+ VALUE num;
+{
+ return rb_funcall(rb_funcall(num, coerce, 1, INT2FIX(0)), '-', 1, num);
+}
+
+static VALUE
+Fnum_dot2(left, right)
+ VALUE left, right;
+{
+ Need_Fixnum(left);
+ Need_Fixnum(right);
+ return range_new(C_Range, left, right);
+}
+
+static VALUE
+Fnum_upto(from, to)
+ VALUE from, to;
+{
+ int i, end;
+
+ end = NUM2INT(to);
+ for (i = NUM2INT(from); i <= end; i++) {
+ rb_yield(INT2FIX(i));
+ }
+
+ return from;
+}
+
+static VALUE
+Fnum_downto(from, to)
+ VALUE from, to;
+{
+ int i, end;
+
+ end = NUM2INT(to);
+ for (i=NUM2INT(from); i >= end; i--) {
+ rb_yield(INT2FIX(i));
+ }
+
+ return from;
+}
+
+static VALUE
+Fnum_step(from, to, step)
+ VALUE from, to;
+{
+ 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;
+}
+
+static VALUE
+Fnum_dotimes(num)
+ VALUE num;
+{
+ int i, end;
+
+ end = NUM2INT(num);
+ for (i=0; i<end; i++) {
+ rb_yield(INT2FIX(i));
+ }
+ return num;
+}
+
+static VALUE
+Fnum_divmod(x, y)
+ VALUE x, y;
+{
+ VALUE div, mod;
+
+ GC_LINK;
+ GC_PRO3(div, rb_funcall(x, '/', 1, y));
+ GC_PRO3(mod, rb_funcall(x, '%', 1, y));
+ GC_UNLINK;
+
+ return assoc_new(div, mod);
+}
+
+static VALUE
+Fnum_is_int(num)
+ VALUE num;
+{
+ return FALSE;
+}
+
+VALUE
+float_new(flt)
+ double flt;
+{
+ NEWOBJ(flo, struct RFloat);
+ OBJSETUP(flo, C_Float, T_FLOAT);
+
+ flo->value = flt;
+ return (VALUE)flo;
+}
+
+static VALUE
+Fflo_new(flo)
+ struct RFloat *flo;
+{
+ Check_Type(flo, T_FLOAT);
+ {
+ NEWOBJ(flo2, struct RFloat);
+ CLONESETUP(flo2, flo);
+
+ flo2->value = flo->value;
+ return (VALUE)flo2;
+ }
+}
+
+static VALUE
+Fflo_to_s(flt)
+ struct RFloat *flt;
+{
+ char buf[32];
+
+ sprintf(buf, "%g", flt->value);
+
+ return str_new2(buf);
+}
+
+static VALUE
+Fflo_coerce(this, other)
+ VALUE this, other;
+{
+ switch (TYPE(other)) {
+ case T_FIXNUM:
+ return float_new((double)FIX2INT(other));
+ case T_FLOAT:
+ return other;
+ case T_BIGNUM:
+ return Fbig_to_f(other);
+ default:
+ Fail("can't coerce %s to Float", rb_class2name(CLASS_OF(other)));
+ }
+ /* not reached */
+ return Qnil;
+}
+
+static VALUE
+Fflo_uminus(flt)
+ struct RFloat *flt;
+{
+ return float_new(-flt->value);
+}
+
+static VALUE
+Fflo_plus(x, y)
+ struct RFloat *x, *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return float_new(x->value + (double)FIX2INT(y));
+ case T_BIGNUM:
+ return float_new(x->value + big2dbl(y));
+ case T_FLOAT:
+ return float_new(x->value + y->value);
+ case T_STRING:
+ return Fstr_plus(obj_as_string(x), y);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_minus(x, y)
+ struct RFloat *x, *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return float_new(x->value - (double)FIX2INT(y));
+ case T_BIGNUM:
+ return float_new(x->value - big2dbl(y));
+ case T_FLOAT:
+ return float_new(x->value - y->value);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_mul(x, y)
+ struct RFloat *x, *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return float_new(x->value * (double)FIX2INT(y));
+ case T_BIGNUM:
+ return float_new(x->value * big2dbl(y));
+ case T_FLOAT:
+ return float_new(x->value * y->value);
+ case T_STRING:
+ return Fstr_times(y, INT2FIX((int)x->value));
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_div(x, y)
+ struct RFloat *x, *y;
+{
+ int f_y;
+ double d;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ f_y = FIX2INT(y);
+ if (f_y == 0) Fail("devided by 0");
+ 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);
+ case T_FLOAT:
+ if (y->value == 0.0) Fail("devided by 0");
+ return float_new(x->value / y->value);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_mod(x, y)
+ struct RFloat *x, *y;
+{
+ double value;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ value = (double)FIX2INT(y);
+ break;
+ case T_BIGNUM:
+ value = big2dbl(y);
+ break;
+ case T_FLOAT:
+ value = y->value;
+ break;
+ default:
+ return num_coerce_bin(x, y);
+ }
+#ifdef HAVE_FMOD
+ {
+ value = fmod(x->value, value);
+ }
+#else
+ {
+ double value1 = x->value;
+ double value2;
+
+ modf(value1/value, &value2);
+ value = value1 - value2 * value;
+ }
+#endif
+
+ return float_new(value);
+}
+
+Fflo_pow(x, y)
+ struct RFloat *x, *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return float_new(pow(x->value, (double)FIX2INT(y)));
+ case T_BIGNUM:
+ return float_new(pow(x->value, big2dbl(y)));
+ case T_FLOAT:
+ return float_new(pow(x->value, y->value));
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_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));
+ case T_FLOAT:
+ return (x->value == y->value)?TRUE:FALSE;
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Fflo_hash(num)
+ struct RFloat *num;
+{
+ double d;
+ char *c;
+ int i, hash;
+
+ d = num->value;
+ c = (char*)&d;
+ for (hash=0, i=0; i<sizeof(double);i++) {
+ hash += c[i] * 971;
+ }
+ if (hash < 0) hash = -hash;
+ return INT2FIX(hash);
+}
+
+static VALUE
+Fflo_cmp(x, y)
+ struct RFloat *x, *y;
+{
+ double a, b;
+
+ a = x->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2INT(y);
+ break;
+
+ case T_BIGNUM:
+ b = big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = y->value;
+ break;
+
+ default:
+ return num_coerce_bin(x, y);
+ }
+ if (a == b) return INT2FIX(0);
+ if (a > b) return INT2FIX(1);
+ return INT2FIX(-1);
+}
+
+static VALUE
+Fflo_to_i(num)
+ struct RFloat *num;
+{
+ double f = num->value;
+ int val;
+
+ if (!FIXABLE(f)) {
+ return dbl2big(f);
+ }
+ val = f;
+ return INT2FIX(val);
+}
+
+static VALUE
+Fflo_to_f(num)
+ VALUE num;
+{
+ return num;
+}
+
+static VALUE
+Fflo_clone(flt)
+ struct RFloat *flt;
+{
+ VALUE flt2 = float_new(flt->value);
+ CLONESETUP(flt2, flt);
+ return flt2;
+}
+
+static VALUE
+Fflo_abs(flt)
+ struct RFloat *flt;
+{
+ double val = fabs(flt->value);
+ return float_new(val);
+}
+
+int
+num2int(val)
+ VALUE val;
+{
+ int result;
+
+ if (val == Qnil) return 0;
+
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ result = FIX2INT(val);
+ break;
+
+ case T_FLOAT:
+ if (RFLOAT(val)->value <= (double) LONG_MAX
+ && RFLOAT(val)->value >= (double) LONG_MIN) {
+ result = (int)RFLOAT(val)->value;
+ }
+ else {
+ Fail("float %g out of rang of integer", RFLOAT(val)->value);
+ }
+ break;
+
+ case T_BIGNUM:
+ return big2int(val);
+
+ default:
+ Fail("failed to convert %s into int", rb_class2name(CLASS_OF(val)));
+ break;
+ }
+ return result;
+}
+
+static VALUE
+to_fixnum(val)
+ VALUE val;
+{
+ return rb_funcall(val, to_i, 0);
+}
+
+static VALUE
+fail_to_fixnum(val)
+ VALUE val;
+{
+ Fail("failed to convert %s into fixnum", rb_class2name(CLASS_OF(val)));
+}
+
+VALUE
+num2fix(val)
+ VALUE val;
+{
+ int v;
+
+ if (val == Qnil) return INT2FIX(0);
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ return val;
+
+ case T_FLOAT:
+ case T_BIGNUM:
+ v = num2int(val);
+ if (!FIXABLE(v))
+ Fail("integer %d out of rang of Fixnum", v);
+ return INT2FIX(v);
+
+ default:
+ return rb_resque(to_fixnum, val, fail_to_fixnum, val);
+ }
+}
+
+static VALUE
+Fint_is_int(num)
+ VALUE num;
+{
+ return TRUE;
+}
+
+static VALUE
+Fint_chr(num)
+ VALUE num;
+{
+ char c;
+ int i = NUM2INT(num);
+
+ if (i < 0 || 0xff < i)
+ Fail("%d out of char range", i);
+ c = i;
+ return str_new(&c, 1);
+}
+
+VALUE
+Ffix_clone(num)
+ VALUE num;
+{
+ return num;
+}
+
+static VALUE
+Ffix_uminus(num)
+ VALUE num;
+{
+ return int2inum(-FIX2INT(num));
+}
+
+VALUE
+fix2str(x, base)
+ VALUE x;
+ int base;
+{
+ char fmt[3], buf[12];
+
+ fmt[0] = '%'; fmt[2] = '\0';
+ 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);
+
+ sprintf(buf, fmt, FIX2INT(x));
+ return str_new2(buf);
+}
+
+VALUE
+Ffix_to_s(in)
+ VALUE in;
+{
+ return fix2str(in, 10);
+}
+
+static VALUE
+Ffix_plus(x, y)
+ VALUE x;
+ struct RFloat *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ {
+ int a, b, c;
+ VALUE r;
+
+ a = FIX2INT(x);
+ b = FIX2INT(y);
+ c = a + b;
+ r = INT2FIX(c);
+
+ if (FIX2INT(r) != c) {
+ VALUE big1, big2;
+ GC_LINK;
+ GC_PRO3(big1, int2big(a));
+ GC_PRO3(big2, int2big(b));
+ r = Fbig_plus(big1, big2);
+ GC_UNLINK;
+ }
+ return r;
+ }
+ case T_FLOAT:
+ return float_new((double)FIX2INT(x) + y->value);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_minus(x, y)
+ VALUE x;
+ struct RFloat *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ {
+ int a, b, c;
+ VALUE r;
+
+ a = FIX2INT(x);
+ b = FIX2INT(y);
+ c = a - b;
+ r = INT2FIX(c);
+
+ if (FIX2INT(r) != c) {
+ VALUE big1, big2;
+ GC_LINK;
+ GC_PRO3(big1, int2big(a));
+ GC_PRO3(big2, int2big(b));
+ r = Fbig_minus(big1, big2);
+ GC_UNLINK;
+ }
+ return r;
+ }
+ case T_FLOAT:
+ return float_new((double)FIX2INT(x) - y->value);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_mul(x, y)
+ VALUE x;
+ struct RFloat *y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ {
+ int a = FIX2INT(x), b = FIX2INT(y);
+ int c = a * b;
+ VALUE r = INT2FIX(c);
+
+ if (FIX2INT(r) != c) {
+ VALUE big1, big2;
+ GC_LINK;
+ GC_PRO3(big1, int2big(a));
+ GC_PRO3(big2, int2big(b));
+ r = Fbig_mul(big1, big2);
+ GC_UNLINK;
+ }
+ return r;
+ }
+ case T_FLOAT:
+ return float_new((double)FIX2INT(x) * y->value);
+ default:
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_div(x, y)
+ VALUE x;
+ struct RFloat *y;
+{
+ int i;
+
+ if (TYPE(y) == T_FIXNUM) {
+ i = FIX2INT(y);
+ if (i == 0) Fail("devided by 0");
+ i = FIX2INT(x)/i;
+ return INT2FIX(i);
+ }
+ return num_coerce_bin(x, y);
+}
+
+static VALUE
+Ffix_mod(x, y)
+ VALUE x, y;
+{
+ int mod, i;
+
+ if (TYPE(y) == T_FIXNUM) {
+ i = FIX2INT(y);
+ if (i == 0) Fail("devided by 0");
+ i = FIX2INT(x)%i;
+ return INT2FIX(i);
+ }
+ return num_coerce_bin(x, y);
+}
+
+static VALUE
+Ffix_pow(x, y)
+ VALUE x, y;
+{
+ extern double pow();
+ int result;
+
+ if (FIXNUM_P(y)) {
+ result = pow((double)FIX2INT(x), (double)FIX2INT(y));
+ return int2inum(result);
+ }
+ else if (NIL_P(y)) {
+ return INT2FIX(1);
+ }
+ else {
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_equal(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ return (FIX2INT(x) == FIX2INT(y))?TRUE:FALSE;
+ }
+ else if (NIL_P(y)) {
+ return Qnil;
+ }
+ else {
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_cmp(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ int a = FIX2INT(x), b = FIX2INT(y);
+
+ if (a == b) return INT2FIX(0);
+ if (a > b) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+ else {
+ return num_coerce_bin(x, y);
+ }
+}
+
+static VALUE
+Ffix_dot2(left, right)
+ VALUE left, right;
+{
+ Need_Fixnum(right);
+ return range_new(C_Range, left, right);
+}
+
+static VALUE
+Ffix_rev(num)
+ VALUE num;
+{
+ unsigned long val = FIX2UINT(num);
+
+ val = ~val;
+ return INT2FIX(val);
+}
+
+static VALUE
+Ffix_and(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (TYPE(y) == T_BIGNUM) {
+ return Fbig_and(y, x);
+ }
+ val = NUM2INT(x) & NUM2INT(y);
+ return int2inum(val);
+}
+
+static VALUE
+Ffix_or(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (TYPE(y) == T_BIGNUM) {
+ return Fbig_or(y, x);
+ }
+ val = NUM2INT(x) | NUM2INT(y);
+ return INT2FIX(val);
+}
+
+static VALUE
+Ffix_xor(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (TYPE(y) == T_BIGNUM) {
+ return Fbig_xor(y, x);
+ }
+ val = NUM2INT(x) ^ NUM2INT(y);
+ return INT2FIX(val);
+}
+
+static VALUE
+Ffix_lshift(x, y)
+ VALUE x, y;
+{
+ long val, width;
+
+ val = NUM2INT(x);
+ width = NUM2INT(y);
+ if (width > (sizeof(VALUE)*CHAR_BIT-1)
+ || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-width) > 0) {
+ VALUE big;
+ GC_LINK;
+ GC_PRO3(big, int2big(val));
+ big = Fbig_lshift(big, y);
+ GC_UNLINK;
+ return big;
+ }
+ val = val << width;
+ return int2inum(val);
+}
+
+static VALUE
+Ffix_rshift(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ val = RSHIFT(NUM2INT(x), NUM2INT(y));
+ return INT2FIX(val);
+}
+
+static VALUE
+Ffix_aref(fix, idx)
+ VALUE fix, idx;
+{
+ unsigned long val = FIX2INT(fix);
+ int i = FIX2INT(idx);
+
+ if (i < 0 || sizeof(VALUE)*CHAR_BIT-1 < i)
+ return INT2FIX(0);
+ if (val & (1<<i))
+ return INT2FIX(1);
+ return INT2FIX(0);
+}
+
+static VALUE
+Ffix_to_i(num)
+ VALUE num;
+{
+ return num;
+}
+
+static VALUE
+Ffix_to_f(num)
+ VALUE num;
+{
+ double val;
+
+ val = (double)FIX2INT(num);
+
+ return float_new(val);
+}
+
+static VALUE
+Ffix_class(fix)
+ VALUE fix;
+{
+ return C_Fixnum;
+}
+
+static Ffix_abs(fix)
+ VALUE fix;
+{
+ int i = FIX2INT(fix);
+
+ if (fix < 0) i = -i;
+
+ return int2inum(fix);
+}
+
+static VALUE
+Ffix_id2name(fix)
+ VALUE fix;
+{
+ char *name = rb_id2name(FIX2UINT(fix));
+ if (name) return str_new2(name);
+ return Qnil;
+}
+
+extern VALUE M_Comparable;
+extern Fkrn_inspect();
+
+Init_Numeric()
+{
+ coerce = rb_intern("coerce");
+ to_i = rb_intern("to_i");
+
+ C_Numeric = rb_define_class("Numeric", C_Object);
+ rb_include_module(C_Numeric, M_Comparable);
+ rb_define_method(C_Numeric, "+@", Fnum_uplus, 0);
+ rb_define_method(C_Numeric, "-@", Fnum_uminus, 0);
+ rb_define_method(C_Numeric, "..", Fnum_dot2, 1);
+
+ rb_define_method(C_Numeric, "upto", Fnum_upto, 1);
+ rb_define_method(C_Numeric, "downto", Fnum_downto, 1);
+ rb_define_method(C_Numeric, "step", Fnum_step, 2);
+ rb_define_method(C_Numeric, "times", Fnum_dotimes, 0);
+ rb_define_method(C_Numeric, "is_integer", Fnum_is_int, 0);
+ rb_define_method(C_Numeric, "_inspect", Fkrn_inspect, 0);
+
+ C_Integer = rb_define_class("Integer", C_Numeric);
+ rb_define_method(C_Integer, "is_integer", Fint_is_int, 0);
+ rb_define_method(C_Integer, "chr", Fint_chr, 0);
+
+ C_Fixnum = rb_define_class("Fixnum", C_Integer);
+ rb_define_method(C_Fixnum, "to_s", Ffix_to_s, 0);
+ rb_define_method(C_Fixnum, "class", Ffix_class, 0);
+ rb_define_method(C_Fixnum, "clone", Ffix_clone, 0);
+
+ rb_define_method(C_Fixnum, "id2name", Ffix_id2name, 0);
+
+ rb_define_method(C_Fixnum, "-@", Ffix_uminus, 0);
+ rb_define_method(C_Fixnum, "+", Ffix_plus, 1);
+ rb_define_method(C_Fixnum, "-", Ffix_minus, 1);
+ rb_define_method(C_Fixnum, "*", Ffix_mul, 1);
+ rb_define_method(C_Fixnum, "/", Ffix_div, 1);
+ rb_define_method(C_Fixnum, "%", Ffix_mod, 1);
+ rb_define_method(C_Fixnum, "**", Ffix_pow, 1);
+
+ rb_define_method(C_Fixnum, "abs", Ffix_abs, 0);
+
+ rb_define_method(C_Fixnum, "==", Ffix_equal, 1);
+ rb_define_method(C_Fixnum, "<=>", Ffix_cmp, 1);
+ rb_define_method(C_Fixnum, "..", Ffix_dot2, 1);
+
+ rb_define_method(C_Fixnum, "~", Ffix_rev, 0);
+ rb_define_method(C_Fixnum, "&", Ffix_and, 1);
+ rb_define_method(C_Fixnum, "|", Ffix_or, 1);
+ rb_define_method(C_Fixnum, "^", Ffix_xor, 1);
+ rb_define_method(C_Fixnum, "[]", Ffix_aref, 1);
+
+ rb_define_method(C_Fixnum, "<<", Ffix_lshift, 1);
+ rb_define_method(C_Fixnum, ">>", Ffix_rshift, 1);
+
+ rb_define_method(C_Fixnum, "to_i", Ffix_to_i, 0);
+ rb_define_method(C_Fixnum, "to_f", Ffix_to_f, 0);
+
+ C_Float = rb_define_class("Float", C_Numeric);
+ rb_define_single_method(C_Float, "new", Fflo_new, 1);
+ rb_define_method(C_Float, "clone", Fflo_clone, 0);
+ rb_define_method(C_Float, "to_s", Fflo_to_s, 0);
+ rb_define_method(C_Float, "coerce", Fflo_coerce, 1);
+ rb_define_method(C_Float, "-@", Fflo_uminus, 0);
+ rb_define_method(C_Float, "+", Fflo_plus, 1);
+ rb_define_method(C_Float, "-", Fflo_minus, 1);
+ rb_define_method(C_Float, "*", Fflo_mul, 1);
+ rb_define_method(C_Float, "/", Fflo_div, 1);
+ rb_define_method(C_Float, "%", Fflo_mod, 1);
+ rb_define_method(C_Float, "**", Fflo_pow, 1);
+ rb_define_method(C_Float, "==", Fflo_eq, 1);
+ rb_define_method(C_Float, "<=>", Fflo_cmp, 1);
+ rb_define_method(C_Float, "hash", Fflo_hash, 0);
+ rb_define_method(C_Float, "to_i", Fflo_to_i, 0);
+ rb_define_method(C_Float, "to_f", Fflo_to_f, 0);
+ rb_define_method(C_Float, "abs", Fflo_abs, 0);
+}
diff --git a/object.c b/object.c
new file mode 100644
index 0000000000..fd00c2f618
--- /dev/null
+++ b/object.c
@@ -0,0 +1,458 @@
+/************************************************
+
+ object.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Thu Jul 15 12:01:24 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "st.h"
+#include <stdio.h>
+
+VALUE C_Kernel;
+VALUE C_Object;
+VALUE C_Module;
+VALUE C_Class;
+VALUE C_Nil;
+VALUE C_Data;
+VALUE C_Method;
+
+struct st_table *new_idhash();
+
+VALUE Fsprintf();
+VALUE Ffail();
+VALUE Fexit();
+VALUE Feval();
+VALUE Fapply();
+VALUE Fdefined();
+VALUE Fcaller();
+
+VALUE obj_responds_to();
+VALUE obj_alloc();
+VALUE Ffix_clone();
+
+static ID eq, match;
+
+static VALUE
+P_true(obj)
+ VALUE obj;
+{
+ return TRUE;
+}
+
+static VALUE
+P_false(obj)
+ VALUE obj;
+{
+ return FALSE;
+}
+
+static VALUE
+Fkrn_equal(obj, other)
+ VALUE obj, other;
+{
+ if (obj == other) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Fkrn_hash(obj)
+ VALUE obj;
+{
+ return obj;
+}
+
+static VALUE
+Fkrn_to_a(obj)
+ VALUE obj;
+{
+ return ary_new3(1, obj);
+}
+
+static VALUE
+Fkrn_id(obj)
+ VALUE obj;
+{
+ return obj | FIXNUM_FLAG;
+}
+
+static VALUE
+Fkrn_noteq(obj, other)
+ VALUE obj, other;
+{
+ if (rb_funcall(obj, eq, 1, other)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static VALUE
+Fkrn_nmatch(obj, other)
+ VALUE obj, other;
+{
+ if (rb_funcall(obj, match, 1, other)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static VALUE
+Fkrn_class(obj)
+ struct RBasic *obj;
+{
+ return obj->class;
+}
+
+VALUE
+Fkrn_to_s(obj)
+ VALUE obj;
+{
+ char buf[256];
+
+ sprintf(buf, "#<%s: 0x%x>", rb_class2name(CLASS_OF(obj)), obj);
+ return str_new2(buf);
+}
+
+VALUE
+Fkrn_inspect(obj)
+ VALUE obj;
+{
+ return rb_funcall(obj, rb_intern("to_s"), 0, Qnil);
+}
+
+static
+obj_inspect(id, value, str)
+ ID id;
+ VALUE value;
+ struct RString *str;
+{
+ VALUE str2;
+ char *ivname;
+
+ if (str->ptr[0] == '-') {
+ str->ptr[0] = '#';
+ }
+ else {
+ str_cat(str, ", ", 2);
+ }
+ GC_LINK;
+ ivname = rb_id2name(id);
+ str_cat(str, ivname, strlen(ivname));
+ str_cat(str, "=", 1);
+ GC_PRO3(str2, rb_funcall(value, rb_intern("_inspect"), 0, Qnil));
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ GC_UNLINK;
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+Fobj_inspect(obj)
+ struct RBasic *obj;
+{
+ VALUE str;
+ char buf[256];
+
+ if (FIXNUM_P(obj) || !obj->iv_tbl) return Fkrn_to_s(obj);
+
+ GC_LINK;
+ sprintf(buf, "-<%s: ", rb_class2name(CLASS_OF(obj)));
+ GC_PRO3(str, str_new2(buf));
+ st_foreach(obj->iv_tbl, obj_inspect, str);
+ str_cat(str, ">", 1);
+ GC_UNLINK;
+
+ return str;
+}
+
+VALUE
+obj_is_member_of(obj, c)
+ VALUE obj, c;
+{
+ struct RClass *class = (struct RClass*)CLASS_OF(obj);
+
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ Fail("class or module required");
+ }
+
+ while (FL_TEST(class, FL_SINGLE)) {
+ class = class->super;
+ }
+ if (c == (VALUE)class) return TRUE;
+ return FALSE;
+}
+
+VALUE
+obj_is_kind_of(obj, c)
+ VALUE obj, c;
+{
+ struct RClass *class = (struct RClass*)CLASS_OF(obj);
+
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ Fail("class or module required");
+ }
+
+ while (class) {
+ if ((VALUE)class == c || RCLASS(class)->m_tbl == RCLASS(c)->m_tbl)
+ return TRUE;
+ class = class->super;
+ }
+ return FALSE;
+}
+
+static VALUE
+Fobj_clone(obj)
+ VALUE obj;
+{
+ VALUE clone;
+
+ Check_Type(obj, T_OBJECT);
+
+ clone = obj_alloc(RBASIC(obj)->class);
+ GC_LINK;
+ GC_PRO(clone);
+ if (RBASIC(obj)->iv_tbl) {
+ RBASIC(clone)->iv_tbl = st_copy(RBASIC(obj)->iv_tbl);
+ }
+ RBASIC(clone)->class = single_class_clone(RBASIC(obj)->class);
+ GC_UNLINK;
+
+ return clone;
+}
+
+static VALUE
+Fiterator_p()
+{
+ if (the_env->iterator > 1 && the_env->iterator < 4) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+Fnil_to_s(obj)
+ VALUE obj;
+{
+ return str_new2("nil");
+}
+
+static VALUE
+Fnil_class(nil)
+ VALUE nil;
+{
+ return C_Nil;
+}
+
+static VALUE
+Fnil_plus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ case T_FLOAT:
+ case T_STRING:
+ 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)));
+ }
+ return Qnil; /* not reached */
+}
+
+static VALUE
+Fmain_to_s(obj)
+ VALUE obj;
+{
+ return str_new2("main");
+}
+
+VALUE
+obj_alloc(class)
+ VALUE class;
+{
+ NEWOBJ(obj, struct RObject);
+ OBJSETUP(obj, class, T_OBJECT);
+
+ return (VALUE)obj;
+}
+
+static VALUE
+Fcls_new(class, args)
+ VALUE class, args;
+{
+ return obj_alloc(class);
+}
+
+static VALUE
+Fcls_to_s(class)
+ VALUE class;
+{
+ return str_new2(rb_class2name(class));
+}
+
+static VALUE
+Fcls_attr(class, args)
+ VALUE class, args;
+{
+ VALUE name, pub;
+
+ rb_scan_args(args, "11", &name, &pub);
+ Check_Type(name, T_STRING);
+ rb_define_attr(class, RSTRING(name)->ptr, pub);
+ return Qnil;
+}
+
+static VALUE
+Fcant_clone(obj)
+ VALUE obj;
+{
+ Fail("can't clone %s", rb_class2name(CLASS_OF(obj)));
+}
+
+static VALUE
+Fdata_class(data)
+ VALUE data;
+{
+ return C_Data;
+}
+
+static VALUE boot_defclass(name, super)
+ char *name;
+ VALUE super;
+{
+ struct RClass *obj = (struct RClass*)class_new(super);
+
+ rb_name_class(obj, rb_intern(name));
+ return (VALUE)obj;
+}
+
+VALUE TopSelf;
+
+Init_Object()
+{
+ VALUE metaclass;
+
+ C_Kernel = boot_defclass("Kernel", Qnil);
+ C_Object = boot_defclass("Object", C_Kernel);
+ C_Module = boot_defclass("Module", C_Object);
+ C_Class = boot_defclass("Class", C_Module);
+
+ metaclass = RBASIC(C_Kernel)->class = single_class_new(C_Class);
+ metaclass = RBASIC(C_Object)->class = single_class_new(metaclass);
+ metaclass = RBASIC(C_Module)->class = single_class_new(metaclass);
+ metaclass = RBASIC(C_Class)->class = single_class_new(metaclass);
+
+ /*
+ * +-------nil +---------------------+
+ * | ^ | |
+ * | | | |
+ * | Kernel----->(Kernel) |
+ * | ^ ^ ^ ^ |
+ * | | | | | |
+ * | +---+ +-----+ | +---+ |
+ * | | +------|---+ | |
+ * | | | | | |
+ * +->Nil->(Nil) Object---->(Object) |
+ * ^ ^ ^ ^ |
+ * | | | | |
+ * | | +-------+ | |
+ * | | | | |
+ * | +---------+ +------+ |
+ * | | | | |
+ * +--------+ | Module--->(Module) |
+ * | | ^ ^ |
+ * OtherClass-->(OtherClass) | | |
+ * Class---->(Class) |
+ * ^ |
+ * | |
+ * +-----+
+ *
+ * + all metaclasses are instance of class Class
+ */
+
+ rb_define_method(C_Kernel, "is_nil", P_false, 0);
+ rb_define_method(C_Kernel, "!", P_false, 0);
+ rb_define_method(C_Kernel, "==", Fkrn_equal, 1);
+ rb_define_alias(C_Kernel, "equal", "==");
+ rb_define_method(C_Kernel, "hash", Fkrn_hash, 0);
+ rb_define_method(C_Kernel, "id", Fkrn_id, 0);
+ rb_define_method(C_Kernel, "class", Fkrn_class, 0);
+ rb_define_method(C_Kernel, "!=", Fkrn_noteq, 1);
+ rb_define_alias(C_Kernel, "=~", "==");
+ rb_define_method(C_Kernel, "!~", Fkrn_nmatch, 1);
+
+ rb_define_method(C_Kernel, "to_a", Fkrn_to_a, 0);
+ rb_define_method(C_Kernel, "to_s", Fkrn_to_s, 0);
+ rb_define_method(C_Kernel, "_inspect", Fkrn_inspect, 0);
+
+ rb_define_func(C_Kernel, "caller", Fcaller, -2);
+ rb_define_func(C_Kernel, "fail", Ffail, -2);
+ rb_define_func(C_Kernel, "exit", Fexit, -2);
+ rb_define_func(C_Kernel, "eval", Feval, 1);
+ rb_define_func(C_Kernel, "defined", Fdefined, 1);
+ rb_define_func(C_Kernel, "sprintf", Fsprintf, -1);
+ rb_define_alias(C_Kernel, "format", "sprintf");
+ rb_define_func(C_Kernel, "iterator_p", Fiterator_p, 0);
+
+ rb_define_method(C_Kernel, "apply", Fapply, -2);
+
+ rb_define_const(C_Kernel, "%TRUE", TRUE);
+ rb_define_const(C_Kernel, "%FALSE", FALSE);
+
+ rb_define_method(C_Object, "_inspect", Fobj_inspect, 0);
+
+ rb_define_method(C_Object, "responds_to", obj_responds_to, 1);
+ rb_define_method(C_Object, "is_member_of", obj_is_member_of, 1);
+ rb_define_method(C_Object, "is_kind_of", obj_is_kind_of, 1);
+ rb_define_method(C_Object, "clone", Fobj_clone, 0);
+
+ rb_define_method(C_Module, "to_s", Fcls_to_s, 0);
+ rb_define_method(C_Module, "clone", Fcant_clone, 0);
+ rb_define_func(C_Module, "attr", Fcls_attr, -2);
+
+ rb_define_method(C_Class, "new", Fcls_new, -2);
+
+ C_Nil = rb_define_class("Nil", C_Kernel);
+ rb_define_method(C_Nil, "to_s", Fnil_to_s, 0);
+ rb_define_method(C_Nil, "clone", Ffix_clone, 0);
+ rb_define_method(C_Nil, "class", Fnil_class, 0);
+
+ rb_define_method(C_Nil, "is_nil", P_true, 0);
+ rb_define_method(C_Nil, "!", P_true, 0);
+
+ /* for compare cascading. */
+ rb_define_method(C_Nil, ">", P_false, 1);
+ rb_define_alias(C_Nil, ">=", ">");
+ rb_define_alias(C_Nil, "<", ">");
+ rb_define_alias(C_Nil, "<=", ">");
+
+ /* default addition */
+ rb_define_method(C_Nil, "+", Fnil_plus, 1);
+
+ C_Data = rb_define_class("Data", C_Kernel);
+ rb_define_method(C_Data, "clone", Fcant_clone, 0);
+ rb_define_method(C_Data, "class", Fdata_class, 0);
+
+ C_Method = rb_define_class("Method", C_Kernel);
+ rb_define_method(C_Data, "clone", Fcant_clone, 0);
+
+ eq = rb_intern("==");
+ match = rb_intern("=~");
+
+ Qself = TopSelf = obj_alloc(C_Object);
+ rb_define_single_method(TopSelf, "to_s", Fmain_to_s, 0);
+}
diff --git a/pack.c b/pack.c
new file mode 100644
index 0000000000..18de67efea
--- /dev/null
+++ b/pack.c
@@ -0,0 +1,849 @@
+/************************************************
+
+ pack.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Thu Feb 10 15:17:05 JST 1994
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include <ctype.h>
+#include <sys/types.h>
+
+#define swaps(x) ((((x)&0xFF)<<8) + (((x)>>8)&0xFF))
+#define swapl(x) ((((x)&0xFF)<<24) \
+ +(((x)>>24)&0xFF) \
+ +(((x)&0x0000FF00)<<8) \
+ +(((x)&0x00FF0000)>>8) )
+#ifdef WORDS_BIGENDIAN
+#define ntohs(x) (x)
+#define ntohl(x) (x)
+#define htons(x) (x)
+#define htonl(x) (x)
+#define htovs(x) swaps(x)
+#define htovl(x) swapl(x)
+#define vtohs(x) swaps(x)
+#define vtohl(x) swapl(x)
+#else /* LITTLE ENDIAN */
+#define ntohs(x) swaps(x)
+#define ntohl(x) swapl(x)
+#define htons(x) swaps(x)
+#define htonl(x) swapl(x)
+#define htovs(x) (x)
+#define htovl(x) (x)
+#define vtohs(x) (x)
+#define vtohl(x) (x)
+#endif
+
+extern VALUE C_String, C_Array;
+double atof();
+
+static char *toofew = "too few arguments";
+
+int strtoul();
+static void encodes();
+
+static VALUE
+Fpck_pack(ary, fmt)
+ struct RArray *ary;
+ struct RString *fmt;
+{
+ static char *nul10 = "\0\0\0\0\0\0\0\0\0\0";
+ static char *spc10 = " ";
+ char *p, *pend;
+ VALUE res, from;
+ char type;
+ int items, len, idx;
+ char *ptr;
+ int plen;
+
+ Check_Type(fmt, T_STRING);
+
+ p = fmt->ptr;
+ pend = fmt->ptr + fmt->len;
+ GC_LINK;
+ GC_PRO2(from);
+ GC_PRO3(res, str_new(0, 0));
+
+ items = ary->len;
+ idx = 0;
+
+#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : Fail(toofew))
+
+ while (p < pend) {
+ type = *p++; /* get data type */
+
+ if (*p == '*') { /* set data length */
+ len = index("@Xxu", type) ? 0 : items;
+ p++;
+ }
+ else if (isdigit(*p)) {
+ len = strtoul(p, &p, 10);
+ }
+ else {
+ len = 1;
+ }
+
+ switch (type) {
+ case 'A': case 'a':
+ case 'B': case 'b':
+ case 'H': case 'h':
+ from = NEXTFROM;
+ if (from == Qnil) {
+ ptr = Qnil;
+ plen = 0;
+ }
+ else {
+ from = obj_as_string(from);
+ ptr = RSTRING(from)->ptr;
+ plen = RSTRING(from)->len;
+ }
+
+ if (p[-1] == '*')
+ len = plen;
+
+ 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)
+ str_cat(res, ptr, len);
+ else {
+ str_cat(res, ptr, plen);
+ len == plen;
+ while (len >= 10) {
+ str_cat(res, spc10, 10);
+ len -= 10;
+ }
+ str_cat(res, spc10, len);
+ }
+ break;
+
+ case 'b':
+ {
+ int byte = 0;
+ int i;
+
+ for (i=0; i++ < len; ptr++) {
+ if (*ptr & 1)
+ byte |= 128;
+ if (i & 7)
+ byte >>= 1;
+ else {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte >>= 7 - (len & 7);
+ c = byte & 0xff;
+ str_cat(res, &c, 1);
+ }
+ }
+ break;
+
+ case 'B':
+ {
+ int byte = 0;
+ int i;
+
+ for (i=0; i++ < len; ptr++) {
+ byte |= *ptr & 1;
+ if (i & 7)
+ byte <<= 1;
+ else {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte <<= 7 - (len & 7);
+ c = byte & 0xff;
+ str_cat(res, &c, 1);
+ }
+ }
+ break;
+
+ case 'h':
+ {
+ int byte = 0;
+ int i;
+
+ for (i=0; i++ < len; ptr++) {
+ if (isxdigit(*ptr)) {
+ if (isalpha(*ptr))
+ byte |= (((*ptr & 15) + 9) & 15) << 4;
+ else
+ byte |= (*ptr & 15) << 4;
+ if (i & 1)
+ byte >>= 4;
+ else {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ }
+ if (len & 1) {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ }
+ }
+ break;
+
+ case 'H':
+ {
+ int byte = 0;
+ int i;
+
+ for (i=0; i++ < len; ptr++) {
+ if (isxdigit(*ptr)) {
+ if (isalpha(*ptr))
+ byte |= ((*ptr & 15) + 9) & 15;
+ else
+ byte |= *ptr & 15;
+ if (i & 1)
+ byte <<= 4;
+ else {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ }
+ if (len & 1) {
+ char c = byte & 0xff;
+ str_cat(res, &c, 1);
+ }
+ }
+ break;
+ }
+ break;
+
+ case 'c':
+ case 'C':
+ while (len-- > 0) {
+ char c;
+
+ from = NEXTFROM;
+ if (from == Qnil) c = 0;
+ else {
+ c = NUM2INT(from);
+ }
+ str_cat(res, &c, sizeof(char));
+ }
+ break;
+
+ case 's':
+ case 'S':
+ while (len-- > 0) {
+ short s;
+
+ from = NEXTFROM;
+ if (from == Qnil) s = 0;
+ else {
+ s = NUM2INT(from);
+ }
+ str_cat(res, &s, sizeof(short));
+ }
+ break;
+
+ case 'i':
+ case 'I':
+ while (len-- > 0) {
+ int i;
+
+ from = NEXTFROM;
+ if (from == Qnil) i = 0;
+ else {
+ i = NUM2INT(from);
+ }
+ str_cat(res, &i, sizeof(int));
+ }
+ break;
+
+ case 'l':
+ case 'L':
+ while (len-- > 0) {
+ long l;
+
+ from = NEXTFROM;
+ if (from == Qnil) l = 0;
+ else {
+ l = NUM2INT(from);
+ }
+ str_cat(res, &l, sizeof(long));
+ }
+ break;
+
+ case 'n':
+ while (len-- > 0) {
+ short s;
+
+ from = NEXTFROM;
+ if (from == Qnil) s = 0;
+ else {
+ s = NUM2INT(from);
+ }
+ s = htons(s);
+ str_cat(res, &s, sizeof(short));
+ }
+ break;
+
+ case 'N':
+ while (len-- > 0) {
+ long l;
+
+ from = NEXTFROM;
+ if (from == Qnil) l = 0;
+ else {
+ l = NUM2INT(from);
+ }
+ l = htonl(l);
+ str_cat(res, &l, sizeof(long));
+ }
+ break;
+
+ case 'f':
+ case 'F':
+ while (len-- > 0) {
+ float f;
+
+ from = NEXTFROM;
+ switch (TYPE(from)) {
+ case T_FLOAT:
+ f = RFLOAT(from)->value;
+ break;
+ case T_STRING:
+ f = atof(RSTRING(from)->ptr);
+ default:
+ f = (float)NUM2INT(from);
+ break;
+ }
+ str_cat(res, &f, sizeof(float));
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ while (len-- > 0) {
+ double d;
+
+ from = NEXTFROM;
+ switch (TYPE(from)) {
+ case T_FLOAT:
+ d = RFLOAT(from)->value;
+ break;
+ case T_STRING:
+ d = atof(RSTRING(from)->ptr);
+ default:
+ d = (double)NUM2INT(from);
+ break;
+ }
+ str_cat(res, &d, sizeof(double));
+ }
+ break;
+
+ case 'v':
+ while (len-- > 0) {
+ short s;
+
+ from = NEXTFROM;
+ if (from == Qnil) s = 0;
+ else {
+ s = NUM2INT(from);
+ }
+ s = htovs(s);
+ str_cat(res, &s, sizeof(short));
+ }
+ break;
+
+ case 'V':
+ while (len-- > 0) {
+ long l;
+
+ from = NEXTFROM;
+ if (from == Qnil) l = 0;
+ else {
+ l = NUM2INT(from);
+ }
+ l = htovl(l);
+ str_cat(res, &l, sizeof(long));
+ }
+ break;
+
+ case 'x':
+ grow:
+ while (len >= 10) {
+ str_cat(res, nul10, 10);
+ len -= 10;
+ }
+ str_cat(res, nul10, len);
+ break;
+
+ case 'X':
+ shrink:
+ if (RSTRING(res)->len < len)
+ Fail("X outside of string");
+ RSTRING(res)->len -= len;
+ RSTRING(res)->ptr[RSTRING(res)->len] = '\0';
+ break;
+
+ case '@':
+ len -= RSTRING(res)->len;
+ if (len > 0) goto grow;
+ len = -len;
+ if (len > 0) goto shrink;
+ break;
+
+ case '%':
+ Fail("% may only be used in unpack");
+ break;
+
+ case 'u':
+ from = obj_as_string(NEXTFROM);
+ ptr = RSTRING(from)->ptr;
+ plen = RSTRING(from)->len;
+
+ if (len <= 1)
+ len = 45;
+ else
+ len = len / 3 * 3;
+ while (plen > 0) {
+ int todo;
+
+ if (plen > len)
+ todo = len;
+ else
+ todo = plen;
+ encodes(res, ptr, todo);
+ plen -= todo;
+ ptr += todo;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ GC_UNLINK;
+
+ return res;
+}
+
+static void
+encodes(str, s, len)
+ struct RString *str;
+ char *s;
+ int len;
+{
+ char hunk[4];
+ char *p, *pend;
+
+ p = str->ptr + str->len;
+ *hunk = len + ' ';
+ str_cat(str, hunk, 1);
+ while (len > 0) {
+ hunk[0] = ' ' + (077 & (*s >> 2));
+ hunk[1] = ' ' + (077 & ((*s << 4) & 060 | (s[1] >> 4) & 017));
+ hunk[2] = ' ' + (077 & ((s[1] << 2) & 074 | (s[2] >> 6) & 03));
+ hunk[3] = ' ' + (077 & (s[2] & 077));
+ str_cat(str, hunk, 4);
+ s += 3;
+ len -= 3;
+ }
+ pend = str->ptr + str->len;
+ while (p < pend) {
+ if (*p == ' ')
+ *p = '`';
+ p++;
+ }
+ str_cat(str, "\n", 1);
+}
+
+static VALUE
+Fpck_unpack(str, fmt)
+ struct RString *str, *fmt;
+{
+ static char *hexdigits = "0123456789abcdef0123456789ABCDEFx";
+ char *s, *send;
+ char *p, *pend;
+ VALUE ary;
+ char type;
+ int len;
+
+ Check_Type(fmt, T_STRING);
+
+ s = str->ptr;
+ send = s + str->len;
+ p = fmt->ptr;
+ pend = p + fmt->len;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ while (p < pend) {
+ retry:
+ type = *p++;
+ if (*p == '*') {
+ len = send - s;
+ p++;
+ }
+ else if (isdigit(*p)) {
+ len = strtoul(p, &p, 10);
+ }
+ else {
+ len = (type != '@');
+ }
+
+ switch (type) {
+ case '%':
+ Fail("% is not supported(yet)");
+ break;
+
+ case 'A':
+ if (len > send - s) len = send - s;
+ {
+ char *t = s + len - 1;
+
+ while (t >= s) {
+ if (*t != ' ' && *t != '\0') break;
+ t--;
+ len--;
+ }
+ }
+ case 'a':
+ if (len > send - s) len = send - s;
+ Fary_push(ary, str_new(s, len));
+ s += len;
+ break;
+
+ case 'b':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits, i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ Fary_push(ary, bitstr = str_new(0, len + 1));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 7) bits >>= 1;
+ else bits = *s++;
+ *t++ = (bits & 1) ? '1' : '0';
+ }
+ *t = '\0';
+ }
+ break;
+
+ case 'B':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits, i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ Fary_push(ary, bitstr = str_new(0, len + 1));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 7) bits <<= 1;
+ else bits = *s++;
+ *t++ = (bits & 128) ? '1' : '0';
+ }
+ *t = '\0';
+ }
+ break;
+
+ case 'h':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits, i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ Fary_push(ary, bitstr = str_new(0, len + 1));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits >>= 4;
+ else
+ bits = *s++;
+ *t++ = hexdigits[bits & 15];
+ }
+ *t = '\0';
+ }
+ break;
+
+ case 'H':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits, i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ Fary_push(ary, bitstr = str_new(0, len + 1));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits <<= 4;
+ else
+ bits = *s++;
+ *t++ = hexdigits[(bits >> 4) & 15];
+ }
+ *t = '\0';
+ }
+ break;
+
+ case 'c':
+ if (len > send - s)
+ len = send - s;
+ while (len-- > 0) {
+ char c = *s++;
+ Fary_push(ary, INT2FIX(c));
+ }
+ break;
+
+ case 'C':
+ if (len > send - s)
+ len = send - s;
+ while (len-- > 0) {
+ unsigned char c = *s++;
+ Fary_push(ary, INT2FIX(c));
+ }
+ break;
+
+ case 's':
+ if (len >= (send - s) / sizeof(short))
+ len = (send - s) / sizeof(short);
+ while (len-- > 0) {
+ short tmp;
+ memcpy(&tmp, s, sizeof(short));
+ s += sizeof(short);
+ Fary_push(ary, INT2FIX(tmp));
+ }
+ break;
+
+ case 'S':
+ if (len >= (send - s) / sizeof(unsigned short))
+ len = (send - s) / sizeof(unsigned short);
+ while (len-- > 0) {
+ unsigned short tmp;
+ memcpy(&tmp, s, sizeof(unsigned short));
+ s += sizeof(unsigned short);
+ Fary_push(ary, INT2FIX(tmp));
+ }
+ break;
+
+ case 'i':
+ if (len >= (send - s) / sizeof(int))
+ len = (send - s) / sizeof(int);
+ while (len-- > 0) {
+ int tmp;
+ memcpy(&tmp, s, sizeof(int));
+ s += sizeof(int);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'I':
+ if (len >= (send - s) / sizeof(unsigned int))
+ len = (send - s) / sizeof(unsigned int);
+ while (len-- > 0) {
+ unsigned int tmp;
+ memcpy(&tmp, s, sizeof(unsigned int));
+ s += sizeof(unsigned int);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'l':
+ if (len >= (send - s) / sizeof(long))
+ len = (send - s) / sizeof(long);
+ while (len-- > 0) {
+ long tmp;
+ memcpy(&tmp, s, sizeof(long));
+ s += sizeof(long);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'L':
+ if (len >= (send - s) / sizeof(unsigned long))
+ len = (send - s) / sizeof(unsigned long);
+ while (len-- > 0) {
+ unsigned long tmp;
+ memcpy(&tmp, s, sizeof(unsigned long));
+ s += sizeof(unsigned long);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'n':
+ if (len >= (send - s) / sizeof(short))
+ len = (send - s) / sizeof(short);
+ while (len-- > 0) {
+ short tmp;
+ memcpy(&tmp, s, sizeof(short));
+ s += sizeof(short);
+ tmp = ntohs(tmp);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'N':
+ if (len >= (send - s) / sizeof(long))
+ len = (send - s) / sizeof(long);
+ while (len-- > 0) {
+ long tmp;
+ memcpy(&tmp, s, sizeof(long));
+ s += sizeof(long);
+ tmp = ntohl(tmp);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'F':
+ case 'f':
+ if (len >= (send - s) / sizeof(float))
+ len = (send - s) / sizeof(float);
+ while (len-- > 0) {
+ float tmp;
+ memcpy(&tmp, s, sizeof(float));
+ s += sizeof(float);
+ Fary_push(ary, float_new((double)tmp));
+ }
+ break;
+
+ case 'D':
+ case 'd':
+ if (len >= (send - s) / sizeof(double))
+ len = (send - s) / sizeof(double);
+ while (len-- > 0) {
+ double tmp;
+ memcpy(&tmp, s, sizeof(double));
+ s += sizeof(double);
+ Fary_push(ary, float_new(tmp));
+ }
+ break;
+
+ case 'v':
+ if (len >= (send - s) / sizeof(short))
+ len = (send - s) / sizeof(short);
+ while (len-- > 0) {
+ short tmp;
+ memcpy(&tmp, s, sizeof(short));
+ s += sizeof(short);
+ tmp = vtohs(tmp);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'V':
+ if (len >= (send - s) / sizeof(long))
+ len = (send - s) / sizeof(long);
+ while (len-- > 0) {
+ long tmp;
+ memcpy(&tmp, s, sizeof(long));
+ s += sizeof(long);
+ tmp = vtohl(tmp);
+ Fary_push(ary, int2inum(tmp));
+ }
+ break;
+
+ case 'u':
+ {
+ VALUE str = str_new(0, (send - s)*3/4);
+ char *ptr = RSTRING(str)->ptr;
+
+ while (s < send && *s > ' ' && *s < 'a') {
+ int a,b,c,d;
+ char hunk[4];
+
+ hunk[3] = '\0';
+ len = (*s++ - ' ') & 077;
+ while (len > 0) {
+ if (s < send && *s >= ' ')
+ a = (*s++ - ' ') & 077;
+ else
+ a = 0;
+ if (s < send && *s >= ' ')
+ b = (*s++ - ' ') & 077;
+ else
+ b = 0;
+ if (s < send && *s >= ' ')
+ c = (*s++ - ' ') & 077;
+ else
+ c = 0;
+ if (s < send && *s >= ' ')
+ d = (*s++ - ' ') & 077;
+ else
+ d = 0;
+ hunk[0] = a << 2 | b >> 4;
+ hunk[1] = b << 4 | c >> 2;
+ hunk[2] = c << 6 | d;
+ memcpy(ptr, hunk, len > 3 ? 3 : len);
+ ptr += 3;
+ len -= 3;
+ }
+ if (s[0] == '\n')
+ s++;
+ else if (s[1] == '\n') /* possible checksum byte */
+ s += 2;
+ }
+ Fary_push(ary, str);
+ }
+ break;
+
+ case '@':
+ s = str->ptr + len;
+ break;
+
+ case 'X':
+ if (len > s - str->ptr)
+ Fail("X outside of string");
+ s -= len;
+ break;
+
+ case 'x':
+ if (len > send - s)
+ Fail("x outside of string");
+ s += len;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ GC_UNLINK;
+ return ary;
+}
+
+Init_pack()
+{
+ rb_define_method(C_Array, "pack", Fpck_pack, 1);
+ rb_define_method(C_String, "unpack", Fpck_unpack, 1);
+}
diff --git a/parse.y b/parse.y
new file mode 100644
index 0000000000..0f4f640934
--- /dev/null
+++ b/parse.y
@@ -0,0 +1,2585 @@
+/************************************************
+
+ parse.y -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:34 $
+ created at: Fri May 28 18:02:42 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+%{
+
+#define YYDEBUG 1
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "st.h"
+
+#include "ident.h"
+#define is_id_nonop(id) ((id)>LAST_TOKEN)
+#define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
+#define is_global_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
+#define is_instance_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE)
+#define is_variable_id(id) (is_id_nonop(id)&&((id)&ID_VARMASK))
+#define is_attrset_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
+#define is_const_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
+
+struct op_tbl {
+ ID tok;
+ char *name;
+};
+
+NODE *eval_tree = Qnil;
+static int in_regexp;
+
+enum {
+ KEEP_STATE = 0, /* don't change lex_state. */
+ EXPR_BEG, /* ignore newline, +/- is a sign. */
+ EXPR_MID, /* newline significant, +/- is a sign. */
+ EXPR_END, /* +/- is a operator. newline significant */
+};
+
+static int lex_state;
+
+static ID cur_class = Qnil, cur_mid = Qnil;
+static int in_module, in_single;
+
+static void value_expr();
+static NODE *cond();
+static NODE *cond2();
+
+static NODE *block_append();
+static NODE *list_append();
+static NODE *list_concat();
+static NODE *list_copy();
+static NODE *call_op();
+
+static NODE *asignable();
+static NODE *aryset();
+static NODE *attrset();
+
+static void push_local();
+static void pop_local();
+static int local_cnt();
+static int local_id();
+static ID *local_tbl();
+
+struct global_entry* rb_global_entry();
+
+static void init_top_local();
+static void setup_top_local();
+%}
+
+%union {
+ struct node *node;
+ VALUE val;
+ ID id;
+}
+
+%token CLASS
+ MODULE
+ DEF
+ FUNC
+ UNDEF
+ INCLUDE
+ IF
+ THEN
+ ELSIF
+ ELSE
+ CASE
+ WHEN
+ UNLESS
+ UNTIL
+ WHILE
+ FOR
+ IN
+ DO
+ USING
+ PROTECT
+ RESQUE
+ ENSURE
+ END
+ REDO
+ BREAK
+ CONTINUE
+ RETURN
+ YIELD
+ SUPER
+ RETRY
+ SELF
+ NIL
+
+%token <id> IDENTIFIER GVAR IVAR CONSTANT
+%token <val> INTEGER FLOAT STRING REGEXP
+
+%type <node> singleton inc_list
+%type <val> literal numeric
+%type <node> compexpr exprs expr expr2 primary var_ref
+%type <node> if_tail opt_else cases resque ensure opt_using
+%type <node> call_args opt_args args f_arglist f_args f_arg
+%type <node> assoc_list assocs assoc
+%type <node> mlhs mlhs_head mlhs_tail lhs
+%type <id> superclass variable symbol
+%type <id> fname fname0 op rest_arg end_mark
+
+%token UPLUS /* unary+ */
+%token UMINUS /* unary- */
+%token POW /* ** */
+%token CMP /* <=> */
+%token EQ /* == */
+%token NEQ /* != <> */
+%token GEQ /* >= */
+%token LEQ /* <= */
+%token AND OR /* && and || */
+%token MATCH NMATCH /* =~ and !~ */
+%token DOT2 DOT3 /* .. and ... */
+%token AREF ASET /* [] and []= */
+%token LSHFT RSHFT /* << and >> */
+%token COLON2 /* :: */
+%token <id> SELF_ASGN /* +=, -= etc. */
+%token ASSOC /* => */
+
+/*
+ * precedence table
+ */
+
+%left YIELD RETURN
+%right '=' SELF_ASGN
+%right COLON2
+%nonassoc DOT2 DOT3
+%left OR
+%left AND
+%left '|' '^'
+%left '&'
+%nonassoc CMP EQ NEQ MATCH NMATCH
+%left '>' GEQ '<' LEQ
+%left LSHFT RSHFT
+%left '+' '-'
+%left '*' '/' '%'
+%right POW
+%right '!' '~' UPLUS UMINUS
+
+%token LAST_TOKEN
+
+%%
+program : {
+ lex_state = EXPR_BEG;
+ init_top_local();
+ }
+ compexpr
+ {
+ eval_tree = block_append(eval_tree, $2);
+ setup_top_local();
+ }
+
+compexpr : exprs opt_term
+
+exprs : /* none */
+ {
+ $$ = Qnil;
+ }
+ | expr
+ | exprs term expr
+ {
+ $$ = block_append($1, $3);
+ }
+ | exprs error expr
+ {
+ yyerrok;
+ $$ = $1;
+ }
+
+expr : CLASS IDENTIFIER superclass
+ {
+ if (cur_class || cur_mid || in_single)
+ Error("nested class definition");
+ cur_class = $2;
+ push_local();
+ }
+ compexpr
+ END end_mark
+ {
+ if ($7 && $7 != CLASS) {
+ Error("unmatched end keyword(expected `class')");
+ }
+ $$ = NEW_CLASS($2, $5, $3);
+ pop_local();
+ cur_class = Qnil;
+ }
+ | MODULE IDENTIFIER
+ {
+ if (cur_class != Qnil)
+ Error("nested module definition");
+ cur_class = $2;
+ in_module = 1;
+ push_local();
+ }
+ compexpr
+ END end_mark
+ {
+ if ($6 && $6 != MODULE) {
+ Error("unmatched end keyword(expected `module')");
+ }
+ $$ = NEW_MODULE($2, $4);
+ pop_local();
+ cur_class = Qnil;
+ in_module = 0;
+ }
+ | DEF fname
+ {
+ if (cur_mid || in_single)
+ Error("nested method definition");
+ cur_mid = $2;
+ push_local();
+ }
+ f_arglist compexpr
+ END end_mark
+ {
+ if ($7 && $7 != DEF) {
+ Error("unmatched end keyword(expected `def')");
+ }
+ $$ = NEW_DEFN($2, NEW_RFUNC($4, $5), MTH_METHOD);
+ pop_local();
+ cur_mid = Qnil;
+ }
+ | DEF FUNC fname
+ {
+ if (cur_mid || in_single)
+ Error("nested method definition");
+ cur_mid = $3;
+ push_local();
+ }
+ f_arglist compexpr
+ END end_mark
+ {
+ if ($8 && $8 != DEF) {
+ Error("unmatched end keyword(expected `def')");
+ }
+ $$ = NEW_DEFN($3, NEW_RFUNC($5, $6), MTH_FUNC);
+ pop_local();
+ cur_mid = Qnil;
+ }
+ | DEF singleton '.' fname
+ {
+ value_expr($2);
+ in_single++;
+ push_local();
+ }
+ f_arglist
+ compexpr
+ END end_mark
+ {
+ if ($9 && $9 != DEF) {
+ Error("unmatched end keyword(expected `def')");
+ }
+ $$ = NEW_DEFS($2, $4, NEW_RFUNC($6, $7));
+ pop_local();
+ in_single--;
+ }
+ | UNDEF fname
+ {
+ $$ = NEW_UNDEF($2);
+ }
+ | DEF fname fname
+ {
+ $$ = NEW_ALIAS($2, $3);
+ }
+ | INCLUDE inc_list
+ {
+ if (cur_mid || in_single)
+ Error("include appeared in method definition");
+ $$ = $2;
+ }
+ | mlhs '=' args
+ {
+ NODE *rhs;
+
+ if ($3->nd_next == Qnil) {
+ rhs = $3->nd_head;
+ free($3);
+ }
+ else {
+ rhs = $3;
+ }
+
+ $$ = NEW_MASGN($1, rhs);
+ }
+ | expr2
+
+
+mlhs : mlhs_head
+ | mlhs_head mlhs_tail
+ {
+ $$ = list_concat($1, $2);
+ }
+
+mlhs_head : variable comma
+ {
+ $$ = NEW_LIST(asignable($1, Qnil));
+ }
+ | primary '[' args rbracket comma
+ {
+ $$ = NEW_LIST(aryset($1, $3, Qnil));
+ }
+ | primary '.' IDENTIFIER comma
+ {
+ $$ = NEW_LIST(attrset($1, $3, Qnil));
+ }
+
+mlhs_tail : lhs
+ {
+ $$ = NEW_LIST($1);
+ }
+ | mlhs_tail comma lhs
+ {
+ $$ = list_append($1, $3);
+ }
+
+lhs : variable
+ {
+ $$ = asignable($1, Qnil);
+ }
+ | primary '[' args rbracket
+ {
+ $$ = aryset($1, $3, Qnil);
+ }
+ | primary '.' IDENTIFIER
+ {
+ $$ = attrset($1, $3, Qnil);
+ }
+
+superclass : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ':' IDENTIFIER
+ {
+ $$ = $2;
+ }
+
+inc_list : IDENTIFIER
+ {
+ $$ = NEW_INC($1);
+ }
+ | inc_list comma IDENTIFIER
+ {
+ $$ = block_append($1, NEW_INC($3));
+ }
+ | error
+ {
+ $$ = Qnil;
+ }
+ | inc_list comma error
+
+fname : fname0
+ | IVAR
+
+fname0 : IDENTIFIER
+ | IDENTIFIER '='
+ {
+ ID id = $1;
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+ $$ = id;
+ }
+ | op
+
+op : COLON2 { $$ = COLON2; }
+ | DOT2 { $$ = DOT2; }
+ | '|' { $$ = '|'; }
+ | '^' { $$ = '^'; }
+ | '&' { $$ = '&'; }
+ | CMP { $$ = CMP; }
+ | EQ { $$ = EQ; }
+ | NEQ { $$ = NEQ; }
+ | MATCH { $$ = MATCH; }
+ | NMATCH { $$ = NMATCH; }
+ | '>' { $$ = '>'; }
+ | GEQ { $$ = GEQ; }
+ | '<' { $$ = '<'; }
+ | LEQ { $$ = LEQ; }
+ | LSHFT { $$ = LSHFT; }
+ | RSHFT { $$ = RSHFT; }
+ | '+' { $$ = '+'; }
+ | '-' { $$ = '-'; }
+ | '*' { $$ = '*'; }
+ | '/' { $$ = '/'; }
+ | '%' { $$ = '%'; }
+ | POW { $$ = POW; }
+ | '!' { $$ = '!'; }
+ | '~' { $$ = '~'; }
+ | '!' '@' { $$ = '!'; }
+ | '~' '@' { $$ = '~'; }
+ | '-' '@' { $$ = UMINUS; }
+ | '+' '@' { $$ = UPLUS; }
+ | '[' ']' { $$ = AREF; }
+ | '[' ']' '=' { $$ = ASET; }
+
+f_arglist : '(' f_args rparen
+ {
+ $$ = $2;
+ }
+ | term
+ {
+ $$ = Qnil;
+ }
+
+f_args : /* no arg */
+ {
+ $$ = Qnil;
+ }
+ | f_arg
+ {
+ $$ = NEW_ARGS($1, -1);
+ }
+ | f_arg comma rest_arg
+ {
+ $$ = NEW_ARGS($1, $3);
+ }
+ | rest_arg
+ {
+ $$ = NEW_ARGS(Qnil, $1);
+ }
+ | f_arg error
+ {
+ $$ = NEW_ARGS($1, -1);
+ }
+ | error
+ {
+ $$ = Qnil;
+ }
+
+f_arg : IDENTIFIER
+ {
+ if (!is_local_id($1))
+ Error("formal argument must be local variable");
+ $$ = NEW_LIST(local_cnt($1));
+ }
+ | f_arg comma IDENTIFIER
+ {
+ if (!is_local_id($3))
+ Error("formal argument must be local variable");
+ $$ = list_append($1, local_cnt($3));
+ }
+
+rest_arg : '*' IDENTIFIER
+ {
+ if (!is_local_id($2))
+ Error("rest argument must be local variable");
+ $$ = local_cnt($2);
+ }
+
+singleton : var_ref
+ {
+ if ($1->type == NODE_SELF) {
+ $$ = NEW_SELF();
+ }
+ else if ($1->type == NODE_NIL) {
+ Error("Can't define single method for nil.");
+ freenode($1);
+ $$ = Qnil;
+ }
+ else {
+ $$ = $1;
+ }
+ }
+ | '(' compexpr rparen
+ {
+ switch ($2->type) {
+ case NODE_STR:
+ case NODE_LIT:
+ case NODE_ARRAY:
+ case NODE_ZARRAY:
+ Error("Can't define single method for literals.");
+ default:
+ break;
+ }
+ $$ = $2;
+ }
+
+expr2 : IF expr2 then
+ compexpr
+ if_tail
+ END end_mark
+ {
+ if ($7 && $7 != IF) {
+ Error("unmatched end keyword(expected `if')");
+ }
+ $$ = NEW_IF(cond($2), $4, $5);
+ }
+ | UNLESS expr2 then
+ compexpr opt_else END end_mark
+ {
+ if ($7 && $7 != UNLESS) {
+ Error("unmatched end keyword(expected `if')");
+ }
+ $$ = NEW_UNLESS(cond($2), $4, $5);
+ }
+ | CASE expr2 opt_term
+ cases
+ END end_mark
+ {
+ if ($6 && $6 != CASE) {
+ Error("unmatched end keyword(expected `case')");
+ }
+ value_expr($2);
+ $$ = NEW_CASE($2, $4);
+ }
+ | WHILE expr2 term compexpr END end_mark
+ {
+ if ($6 && $6 != WHILE) {
+ Error("unmatched end keyword(expected `while')");
+ }
+ $$ = NEW_WHILE(cond($2), $4);
+ }
+ | UNTIL expr2 term compexpr END end_mark
+ {
+ if ($6 && $6 != UNTIL) {
+ Error("unmatched end keyword(expected `until')");
+ }
+ $$ = NEW_UNTIL(cond($2), $4);
+ }
+ | FOR lhs IN expr2 term
+ compexpr
+ END end_mark
+ {
+ if ($8 && $8 != FOR) {
+ Error("unmatched end keyword(expected `for')");
+ }
+ value_expr($4);
+ $$ = NEW_FOR($2, $4, $6);
+ }
+ | DO expr2 opt_using
+ compexpr
+ END end_mark
+ {
+ if ($6 && $6 != DO) {
+ Error("unmatched end keyword(expected `do')");
+ }
+ value_expr($2);
+ $$ = NEW_DO($3, $2, $4);
+ }
+ | PROTECT
+ compexpr
+ resque
+ ensure
+ END end_mark
+ {
+ if ($6 && $6 != PROTECT) {
+ Error("unmatched end keyword(expected `protect')");
+ }
+ if ($3 == Qnil && $4 == Qnil) {
+ Warning("useless protect clause");
+ $$ = $2;
+ }
+ else {
+ $$ = NEW_PROT($2, $3, $4);
+ }
+ }
+ | REDO
+ {
+ $$ = NEW_REDO();
+ }
+ | BREAK
+ {
+ $$ = NEW_BREAK();
+ }
+ | CONTINUE
+ {
+ $$ = NEW_CONT();
+ }
+ | RETRY
+ {
+ $$ = NEW_RETRY();
+ }
+ | RETURN expr2
+ {
+ value_expr($2);
+ if (!cur_mid && !in_single)
+ Error("return appeared outside of method");
+ $$ = NEW_RET($2);
+ }
+ | RETURN
+ {
+ if (!cur_mid && !in_single)
+ Error("return appeared outside of method");
+ $$ = NEW_RET(Qnil);
+ }
+ | variable '=' expr2
+ {
+ value_expr($3);
+ $$ = asignable($1, $3);
+ }
+ | primary '[' args rbracket '=' expr2
+ {
+ value_expr($6);
+ $$ = aryset($1, $3, $6);
+ }
+ | primary '.' IDENTIFIER '=' expr2
+ {
+ value_expr($5);
+ $$ = attrset($1, $3, $5);
+ }
+ | variable SELF_ASGN expr2
+ {
+ NODE *val;
+
+ value_expr($3);
+ if (is_local_id($1)) {
+ val = NEW_LVAR($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));
+ }
+ | primary '[' args rbracket SELF_ASGN expr2
+ {
+ NODE *rval, *args;
+ value_expr($1);
+ value_expr($6);
+
+ args = list_copy($3);
+ rval = NEW_CALL2($1, AREF, args);
+
+ args = list_append($3, call_op(rval, $5, 1, $6));
+ $$ = NEW_CALL($1, ASET, args);
+ }
+ | primary '.' IDENTIFIER SELF_ASGN expr2
+ {
+ ID id = $3;
+ NODE *rval;
+
+ value_expr($1);
+ value_expr($5);
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+
+ rval = call_op(NEW_CALL2($1, $3, Qnil), $4, 1, $5);
+ $$ = NEW_CALL($1, id, NEW_LIST(rval));
+ }
+ | YIELD expr2
+ {
+ value_expr($2);
+ $$ = NEW_YIELD($2);
+ }
+ | expr2 DOT2 expr2
+ {
+ $$ = call_op($1, DOT2, 1, $3);
+ }
+ | expr2 DOT3 expr2
+ {
+ $$ = NEW_DOT3(cond2($1), cond2($3));
+ }
+ | expr2 '+' expr2
+ {
+ $$ = call_op($1, '+', 1, $3);
+ }
+ | expr2 '-' expr2
+ {
+ $$ = call_op($1, '-', 1, $3);
+ }
+ | expr2 '*' expr2
+ {
+ $$ = call_op($1, '*', 1, $3);
+ }
+ | expr2 '/' expr2
+ {
+ $$ = call_op($1, '/', 1, $3);
+ }
+ | expr2 '%' expr2
+ {
+ $$ = call_op($1, '%', 1, $3);
+ }
+ | expr2 POW expr2
+ {
+ $$ = call_op($1, POW, 1, $3);
+ }
+ | '+' expr2 %prec UPLUS
+ {
+ $$ = call_op($2, UPLUS, 0);
+
+ }
+ | '-' expr2 %prec UMINUS
+ {
+ $$ = call_op($2, UMINUS, 0);
+ }
+ | expr2 '|' expr2
+ {
+ $$ = call_op($1, '|', 1, $3);
+ }
+ | expr2 '^' expr2
+ {
+ $$ = call_op($1, '^', 1, $3);
+ }
+ | expr2 '&' expr2
+ {
+ $$ = call_op($1, '&', 1, $3);
+ }
+ | expr2 CMP expr2
+ {
+ $$ = call_op($1, CMP, 1, $3);
+ }
+ | expr2 '>' expr2
+ {
+ $$ = call_op($1, '>', 1, $3);
+ }
+ | expr2 GEQ expr2
+ {
+ $$ = call_op($1, GEQ, 1, $3);
+ }
+ | expr2 '<' expr2
+ {
+ $$ = call_op($1, '<', 1, $3);
+ }
+ | expr2 LEQ expr2
+ {
+ $$ = call_op($1, LEQ, 1, $3);
+ }
+ | expr2 EQ expr2
+ {
+ $$ = call_op($1, EQ, 1, $3);
+ }
+ | expr2 NEQ expr2
+ {
+ $$ = call_op($1, NEQ, 1, $3);
+ }
+ | expr2 MATCH expr2
+ {
+ $$ = call_op($1, MATCH, 1, $3);
+ }
+ | expr2 NMATCH expr2
+ {
+ $$ = call_op($1, NMATCH, 1, $3);
+ }
+ | '!' expr2
+ {
+ $$ = call_op(cond($2), '!', 0);
+ }
+ | '~' expr2
+ {
+ $$ = call_op($2, '~', 0);
+ }
+ | expr2 LSHFT expr2
+ {
+ $$ = call_op($1, LSHFT, 1, $3);
+ }
+ | expr2 RSHFT expr2
+ {
+ $$ = call_op($1, RSHFT, 1, $3);
+ }
+ | expr2 COLON2 expr2
+ {
+ $$ = call_op($1, COLON2, 1, $3);
+ }
+ | expr2 AND expr2
+ {
+ $$ = NEW_AND(cond($1), cond($3));
+ }
+ | expr2 OR expr2
+ {
+ $$ = NEW_OR(cond($1), cond($3));
+ }
+ |primary
+ {
+ $$ = $1;
+ }
+
+then : term
+ | THEN
+ | term THEN
+
+if_tail : opt_else
+ | ELSIF expr2 then
+ compexpr
+ if_tail
+ {
+ $$ = NEW_IF(cond($2), $4, $5);
+ }
+
+opt_else : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ELSE compexpr
+ {
+ $$ = $2;
+ }
+
+opt_using : term
+ {
+ $$ = Qnil;
+ }
+ | opt_term USING lhs term
+ {
+ $$ = $3;
+ }
+
+cases : opt_else
+ | WHEN args term
+ compexpr
+ cases
+ {
+ $$ = NEW_WHEN($2, $4, $5);
+ }
+
+resque : /* none */
+ {
+ $$ = Qnil;
+ }
+ | RESQUE compexpr
+ {
+ if ($2 == Qnil)
+ $$ = (NODE*)1;
+ else
+ $$ = $2;
+ }
+
+ensure : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ENSURE compexpr
+ {
+ $$ = $2;
+ }
+
+call_args : /* none */
+ {
+ $$ = Qnil;
+ }
+ | args
+ | '*' exprs
+ {
+ $$ = $2;
+ }
+ | args comma '*' exprs
+ {
+ $$ = call_op($1, '+', 1, $4);
+ }
+
+opt_args : /* none */
+ {
+ $$ = Qnil;
+ }
+ | args
+
+args : expr2
+ {
+ value_expr($1);
+ $$ = NEW_LIST($1);
+ }
+ | args comma expr2
+ {
+ value_expr($3);
+ $$ = list_append($1, $3);
+ }
+
+primary : var_ref
+ | '(' compexpr rparen
+ {
+ $$ = $2;
+ }
+
+ | STRING
+ {
+ literalize($1);
+ $$ = NEW_STR($1);
+ }
+ | primary '[' args rbracket
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, AREF, $3);
+ }
+ | literal
+ {
+ literalize($1);
+ $$ = NEW_LIT($1);
+ }
+ | '[' opt_args rbracket
+ {
+ if ($2 == Qnil)
+ $$ = NEW_ZARRAY(); /* zero length array*/
+ else {
+ $$ = $2;
+ }
+ }
+ | lbrace assoc_list rbrace
+ {
+ $$ = NEW_HASH($2);
+ }
+ | primary '.' IDENTIFIER '(' call_args rparen
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, $5);
+ }
+ | primary '.' IDENTIFIER
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, Qnil);
+ }
+ | IDENTIFIER '(' call_args rparen
+ {
+ $$ = NEW_CALL(Qnil, $1, $3);
+ }
+ | IVAR '(' call_args rparen
+ {
+ $$ = NEW_CALL(Qnil, $1, $3);
+ }
+ | SUPER '(' call_args rparen
+ {
+ if (!cur_mid && !in_single)
+ Error("super called outside of method");
+ $$ = NEW_SUPER($3);
+ }
+ | SUPER
+ {
+ if (!cur_mid && !in_single)
+ Error("super called outside of method");
+ $$ = NEW_ZSUPER();
+ }
+
+literal : numeric
+ | '\\' symbol
+ {
+ $$ = INT2FIX($2);
+ }
+ | '/' {in_regexp = 1;} REGEXP
+ {
+ $$ = $3;
+ }
+
+symbol : fname0
+ | IVAR
+ | GVAR
+ | CONSTANT
+
+numeric : INTEGER
+ | FLOAT
+
+variable : IDENTIFIER
+ | IVAR
+ | GVAR
+ | CONSTANT
+ | NIL
+ {
+ $$ = NIL;
+ }
+ | SELF
+ {
+ $$ = SELF;
+ }
+
+var_ref : variable
+ {
+ if ($1 == SELF) {
+ $$ = NEW_SELF();
+ }
+ else if ($1 == NIL) {
+ $$ = NEW_NIL();
+ }
+ else if (is_local_id($1)) {
+ if (local_id($1))
+ $$ = NEW_LVAR($1);
+ else
+ $$ = NEW_MVAR($1);
+ }
+ else if (is_global_id($1)) {
+ $$ = NEW_GVAR($1);
+ }
+ else if (is_instance_id($1)) {
+ $$ = NEW_IVAR($1);
+ }
+ else if (is_const_id($1)) {
+ $$ = NEW_CVAR($1);
+ }
+ }
+
+assoc_list : /* none */
+ {
+ $$ = Qnil;
+ }
+ | assocs
+
+assocs : assoc
+ | assocs comma assoc
+ {
+ $$ = list_concat($1, $3);
+ }
+
+assoc : expr2 ASSOC expr2
+ {
+ $$ = NEW_LIST($1);
+ $$ = list_append($$, $3);
+ }
+
+end_mark : CLASS { $$ = CLASS; }
+ | MODULE { $$ = MODULE; }
+ | DEF { $$ = DEF; }
+ | FUNC { $$ = FUNC; }
+ | IF { $$ = IF; }
+ | UNLESS { $$ = UNLESS; }
+ | CASE { $$ = CASE; }
+ | WHILE { $$ = WHILE; }
+ | UNTIL { $$ = UNTIL; }
+ | FOR { $$ = FOR; }
+ | DO { $$ = DO; }
+ | PROTECT { $$ = PROTECT; }
+ | { $$ = Qnil;}
+
+opt_term : /* none */
+ | term
+
+term : sc
+ | nl
+
+sc : ';' { yyerrok; }
+nl : '\n' { yyerrok; }
+
+rparen : ')' { yyerrok; }
+rbracket : ']' { yyerrok; }
+lbrace : '{' { yyerrok; }
+rbrace : '}' { yyerrok; }
+comma : ',' { yyerrok; }
+%%
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "regex.h"
+
+#define is_identchar(c) ((c)!=-1&&(isalnum(c) || (c) == '_' || ismbchar(c)))
+
+static char *tokenbuf = NULL;
+static int tokidx, toksiz = 0;
+
+char *xmalloc();
+char *xrealloc();
+VALUE newregexp();
+VALUE newstring();
+VALUE newfloat();
+VALUE newinteger();
+char *strdup();
+
+#define EXPAND_B 1
+#define LEAVE_BS 2
+
+static void read_escape();
+
+char *sourcefile; /* current source file */
+int sourceline; /* current line no. */
+
+static char *lex_p;
+static int lex_len;
+
+lex_setsrc(src, ptr, len)
+ char *src;
+ char *ptr;
+ int len;
+{
+ sourcefile = (char*)strdup(src);
+
+ sourceline = 1;
+ lex_p = ptr;
+ lex_len = len;
+}
+
+#define nextc() ((--lex_len>=0)?(*lex_p++):-1)
+#define pushback() (lex_len++, lex_p--)
+
+#define tokfix() (tokenbuf[tokidx]='\0')
+#define tok() tokenbuf
+#define toklen() tokidx
+#define toknow() &toknbuf[tokidx]
+
+char *
+newtok()
+{
+ tokidx = 0;
+ if (!tokenbuf) {
+ toksiz = 60;
+ tokenbuf = ALLOC_N(char, 60);
+ }
+ if (toksiz > 1024) {
+ REALLOC_N(tokenbuf, char, 60);
+ }
+ return tokenbuf;
+}
+
+void
+tokadd(c)
+ char c;
+{
+ if (tokidx >= toksiz) {
+ toksiz *= 2;
+ REALLOC_N(tokenbuf, char, toksiz);
+ }
+ tokenbuf[tokidx++] = c;
+}
+
+#define LAST(v) ((v)-1 + sizeof(v)/sizeof(v[0]))
+
+static struct kwtable {
+ char *name;
+ int id;
+ int state;
+} kwtable [] = {
+ "__END__", 0, KEEP_STATE,
+ "break", BREAK, EXPR_END,
+ "case", CASE, KEEP_STATE,
+ "class", CLASS, KEEP_STATE,
+ "continue", CONTINUE, EXPR_END,
+ "def", DEF, KEEP_STATE,
+ "do", DO, KEEP_STATE,
+ "else", ELSE, EXPR_BEG,
+ "elsif", ELSIF, EXPR_BEG,
+ "end", END, EXPR_END,
+ "ensure", ENSURE, EXPR_BEG,
+ "for", FOR, KEEP_STATE,
+ "func", FUNC, KEEP_STATE,
+ "if", IF, KEEP_STATE,
+ "in", IN, EXPR_BEG,
+ "include", INCLUDE, EXPR_BEG,
+ "module", MODULE, KEEP_STATE,
+ "nil", NIL, EXPR_END,
+ "protect", PROTECT, KEEP_STATE,
+ "redo", REDO, EXPR_END,
+ "resque", RESQUE, EXPR_BEG,
+ "retry", RETRY, EXPR_END,
+ "return", RETURN, EXPR_MID,
+ "self", SELF, EXPR_END,
+ "super", SUPER, EXPR_END,
+ "then", THEN, EXPR_BEG,
+ "undef", UNDEF, EXPR_BEG,
+ "unless", UNLESS, EXPR_BEG,
+ "until", UNTIL, EXPR_BEG,
+ "using", USING, KEEP_STATE,
+ "when", WHEN, EXPR_BEG,
+ "while", WHILE, KEEP_STATE,
+ "yield", YIELD, EXPR_BEG,
+};
+
+yylex()
+{
+ register int c;
+ struct kwtable *low = kwtable, *mid, *high = LAST(kwtable);
+ int last;
+
+ if (in_regexp) {
+ int in_brack = 0;
+ int re_start = sourceline;
+
+ in_regexp = 0;
+ newtok();
+ while (c = nextc()) {
+ switch (c) {
+ case '[':
+ in_brack = 1;
+ break;
+ case ']':
+ in_brack = 0;
+ break;
+ case '\\':
+ if ((c = nextc()) == -1) {
+ sourceline = re_start;
+ Error("unterminated regexp meets end of file");
+ return 0;
+ }
+ else if (c == '\n') {
+ sourceline++;
+ }
+ else if (in_brack && c == 'b') {
+ tokadd('\b');
+ }
+ else {
+ pushback();
+ read_escape(LEAVE_BS);
+ }
+ continue;
+
+ case '/': /* end of the regexp */
+ if (in_brack)
+ break;
+
+ tokfix();
+ yylval.val = regexp_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return REGEXP;
+
+ case -1:
+ Error("unterminated regexp");
+ return 0;
+
+ default:
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ break;
+ }
+ tokadd(c);
+ }
+ }
+
+retry:
+ switch (c = nextc()) {
+ case '\0':
+ case '\004':
+ case '\032':
+ case -1: /* end of script. */
+ return 0;
+
+ /* white spaces */
+ case ' ': case '\t': case '\f': case '\r':
+ goto retry;
+
+ case '#': /* it's a comment */
+ while ((c = nextc()) != '\n') {
+ if (c == -1)
+ return 0;
+ }
+ /* fall through */
+ case '\n':
+ sourceline++;
+ if (lex_state == EXPR_BEG) goto retry;
+ lex_state = EXPR_BEG;
+ return '\n';
+
+ case '*':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '*') {
+ if (nextc() == '=') {
+ yylval.id = POW;
+ return SELF_ASGN;
+ }
+ pushback();
+ return POW;
+ }
+ else if (c == '=') {
+ yylval.id = '*';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '*';
+
+ case '!':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return NEQ;
+ }
+ if (c == '~') {
+ return NMATCH;
+ }
+ pushback();
+ return '!';
+
+ case '=':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return EQ;
+ }
+ if (c == '~') {
+ return MATCH;
+ }
+ else if (c == '>') {
+ return ASSOC;
+ }
+ pushback();
+ return '=';
+
+ case '<':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ if ((c = nextc()) == '>') {
+ return CMP;
+ }
+ pushback();
+ return LEQ;
+ }
+ if (c == '<') {
+ if (nextc() == '=') {
+ yylval.id = LSHFT;
+ return SELF_ASGN;
+ }
+ pushback();
+ return LSHFT;
+ }
+ pushback();
+ return '<';
+
+ case '>':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return GEQ;
+ }
+ if (c == '>') {
+ if (nextc() == '=') {
+ yylval.id = RSHFT;
+ return SELF_ASGN;
+ }
+ pushback();
+ return RSHFT;
+ }
+ pushback();
+ return '>';
+
+ case '"':
+ {
+ int 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();
+ if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == '"') {
+ tokadd(c);
+ }
+ else {
+ pushback();
+ read_escape(LEAVE_BS | EXPAND_B);
+ }
+ continue;
+ }
+ tokadd(c);
+ }
+ tokfix();
+ yylval.val = str_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return STRING;
+ }
+
+ case '\'':
+ {
+ int 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;
+ }
+
+ case '?':
+ if ((c = nextc()) == '\\') {
+ newtok();
+ read_escape(EXPAND_B);
+ c = tok()[0];
+ }
+ c &= 0xff;
+ yylval.val = INT2FIX(c);
+ lex_state = EXPR_END;
+ return INTEGER;
+
+ case '&':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '&') {
+ return AND;
+ }
+ else if (c == '=') {
+ yylval.id = '&';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '&';
+
+ case '|':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '|') {
+ return OR;
+ }
+ else if (c == '=') {
+ yylval.id = '|';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '|';
+
+ case '+':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ c = nextc();
+ pushback();
+ if (isdigit(c)) {
+ goto start_num;
+ }
+ }
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = '+';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '+';
+
+ case '-':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ c = nextc();
+ pushback();
+ if (isdigit(c)) {
+ c = '-';
+ goto start_num;
+ }
+ }
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = '-';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '-';
+
+ case '.':
+ if ((c = nextc()) == '.') {
+ if ((c = nextc()) == '.') {
+ return DOT3;
+ }
+ pushback();
+ lex_state = EXPR_BEG;
+ return DOT2;
+ }
+ pushback();
+ if (!isdigit(c)) {
+ lex_state = EXPR_BEG;
+ return '.';
+ }
+ c = '.';
+ /* fall through */
+
+ start_num:
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int is_float, seen_point, seen_e;
+
+ lex_state = EXPR_END;
+ newtok();
+ if (c == '0') {
+ c = nextc();
+ if (c == 'x' || c == 'X') {
+ /* hexadecimal */
+ while (c = nextc()) {
+ if (!isxdigit(c)) break;
+ tokadd(c);
+ }
+ pushback();
+ tokfix();
+ yylval.val = str2inum(tok(), 16);
+ return INTEGER;
+ }
+ else if (c >= '0' && c <= '9') {
+ /* octal */
+ do {
+ tokadd(c);
+ c = nextc();
+ } while (c >= '0' && c <= '9');
+ pushback();
+ tokfix();
+#if 0
+ yylval.val = INT2FIX(strtoul(tok(), Qnil, 8));
+#else
+ yylval.val = str2inum(tok(), 8);
+#endif
+ 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':
+ case '5': case '6': case '7': case '8': case '9':
+ tokadd(c);
+ break;
+
+ case '.':
+ if (seen_point) {
+ goto decode_num;
+ }
+ c = nextc();
+ if (!isdigit(c)) {
+ pushback();
+ goto decode_num;
+ }
+ tokadd('.');
+ tokadd(c);
+ is_float++;
+ seen_point++;
+ break;
+
+ case 'e':
+ case 'E':
+ if (seen_e) {
+ goto decode_num;
+ }
+ tokadd(c);
+ seen_e++;
+ is_float++;
+ if ((c = nextc()) == '-' || c == '+')
+ tokadd(c);
+ else
+ continue;
+ break;
+
+ case '_': /* `_' in decimal just ignored */
+ break;
+
+ default:
+ goto decode_num;
+ }
+ c = nextc();
+ }
+
+ decode_num:
+ pushback();
+ tokfix();
+ if (is_float) {
+ double atof();
+
+ yylval.val = float_new(atof(tok()));
+ return FLOAT;
+ }
+ yylval.val = str2inum(tok(), 10);
+ return INTEGER;
+ }
+
+ case ']':
+ case '}':
+ case ')':
+ lex_state = EXPR_END;
+ return c;
+
+ case ':':
+ lex_state = EXPR_BEG;
+ if (nextc() == ':') {
+ return COLON2;
+ }
+ pushback();
+ return ':';
+
+ case '/':
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '/';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+
+ case '^':
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '^';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+
+ case ',':
+ case ';':
+ case '`':
+ case '[':
+ case '(':
+ case '{':
+ case '~':
+ lex_state = EXPR_BEG;
+ return c;
+
+ case '\\':
+ c = nextc();
+ if (c == '\n') goto retry; /* skip \\n */
+ lex_state = EXPR_BEG;
+ pushback();
+ return '\\';
+
+ case '%':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ /* class constant */
+ newtok();
+ tokadd('%');
+ c = nextc();
+ break;
+ }
+ else {
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '%';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+ }
+
+ case '$':
+ newtok();
+ tokadd(c);
+ c = nextc();
+ switch (c) {
+ case '*': /* $*: argv */
+ case '$': /* $$: pid */
+ case '?': /* $?: last status */
+ case '!': /* $!: error string */
+ case '@': /* $@: error position */
+ case '/': /* $/: input record separator */
+ case '\\': /* $\: output record separator */
+ case ',': /* $,: output field separator */
+ case '.': /* $.: last read line number */
+ case '_': /* $_: last read line string */
+ case '&': /* $&: last match */
+ case '~': /* $~: match-data */
+ case '=': /* $=: ignorecase */
+ tokadd(c);
+ tokadd('\0');
+ goto id_fetch;
+
+ default:
+ if (is_identchar(c))
+ break;
+ pushback();
+ return tok()[0];
+ }
+ break;
+
+ case '@':
+ c = nextc();
+ if (!is_identchar(c)) {
+ pushback();
+ return '@';
+ }
+ newtok();
+ tokadd('@');
+ break;
+
+ default:
+ if (c != '_' && !isalpha(c) && !ismbchar(c)) {
+ Error("Invalid char '%c' in expression", c);
+ goto retry;
+ }
+
+ newtok();
+ break;
+ }
+
+ while (is_identchar(c)) {
+ tokadd(c);
+ if (ismbchar(c)) {
+ c = nextc();
+ tokadd(c);
+ }
+ c = nextc();
+ }
+ pushback();
+ tokfix();
+
+ /* See if it is a reserved word. */
+ while (low <= high) {
+ mid = low + (high - low)/2;
+ if (( c = strcmp(mid->name, tok())) == 0) {
+ if (mid->state != KEEP_STATE) {
+ lex_state = mid->state;
+ }
+ return mid->id;
+ }
+ else if (c < 0) {
+ low = mid + 1;
+ }
+ else {
+ high = mid - 1;
+ }
+ }
+
+ id_fetch:
+ lex_state = EXPR_END;
+ yylval.id = rb_intern(tok());
+ switch (tok()[0]) {
+ case '%':
+ return CONSTANT;
+ case '$':
+ return GVAR;
+ case '@':
+ return IVAR;
+ default:
+ return IDENTIFIER;
+ }
+}
+
+static void
+read_escape(flag)
+ int flag;
+{
+ char c;
+
+ switch (c = nextc()) {
+ case '\\': /* Backslash */
+ tokadd('\\');
+ break;
+
+ case 'n': /* newline */
+ tokadd('\n');
+ break;
+
+ case 't': /* horizontal tab */
+ tokadd('\t');
+ break;
+
+ case 'r': /* carriage-return */
+ tokadd('\r');
+ break;
+
+ case 'f': /* form-feed */
+ tokadd('\r');
+ break;
+
+ case 'v': /* vertical tab */
+ tokadd('\13');
+ break;
+
+ case 'a':
+ tokadd('\a');
+ break;
+
+ case 'e': /* escape */
+ tokadd(033);
+ break;
+
+ case 'M':
+ if ((c = nextc()) != '-') {
+ Error("Invalid escape character syntax");
+ tokadd('\0');
+ return;
+ }
+ if ((c = nextc()) == '\\') {
+ read_escape(flag);
+ tokenbuf[tokidx-1] |= 0200; /* kludge */
+ }
+ else {
+ tokadd((c & 0xff) | 0200);
+ }
+ break;
+
+ case 'C':
+ if ((c = nextc()) != '-') {
+ Error("Invalid escape character syntax");
+ tokadd('\0');
+ return;
+ }
+ case '^':
+ if ((c = nextc())== '\\') {
+ read_escape (flag);
+ tokenbuf[tokidx-1] &= 0237; /* kludge */
+ }
+ else if (c == '?')
+ tokadd(0177);
+ else
+ tokadd(c & 0237);
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ { /* octal constant */
+ register int i = c - '0';
+ register int count = 0;
+
+ while (++count < 3) {
+ if ((c = nextc()) >= '0' && c <= '7') {
+ i *= 8;
+ i += c - '0';
+ }
+ else {
+ pushback();
+ break;
+ }
+ }
+ tokadd(i&0xff);
+ }
+ break;
+
+ case 'x': /* hex constant */
+ {
+ register int i = c - '0';
+ register int count = 0;
+
+ while (++count < 2) {
+ c = nextc();
+ if ((c = nextc()) >= '0' && c <= '9') {
+ i *= 16;
+ i += c - '0';
+ }
+ else if ((int)index("abcdefABCDEF", (c = nextc()))) {
+ i *= 16;
+ i += toupper(c) - 'A' + 10;
+ }
+ else {
+ pushback();
+ break;
+ }
+ }
+ tokadd(i&0xff);
+ }
+ break;
+
+ case 'b': /* backspace */
+ if (flag & EXPAND_B) {
+ tokadd('\b');
+ return;
+ }
+ /* go turough */
+ default:
+ if (flag & LEAVE_BS) {
+ tokadd('\\');
+ }
+ tokadd(c);
+ break;
+ }
+}
+
+NODE*
+newnode(type, a0, a1, a2)
+ enum node_type type;
+ NODE *a0, *a1, *a2;
+{
+ NODE *n = ALLOC(NODE);
+
+ n->type = type;
+ n->line = sourceline;
+ n->src = sourcefile;
+
+ n->u1.node = a0;
+ n->u2.node = a1;
+ n->u3.node = a2;
+
+ return n;
+}
+
+static NODE*
+block_append(head, tail)
+ NODE *head, *tail;
+{
+ extern int verbose;
+
+ if (tail == Qnil) return head;
+ if (head == Qnil) return tail;
+
+ if (head->type != NODE_BLOCK)
+ head = NEW_BLOCK(head);
+
+ if (head->nd_last == Qnil) head->nd_last = head;
+
+ if (verbose) {
+ switch (head->nd_last->nd_head->type) {
+ case NODE_BREAK:
+ case NODE_CONTINUE:
+ case NODE_REDO:
+ case NODE_RETURN:
+ case NODE_RETRY:
+ Warning("statement not reached");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (tail->type == NODE_BLOCK) {
+ head->nd_last->nd_next = tail;
+ head->nd_last = tail->nd_last;
+ }
+ else {
+ head->nd_last->nd_next = NEW_BLOCK(tail);
+ head->nd_last = head->nd_last->nd_next;
+ }
+ return head;
+}
+
+static NODE*
+list_append(head, tail)
+ NODE *head, *tail;
+{
+ if (head == Qnil) return NEW_ARRAY(tail);
+
+ if (head->nd_last == Qnil) head->nd_last = head;
+
+ head->nd_last->nd_next = NEW_ARRAY(tail);
+ head->nd_last = head->nd_last->nd_next;
+ return head;
+}
+
+static NODE*
+list_concat(head, tail)
+ NODE *head, *tail;
+{
+ NODE *last;
+
+ if (head->type != NODE_ARRAY || tail->type != NODE_ARRAY)
+ Bug("list_concat() called with non-list");
+
+ last = (head->nd_last)?head->nd_last:head;
+ last->nd_next = tail;
+ head->nd_last = tail->nd_last;
+
+ return head;
+}
+
+static NODE*
+list_copy(list)
+ NODE *list;
+{
+ NODE *tmp;
+
+ if (list == Qnil) return Qnil;
+
+ tmp = Qnil;
+ while(list) {
+ tmp = list_append(tmp, list->nd_head);
+ list = list->nd_next;
+ }
+ return tmp;
+}
+
+void freenode(node)
+ NODE *node;
+{
+ if (node == Qnil) return;
+
+ switch (node->type) {
+ case NODE_BLOCK:
+ case NODE_ARRAY:
+ freenode(node->nd_head);
+ freenode(node->nd_next);
+ break;
+ case NODE_IF:
+ case NODE_WHEN:
+ case NODE_PROT:
+ freenode(node->nd_cond);
+ freenode(node->nd_body);
+ freenode(node->nd_else);
+ break;
+ case NODE_CASE:
+ case NODE_WHILE:
+ case NODE_UNTIL:
+ case NODE_AND:
+ case NODE_OR:
+ freenode(node->nd_head);
+ freenode(node->nd_body);
+ break;
+ case NODE_DO:
+ case NODE_FOR:
+ freenode(node->nd_ibdy);
+ freenode(node->nd_iter);
+ break;
+ case NODE_LASGN:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ freenode(node->nd_value);
+ break;
+ case NODE_CALL:
+ case NODE_SUPER:
+ freenode(node->nd_recv);
+ case NODE_CALL2:
+ freenode(node->nd_args);
+ break;
+ case NODE_DEFS:
+ freenode(node->nd_recv);
+ break;
+ case NODE_RETURN:
+ case NODE_YIELD:
+ freenode(node->nd_stts);
+ break;
+ case NODE_STR:
+ case NODE_LIT:
+ unliteralize(node->nd_lit);
+ break;
+ case NODE_CONST:
+ unliteralize(node->nd_cval);
+ break;
+ case NODE_ARGS:
+ freenode(node->nd_frml);
+ break;
+ case NODE_SCOPE:
+ free(node->nd_tbl);
+ freenode(node->nd_body);
+ break;
+ case NODE_DEFN:
+ case NODE_ZARRAY:
+ case NODE_CFUNC:
+ case NODE_BREAK:
+ case NODE_CONTINUE:
+ case NODE_RETRY:
+ case NODE_LVAR:
+ case NODE_GVAR:
+ case NODE_IVAR:
+ case NODE_MVAR:
+ case NODE_CLASS:
+ case NODE_MODULE:
+ case NODE_INC:
+ case NODE_NIL:
+ break;
+ default:
+ Bug("freenode: unknown node type %d", node->type);
+ break;
+ }
+ free(node);
+}
+
+struct call_arg {
+ ID id;
+ VALUE recv;
+ int narg;
+ VALUE arg;
+};
+
+static VALUE
+call_lit(arg)
+ struct call_arg *arg;
+{
+ return rb_funcall(arg->recv, arg->id, arg->narg, arg->arg);
+}
+
+static VALUE
+except_lit()
+{
+ extern VALUE errstr;
+
+ Error("%s", RSTRING(errstr)->ptr);
+ return Qnil;
+}
+
+static NODE *
+call_op(recv, id, narg, arg1)
+ NODE *recv;
+ ID id;
+ int narg;
+ NODE *arg1;
+{
+ NODE *args;
+
+ value_expr(recv);
+ if (narg == 1)
+ value_expr(arg1);
+
+ if (recv->type != NODE_LIT || recv->type != NODE_STR
+ || (narg == 0 && id == '~'
+ && (TYPE(recv->nd_lit)==T_REGEXP || TYPE(recv->nd_lit)==T_STRING))
+ || arg1->type == NODE_LIT || arg1->type == NODE_STR) {
+ if (narg > 0) {
+ args = NEW_ARRAY(arg1);
+ args->nd_argc = 1;
+ }
+ else {
+ args = Qnil;
+ }
+ return NEW_CALL(recv, id, args);
+ }
+ else {
+ struct call_arg arg_data;
+ NODE *result;
+
+ arg_data.recv = recv->nd_lit;
+ arg_data.id = id;
+ arg_data.narg = narg;
+ if (narg == 1) arg_data.arg = arg1->nd_lit;
+ result = NEW_LIT(rb_resque(call_lit, &arg_data, except_lit, Qnil));
+ freenode(recv);
+ if (narg == 1) freenode(arg1);
+ return result;
+ }
+}
+
+static NODE*
+asignable(id, val)
+ ID id;
+ NODE *val;
+{
+ NODE *lhs;
+
+ if (id == SELF) {
+ lhs = Qnil;
+ Error("Can't change the value of self");
+ }
+ else if (id == NIL) {
+ lhs = Qnil;
+ Error("Can't asign to nil");
+ }
+ else if (is_local_id(id)) {
+ lhs = NEW_LASGN(id, val);
+ }
+ else if (is_global_id(id)) {
+ lhs = NEW_GASGN(id, val);
+ }
+ else if (is_instance_id(id)) {
+ lhs = NEW_IASGN(id, val);
+ }
+ else if (is_const_id(id)) {
+ if (cur_mid || in_single)
+ Error("class constant asigned in method body");
+ lhs = NEW_CASGN(id, val);
+ }
+ else {
+ Bug("bad id for variable");
+ }
+ return lhs;
+}
+
+static NODE *
+aryset(recv, idx, val)
+ NODE *recv, *idx, *val;
+{
+ NODE *args;
+
+ value_expr(recv);
+ return NEW_CALL(recv, ASET, list_append(idx, val));
+}
+
+static NODE *
+attrset(recv, id, val)
+ NODE *recv, *val;
+ ID id;
+{
+ value_expr(recv);
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+
+ return NEW_CALL(recv, id, NEW_ARRAY(val));
+}
+
+static void
+value_expr(node)
+ NODE *node;
+{
+ switch (node->type) {
+ case NODE_RETURN:
+ case NODE_CONTINUE:
+ case NODE_BREAK:
+ case NODE_REDO:
+ case NODE_RETRY:
+ Error("void value expression");
+ break;
+
+ case NODE_BLOCK:
+ if (node->nd_last)
+ return value_expr(node->nd_last->nd_head);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static NODE*
+cond(node)
+ NODE *node;
+{
+ value_expr(node);
+ if (node->type == NODE_STR) {
+ return call_op(NEW_GVAR(rb_intern("$_")),MATCH,1,node);
+ }
+ else if (node->type == NODE_LIT && TYPE(node->nd_lit) == T_REGEXP) {
+ return call_op(node,MATCH,1,NEW_GVAR(rb_intern("$_")));
+ }
+ return node;
+}
+
+static NODE*
+cond2(node)
+ NODE *node;
+{
+ node = cond(node);
+ if (node->type == NODE_LIT) {
+ if (FIXNUM_P(node->nd_lit)) {
+ return call_op(node,EQ,1,NEW_GVAR(rb_intern("$.")));
+ }
+ }
+ return node;
+}
+
+st_table *new_idhash();
+
+static struct local_vars {
+ ID *tbl;
+ int cnt;
+ struct local_vars *next;
+} *lvtbl;
+
+static void
+push_local()
+{
+ struct local_vars *local;
+
+ local = ALLOC(struct local_vars);
+ local->next = lvtbl;
+ local->cnt = 0;
+ local->tbl = Qnil;
+ lvtbl = local;
+}
+
+void
+pop_local()
+{
+ struct local_vars *local = lvtbl;
+
+ lvtbl = local->next;
+ if (local->tbl) local->tbl[0] = local->cnt;
+ free(local);
+}
+
+static ID*
+local_tbl()
+{
+ return lvtbl->tbl;
+}
+
+static int
+local_cnt(id)
+ ID id;
+{
+ int cnt, max;
+
+ if (id == 0) return lvtbl->cnt;
+
+ for (cnt=0, max=lvtbl->cnt; cnt<max ;cnt++) {
+ if (lvtbl->tbl[cnt+1] == id) return cnt;
+ }
+
+ if (lvtbl->tbl == Qnil)
+ lvtbl->tbl = ALLOC_N(ID, 2);
+ else
+ REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2);
+
+ lvtbl->tbl[lvtbl->cnt+1] = id;
+ return lvtbl->cnt++;
+}
+
+static int
+local_id(id)
+ ID id;
+{
+ int i, max;
+
+ if (lvtbl == Qnil) return FALSE;
+ for (i=1, max=lvtbl->cnt+1; i<max; i++) {
+ if (lvtbl->tbl[i] == id) return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+init_top_local()
+{
+ if (lvtbl == Qnil) {
+ push_local();
+ }
+ else if (the_env->local_tbl) {
+ lvtbl->cnt = the_env->local_tbl[0];
+ }
+ else {
+ lvtbl->cnt = 0;
+ }
+ lvtbl->tbl = the_env->local_tbl;
+}
+
+static void
+setup_top_local()
+{
+ if (lvtbl->cnt > 0) {
+ if (the_env->local_vars == Qnil) {
+ the_env->local_vars = ALLOC_N(VALUE, lvtbl->cnt);
+ bzero(the_env->local_vars, lvtbl->cnt * sizeof(VALUE));
+ }
+ else {
+ int i;
+
+ REALLOC_N(the_env->local_vars, VALUE, lvtbl->cnt);
+ for (i=lvtbl->tbl[0]; i<lvtbl->cnt; i++) {
+ the_env->local_vars[i] = Qnil;
+ }
+ }
+ lvtbl->tbl[0] = lvtbl->cnt;
+ the_env->local_tbl = lvtbl->tbl;
+ }
+ else {
+ the_env->local_vars = Qnil;
+ }
+}
+
+void
+yyappend_print()
+{
+ eval_tree =
+ block_append(eval_tree, NEW_CALL(Qnil, rb_intern("print"),
+ NEW_ARRAY(NEW_GVAR(rb_intern("$_")))));
+}
+
+void
+yywhole_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)),
+ eval_tree);
+ }
+ if (chop) {
+ eval_tree =
+ block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")),
+ rb_intern("chop"), Qnil), eval_tree);
+ }
+ eval_tree = NEW_WHILE(NEW_CALL(0,rb_intern("gets"),0),eval_tree);
+}
+
+static struct op_tbl rb_op_tbl[] = {
+ DOT2, "..",
+ '+', "+",
+ '-', "-",
+ '+', "+(binary)",
+ '-', "-(binary)",
+ '*', "*",
+ '/', "/",
+ '%', "%",
+ POW, "**",
+ UPLUS, "+(unary)",
+ UMINUS, "-(unary)",
+ UPLUS, "+@",
+ UMINUS, "-@",
+ '|', "|",
+ '^', "^",
+ '&', "&",
+ CMP, "<=>",
+ '>', ">",
+ GEQ, ">=",
+ '<', "<",
+ LEQ, "<=",
+ EQ, "==",
+ NEQ, "!=",
+ MATCH, "=~",
+ NMATCH, "!~",
+ '!', "!",
+ '~', "~",
+ '!', "!(unary)",
+ '~', "~(unary)",
+ '!', "!@",
+ '~', "~@",
+ AREF, "[]",
+ ASET, "[]=",
+ LSHFT, "<<",
+ RSHFT, ">>",
+ COLON2, "::",
+ Qnil, Qnil,
+};
+
+char *rb_id2name();
+char *rb_class2name();
+
+st_table *rb_symbol_tbl;
+
+#define sym_tbl rb_symbol_tbl
+
+void
+Init_sym()
+{
+ int strcmp();
+
+ sym_tbl = st_init_table(strcmp, st_strhash);
+}
+
+ID
+rb_intern(name)
+ char *name;
+{
+ static ID last_id = LAST_TOKEN;
+ int id;
+ int last;
+
+ if (st_lookup(sym_tbl, name, &id))
+ return id;
+
+ id = ++last_id;
+ id <<= 3;
+ switch (name[0]) {
+ case '$':
+ id |= ID_GLOBAL;
+ break;
+ case '@':
+ id |= ID_INSTANCE;
+ break;
+ case '%':
+ if (name[1] != '\0') {
+ id |= ID_CONST;
+ break;
+ }
+ /* fall through */
+ default:
+ if (name[0] != '_' && !isalpha(name[0]) && !ismbchar(name[0])) {
+ /* operator */
+ int i;
+
+ id = Qnil;
+ for (i=0; rb_op_tbl[i].tok; i++) {
+ if (strcmp(rb_op_tbl[i].name, name) == 0) {
+ id = rb_op_tbl[i].tok;
+ break;
+ }
+ }
+ if (id == Qnil) Bug("Unknown operator `%s'", name);
+ break;
+ }
+ last = strlen(name)-1;
+ if (name[last] == '=') {
+ /* attribute asignment */
+ char *buf = (char*)alloca(last+1);
+
+ strncpy(buf, name, last);
+ buf[last] = '\0';
+ id = rb_intern(buf);
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+ }
+ else {
+ id |= ID_LOCAL;
+ }
+ break;
+ }
+ st_add_direct(sym_tbl, strdup(name), id);
+ return id;
+}
+
+static char *find_ok;
+
+static
+id_find(name, id1, id2)
+ char *name;
+ ID id1, id2;
+{
+ if (id1 == id2) {
+ find_ok = name;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+char *
+rb_id2name(id)
+ ID id;
+{
+ find_ok = Qnil;
+
+ if (id < LAST_TOKEN) {
+ int i = 0;
+
+ for (i=0; rb_op_tbl[i].tok; i++) {
+ if (rb_op_tbl[i].tok == id)
+ return rb_op_tbl[i].name;
+ }
+ }
+
+ st_foreach(sym_tbl, id_find, id);
+ if (!find_ok && is_attrset_id(id)) {
+ char *res;
+ ID id2;
+
+ id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL;
+ res = rb_id2name(id2);
+
+ if (res) {
+ char *buf = (char*)alloca(strlen(res)+2);
+
+ strcpy(buf, res);
+ strcat(buf, "=");
+ rb_intern(buf);
+ return rb_id2name(id);
+ }
+ }
+ return find_ok;
+}
+
+char *
+rb_class2name(class)
+ struct RClass *class;
+{
+ extern st_table *rb_class_tbl;
+
+ find_ok = Qnil;
+
+ switch (TYPE(class)) {
+ case T_CLASS:
+ case T_MODULE:
+ case T_ICLASS:
+ break;
+ default:
+ Fail("0x%x is not a class/module", class);
+ }
+
+ while (FL_TEST(class, FL_SINGLE)) {
+ class = (struct RClass*)class->super;
+ }
+
+ st_foreach(rb_class_tbl, id_find, class);
+ if (find_ok) {
+ return rb_id2name((ID)find_ok);
+ }
+ Bug("class 0x%x not named", class);
+}
diff --git a/process.c b/process.c
new file mode 100644
index 0000000000..7a1dd398c5
--- /dev/null
+++ b/process.c
@@ -0,0 +1,849 @@
+/************************************************
+
+ process.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Tue Aug 10 14:30:50 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "st.h"
+VALUE rb_readonly_hook();
+
+static VALUE
+get_pid()
+{
+ return INT2FIX(getpid());
+}
+
+static VALUE
+get_ppid()
+{
+ return INT2FIX(getppid());
+}
+
+static VALUE status;
+
+#if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4)
+static st_table *pid_tbl;
+#else
+# define WAIT_CALL
+#endif
+
+int
+rb_waitpid(pid, flags)
+ int pid;
+ int flags;
+{
+ int result, st;
+
+#ifdef HAVE_WAITPID
+ result = waitpid(pid, &st, flags);
+#else
+#ifdef HAVE_WAIT4
+ result = wait4(pid, &st, flags, NULL);
+#else
+ if (pid_tbl && st_lookup(pid_tbl, pid, &st)) {
+ status = INT2FIX(st);
+ st_delete(pid_tbl, &pid, NULL);
+ return pid;
+ }
+
+ if (flags)
+ Fail("Can't do waitpid with flags");
+
+ for (;;) {
+ result = wait(&st);
+ if (result < 0) return -1;
+ if (result == pid) {
+ break;
+ }
+ if (!pid_tbl)
+ pid_tbl = st_init_table(ST_NUMCMP, ST_NUMHASH);
+ st_insert(pid_tbl, pid, st);
+ }
+#endif
+#endif
+ status = INT2FIX(st);
+ return result;
+}
+
+#ifndef WAIT_CALL
+static int wait_pid;
+static int wait_status;
+
+static wait_each(key, value)
+ int key, value;
+{
+ wait_pid = key;
+ wait_status = value;
+ return ST_DELETE;
+}
+#endif
+
+static VALUE
+Fwait(obj)
+{
+ 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;
+ }
+#endif
+
+ if ((pid = wait(&state)) < 0) {
+ if (errno == ECHILD) return Qnil;
+ rb_sys_fail(Qnil);
+ }
+ status = INT2FIX(state);
+ return INT2FIX(pid);
+}
+
+static VALUE
+Fwaitpid(obj, vpid, vflags)
+ VALUE obj, vpid, vflags;
+{
+ int pid, flags;
+
+ if (vflags == Qnil) flags = Qnil;
+ else flags = FIX2UINT(vflags);
+
+ if ((pid = rb_waitpid(FIX2UINT(vpid), flags)) < 0)
+ rb_sys_fail(Qnil);
+ return INT2FIX(pid);
+}
+
+char *strtok();
+
+rb_proc_exec(str)
+ char *str;
+{
+ char *s = str, *t;
+ char **argv, **a;
+
+ for (s=str; *s; s++) {
+ if (*s != ' ' && !isalpha(*s) && index("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
+ execl("/bin/sh", "sh", "-c", str, (char *)NULL);
+ return -1;
+ }
+ }
+ a = argv = (char**)alloca(((s - str)/2+2)*sizeof(char*));
+ s = (char*)alloca(s - str + 1);
+ strcpy(s, str);
+ if (*a++ = strtok(s, " \t")) {
+ while (t = strtok(NULL, " \t")) {
+ *a++ = t;
+ }
+ *a = NULL;
+ }
+ if (argv[0]) {
+ execvp(argv[0], argv);
+ }
+ return -1;
+}
+
+static VALUE
+Fexec(obj, str)
+ VALUE obj;
+ struct RString *str;
+{
+ Check_Type(str, T_STRING);
+ rb_proc_exec(str->ptr);
+ rb_sys_fail(str->ptr);
+}
+
+static VALUE
+Ffork(obj)
+ VALUE obj;
+{
+ int pid;
+
+ switch (pid = fork()) {
+ case 0:
+ return Qnil;
+
+ case -1:
+ rb_sys_fail(Qnil);
+ break;
+
+ default:
+ return INT2FIX(pid);
+ }
+}
+
+static VALUE
+F_exit(obj, status)
+ VALUE obj, status;
+{
+ int code = -1;
+
+ if (FIXNUM_P(status)) {
+ code = INT2FIX(status);
+ }
+
+ _exit(code);
+
+ /* not reached */
+ return Qnil;
+}
+
+void
+rb_syswait(pid)
+ int pid;
+{
+ RETSIGTYPE (*hfunc)(), (*ifunc)(), (*qfunc)();
+
+ 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");
+
+ signal(SIGHUP, hfunc);
+ signal(SIGINT, ifunc);
+ signal(SIGQUIT, qfunc);
+}
+
+static VALUE
+Fsystem(obj, str)
+ VALUE obj;
+ struct RString *str;
+{
+ int pid, w;
+
+ Check_Type(str, T_STRING);
+
+ fflush(stdin); /* is it really needed? */
+ fflush(stdout);
+ fflush(stderr);
+ if (*str->ptr == '\0') return INT2FIX(0);
+
+ retry:
+ switch (pid = vfork()) {
+ case 0:
+ rb_proc_exec(str->ptr);
+ _exit(127);
+ break; /* not reached */
+
+ case -1:
+ if (errno == EAGAIN) {
+ sleep(5);
+ goto retry;
+ }
+ rb_sys_fail(str->ptr);
+ break;
+
+ default:
+ rb_syswait(pid);
+ }
+
+ return status;
+}
+
+static struct signals {
+ char *signm;
+ int signo;
+} siglist [] = {
+#ifdef SIGHUP
+ "HUP", SIGHUP,
+#endif
+#ifdef SIGINT
+ "INT", SIGINT,
+#endif
+#ifdef SIGQUIT
+ "QUIT", SIGQUIT,
+#endif
+#ifdef SIGILL
+ "ILL", SIGILL,
+#endif
+#ifdef SIGTRAP
+ "TRAP", SIGTRAP,
+#endif
+#ifdef SIGIOT
+ "IOT", SIGIOT,
+#endif
+#ifdef SIGABRT
+ "ABRT", SIGABRT,
+#endif
+#ifdef SIGEMT
+ "EMT", SIGEMT,
+#endif
+#ifdef SIGFPE
+ "FPE", SIGFPE,
+#endif
+#ifdef SIGKILL
+ "KILL", SIGKILL,
+#endif
+#ifdef SIGBUS
+ "BUS", SIGBUS,
+#endif
+#ifdef SIGSEGV
+ "SEGV", SIGSEGV,
+#endif
+#ifdef SIGSYS
+ "SYS", SIGSYS,
+#endif
+#ifdef SIGPIPE
+ "PIPE", SIGPIPE,
+#endif
+#ifdef SIGALRM
+ "ALRM", SIGALRM,
+#endif
+#ifdef SIGTERM
+ "TERM", SIGTERM,
+#endif
+#ifdef SIGURG
+ "URG", SIGURG,
+#endif
+#ifdef SIGSTOP
+ "STOP", SIGSTOP,
+#endif
+#ifdef SIGTSTP
+ "TSTP", SIGTSTP,
+#endif
+#ifdef SIGCONT
+ "CONT", SIGCONT,
+#endif
+#ifdef SIGCHLD
+ "CHLD", SIGCHLD,
+#endif
+#ifdef SIGCLD
+ "CLD", SIGCLD,
+#else
+# ifdef SIGCHLD
+ "CLD", SIGCHLD,
+# endif
+#endif
+#ifdef SIGTTIN
+ "TTIN", SIGTTIN,
+#endif
+#ifdef SIGTTOU
+ "TTOU", SIGTTOU,
+#endif
+#ifdef SIGIO
+ "IO", SIGIO,
+#endif
+#ifdef SIGXCPU
+ "XCPU", SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ "XFSZ", SIGXFSZ,
+#endif
+#ifdef SIGVTALRM
+ "VTALRM", SIGVTALRM,
+#endif
+#ifdef SIGPROF
+ "PROF", SIGPROF,
+#endif
+#ifdef SIGWINCH
+ "WINCH", SIGWINCH,
+#endif
+#ifdef SIGUSR1
+ "USR1", SIGUSR1,
+#endif
+#ifdef SIGUSR2
+ "USR2", SIGUSR2,
+#endif
+#ifdef SIGLOST
+ "LOST", SIGLOST,
+#endif
+#ifdef SIGMSG
+ "MSG", SIGMSG,
+#endif
+#ifdef SIGPWR
+ "PWR", SIGPWR,
+#endif
+#ifdef SIGPOLL
+ "POLL", SIGPOLL,
+#endif
+#ifdef SIGDANGER
+ "DANGER", SIGDANGER,
+#endif
+#ifdef SIGMIGRATE
+ "MIGRATE", SIGMIGRATE,
+#endif
+#ifdef SIGPRE
+ "PRE", SIGPRE,
+#endif
+#ifdef SIGGRANT
+ "GRANT", SIGGRANT,
+#endif
+#ifdef SIGRETRACT
+ "RETRACT", SIGRETRACT,
+#endif
+#ifdef SIGSOUND
+ "SOUND", SIGSOUND,
+#endif
+ NULL, 0,
+};
+
+static int
+signm2signo(nm)
+ char *nm;
+{
+ struct signals *sigs;
+
+ for (sigs = siglist; sigs->signm; sigs++)
+ if (strcmp(sigs->signm, nm) == 0)
+ return sigs->signo;
+ return 0;
+}
+
+static VALUE
+Fkill(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int sig;
+ int i;
+
+ if (argc < 3)
+ Fail("wrong # of arguments -- kill(sig, pid...)");
+ switch (TYPE(argv[1])) {
+ case T_FIXNUM:
+ sig = FIX2UINT(argv[1]);
+ break;
+
+ case T_STRING:
+ {
+ int negative = 0;
+
+ char *s = RSTRING(argv[1])->ptr;
+ if (*s == '-') {
+ negative++;
+ s++;
+ }
+ if (strncmp("SIG", s, 3) == 0)
+ s += 3;
+ if((sig = signm2signo(s)) == 0)
+ Fail("Unrecognized signal name `%s'", s);
+
+ if (negative)
+ sig = -sig;
+ }
+ break;
+
+ default:
+ Fail("bad signal type %s", rb_class2name(CLASS_OF(argv[1])));
+ break;
+ }
+
+ if (sig < 0) {
+ sig = -sig;
+ for (i=2; i<argc; i++) {
+ int pid = NUM2INT(argv[i]);
+#ifdef HAS_KILLPG
+ if (killpg(pid, sig) < 0)
+#else
+ if (kill(-pid, sig) < 0)
+#endif
+ rb_sys_fail(Qnil);
+ }
+ }
+ else {
+ for (i=2; i<argc; i++) {
+ Check_Type(argv[i], T_FIXNUM);
+ if (kill(FIX2UINT(argv[i]), sig) < 0)
+ rb_sys_fail(Qnil);
+ }
+ }
+ return INT2FIX(i-2);
+}
+
+static VALUE trap_list[NSIG];
+#ifdef SAFE_SIGHANDLE
+static int trap_pending_list[NSIG];
+int trap_pending;
+static int trap_immediate;
+#endif
+
+void
+mark_trap_list()
+{
+ int i;
+
+ for (i=0; i<NSIG; i++) {
+ if (trap_list[i])
+ mark(trap_list[i]);
+ }
+}
+
+static RETSIGTYPE
+sighandle(sig)
+ int sig;
+{
+ if (sig >= NSIG || trap_list[sig] == Qnil)
+ Fail("trap_handler: Bad signal %d", sig);
+
+#ifndef HAVE_BSD_SIGNALS
+ signal(sig, sighandle);
+#endif
+
+#ifdef SAFE_SIGHANDLE
+ if (trap_immediate)
+ rb_trap_eval(trap_list[sig]);
+ else {
+ trap_pending++;
+ trap_pending_list[sig]++;
+ }
+#else
+ rb_trap_eval(trap_list[sig]);
+#endif
+}
+
+void
+rb_trap_exit()
+{
+ if (trap_list[0])
+ rb_trap_eval(trap_list[0]);
+}
+
+#if defined(SAFE_SIGHANDLE)
+rb_trap_exec()
+{
+ int i;
+
+ trap_pending = 0;
+ for (i=0; i<NSIG; i++) {
+ if (trap_pending_list[i]) {
+ trap_pending_list[i] = 0;
+ rb_trap_eval(trap_list[i]);
+ }
+ }
+}
+
+#ifdef HAVE_SYSCALL_H
+#include <syscall.h>
+
+#ifdef SYS_read
+int
+read(fd, buf, nbytes)
+ int fd, nbytes;
+ char *buf;
+{
+ int res;
+
+ trap_immediate++;
+ res = syscall(SYS_read, fd, buf, nbytes);
+ trap_immediate = 0;
+ return res;
+}
+#endif /* SYS_read */
+
+#ifdef SYS_wait
+int
+wait(status)
+ union wait *status;
+{
+ int res;
+
+ trap_immediate++;
+ res = syscall(SYS_wait, status);
+ trap_immediate =0;
+ return res;
+}
+#endif /* SYS_wait */
+
+#ifdef SYS_sigpause
+int
+sigpause(mask)
+ int mask;
+{
+ int res;
+
+ trap_immediate++;
+ res = syscall(SYS_sigpause, mask);
+ trap_immediate =0;
+ return res;
+}
+#endif /* SYS_sigpause */
+
+/* linux syscall(select) doesn't work file. */
+#if defined(SYS_select) && !defined(linux)
+#include <sys/types.h>
+
+int
+select(nfds, readfds, writefds, exceptfds, timeout)
+ int nfds;
+ fd_set *readfds, *writefds, *exceptfds;
+ struct timeval *timeout;
+{
+ int res;
+
+ trap_immediate++;
+ res = syscall(SYS_select, nfds, readfds, writefds, exceptfds, timeout);
+ trap_immediate =0;
+ return res;
+}
+#endif /* SYS_select */
+
+#endif /* HAVE_SYSCALL_H */
+#endif /* SAFE_SIGHANDLE */
+
+static VALUE
+Ftrap(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ RETSIGTYPE (*func)();
+ VALUE command;
+ int i, sig;
+ int mask;
+
+ if (argc < 3)
+ Fail("wrong # of arguments -- kill(cmd, sig...)");
+
+ /* disable interrupt */
+ mask = sigblock(~0);
+
+ func = sighandle;
+
+ if (argv[1] == Qnil) {
+ func = SIG_IGN;
+ command = Qnil;
+ }
+ else {
+ Check_Type(argv[1], T_STRING);
+ command = argv[1];
+ if (RSTRING(argv[1])->len == 0) {
+ func = SIG_IGN;
+ }
+ else if (RSTRING(argv[1])->len == 7) {
+ if (strncmp(RSTRING(argv[1])->ptr, "SIG_IGN", 7) == 0) {
+ func = SIG_IGN;
+ }
+ else if (strncmp(RSTRING(argv[1])->ptr, "SIG_DFL", 7) == 0) {
+ func = SIG_DFL;
+ }
+ else if (strncmp(RSTRING(argv[1])->ptr, "DEFAULT", 7) == 0) {
+ func = SIG_DFL;
+ }
+ }
+ else if (RSTRING(argv[1])->len == 6) {
+ if (strncmp(RSTRING(argv[1])->ptr, "IGNORE", 6) == 0) {
+ func = SIG_IGN;
+ }
+ }
+ }
+ if (func == SIG_IGN || func == SIG_DFL)
+ command = Qnil;
+
+ for (i=2; i<argc; i++) {
+ if (TYPE(argv[i]) == T_STRING) {
+ char *s = RSTRING(argv[i])->ptr;
+
+ if (strncmp("SIG", s, 3) == 0)
+ s += 3;
+ sig = signm2signo(s);
+ if (sig == 0 && strcmp(s, "EXIT") != 0)
+ Fail("Invalid signal SIG%s", s);
+ }
+ else {
+ sig = NUM2INT(argv[i]);
+ }
+ if (i < 0 || i > NSIG)
+ Fail("Invalid signal no %d", sig);
+
+ signal(sig, sighandle);
+ trap_list[sig] = command;
+ /* enable at least specified signal. */
+ mask &= ~sigmask(sig);
+ }
+ sigsetmask(mask);
+ return Qnil;
+}
+
+Fsleep(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int beg, end;
+
+ beg = time(0);
+ if (argc == 1) {
+ sleep((32767<<16)+32767);
+ }
+ else if (argc == 2) {
+ sleep(NUM2INT(argv[1]));
+ }
+ else {
+ Fail("wrong # of arguments");
+ }
+
+ end = time(0) - beg;
+
+ return int2inum(end);
+}
+
+static VALUE
+Fproc_getpgrp(obj, args)
+ VALUE obj, args;
+{
+ VALUE vpid;
+ int pid, pgrp;
+
+ rb_scan_args(args, "01", &vpid);
+ if (vpid == Qnil) {
+ pid = 0;
+ }
+ else {
+ pid = NUM2INT(vpid);
+ }
+
+ pgrp = getpgrp(pid);
+ return INT2FIX(pgrp);
+}
+
+static VALUE
+Fproc_setpgrp(obj, pid, pgrp)
+ VALUE obj, pid, pgrp;
+{
+ int ipid, ipgrp;
+
+ ipid = NUM2INT(pid);
+ ipgrp = NUM2INT(pgrp);
+
+ if (getpgrp(ipid, ipgrp) == -1) rb_sys_fail(Qnil);
+
+ return Qnil;
+}
+
+static VALUE
+Fproc_getpriority(obj, which, who)
+ VALUE obj, which, who;
+{
+ int prio, iwhich, iwho;
+
+ iwhich = NUM2INT(which);
+ iwho = NUM2INT(who);
+
+ prio = getpriority(iwhich, iwho);
+ if (prio == -1) rb_sys_fail(Qnil);
+ return INT2FIX(prio);
+}
+
+static VALUE
+Fproc_setpriority(obj, which, who, prio)
+ VALUE obj, which, who, prio;
+{
+ int iwhich, iwho, iprio;
+
+ iwhich = NUM2INT(which);
+ iwho = NUM2INT(who);
+ iprio = NUM2INT(prio);
+
+ if (setpriority(iwhich, iwho, iprio) == -1)
+ rb_sys_fail(Qnil);
+ return Qnil;
+}
+
+static VALUE
+Fproc_getuid(obj)
+ VALUE obj;
+{
+ int uid = getuid();
+ return INT2FIX(uid);
+}
+
+static VALUE
+Fproc_setuid(obj, id)
+ VALUE obj, id;
+{
+ int uid;
+
+ uid = NUM2INT(id);
+#ifdef HAVE_SETRUID
+ setruid(uid);
+#else
+#ifdef HAVE_SETREUID
+ setreuid(uid, -1);
+#else
+ {
+ if (geteuid() == uid)
+ setuid(uid);
+ else
+ Fail("getruid not implemented");
+ }
+#endif
+#endif
+ return INT2FIX(uid);
+}
+
+static VALUE
+Fproc_geteuid(obj)
+ VALUE obj;
+{
+ int euid = geteuid();
+ return INT2FIX(euid);
+}
+
+static VALUE
+Fproc_seteuid(obj, euid)
+ VALUE obj, euid;
+{
+ if (seteuid(NUM2INT(euid)) == -1)
+ rb_sys_fail(Qnil);
+ return euid;
+}
+
+VALUE M_Process;
+Init_process()
+{
+ extern VALUE C_Kernel;
+
+ rb_define_variable("$$", Qnil, get_pid, rb_readonly_hook);
+ rb_define_variable("$?", &status, Qnil, rb_readonly_hook);
+ rb_define_func(C_Kernel, "exec", Fexec, 1);
+ rb_define_func(C_Kernel, "fork", Ffork, 0);
+ rb_define_func(C_Kernel, "_exit", Ffork, 1);
+ rb_define_func(C_Kernel, "wait", Fwait, 0);
+ rb_define_func(C_Kernel, "waitpid", Fwaitpid, 2);
+ rb_define_func(C_Kernel, "system", Fsystem, 1);
+ rb_define_func(C_Kernel, "kill", Fkill, -1);
+ rb_define_func(C_Kernel, "trap", Ftrap, -1);
+ rb_define_func(C_Kernel, "sleep", Fsleep, -1);
+
+ M_Process = rb_define_module("Process");
+
+ rb_define_single_method(M_Process, "fork", Ffork, 0);
+ rb_define_single_method(M_Process, "_exit", Ffork, 1);
+ rb_define_single_method(M_Process, "wait", Fwait, 0);
+ rb_define_single_method(M_Process, "waitpid", Fwaitpid, 2);
+ rb_define_single_method(M_Process, "kill", Fkill, -1);
+
+ rb_define_mfunc(M_Process, "pid", get_pid, 0);
+ rb_define_mfunc(M_Process, "ppid", get_ppid, 0);
+
+ rb_define_mfunc(M_Process, "getpgrp", Fproc_getpgrp, -2);
+ rb_define_mfunc(M_Process, "setpgrp", Fproc_setpgrp, 2);
+
+ rb_define_mfunc(M_Process, "getpriority", Fproc_getpriority, 2);
+ rb_define_mfunc(M_Process, "setpriority", Fproc_setpriority, 3);
+
+ rb_define_const(M_Process, "%PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
+ rb_define_const(M_Process, "%PRIO_PGRP", INT2FIX(PRIO_PGRP));
+ rb_define_const(M_Process, "%PRIO_USER", INT2FIX(PRIO_USER));
+
+ rb_define_single_method(M_Process, "uid", Fproc_getuid, 0);
+ rb_define_method(M_Process, "uid", Fproc_getuid, 0);
+ rb_define_single_method(M_Process, "uid=", Fproc_setuid, 1);
+ rb_define_method(M_Process, "uid=", Fproc_setuid, 1);
+ rb_define_single_method(M_Process, "euid", Fproc_geteuid, 0);
+ rb_define_method(M_Process, "euid", Fproc_geteuid, 0);
+ rb_define_single_method(M_Process, "euid=", Fproc_seteuid, 1);
+ rb_define_method(M_Process, "euid=", Fproc_seteuid, 1);
+}
diff --git a/random.c b/random.c
new file mode 100644
index 0000000000..ed21c7ec2b
--- /dev/null
+++ b/random.c
@@ -0,0 +1,80 @@
+/************************************************
+
+ random.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Fri Dec 24 16:39:21 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+static int first = 1;
+static char state[256];
+
+static VALUE
+Fsrand(obj, args)
+ VALUE obj, args;
+{
+ int seed, old;
+#ifdef HAVE_RANDOM
+ static int saved_seed;
+#endif
+
+ if (rb_scan_args(args, "01", &seed) == 0) {
+ seed = time(0);
+ }
+ else {
+ seed = NUM2INT(seed);
+ }
+
+#ifdef HAVE_RANDOM
+ if (first == 1) {
+ initstate(1, state, sizeof state);
+ first = 0;
+ }
+ else {
+ setstate(state);
+ }
+
+ srandom(seed);
+ old = saved_seed;
+ saved_seed = seed;
+
+ return int2inum(old);
+#else
+ old = srand(seed);
+ return int2inum(old);
+#endif
+}
+
+static VALUE
+Frand(obj, max)
+ VALUE obj, max;
+{
+ int val;
+
+#ifdef HAVE_RANDOM
+ if (first == 1) {
+ initstate(1, state, sizeof state);
+ first = 0;
+ }
+ val = random() % NUM2INT(max);
+#else
+ val = rand() % NUM2INT(max);
+#endif
+
+ if (val < 0) val = -val;
+ return int2inum(val);
+}
+
+Init_Random()
+{
+ extern VALUE C_Kernel;
+
+ rb_define_func(C_Kernel, "srand", Fsrand, -2);
+ rb_define_func(C_Kernel, "rand", Frand, 1);
+}
diff --git a/range.c b/range.c
new file mode 100644
index 0000000000..816ce27a1a
--- /dev/null
+++ b/range.c
@@ -0,0 +1,149 @@
+/************************************************
+
+ range.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Thu Aug 19 17:46:47 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+
+VALUE M_Comparable;
+VALUE C_Range;
+
+static ID next, eq;
+
+VALUE
+range_new(class, start, end)
+ VALUE class, start, end;
+{
+ VALUE obj;
+
+ if (!obj_is_kind_of(start, M_Comparable) || TYPE(start) != TYPE(end)) {
+ Fail("bad value for range");
+ }
+
+ obj = obj_alloc(class);
+
+ rb_iv_set(obj, "start", start);
+ rb_iv_set(obj, "end", end);
+
+ return obj;
+}
+
+static VALUE
+Frng_match(rng, obj)
+ VALUE rng, obj;
+{
+ VALUE beg, end;
+
+ beg = rb_iv_get(rng, "start");
+ end = rb_iv_get(rng, "end");
+
+ if (FIXNUM_P(beg) && FIXNUM_P(obj)) {
+ if (FIX2INT(beg) <= FIX2INT(obj) && FIX2INT(obj) <= FIX2INT(end)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else {
+ if (rb_funcall(beg, rb_intern("<="), 1, obj) &&
+ rb_funcall(end, rb_intern(">="), 1, obj)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+static VALUE
+Frng_each(obj)
+ VALUE obj;
+{
+ VALUE b, e, current;
+
+ b = rb_iv_get(obj, "start");
+ e = rb_iv_get(obj, "end");
+
+ if (FIXNUM_P(b)) { /* fixnum is a special case(for performance) */
+ int beg, end, i;
+
+ beg = FIX2INT(b);
+ end = FIX2INT(e);
+
+ for (i=beg; i<=end; i++) {
+ rb_yield(INT2FIX(i));
+ }
+ }
+ else {
+ GC_LINK;
+ GC_PRO3(current, b);
+ for (;;) {
+ rb_yield(current);
+ if (rb_funcall(current, eq, 1, e)) break;
+ current = rb_funcall(current, next, 0);
+ }
+ GC_UNLINK;
+ }
+
+ return Qnil;
+}
+
+static VALUE
+Frng_start(obj)
+ VALUE obj;
+{
+ VALUE b;
+
+ b = rb_iv_get(obj, "start");
+ return b;
+}
+
+static VALUE
+Frng_end(obj)
+ VALUE obj;
+{
+ VALUE e;
+
+ e = rb_iv_get(obj, "end");
+ return e;
+}
+
+static VALUE
+Frng_to_s(obj)
+ VALUE obj;
+{
+ int beg, end;
+ VALUE fmt, str, args[4];
+
+
+ beg = rb_iv_get(obj, "start");
+ end = rb_iv_get(obj, "end");
+
+ GC_LINK;
+ GC_PRO3(fmt, str_new2("%d..%d"));
+ args[0] = obj; args[1] = fmt; args[2]= beg; args[3] = end;
+ str = Fsprintf(4, args);
+ GC_UNLINK;
+
+ return str;
+}
+
+extern VALUE M_Enumerable;
+
+Init_Range()
+{
+ C_Range = rb_define_class("Range", C_Object);
+ rb_include_module(C_Range, M_Enumerable);
+ rb_define_method(C_Range, "=~", Frng_match, 1);
+ rb_define_method(C_Range, "each", Frng_each, 0);
+ rb_define_method(C_Range, "start", Frng_start, 0);
+ rb_define_method(C_Range, "end", Frng_end, 0);
+ rb_define_method(C_Range, "to_s", Frng_to_s, 0);
+
+ eq = rb_intern("==");
+ next = rb_intern("next");
+}
diff --git a/re.c b/re.c
new file mode 100644
index 0000000000..ac7047833c
--- /dev/null
+++ b/re.c
@@ -0,0 +1,442 @@
+/************************************************
+
+ re.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:36 $
+ created at: Mon Aug 9 18:24:49 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "re.h"
+
+/* Generate compiled regular expressions */
+#if 'a' == 97 /* it's ascii */
+static char casetable[] = {
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ /* ' ' '!' '"' '#' '$' '%' '&' ''' */
+ '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+ /* '(' ')' '*' '+' ',' '-' '.' '/' */
+ '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+ /* '0' '1' '2' '3' '4' '5' '6' '7' */
+ '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+ /* '8' '9' ':' ';' '<' '=' '>' '?' */
+ '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+ /* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' */
+ '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */
+ '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+ /* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' */
+ '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'x' 'y' 'z' '{' '|' '}' '~' */
+ '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+ '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+ '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+ '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+ '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
+ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
+ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
+ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
+ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+#else
+>>> "You lose. You will need a translation table for your character set." <<<
+#endif
+
+#define min(a,b) (((a)>(b))?(b):(a))
+
+int
+str_cicmp(str1, str2)
+ struct RString *str1, *str2;
+{
+ int len, i;
+ char *p1, *p2;
+
+ len = min(str1->len, str2->len);
+ p1 = str1->ptr; p2 = str2->ptr;
+
+ for (i = 0; i < len; i++, p1++, p2++) {
+ if (casetable[*p1] != casetable[*p2])
+ return casetable[*p1] - casetable[*p2];
+ }
+ return str1->len - str2->len;
+}
+
+Regexp*
+make_regexp(s, len)
+char *s;
+int len;
+{
+ Regexp *rp;
+ char *err;
+ register int c;
+
+ /* Handle escaped characters first. */
+
+ /* Build a copy of the string (in dest) with the
+ escaped characters translated, and generate the regex
+ from that.
+ */
+
+ rp = ALLOC(Regexp);
+ bzero((char *)rp, sizeof(Regexp));
+ rp->pat.buffer = ALLOC_N(char, 16);
+ rp->pat.allocated = 16;
+ rp->pat.fastmap = ALLOC_N(char, 256);
+
+ if ((err = re_compile_pattern(s, (size_t)len, &(rp->pat))) != NULL)
+ Fail("%s: /%s/", err, s);
+ return rp;
+}
+
+struct match {
+ UINT len;
+ char *ptr;
+ struct re_registers regs;
+};
+
+static void
+free_match(data)
+ struct match *data;
+{
+ free(data->ptr);
+}
+
+VALUE last_match_data;
+
+int
+research(reg, str, start, ignorecase)
+ struct RRegexp *reg;
+ struct RString *str;
+ int start;
+ int ignorecase;
+{
+ int result;
+
+ if (ignorecase)
+ reg->ptr->pat.translate = casetable;
+ else
+ reg->ptr->pat.translate = NULL;
+
+ if (start >= str->len) return -1;
+ result = re_search(&(reg->ptr->pat), str->ptr, str->len,
+ start, str->len - start, &(reg->ptr->regs));
+
+ if (result >= 0) {
+ struct RData *obj;
+ struct match *data;
+ int beg, i;
+
+ obj = (struct RData*)newobj(sizeof(struct RData)+sizeof(struct match));
+ OBJSETUP(obj, C_Data, T_DATA);
+ obj->dfree = free_match;
+ data = (struct match*)DATA_PTR(obj);
+ bzero(data, sizeof(struct match));
+ beg = reg->ptr->regs.start[0];
+ data->len = reg->ptr->regs.end[0] - beg;
+ data->ptr = ALLOC_N(char, data->len+1);
+ memcpy(data->ptr, str->ptr + beg, data->len);
+ data->ptr[data->len] = '\0';
+ for (i=0; i<RE_NREGS; i++) {
+ if (reg->ptr->regs.start[i] == -1) break;
+ data->regs.start[i] = reg->ptr->regs.start[i] - beg;
+ data->regs.end[i] = reg->ptr->regs.end[i] - beg;
+ }
+ last_match_data = (VALUE)obj;
+ }
+
+ return result;
+}
+
+static VALUE
+nth_match(nth)
+ int nth;
+{
+ if (nth >= RE_NREGS) {
+ Fail("argument out of range %d, %d", nth, RE_NREGS);
+ }
+ if (last_match_data) {
+ int start, end, len;
+ struct match *match;
+
+ match = (struct match*)DATA_PTR(last_match_data);
+ if (nth == 0) return str_new(match->ptr, match->len);
+ start = match->regs.start[nth];
+ if (start == -1) return Qnil;
+ end = match->regs.end[nth];
+ len = end - start;
+ return str_new(match->ptr + start, len);
+ }
+ return Qnil;
+}
+
+VALUE
+re_last_match(id)
+ ID id;
+{
+ return nth_match(0);
+}
+
+#ifdef __STDC__
+#define CONCAT(a,b) a##b
+#else
+#define CONCAT(a,b) a/**/b
+#endif
+
+#define GET_MATCH(n) CONCAT(get_macth,n)
+#define GET_MATCH_FUNC(n) GET_MATCH(n)(id) ID id; { return nth_match(n); }
+
+GET_MATCH_FUNC(1);
+GET_MATCH_FUNC(2);
+GET_MATCH_FUNC(3);
+GET_MATCH_FUNC(4);
+GET_MATCH_FUNC(5);
+GET_MATCH_FUNC(6);
+GET_MATCH_FUNC(7);
+GET_MATCH_FUNC(8);
+GET_MATCH_FUNC(9);
+
+static VALUE
+store_match_data(val)
+ struct RArray *val;
+{
+ Check_Type(val, T_DATA);
+ return (VALUE)val;
+}
+
+void
+reg_free(rp)
+Regexp *rp;
+{
+ free(rp->pat.buffer);
+ free(rp->pat.fastmap);
+ free(rp);
+}
+
+void
+reg_error(s)
+const char *s;
+{
+ Fail(s);
+}
+
+VALUE ignorecase;
+VALUE C_Regexp;
+
+static VALUE
+regexp_new_1(class, s, len)
+ VALUE class;
+ char *s;
+ int len;
+{
+ NEWOBJ(re, struct RRegexp);
+ OBJSETUP(re, class, T_REGEXP);
+
+ re->ptr = make_regexp(s, len);
+ re->str = ALLOC_N(char, len+1);
+ memcpy(re->str, s, len);
+ re->str[len] = '\0';
+ re->len = len;
+ return (VALUE)re;
+}
+
+VALUE
+regexp_new(s, len)
+ char *s;
+ int len;
+{
+ return regexp_new_1(C_Regexp, s, len);
+}
+
+static VALUE str_cache, reg_cache;
+
+VALUE
+re_regcomp(str)
+ struct RString *str;
+{
+ if (str_cache && RSTRING(str_cache)->len == str->len &&
+ memcmp(RSTRING(str_cache)->ptr, str->ptr, str->len))
+ return reg_cache;
+
+ str_cache = (VALUE)str;
+ return reg_cache = regexp_new(str->ptr, str->len);
+}
+
+VALUE
+Freg_match(re, str)
+ struct RRegexp *re;
+ struct RString *str;
+{
+ int start;
+
+ Check_Type(str, T_STRING);
+ start = research(re, str, 0, ignorecase);
+ if (start == -1) {
+ return Qnil;
+ }
+ return INT2FIX(start);
+}
+
+VALUE
+Freg_match2(re)
+ struct RRegexp *re;
+{
+ extern VALUE rb_lastline;
+ int start;
+
+ if (TYPE(rb_lastline) != T_STRING)
+ Fail("$_ is not a string");
+
+ start = research(re, rb_lastline, 0, ignorecase);
+ if (start == -1) {
+ return Qnil;
+ }
+ return INT2FIX(start);
+}
+
+static VALUE
+Freg_compile(re, str)
+ VALUE re;
+ struct RString *str;
+{
+ Check_Type(str, T_STRING);
+ return regexp_new_1(re, str->ptr, str->ptr);
+}
+
+static VALUE
+Freg_new(re, src)
+ VALUE re, src;
+{
+ switch (TYPE(src)) {
+ case T_STRING:
+ return regexp_new_1(re, RREGEXP(src)->ptr, RREGEXP(src)->len);
+
+ case T_REGEXP:
+ return regexp_new_1(re, RREGEXP(src)->str, RREGEXP(src)->len);
+
+ default:
+ Check_Type(src, T_REGEXP);
+ }
+ /* not reached */
+ return Qnil;
+}
+
+static VALUE
+Freg_clone(re)
+ struct RRegexp *re;
+{
+ return regexp_new_1(CLASS_OF(re), re->str, re->len);
+}
+
+VALUE
+re_regsub(str)
+ struct RString *str;
+{
+ VALUE val;
+ char *p, *s, *e, c;
+ int no, len;
+
+ p = s = str->ptr;
+ e = s + str->len;
+
+ GC_LINK;
+ GC_PRO2(val);
+ while (s < e) {
+ c = *s++;
+ if (c == '&')
+ no = 0;
+ else if (c == '\\' && '0' <= *s && *s <= '9')
+ no = *s++ - '0';
+ else
+ no = -1;
+
+ if (no >= 0 || c == '\\') {
+ if (val == Qnil) {
+ val = str_new(p, s-p-2);
+ }
+ else {
+ str_cat(val, p, s-p-2);
+ }
+ p = s;
+ }
+
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*s == '\\' || *s == '&'))
+ p = ++s;
+ } else if (last_match_data) {
+ struct match *match;
+
+#define BEG(no) match->regs.start[no]
+#define END(no) match->regs.end[no]
+
+ match = (struct match*)DATA_PTR(last_match_data);
+ if (BEG(no) == -1) continue;
+ str_cat(val, match->ptr+BEG(no), END(no)-BEG(no));
+ }
+ }
+ GC_UNLINK;
+
+ if (val == Qnil) return (VALUE)str;
+ if (RSTRING(val)->len == 0) {
+ obj_free(val); /* free for cost */
+ return (VALUE)str;
+ }
+ return val;
+}
+
+long reg_syntax = RE_SYNTAX_POSIX_EXTENDED;
+VALUE rb_readonly_hook();
+
+void
+Init_Regexp()
+{
+ (void) re_set_syntax(reg_syntax);
+
+ rb_define_variable("$~", last_match_data, Qnil, store_match_data);
+
+ rb_define_variable("$&", Qnil, re_last_match, rb_readonly_hook);
+
+ rb_define_variable("$1", Qnil, GET_MATCH(1), rb_readonly_hook);
+ rb_define_variable("$2", Qnil, GET_MATCH(2), rb_readonly_hook);
+ rb_define_variable("$3", Qnil, GET_MATCH(3), rb_readonly_hook);
+ rb_define_variable("$4", Qnil, GET_MATCH(4), rb_readonly_hook);
+ rb_define_variable("$5", Qnil, GET_MATCH(5), rb_readonly_hook);
+ rb_define_variable("$6", Qnil, GET_MATCH(6), rb_readonly_hook);
+ rb_define_variable("$7", Qnil, GET_MATCH(7), rb_readonly_hook);
+ rb_define_variable("$8", Qnil, GET_MATCH(8), rb_readonly_hook);
+ rb_define_variable("$9", Qnil, GET_MATCH(9), rb_readonly_hook);
+
+ rb_define_variable("$=", &ignorecase, Qnil, Qnil);
+
+ C_Regexp = rb_define_class("Regexp", C_Object);
+ rb_define_single_method(C_Regexp, "new", Freg_new, 1);
+ rb_define_single_method(C_Regexp, "compile", Freg_compile, 1);
+
+ rb_define_method(C_Regexp, "=~", Freg_match, 1);
+ rb_define_method(C_Regexp, "~", Freg_match2, 0);
+
+ rb_global_variable(&str_cache);
+ rb_global_variable(&reg_cache);
+}
diff --git a/re.h b/re.h
new file mode 100644
index 0000000000..e483080fc5
--- /dev/null
+++ b/re.h
@@ -0,0 +1,28 @@
+/************************************************
+
+ re.h -
+
+ $Author: matz $
+ $Revision: 1.1.1.1 $
+ $Date: 1994/06/17 14:23:50 $
+ created at: Thu Sep 30 14:18:32 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#ifndef RE_H
+#define RE_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "regex.h"
+typedef struct Regexp {
+ struct re_pattern_buffer pat;
+ struct re_registers regs;
+} Regexp;
+
+VALUE re_regcomp();
+VALUE re_regsub();
+#endif
diff --git a/regex.c b/regex.c
new file mode 100644
index 0000000000..ae6db7cfaa
--- /dev/null
+++ b/regex.c
@@ -0,0 +1,3237 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 1985, 1989-90 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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. */
+/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto)
+ Last change: May 21, 1993 by t^2 */
+
+
+/* To test, compile with -Dtest. This Dtestable feature turns this into
+ a self-contained program which reads a pattern, describes how it
+ compiles, then reads a string and searches for it.
+
+ On the other hand, if you compile with both -Dtest and -Dcanned you
+ can run some tests we've already thought of. */
+
+
+/* We write fatal error messages on standard error. */
+#include <stdio.h>
+
+/* isalpha(3) etc. are used for the character classes. */
+#include <ctype.h>
+
+#ifdef emacs
+
+/* The `emacs' switch turns on certain special matching commands
+ that make sense only in emacs. */
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+#else /* not emacs */
+
+#define RUBY
+#include <sys/types.h>
+
+#ifdef __STDC__
+#define P(s) s
+#define MALLOC_ARG_T size_t
+#else
+#define P(s) ()
+#define MALLOC_ARG_T unsigned
+#define volatile
+#define const
+#endif
+
+/* #define NO_ALLOCA /* try it out for now */
+#ifndef NO_ALLOCA
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#ifndef atarist
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#endif /* atarist */
+#else
+#if defined(sparc) && !defined(__GNUC__)
+#include <alloca.h>
+#else
+char *alloca ();
+#endif
+#endif /* __GNUC__ */
+
+#define FREE_AND_RETURN_VOID(stackb) return
+#define FREE_AND_RETURN(stackb,val) return(val)
+#define DOUBLE_STACK(stackx,stackb,len) \
+ (stackx = (unsigned char **) alloca (2 * len \
+ * sizeof (unsigned char *)),\
+ /* Only copy what is in use. */ \
+ (unsigned char **) memcpy (stackx, stackb, len * sizeof (char *)))
+#else /* NO_ALLOCA defined */
+#define FREE_AND_RETURN_VOID(stackb) free(stackb);return
+#define FREE_AND_RETURN(stackb,val) free(stackb);return(val)
+#define DOUBLE_STACK(stackx,stackb,len) \
+ (unsigned char **)xrealloc (stackb, 2 * len * sizeof (unsigned char *))
+#endif /* NO_ALLOCA */
+
+static void store_jump P((char *, int, char *));
+static void insert_jump P((int, char *, char *, char *));
+static void store_jump_n P((char *, int, char *, unsigned));
+static void insert_jump_n P((int, char *, char *, char *, unsigned));
+static void insert_op_2 P((int, char *, char *, int, int ));
+static int memcmp_translate P((unsigned char *, unsigned char *,
+ int, unsigned char *));
+long re_set_syntax P((long));
+
+/* Define the syntax stuff, so we can do the \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
+#define Sword 1
+#endif
+
+#define SYNTAX(c) re_syntax_table[c]
+
+
+#ifdef SYNTAX_TABLE
+
+char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+static char re_syntax_table[256];
+static void init_syntax_once P((void));
+
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ memset (re_syntax_table, 0, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ /* Add specific syntax for ISO Latin-1. */
+ for (c = 0300; c <= 0377; c++)
+ re_syntax_table[c] = Sword;
+ re_syntax_table[0327] = 0;
+ re_syntax_table[0367] = 0;
+
+ done = 1;
+}
+
+#endif /* SYNTAX_TABLE */
+#undef P
+#endif /* emacs */
+
+
+/* Sequents are missing isgraph. */
+#ifndef isgraph
+#define isgraph(c) (isprint((c)) && !isspace((c)))
+#endif
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+
+/* These are the command codes that appear in compiled regular
+ expressions, one per byte. Some command codes are followed by
+ argument bytes. A command code can specify any interpretation
+ whatsoever for its arguments. Zero-bytes may appear in the compiled
+ regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+enum regexpcode
+ {
+ unused=0,
+ exactn=1, /* Followed by one byte giving n, then by n literal bytes. */
+ begline, /* Fail unless at beginning of line. */
+ endline, /* Fail unless at end of line. */
+ jump, /* Followed by two bytes giving relative address to jump to. */
+ on_failure_jump, /* Followed by two bytes giving relative address of
+ place to resume at in case of failure. */
+ finalize_jump, /* Throw away latest failure point and then jump to
+ address. */
+ maybe_finalize_jump, /* Like jump but finalize if safe to do so.
+ This is used to jump back to the beginning
+ of a repeat. If the command that follows
+ this jump is clearly incompatible with the
+ one at the beginning of the repeat, such that
+ we can be sure that there is no use backtracking
+ out of repetitions already completed,
+ then we finalize. */
+ dummy_failure_jump, /* Jump, and push a dummy failure point. This
+ failure point will be thrown away if an attempt
+ is made to use it for a failure. A + construct
+ makes this before the first repeat. Also
+ use it as an intermediary kind of jump when
+ compiling an or construct. */
+ succeed_n, /* Used like on_failure_jump except has to succeed n times;
+ then gets turned into an on_failure_jump. The relative
+ address following it is useless until then. The
+ address is followed by two bytes containing n. */
+ jump_n, /* Similar to jump, but jump n times only; also the relative
+ address following is in turn followed by yet two more bytes
+ containing n. */
+ set_number_at, /* Set the following relative location to the
+ subsequent number. */
+ anychar, /* Matches any (more or less) one character. */
+ charset, /* Matches any one char belonging to specified set.
+ First following byte is number of bitmap bytes.
+ Then come bytes for a bitmap saying which chars are in.
+ Bits in each byte are ordered low-bit-first.
+ A character is in the set if its bit is 1.
+ A character too large to have a bit in the map
+ is automatically not in the set. */
+ charset_not, /* Same parameters as charset, but match any character
+ that is not one of those specified. */
+ start_memory, /* Start remembering the text that is matched, for
+ storing in a memory register. Followed by one
+ byte containing the register number. Register numbers
+ must be in the range 0 through RE_NREGS. */
+ stop_memory, /* Stop remembering the text that is matched
+ and store it in a memory register. Followed by
+ one byte containing the register number. Register
+ numbers must be in the range 0 through RE_NREGS. */
+ duplicate, /* Match a duplicate of something remembered.
+ Followed by one byte containing the index of the memory
+ register. */
+ before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+ begbuf, /* Succeeds if at beginning of buffer. */
+ endbuf, /* Succeeds if at end of buffer. */
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound,/* Succeeds if not at a word boundary. */
+ syntaxspec, /* Matches any character whose syntax is specified.
+ followed by a byte which contains a syntax code,
+ e.g., Sword. */
+ notsyntaxspec /* Matches any character whose syntax differs from
+ that specified. */
+ };
+
+
+/* Number of failure points to allocate space for initially,
+ when matching. If this number is exceeded, more space is allocated,
+ so it is not a hard limit. */
+
+#ifndef NFAILURES
+#define NFAILURES 80
+#endif
+
+#ifdef CHAR_UNSIGNED
+#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */
+#endif
+#ifndef SIGN_EXTEND_CHAR
+#define SIGN_EXTEND_CHAR(x) (x)
+#endif
+
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+#define STORE_NUMBER(destination, number) \
+ { (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; }
+
+/* Same as STORE_NUMBER, except increment the destination pointer to
+ the byte after where the number is stored. Watch out that values for
+ DESTINATION such as p + 1 won't work, whereas p will. */
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ { STORE_NUMBER(destination, number); \
+ (destination) += 2; }
+
+
+/* Put into DESTINATION a number stored in two contingous bytes starting
+ at SOURCE. */
+#define EXTRACT_NUMBER(destination, source) \
+ { (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; }
+
+/* Same as EXTRACT_NUMBER, except increment the pointer for source to
+ point to second byte of SOURCE. Note that SOURCE has to be a value
+ such as p, not, e.g., p + 1. */
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ { EXTRACT_NUMBER (destination, source); \
+ (source) += 2; }
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit-mask comprised of the various bits
+ defined in regex.h. */
+
+long
+re_set_syntax (syntax)
+ long syntax;
+{
+ long ret;
+
+ ret = obscure_syntax;
+ obscure_syntax = syntax;
+ return ret;
+}
+
+/* Set by re_set_syntax to the current regexp syntax to recognize. */
+#ifdef EUC
+#define DEFAULT_MBCTYPE RE_MBCTYPE_EUC
+#else
+#ifdef SJIS
+#define DEFAULT_MBCTYPE RE_MBCTYPE_SJIS
+#else
+#define DEFAULT_MBCTYPE 0
+#endif
+#endif
+long obscure_syntax = DEFAULT_MBCTYPE;
+
+
+/* Macros for re_compile_pattern, which is found below these definitions. */
+
+#define CHAR_CLASS_MAX_LENGTH 6
+
+/* Fetch the next character in the uncompiled pattern, translating it if
+ necessary. */
+#define PATFETCH(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; \
+ if (translate && !ismbchar (c)) \
+ c = (unsigned char) translate[(unsigned char) c]; }
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; }
+
+#define PATUNFETCH p--
+
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 28
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ { \
+ while (b - bufp->buffer + (n) >= bufp->allocated) \
+ EXTEND_BUFFER; \
+ }
+
+/* Make sure we have one more byte of buffer space and then add CH to it. */
+#define BUFPUSH(ch) \
+ { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (char) (ch); \
+ }
+
+/* Extend the buffer by twice its current size via reallociation and
+ reset the pointers that pointed into the old allocation to point to
+ the correct places in the new allocation. If extending the buffer
+ results in it being larger than 1 << 16, then flag memory exhausted. */
+#define EXTEND_BUFFER \
+ { char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == (1L<<16)) goto too_big; \
+ bufp->allocated *= 2; \
+ if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \
+ bufp->buffer = (char *) xrealloc (bufp->buffer, bufp->allocated); \
+ if (bufp->buffer == 0) \
+ goto memory_exhausted; \
+ b = (b - old_buffer) + bufp->buffer; \
+ if (fixup_jump) \
+ fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ }
+
+/* Set the bit for character C in a character set list. */
+#define SET_LIST_BIT(c) \
+ (b[(unsigned char) (c) / BYTEWIDTH] \
+ |= 1 << ((unsigned char) (c) % BYTEWIDTH))
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (isdigit (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+/* Subroutines for re_compile_pattern. */
+/* static void store_jump (), insert_jump (), store_jump_n (),
+ insert_jump_n (), insert_op_2 (); */
+
+#define STORE_MBC(p, c) \
+ ((p)[0] = (unsigned char)(c >> 8), (p)[1] = (unsigned char)(c))
+#define STORE_MBC_AND_INCR(p, c) \
+ (*(p)++ = (unsigned char)(c >> 8), *(p)++ = (unsigned char)(c))
+
+#define EXTRACT_MBC(p) \
+ ((unsigned char)(p)[0] << 8 | (unsigned char)(p)[1])
+#define EXTRACT_MBC_AND_INCR(p) \
+ ((p) += 2, (unsigned char)(p)[-2] << 8 | (unsigned char)(p)[-1])
+
+#define EXTRACT_UNSIGNED(p) \
+ ((unsigned char)(p)[0] | (unsigned char)(p)[1] << 8)
+#define EXTRACT_UNSIGNED_AND_INCR(p) \
+ ((p) += 2, (unsigned char)(p)[-2] | (unsigned char)(p)[-1] << 8)
+
+/* Handle (mb)?charset(_not)?.
+
+ Structure of mbcharset(_not)? in compiled pattern.
+
+ struct {
+ unsinged char id; mbcharset(_not)?
+ unsigned char sbc_size;
+ unsigned char sbc_map[sbc_size]; same as charset(_not)? up to here.
+ unsigned short mbc_size; number of intervals.
+ struct {
+ unsigned short beg; beginning of interval.
+ unsigned short end; end of interval.
+ } intervals[mbc_size];
+ }; */
+
+static void
+#ifdef __STDC__
+set_list_bits (unsigned short c1, unsigned short c2,
+ unsigned char *b, const unsigned char *translate)
+#else
+set_list_bits (c1, c2, b, translate)
+ unsigned short c1, c2;
+ unsigned char *b;
+ const unsigned char *translate;
+#endif
+{
+ enum regexpcode op = (enum regexpcode) b[-2];
+ unsigned char sbc_size = b[-1];
+ unsigned short mbc_size = EXTRACT_UNSIGNED (&b[sbc_size]);
+ unsigned short c, beg, end, upb;
+
+ if (c1 > c2)
+ return;
+ if (c1 < 1 << BYTEWIDTH) {
+ upb = c2;
+ if (1 << BYTEWIDTH <= upb)
+ upb = (1 << BYTEWIDTH) - 1; /* The last single-byte char */
+ if (sbc_size <= upb / BYTEWIDTH) {
+ /* Allocate maximum size so it never happens again. */
+ /* NOTE: memcpy() would not work here. */
+ memmove (&b[(1 << BYTEWIDTH) / BYTEWIDTH], &b[sbc_size], 2 + mbc_size*4);
+ memset (&b[sbc_size], 0, (1 << BYTEWIDTH) / BYTEWIDTH - sbc_size);
+ b[-1] = sbc_size = (1 << BYTEWIDTH) / BYTEWIDTH;
+ }
+ if (!translate) {
+ for (; c1 <= upb; c1++)
+ if (!ismbchar (c1))
+ SET_LIST_BIT (c1);
+ }
+ else
+ for (; c1 <= upb; c1++)
+ if (!ismbchar (c1))
+ SET_LIST_BIT (translate[c1]);
+ if (c2 < 1 << BYTEWIDTH)
+ return;
+ c1 = 0x8000; /* The first wide char */
+ }
+ b = &b[sbc_size + 2];
+
+ for (beg = 0, upb = mbc_size; beg < upb; ) {
+ unsigned short mid = (beg + upb) >> 1;
+
+ if (c1 - 1 > EXTRACT_MBC (&b[mid*4 + 2]))
+ beg = mid + 1;
+ else
+ upb = mid;
+ }
+
+ for (end = beg, upb = mbc_size; end < upb; ) {
+ unsigned short mid = (end + upb) >> 1;
+
+ if (c2 >= EXTRACT_MBC (&b[mid*4]) - 1)
+ end = mid + 1;
+ else
+ upb = mid;
+ }
+
+ if (beg != end) {
+ if (c1 > EXTRACT_MBC (&b[beg*4]))
+ c1 = EXTRACT_MBC (&b[beg*4]);
+ if (c2 < EXTRACT_MBC (&b[(end - 1)*4]))
+ c2 = EXTRACT_MBC (&b[(end - 1)*4]);
+ }
+ if (end < mbc_size && end != beg + 1)
+ /* NOTE: memcpy() would not work here. */
+ memmove (&b[(beg + 1)*4], &b[end*4], (mbc_size - end)*4);
+ STORE_MBC (&b[beg*4 + 0], c1);
+ STORE_MBC (&b[beg*4 + 2], c2);
+ mbc_size += beg + 1 - end;
+ STORE_NUMBER (&b[-2], mbc_size);
+}
+
+static int
+#ifdef __STDC__
+is_in_list (unsigned short c, const unsigned char *b)
+#else
+is_in_list (c, b)
+ unsigned short c;
+ const unsigned char *b;
+#endif
+{
+ unsigned short size;
+ int in = (enum regexpcode) b[-1] == charset_not;
+
+ size = *b++;
+ if (c < 1 << BYTEWIDTH) {
+ if (c / BYTEWIDTH < size && b[c / BYTEWIDTH] & 1 << c % BYTEWIDTH)
+ in = !in;
+ }
+ else {
+ unsigned short i, j;
+
+ b += size + 2;
+ size = EXTRACT_UNSIGNED (&b[-2]);
+
+ for (i = 0, j = size; i < j; ) {
+ unsigned short k = (i + j) >> 1;
+
+ if (c > EXTRACT_MBC (&b[k*4 + 2]))
+ i = k + 1;
+ else
+ j = k;
+ }
+ if (i < size && EXTRACT_MBC (&b[i*4]) <= c
+ && ((unsigned char) c != '\n' && (unsigned char) c != '\0'))
+ in = !in;
+ }
+ return in;
+}
+
+/* re_compile_pattern takes a regular-expression string
+ and converts it into a buffer full of byte commands for matching.
+
+ PATTERN is the address of the pattern string
+ SIZE is the length of it.
+ BUFP is a struct re_pattern_buffer * which points to the info
+ on where to store the byte commands.
+ This structure contains a char * which points to the
+ actual space, which should have been obtained with malloc.
+ re_compile_pattern may use realloc to grow the buffer space.
+
+ The number of bytes of commands can be found out by looking in
+ the `struct re_pattern_buffer' that bufp pointed to, after
+ re_compile_pattern returns. */
+
+char *
+re_compile_pattern (pattern, size, bufp)
+ char *pattern;
+ size_t size;
+ struct re_pattern_buffer *bufp;
+{
+ register char *b = bufp->buffer;
+ register char *p = pattern;
+ char *pend = pattern + size;
+ register unsigned c, c1;
+ char *p0;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell whether a new exact-match
+ character can be added to that command or requires a new `exactn'
+ command. */
+
+ char *pending_exact = 0;
+
+ /* Address of the place where a forward-jump should go to the end of
+ the containing expression. Each alternative of an `or', except the
+ last, ends with a forward-jump of this sort. */
+
+ char *fixup_jump = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells postfix * where to find the start of its operand. */
+
+ char *laststart = 0;
+
+ /* In processing a repeat, 1 means zero matches is allowed. */
+
+ char zero_times_ok;
+
+ /* In processing a repeat, 1 means many matches is allowed. */
+
+ char many_times_ok;
+
+ /* Address of beginning of regexp, or inside of last \(. */
+
+ char *begalt = b;
+
+ /* In processing an interval, at least this many matches must be made. */
+ int lower_bound;
+
+ /* In processing an interval, at most this many matches can be made. */
+ int upper_bound;
+
+ /* Place in pattern (i.e., the {) to which to go back if the interval
+ is invalid. */
+ char *beg_interval = 0;
+
+ /* Stack of information saved by \( and restored by \).
+ Four stack elements are pushed by each \(:
+ First, the value of b.
+ Second, the value of fixup_jump.
+ Third, the value of regnum.
+ Fourth, the value of begalt. */
+
+ int stackb[40];
+ int *stackp = stackb;
+ int *stacke = stackb + 40;
+ int *stackt;
+
+ /* Counts \('s as they are encountered. Remembered for the matching \),
+ where it becomes the register number to put in the stop_memory
+ command. */
+
+ int regnum = 1;
+
+ bufp->fastmap_accurate = 0;
+
+#ifndef emacs
+#ifndef SYNTAX_TABLE
+ /* Initialize the syntax table. */
+ init_syntax_once();
+#endif
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ bufp->allocated = INIT_BUF_SIZE;
+ if (bufp->buffer)
+ /* EXTEND_BUFFER loses when bufp->allocated is 0. */
+ bufp->buffer = (char *) xrealloc (bufp->buffer, INIT_BUF_SIZE);
+ else
+ /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = (char *) xmalloc (INIT_BUF_SIZE);
+ if (!bufp->buffer) goto memory_exhausted;
+ begalt = b = bufp->buffer;
+ }
+
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '$':
+ {
+ char *p1 = p;
+ /* When testing what follows the $,
+ look past the \-constructs that don't consume anything. */
+ if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ while (p1 != pend)
+ {
+ if (*p1 == '\\' && p1 + 1 != pend
+ && (p1[1] == '<' || p1[1] == '>'
+ || p1[1] == '`' || p1[1] == '\''
+#ifdef emacs
+ || p1[1] == '='
+#endif
+ || p1[1] == 'b' || p1[1] == 'B'))
+ p1 += 2;
+ else
+ break;
+ }
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend)
+ goto normal_char;
+ /* Make operand of last vbar end before this `$'. */
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = 0;
+ BUFPUSH (endline);
+ break;
+ }
+ /* $ means succeed if at end of line, but only in special contexts.
+ If validly in the middle of a pattern, it is a normal character. */
+
+ if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend)
+ goto invalid_pattern;
+ if (p1 == pend || *p1 == '\n'
+ || (obscure_syntax & RE_CONTEXT_INDEP_OPS)
+ || (obscure_syntax & RE_NO_BK_PARENS
+ ? *p1 == ')'
+ : *p1 == '\\' && p1[1] == ')')
+ || (obscure_syntax & RE_NO_BK_VBAR
+ ? *p1 == '|'
+ : *p1 == '\\' && p1[1] == '|'))
+ {
+ BUFPUSH (endline);
+ break;
+ }
+ goto normal_char;
+ }
+ case '^':
+ /* ^ means succeed if at beg of line, but only if no preceding
+ pattern. */
+
+ if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart)
+ goto invalid_pattern;
+ if (laststart && p - 2 >= pattern && p[-2] != '\n'
+ && !(obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (p != pattern + 1
+ && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ BUFPUSH (begline);
+ begalt = b;
+ }
+ else
+ BUFPUSH (begline);
+ break;
+
+ case '+':
+ case '?':
+ if ((obscure_syntax & RE_BK_PLUS_QM)
+ || (obscure_syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern, char not special. */
+ if (!laststart)
+ {
+ if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS)
+ goto invalid_pattern;
+ else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+ /* If there is a sequence of repetition chars,
+ collapse it down to just one. */
+ zero_times_ok = 0;
+ many_times_ok = 0;
+ while (1)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+ if (p == pend)
+ break;
+ PATFETCH (c);
+ if (c == '*')
+ ;
+ else if (!(obscure_syntax & RE_BK_PLUS_QM)
+ && (c == '+' || c == '?'))
+ ;
+ else if ((obscure_syntax & RE_BK_PLUS_QM)
+ && c == '\\')
+ {
+ /* int c1; */
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ {
+ /* If more than one repetition is allowed, put in at the
+ end a backward relative jump from b to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump). */
+ GET_BUFFER_SPACE (3);
+ store_jump (b, maybe_finalize_jump, laststart - 3);
+ b += 3; /* Because store_jump put stuff here. */
+ }
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ insert_jump (on_failure_jump, laststart, b + 3, b);
+ pending_exact = 0;
+ b += 3;
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ dummy-failure before the initial on-failure-jump
+ instruction of the loop. This effects a skip over that
+ instruction the first time we hit that loop. */
+ GET_BUFFER_SPACE (6);
+ insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ break;
+
+ case '.':
+ laststart = b;
+ BUFPUSH (anychar);
+ break;
+
+ case '[':
+ if (p == pend)
+ goto invalid_pattern;
+ while (b - bufp->buffer
+ > bufp->allocated - 9 - (1 << BYTEWIDTH) / BYTEWIDTH)
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (*p == '^')
+ {
+ BUFPUSH (charset_not);
+ p++;
+ }
+ else
+ BUFPUSH (charset);
+ p0 = p;
+
+ BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Clear the whole map */
+ memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);
+
+ if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not)
+ SET_LIST_BIT ('\n');
+
+
+ /* Read in characters and ranges, setting map bits. */
+ while (1)
+ {
+ int size;
+
+ if ((size = EXTRACT_UNSIGNED (&b[(1 << BYTEWIDTH) / BYTEWIDTH]))) {
+ /* Ensure the space is enough to hold another interval
+ of multi-byte chars in charset(_not)?. */
+ size = (1 << BYTEWIDTH) / BYTEWIDTH + 2 + size*4 + 4;
+ while (b + size + 1 > bufp->buffer + bufp->allocated)
+ EXTEND_BUFFER;
+ }
+ /* Don't translate while fetching, in case it's a range bound.
+ When we set the bit for the character, we translate it. */
+ PATFETCH_RAW (c);
+
+ /* If set, \ escapes characters when inside [...]. */
+ if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\')
+ {
+ PATFETCH(c1);
+ if (ismbchar (c1)) {
+ unsigned char c2;
+
+ PATFETCH_RAW (c2);
+ c1 = c1 << 8 | c2;
+ set_list_bits (c1, c1, (unsigned char *) b, translate);
+ continue;
+ }
+ SET_LIST_BIT (c1);
+ continue;
+ }
+ if (c == ']')
+ {
+ if (p == p0 + 1)
+ {
+ /* If this is an empty bracket expression. */
+ if ((obscure_syntax & RE_NO_EMPTY_BRACKETS)
+ && p == pend)
+ goto invalid_pattern;
+ }
+ else
+ /* Stop if this isn't merely a ] inside a bracket
+ expression, but rather the end of a bracket
+ expression. */
+ break;
+ }
+ if (ismbchar (c)) {
+ unsigned char c2;
+
+ PATFETCH_RAW (c2);
+ c = c << 8 | c2;
+ }
+ /* Get a range. */
+ if (p[0] == '-' && p[1] != ']')
+ {
+ PATFETCH_RAW (c1);
+ /* Don't translate the range bounds while fetching them. */
+ PATFETCH_RAW (c1);
+ if (ismbchar (c)) {
+ unsigned char c2;
+
+ PATFETCH_RAW (c2);
+ c1 = c1 << 8 | c2;
+ }
+
+ if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1)
+ goto invalid_pattern;
+
+ if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END)
+ && c1 == '-' && *p != ']')
+ goto invalid_pattern;
+
+ set_list_bits (c, c1, (unsigned char *) b, translate);
+ }
+ else if ((obscure_syntax & RE_CHAR_CLASSES)
+ && c == '[' && p[0] == ':')
+ {
+ /* Longest valid character class word has six characters. */
+ char str[CHAR_CLASS_MAX_LENGTH];
+ PATFETCH_RAW (c);
+ c1 = 0;
+ /* If no ] at end. */
+ if (p == pend)
+ goto invalid_pattern;
+ while (1)
+ {
+ /* Don't translate the ``character class'' characters. */
+ PATFETCH_RAW (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+ if (p == pend
+ || c == ']' /* End of the bracket expression. */
+ || p[0] != ']'
+ || p + 1 == pend
+ || (strcmp (str, "alpha") != 0
+ && strcmp (str, "upper") != 0
+ && strcmp (str, "lower") != 0
+ && strcmp (str, "digit") != 0
+ && strcmp (str, "alnum") != 0
+ && strcmp (str, "xdigit") != 0
+ && strcmp (str, "space") != 0
+ && strcmp (str, "print") != 0
+ && strcmp (str, "punct") != 0
+ && strcmp (str, "graph") != 0
+ && strcmp (str, "cntrl") != 0))
+ {
+ /* Undo the ending character, the letters, and leave
+ the leading : and [ (but set bits for them). */
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+#if 1 /* The original was: */
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+#else /* I think this is the right way. */
+ if (translate) {
+ SET_LIST_BIT (translate['[']);
+ SET_LIST_BIT (trasnlate[':']);
+ }
+ else {
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ }
+#endif
+ }
+ else
+ {
+ /* The ] at the end of the character class. */
+ PATFETCH (c);
+ if (c != ']')
+ goto invalid_pattern;
+ for (c = 0; c < (1 << BYTEWIDTH); c++)
+ {
+ if ((strcmp (str, "alpha") == 0 && isalpha (c))
+ || (strcmp (str, "upper") == 0 && isupper (c))
+ || (strcmp (str, "lower") == 0 && islower (c))
+ || (strcmp (str, "digit") == 0 && isdigit (c))
+ || (strcmp (str, "alnum") == 0 && isalnum (c))
+ || (strcmp (str, "xdigit") == 0 && isxdigit (c))
+ || (strcmp (str, "space") == 0 && isspace (c))
+ || (strcmp (str, "print") == 0 && isprint (c))
+ || (strcmp (str, "punct") == 0 && ispunct (c))
+ || (strcmp (str, "graph") == 0 && isgraph (c))
+ || (strcmp (str, "cntrl") == 0 && iscntrl (c)))
+ SET_LIST_BIT (c);
+ }
+ }
+ }
+ else if (translate && c < 1 << BYTEWIDTH)
+ SET_LIST_BIT (translate[c]);
+ else
+ set_list_bits (c, c, (unsigned char *) b, translate);
+ }
+
+ /* Discard any character set/class bitmap bytes that are all
+ 0 at the end of the map. Decrement the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
+ memmove (&b[b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
+ 2 + EXTRACT_UNSIGNED (&b[(1 << BYTEWIDTH) / BYTEWIDTH])*4);
+ b += b[-1] + 2 + EXTRACT_UNSIGNED (&b[b[-1]])*4;
+ break;
+
+ case '(':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_open;
+
+ case ')':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_close;
+
+ case '\n':
+ if (! (obscure_syntax & RE_NEWLINE_OR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '|':
+ if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS)
+ && (! laststart || p == pend))
+ goto invalid_pattern;
+ else if (! (obscure_syntax & RE_NO_BK_VBAR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '{':
+ if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES)
+ && (obscure_syntax & RE_INTERVALS)))
+ goto normal_char;
+ else
+ goto handle_interval;
+
+ case '\\':
+ if (p == pend) goto invalid_pattern;
+ PATFETCH_RAW (c);
+ switch (c)
+ {
+ case '(':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_open:
+ if (stackp == stacke) goto nesting_too_deep;
+
+ /* Laststart should point to the start_memory that we are about
+ to push (unless the pattern has RE_NREGS or more ('s). */
+ *stackp++ = b - bufp->buffer;
+ if (regnum < RE_NREGS)
+ {
+ BUFPUSH (start_memory);
+ BUFPUSH (regnum);
+ }
+ *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
+ *stackp++ = regnum++;
+ *stackp++ = begalt - bufp->buffer;
+ fixup_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case ')':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_close:
+ if (stackp == stackb) goto unmatched_close;
+ begalt = *--stackp + bufp->buffer;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ if (stackp[-1] < RE_NREGS)
+ {
+ BUFPUSH (stop_memory);
+ BUFPUSH (stackp[-1]);
+ }
+ stackp -= 2;
+ fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0;
+ laststart = *--stackp + bufp->buffer;
+ break;
+
+ case '|':
+ if ((obscure_syntax & RE_LIMITED_OPS)
+ || (obscure_syntax & RE_NO_BK_VBAR))
+ goto normal_backsl;
+ handle_bar:
+ if (obscure_syntax & RE_LIMITED_OPS)
+ goto normal_char;
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (6);
+ insert_jump (on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+ /* The alternative before the previous alternative has a
+ jump after it which gets executed if it gets matched.
+ Adjust that jump so it will jump to the previous
+ alternative's analogous jump (put in below, which in
+ turn will jump to the next (if any) alternative's such
+ jump, etc.). The last such jump jumps to the correct
+ final destination. */
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+
+ /* Leave space for a jump after previous alternative---to be
+ filled in later. */
+ fixup_jump = b;
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case '{':
+ if (! (obscure_syntax & RE_INTERVALS)
+ /* Let \{ be a literal. */
+ || ((obscure_syntax & RE_INTERVALS)
+ && (obscure_syntax & RE_NO_BK_CURLY_BRACES))
+ /* If it's the string "\{". */
+ || (p - 2 == pattern && p == pend))
+ goto normal_backsl;
+ handle_interval:
+ beg_interval = p - 1; /* The {. */
+ /* If there is no previous pattern, this isn't an interval. */
+ if (!laststart)
+ {
+ if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS)
+ goto invalid_pattern;
+ else
+ goto normal_backsl;
+ }
+ /* It also isn't an interval if not preceded by an re
+ matching a single character or subexpression, or if
+ the current type of intervals can't handle back
+ references and the previous thing is a back reference. */
+ if (! (*laststart == anychar
+ || *laststart == charset
+ || *laststart == charset_not
+ || *laststart == start_memory
+ || (*laststart == exactn && laststart[1] == 1)
+ || (! (obscure_syntax & RE_NO_BK_REFS)
+ && *laststart == duplicate)))
+ {
+ if (obscure_syntax & RE_NO_BK_CURLY_BRACES)
+ goto normal_char;
+
+ /* Posix extended syntax is handled in previous
+ statement; this is for Posix basic syntax. */
+ if (obscure_syntax & RE_INTERVALS)
+ goto invalid_pattern;
+
+ goto normal_backsl;
+ }
+ lower_bound = -1; /* So can see if are set. */
+ upper_bound = -1;
+ GET_UNSIGNED_NUMBER (lower_bound);
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0)
+ upper_bound = RE_DUP_MAX;
+ }
+ if (upper_bound < 0)
+ upper_bound = lower_bound;
+ if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES))
+ {
+ if (c != '\\')
+ goto invalid_pattern;
+ PATFETCH (c);
+ }
+ if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound
+ || ((obscure_syntax & RE_NO_BK_CURLY_BRACES)
+ && p != pend && *p == '{'))
+ {
+ if (obscure_syntax & RE_NO_BK_CURLY_BRACES)
+ goto unfetch_interval;
+ else
+ goto invalid_pattern;
+ }
+
+ /* If upper_bound is zero, don't want to succeed at all;
+ jump from laststart to b + 3, which will be the end of
+ the buffer after this jump is inserted. */
+
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ insert_jump (jump, laststart, b + 3, b);
+ b += 3;
+ }
+
+ /* Otherwise, after lower_bound number of succeeds, jump
+ to after the jump_n which will be inserted at the end
+ of the buffer, and insert that jump_n. */
+ else
+ { /* Set to 5 if only one repetition is allowed and
+ hence no jump_n is inserted at the current end of
+ the buffer; then only space for the succeed_n is
+ needed. Otherwise, need space for both the
+ succeed_n and the jump_n. */
+
+ unsigned slots_needed = upper_bound == 1 ? 5 : 10;
+
+ GET_BUFFER_SPACE (slots_needed);
+ /* Initialize the succeed_n to n, even though it will
+ be set by its attendant set_number_at, because
+ re_compile_fastmap will need to know it. Jump to
+ what the end of buffer will be after inserting
+ this succeed_n and possibly appending a jump_n. */
+ insert_jump_n (succeed_n, laststart, b + slots_needed,
+ b, lower_bound);
+ b += 5; /* Just increment for the succeed_n here. */
+
+ /* More than one repetition is allowed, so put in at
+ the end of the buffer a backward jump from b to the
+ succeed_n we put in above. By the time we've gotten
+ to this jump when matching, we'll have matched once
+ already, so jump back only upper_bound - 1 times. */
+
+ if (upper_bound > 1)
+ {
+ store_jump_n (b, jump_n, laststart, upper_bound - 1);
+ b += 5;
+ /* When hit this when matching, reset the
+ preceding jump_n's n to upper_bound - 1. */
+ BUFPUSH (set_number_at);
+ GET_BUFFER_SPACE (2);
+ STORE_NUMBER_AND_INCR (b, -5);
+ STORE_NUMBER_AND_INCR (b, upper_bound - 1);
+ }
+ /* When hit this when matching, set the succeed_n's n. */
+ GET_BUFFER_SPACE (5);
+ insert_op_2 (set_number_at, laststart, b, 5, lower_bound);
+ b += 5;
+ }
+ pending_exact = 0;
+ beg_interval = 0;
+ break;
+
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ if (beg_interval)
+ p = beg_interval;
+ else
+ {
+ fprintf (stderr,
+ "regex: no interval beginning to which to backtrack.\n");
+ exit (1);
+ }
+
+ beg_interval = 0;
+ PATFETCH (c); /* normal_char expects char in `c'. */
+ goto normal_char;
+ break;
+
+#ifdef emacs
+ case '=':
+ BUFPUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ BUFPUSH (syntaxspec);
+ PATFETCH (c);
+ BUFPUSH (syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ BUFPUSH (notsyntaxspec);
+ PATFETCH (c);
+ BUFPUSH (syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+#ifdef RUBY
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ while (b - bufp->buffer
+ > bufp->allocated - 9 - (1 << BYTEWIDTH) / BYTEWIDTH)
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (c == 's' || c == 'd') {
+ BUFPUSH (charset);
+ }
+ else {
+ BUFPUSH (charset_not);
+ }
+
+ BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+ memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);
+ if (c == 's' || c == 'S') {
+ SET_LIST_BIT (' ');
+ SET_LIST_BIT ('\t');
+ SET_LIST_BIT ('\n');
+ SET_LIST_BIT ('\r');
+ SET_LIST_BIT ('\f');
+ }
+ else {
+ char cc;
+
+ for (cc = '0'; cc <= '9'; cc++) {
+ SET_LIST_BIT (cc);
+ }
+ }
+
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
+ memmove (&b[b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
+ 2 + EXTRACT_UNSIGNED (&b[(1 << BYTEWIDTH) / BYTEWIDTH])*4);
+ b += b[-1] + 2 + EXTRACT_UNSIGNED (&b[b[-1]])*4;
+ break;
+#endif /* RUBY */
+
+ case 'w':
+ laststart = b;
+ BUFPUSH (wordchar);
+ break;
+
+ case 'W':
+ laststart = b;
+ BUFPUSH (notwordchar);
+ break;
+#ifndef RUBY
+ case '<':
+ BUFPUSH (wordbeg);
+ break;
+
+ case '>':
+ BUFPUSH (wordend);
+ break;
+#endif /* RUBY */
+ case 'b':
+ BUFPUSH (wordbound);
+ break;
+
+ case 'B':
+ BUFPUSH (notwordbound);
+ break;
+
+ case '`':
+ BUFPUSH (begbuf);
+ break;
+
+ case '\'':
+ BUFPUSH (endbuf);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (obscure_syntax & RE_NO_BK_REFS)
+ goto normal_char;
+ c1 = c - '0';
+ if (c1 >= regnum)
+ {
+ if (obscure_syntax & RE_NO_EMPTY_BK_REF)
+ goto invalid_pattern;
+ else
+ goto normal_char;
+ }
+ /* Can't back reference to a subexpression if inside of it. */
+ for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
+ if (*stackt == c1)
+ goto normal_char;
+ laststart = b;
+ BUFPUSH (duplicate);
+ BUFPUSH (c1);
+ break;
+
+ case '+':
+ case '?':
+ if (obscure_syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backsl;
+ break;
+
+ default:
+ normal_backsl:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ if (translate && !ismbchar (c)) c = (unsigned char) translate[c];
+ goto normal_char;
+ }
+ break;
+
+ default:
+ normal_char: /* Expects the character in `c'. */
+ c1 = 0;
+ if (ismbchar (c)) {
+ c1 = c;
+ PATFETCH_RAW (c);
+ }
+ if (!pending_exact || pending_exact + *pending_exact + 1 != b
+ || *pending_exact >= (c1 ? 0176 : 0177)
+ || *p == '*' || *p == '^'
+ || ((obscure_syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((obscure_syntax & RE_INTERVALS)
+ && ((obscure_syntax & RE_NO_BK_CURLY_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ laststart = b;
+ BUFPUSH (exactn);
+ pending_exact = b;
+ BUFPUSH (0);
+ }
+ if (c1) {
+ BUFPUSH (c1);
+ (*pending_exact)++;
+ }
+ BUFPUSH (c);
+ (*pending_exact)++;
+ }
+ }
+
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+
+ if (stackp != stackb) goto unmatched_open;
+
+ bufp->used = b - bufp->buffer;
+ return 0;
+
+ invalid_pattern:
+ return "Invalid regular expression";
+
+ unmatched_open:
+ return "Unmatched \\(";
+
+ unmatched_close:
+ return "Unmatched \\)";
+
+ end_of_pattern:
+ return "Premature end of regular expression";
+
+ nesting_too_deep:
+ return "Nesting too deep";
+
+ too_big:
+ return "Regular expression too big";
+
+ memory_exhausted:
+ return "Memory exhausted";
+}
+
+
+/* Store a jump of the form <OPCODE> <relative address>.
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store. */
+
+static void
+store_jump (from, opcode, to)
+ char *from, *to;
+ int opcode;
+{
+ from[0] = (char)opcode;
+ STORE_NUMBER(from + 1, to - (from + 3));
+}
+
+
+/* Open up space before char FROM, and insert there a jump to TO.
+ CURRENT_END gives the end of the storage not in use, so we know
+ how much data to copy up. OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump (op, from, to, current_end)
+ int op;
+ char *from, *to, *current_end;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 3; /* ...to here. */
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump (from, op, to);
+}
+
+
+/* Store a jump of the form <opcode> <relative address> <n> .
+
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store, N is a number the
+ jump uses, say, to decide how many times to jump.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+store_jump_n (from, opcode, to, n)
+ char *from, *to;
+ int opcode;
+ unsigned n;
+{
+ from[0] = (char)opcode;
+ STORE_NUMBER (from + 1, to - (from + 3));
+ STORE_NUMBER (from + 3, n);
+}
+
+
+/* Similar to insert_jump, but handles a jump which needs an extra
+ number to handle minimum and maximum cases. Open up space at
+ location FROM, and insert there a jump to TO. CURRENT_END gives the
+ end of the storage in use, so we know how much data to copy up. OP is
+ the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump_n (op, from, to, current_end, n)
+ int op;
+ char *from, *to, *current_end;
+ unsigned n;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 5; /* ...to here. */
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump_n (from, op, to, n);
+}
+
+
+/* Open up space at location THERE, and insert operation OP followed by
+ NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so
+ we know how much data to copy up.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_op_2 (op, there, current_end, num_1, num_2)
+ int op;
+ char *there, *current_end;
+ int num_1, num_2;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 5; /* ...to here. */
+
+ while (pfrom != there)
+ *--pto = *--pfrom;
+
+ there[0] = (char)op;
+ STORE_NUMBER (there + 1, num_1);
+ STORE_NUMBER (there + 3, num_2);
+}
+
+
+
+/* Given a pattern, compute a fastmap from it. The fastmap records
+ which of the (1 << BYTEWIDTH) possible characters can start a string
+ that matches the pattern. This fastmap is used by re_search to skip
+ quickly over totally implausible text.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as bufp->fastmap.
+ The other components of bufp describe the pattern to be used. */
+
+void
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *pattern = (unsigned char *) bufp->buffer;
+ int size = bufp->used;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+ register int j, k;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+ unsigned is_a_succeed_n;
+
+#ifndef NO_ALLOCA
+ unsigned char *stackb[NFAILURES];
+ unsigned char **stackp = stackb;
+
+#else
+ unsigned char **stackb;
+ unsigned char **stackp;
+ stackb = (unsigned char **) xmalloc (NFAILURES * sizeof (unsigned char *));
+ stackp = stackb;
+
+#endif /* NO_ALLOCA */
+ memset (fastmap, 0, (1 << BYTEWIDTH));
+ bufp->fastmap_accurate = 1;
+ bufp->can_be_null = 0;
+
+ while (p)
+ {
+ is_a_succeed_n = 0;
+ if (p == pend)
+ {
+ bufp->can_be_null = 1;
+ break;
+ }
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+ case exactn:
+#if 0 /* The original was: */
+ if (translate)
+ fastmap[translate[p[1]]] = 1;
+ else
+ fastmap[p[1]] = 1;
+#else /* The compiled pattern has already been translated. */
+ fastmap[p[1]] = 1;
+#endif
+ break;
+
+ case begline:
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ continue;
+
+ case endline:
+ if (translate)
+ fastmap[translate['\n']] = 1;
+ else
+ fastmap['\n'] = 1;
+
+ if (bufp->can_be_null != 1)
+ bufp->can_be_null = 2;
+ break;
+
+ case jump_n:
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing.
+ Opcode jumped to should be an on_failure_jump.
+ Just treat it like an ordinary jump.
+ For a * loop, it has pushed its failure point already;
+ If so, discard that as redundant. */
+
+ if ((enum regexpcode) *p != on_failure_jump
+ && (enum regexpcode) *p != succeed_n)
+ continue;
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (stackp != stackb && *stackp == p)
+ stackp--;
+ continue;
+
+ case on_failure_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ *++stackp = p + j;
+ if (is_a_succeed_n)
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ continue;
+
+ case succeed_n:
+ is_a_succeed_n = 1;
+ /* Get to the number of times to succeed. */
+ p += 2;
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+ case start_memory:
+ case stop_memory:
+ p++;
+ continue;
+
+ case duplicate:
+ bufp->can_be_null = 1;
+ fastmap['\n'] = 1;
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (j != '\n')
+ fastmap[j] = 1;
+ if (bufp->can_be_null)
+ {
+ FREE_AND_RETURN_VOID(stackb);
+ }
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ break;
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+#else /* not emacs */
+ case syntaxspec:
+ case notsyntaxspec:
+ break;
+#endif /* not emacs */
+
+ case charset:
+ /* NOTE: Charset for single-byte chars never contain
+ multi-byte char. See set_list_bits(). */
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ {
+#if 0 /* The original was: */
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+#else /* The compiled pattern has already been translated. */
+ fastmap[j] = 1;
+#endif
+ }
+ {
+ unsigned short size;
+ unsigned char c, end;
+
+ p += p[-1] + 2;
+ size = EXTRACT_UNSIGNED (&p[-2]);
+ for (j = 0; j < size; j++)
+ /* set bits for 1st bytes of multi-byte chars. */
+ for (c = (unsigned char) p[j*4],
+ end = (unsigned char) p[j*4 + 2];
+ c <= end; c++)
+ /* NOTE: Charset for multi-byte chars might contain
+ single-byte chars. We must reject them. */
+ if (ismbchar (c))
+ fastmap[c] = 1;
+ }
+ break;
+
+ case charset_not:
+ /* S: set of all single-byte chars.
+ M: set of all first bytes that can start multi-byte chars.
+ s: any set of single-byte chars.
+ m: any set of first bytes that can start multi-byte chars.
+
+ We assume S+M = U.
+ ___ _ _
+ s+m = (S*s+M*m). */
+ /* Chars beyond end of map must be allowed */
+ /* NOTE: Charset_not for single-byte chars might contain
+ multi-byte chars. See set_list_bits(). */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ if (!ismbchar (j))
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ {
+ if (!ismbchar (j))
+ fastmap[j] = 1;
+ }
+ {
+ unsigned short size;
+ unsigned char c, beg;
+
+ p += p[-1] + 2;
+ size = EXTRACT_UNSIGNED (&p[-2]);
+ c = 0x8000;
+ for (j = 0; j < size; j++) {
+ for (beg = (unsigned char) p[j*4 + 0]; c < beg; c++)
+ if (ismbchar (c))
+ fastmap[c] = 1;
+ c = (unsigned char) p[j*4 + 2] + 1;
+ }
+ }
+ break;
+
+ case unused: /* pacify gcc -Wall */
+ break;
+ }
+
+ /* Get here means we have successfully found the possible starting
+ characters of one path of the pattern. We need not follow this
+ path any farther. Instead, look at the next alternative
+ remembered in the stack. */
+ if (stackp != stackb)
+ p = *stackp--;
+ else
+ break;
+ }
+ FREE_AND_RETURN_VOID(stackb);
+}
+
+
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (pbufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (pbufp, (char *) 0, 0, string, size, startpos, range,
+ regs, size);
+}
+
+
+/* Using the compiled pattern in PBUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, 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. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively.
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire PBUFP->buffer and its contained
+ subexpressions. Do not consider matching one past the index MSTOP in
+ the virtual concatenation of STRING1 and STRING2.
+
+ 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_2 (pbufp, string1, size1, string2, size2, startpos, range,
+ regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ register int range;
+ struct re_registers *regs;
+ int mstop;
+{
+ register char *fastmap = pbufp->fastmap;
+ register unsigned char *translate = (unsigned char *) pbufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+ int val;
+
+ /* Check for out-of-range starting position. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up range if it would eventually take startpos outside of the
+ virtual concatenation of string1 and string2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !pbufp->fastmap_accurate)
+ re_compile_fastmap (pbufp);
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ long search for a pattern that says it is anchored. */
+ if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf
+ && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ while (1)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot possibly be the start of a match. Note, however, that
+ if the pattern can possibly match the null string, we must
+ test it at each starting point so that we take the first null
+ string we get. */
+
+ if (fastmap && startpos < total_size && pbufp->can_be_null != 1)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register int lim = 0;
+ register unsigned char *p, c;
+ int irange = range;
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ p = ((unsigned char *)
+ &(startpos >= size1 ? string2 - size1 : string1)[startpos]);
+
+ while (range > lim) {
+ c = *p++;
+ if (ismbchar (c)) {
+ if (fastmap[c])
+ break;
+ p++;
+ range--;
+ }
+ else
+ if (fastmap[translate ? translate[c] : c])
+ break;
+ range--;
+ }
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register unsigned char c;
+
+ if (string1 == 0 || startpos >= size1)
+ c = string2[startpos - size1];
+ else
+ c = string1[startpos];
+
+ c &= 0xff;
+ if (translate ? !fastmap[translate[c]] : !fastmap[c])
+ goto advance;
+ }
+ }
+
+ if (range >= 0 && startpos == total_size
+ && fastmap && pbufp->can_be_null == 0)
+ return -1;
+
+ val = re_match_2 (pbufp, string1, size1, string2, size2, startpos,
+ regs, mstop);
+ if (val >= 0)
+ return startpos;
+ if (val == -2)
+ return -2;
+
+#ifndef NO_ALLOCA
+#ifdef C_ALLOCA
+ alloca (0);
+#endif /* C_ALLOCA */
+
+#endif /* NO_ALLOCA */
+ advance:
+ if (!range)
+ break;
+ else if (range > 0) {
+ const char *d = ((startpos >= size1 ? string2 - size1 : string1)
+ + startpos);
+
+ if (ismbchar (*d)) {
+ range--, startpos++;
+ if (!range)
+ break;
+ }
+ range--, startpos++;
+ }
+ else {
+ range++, startpos--;
+ {
+ const char *s, *d, *p;
+
+ if (startpos < size1)
+ s = string1, d = string1 + startpos;
+ else
+ s = string2, d = string2 + startpos - size1;
+ for (p = d; p-- > s && ismbchar(*p); )
+ /* --p >= s would not work on 80[12]?86.
+ (when the offset of s equals 0 other than huge model.) */
+ ;
+ if (!((d - p) & 1)) {
+ if (!range)
+ break;
+ range++, startpos--;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+
+
+#ifndef emacs /* emacs never uses this. */
+int
+re_match (pbufp, string, size, pos, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, pos;
+ struct re_registers *regs;
+{
+ return re_match_2 (pbufp, (char *) 0, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
+
+
+/* The following are used for re_match_2, defined below: */
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */
+
+int re_max_failures = 2000;
+
+/* Routine used by re_match_2. */
+/* static int memcmp_translate (); *//* already declared */
+
+
+/* Structure and accessing macros used in re_match_2: */
+
+struct register_info
+{
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+};
+
+#define IS_ACTIVE(R) ((R).is_active)
+#define MATCHED_SOMETHING(R) ((R).matched_something)
+
+
+/* Macros used by re_match_2: */
+
+
+/* I.e., regstart, regend, and reg_info. */
+
+#define NUM_REG_ITEMS 3
+
+/* We push at most this many things on the stack whenever we
+ fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are
+ arguments to the PUSH_FAILURE_POINT macro. */
+
+#define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2)
+
+
+/* We push this many things on the stack whenever we fail. */
+
+#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2)
+
+
+/* This pushes most of the information about the current state we will want
+ if we ever fail back to it. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place) \
+ { \
+ long last_used_reg, this_reg; \
+ \
+ /* Find out how many registers are active or have been matched. \
+ (Aside from register zero, which is only set at the end.) */ \
+ for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\
+ if (regstart[last_used_reg] != (unsigned char *)(-1L)) \
+ break; \
+ \
+ if (stacke - stackp < NUM_FAILURE_ITEMS) \
+ { \
+ unsigned char **stackx; \
+ unsigned int len = stacke - stackb; \
+ if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \
+ { \
+ FREE_AND_RETURN(stackb,(-2)); \
+ } \
+ \
+ /* Roughly double the size of the stack. */ \
+ stackx = DOUBLE_STACK(stackx,stackb,len); \
+ /* Rearrange the pointers. */ \
+ stackp = stackx + (stackp - stackb); \
+ stackb = stackx; \
+ stacke = stackb + 2 * len; \
+ } \
+ \
+ /* Now push the info for each of those registers. */ \
+ for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \
+ { \
+ *stackp++ = regstart[this_reg]; \
+ *stackp++ = regend[this_reg]; \
+ *stackp++ = (unsigned char *) &reg_info[this_reg]; \
+ } \
+ \
+ /* Push how many registers we saved. */ \
+ *stackp++ = (unsigned char *) last_used_reg; \
+ \
+ *stackp++ = pattern_place; \
+ *stackp++ = string_place; \
+ }
+
+
+/* This pops what PUSH_FAILURE_POINT pushes. */
+
+#define POP_FAILURE_POINT() \
+ { \
+ int temp; \
+ stackp -= 2; /* Remove failure points. */ \
+ temp = (int) *--stackp; /* How many regs pushed. */ \
+ temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \
+ stackp -= temp; /* Remove the register info. */ \
+ }
+
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Is true if there is a first string and if PTR is pointing anywhere
+ inside it or just past the end. */
+
+#define IS_IN_FIRST_STRING(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+
+#define PREFETCH \
+ while (d == dend) \
+ { \
+ /* end of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* end of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Call this when have matched something; it sets `matched' flags for the
+ registers corresponding to the subexpressions of which we currently
+ are inside. */
+#define SET_REGS_MATCHED \
+ { unsigned this_reg; \
+ for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \
+ { \
+ if (IS_ACTIVE(reg_info[this_reg])) \
+ MATCHED_SOMETHING(reg_info[this_reg]) = 1; \
+ else \
+ MATCHED_SOMETHING(reg_info[this_reg]) = 0; \
+ } \
+ }
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of string1 and string2. If there is only one string, we've put it in
+ string2. */
+
+#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END (d == end2)
+
+#define AT_WORD_BOUNDARY \
+ (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d))
+
+/* We have two special cases to check for:
+ 1) if we're past the end of string1, we have to look at the first
+ character in string2;
+ 2) if we're before the beginning of string2, we have to look at the
+ last character in string1; we assume there is a string1, so use
+ this in conjunction with AT_STRINGS_BEG. */
+#define IS_A_LETTER(d) \
+ (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\
+ == Sword)
+
+
+/* Match the pattern described by PBUFP against the virtual
+ concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2,
+ respectively. Start the match at index POS in the virtual
+ concatenation of STRING1 and STRING2. In REGS, return the indices of
+ the virtual concatenation of STRING1 and STRING2 that matched the
+ entire PBUFP->buffer and its contained subexpressions. Do not
+ consider matching one past the index MSTOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ If pbufp->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
+ used directly on the contents of an Emacs buffer.
+
+ -1 is returned if there is no match. -2 is returned if there is an
+ error (such as match stack overflow). Otherwise the value is the
+ length of the substring which was matched. */
+
+int
+re_match_2 (pbufp, string1_arg, size1, string2_arg, size2, pos, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1_arg, *string2_arg;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int mstop;
+{
+ register unsigned char *p = (unsigned char *) pbufp->buffer;
+
+ /* Pointer to beyond end of buffer. */
+ register unsigned char *pend = p + pbufp->used;
+
+ unsigned char *string1 = (unsigned char *) string1_arg;
+ unsigned char *string2 = (unsigned char *) string2_arg;
+ unsigned char *end1; /* Just past end of first string. */
+ unsigned char *end2; /* Just past end of second string. */
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ unsigned char *end_match_1, *end_match_2;
+
+ register unsigned char *d, *dend;
+ register int mcnt; /* Multipurpose. */
+ unsigned char *translate = (unsigned char *) pbufp->translate;
+ unsigned is_a_jump_n = 0;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to the
+ subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where to
+ resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is a
+ ``dummy''; if a failure happens and the failure point is a dummy, it
+ gets discarded and the next next one is tried. */
+
+#ifndef NO_ALLOCA
+ unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES];
+#endif
+ unsigned char **stackb;
+ unsigned char **stackp;
+ unsigned char **stacke;
+
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+
+ unsigned char *regstart[RE_NREGS];
+ unsigned char *regend[RE_NREGS];
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+
+ struct register_info reg_info[RE_NREGS];
+
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+
+ unsigned best_regs_set = 0;
+ unsigned char *best_regstart[RE_NREGS];
+ unsigned char *best_regend[RE_NREGS];
+
+ /* Initialize the stack. */
+#ifdef NO_ALLOCA
+ stackb = (unsigned char **) xmalloc (MAX_NUM_FAILURE_ITEMS * NFAILURES * sizeof (char *));
+#else
+ stackb = initial_stack;
+#endif
+ stackp = stackb;
+ stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES];
+
+#ifdef DEBUG_REGEX
+ fprintf (stderr, "Entering re_match_2(%s%s)\n", string1_arg, string2_arg);
+#endif
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ \( or ( and \) or ) has been seen for. Also set all registers to
+ inactive and mark them as not having matched anything or ever
+ failed. */
+ for (mcnt = 0; mcnt < RE_NREGS; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt] = (unsigned char *) (-1L);
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ if (regs)
+ for (mcnt = 0; mcnt < RE_NREGS; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+
+ /* Set up pointers to ends of strings.
+ Don't allow the second string to be empty unless both are empty. */
+ if (size2 == 0)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (mstop <= size1)
+ {
+ end_match_1 = string1 + mstop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + mstop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data. `dend'
+ is the end of the input string that `d' points within. `d' is
+ advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal string2. */
+
+ if (size1 != 0 && pos <= size1)
+ d = string1 + pos, dend = end_match_1;
+ else
+ d = string2 + pos - size1, dend = end_match_2;
+
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if match is complete, or it drops through if match fails
+ at this starting point in the input data. */
+
+ while (1)
+ {
+#ifdef DEBUG_REGEX
+ fprintf (stderr,
+ "regex loop(%d): matching 0x%02d\n",
+ p - (unsigned char *) pbufp->buffer,
+ *p);
+#endif
+ is_a_jump_n = 0;
+ /* End of pattern means we might have succeeded. */
+ if (p == pend)
+ {
+ /* If not end of string, try backtracking. Otherwise done. */
+ if (d != end_match_2)
+ {
+ if (stackp != stackb)
+ {
+ /* More failure points to try. */
+
+ unsigned in_same_string =
+ IS_IN_FIRST_STRING (best_regend[0])
+ == MATCHING_IN_FIRST_STRING;
+
+ /* If exceeds best match so far, save it. */
+ if (! best_regs_set
+ || (in_same_string && d > best_regend[0])
+ || (! in_same_string && ! MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = 1;
+ best_regend[0] = d; /* Never use regstart[0]. */
+
+ for (mcnt = 1; mcnt < RE_NREGS; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. */
+ d = best_regend[0];
+
+ for (mcnt = 0; mcnt < RE_NREGS; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ }
+
+ /* If caller wants register contents data back, convert it
+ to indices. */
+ if (regs)
+ {
+ regs->start[0] = pos;
+ if (MATCHING_IN_FIRST_STRING)
+ regs->end[0] = d - string1;
+ else
+ regs->end[0] = d - string2 + size1;
+ for (mcnt = 1; mcnt < RE_NREGS; mcnt++)
+ {
+ if (regend[mcnt] == (unsigned char *)(-1L))
+ {
+ regs->start[mcnt] = -1;
+ regs->end[mcnt] = -1;
+ continue;
+ }
+ if (IS_IN_FIRST_STRING (regstart[mcnt]))
+ regs->start[mcnt] = regstart[mcnt] - string1;
+ else
+ regs->start[mcnt] = regstart[mcnt] - string2 + size1;
+
+ if (IS_IN_FIRST_STRING (regend[mcnt]))
+ regs->end[mcnt] = regend[mcnt] - string1;
+ else
+ regs->end[mcnt] = regend[mcnt] - string2 + size1;
+ }
+ }
+ FREE_AND_RETURN(stackb,
+ (d - pos - (MATCHING_IN_FIRST_STRING ?
+ string1 :
+ string2 - size1)));
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+
+ /* \( [or `(', as appropriate] is represented by start_memory,
+ \) by stop_memory. Both of those commands are followed by
+ a register number in the next byte. The text matched
+ within the \( and \) is recorded under that number. */
+ case start_memory:
+ regstart[*p] = d;
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+ p++;
+ break;
+
+ case stop_memory:
+ regend[*p] = d;
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If just failed to match something this time around with a sub-
+ expression that's in a loop, try to force exit from the loop. */
+ if ((! MATCHED_SOMETHING (reg_info[*p])
+ || (enum regexpcode) p[-3] == start_memory)
+ && (p + 1) != pend)
+ {
+ register unsigned char *p2 = p + 1;
+ mcnt = 0;
+ switch (*p2++)
+ {
+ case jump_n:
+ is_a_jump_n = 1;
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p2);
+ if (is_a_jump_n)
+ p2 += 2;
+ break;
+ }
+ p2 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump, exit from the loop by forcing a
+ failure after pushing on the stack the on_failure_jump's
+ jump in the pattern, and d. */
+ if (mcnt < 0 && (enum regexpcode) *p2++ == on_failure_jump)
+ {
+ EXTRACT_NUMBER_AND_INCR (mcnt, p2);
+ PUSH_FAILURE_POINT (p2 + mcnt, d);
+ goto fail;
+ }
+ }
+ p++;
+ break;
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ int regno = *p++; /* Get which register to match against */
+ register unsigned char *d2, *dend2;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((IS_IN_FIRST_STRING (regstart[regno])
+ == IS_IN_FIRST_STRING (regend[regno]))
+ ? regend[regno] : end_match_1);
+ while (1)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+ d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH;
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? memcmp_translate (d, d2, mcnt, translate)
+ : memcmp ((char *)d, (char *)d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+ case anychar:
+ PREFETCH; /* Fetch a data character. */
+ /* Match anything but a newline, maybe even a null. */
+ if (ismbchar (*d)) {
+ if (d + 1 == dend || d[1] == '\n' || d[1] == '\0')
+ goto fail;
+ SET_REGS_MATCHED;
+ d += 2;
+ break;
+ }
+ if ((translate ? translate[*d] : *d) == '\n'
+ || ((obscure_syntax & RE_DOT_NOT_NULL)
+ && (translate ? translate[*d] : *d) == '\000'))
+ goto fail;
+ SET_REGS_MATCHED;
+ d++;
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ int not; /* Nonzero for charset_not. */
+ register int c;
+ if (*(p - 1) == (unsigned char) charset_not)
+ not = 1;
+
+ PREFETCH; /* Fetch a data character. */
+
+ c = (unsigned char) *d;
+ if (ismbchar (c)) {
+ c <<= 8;
+ if (d + 1 != dend)
+ c |= (unsigned char) d[1];
+ }
+ else if (translate)
+ c = (unsigned char) translate[c];
+
+ not = is_in_list (c, p);
+
+ p += 1 + *p + 2 + EXTRACT_UNSIGNED (&p[1 + *p])*4;
+
+ if (!not) goto fail;
+ SET_REGS_MATCHED;
+ d++;
+ if (d != dend && c >= 1 << BYTEWIDTH)
+ d++;
+ break;
+ }
+
+ case begline:
+ if ((size1 != 0 && d == string1)
+ || (size1 == 0 && size2 != 0 && d == string2)
+ || (d && d[-1] == '\n')
+ || (size1 == 0 && size2 == 0))
+ break;
+ else
+ goto fail;
+
+ case endline:
+ if (d == end2
+ || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
+ break;
+ goto fail;
+
+ /* `or' constructs are handled by starting each alternative with
+ an on_failure_jump that points to the start of the next
+ alternative. Each alternative except the last ends with a
+ jump to the joining point. (Actually, each jump except for
+ the last one really jumps to the following jump, because
+ tensioning the jumps is a hassle.) */
+
+ /* The start of a stupid repeat has an on_failure_jump that points
+ past the end of the repeat text. This makes a failure point so
+ that on failure to match a repetition, matching restarts past
+ as many repetitions have been found with no way to fail and
+ look for another one. */
+
+ /* A smart repeat is similar but loops back to the on_failure_jump
+ so that each repetition makes another failure point. */
+
+ case on_failure_jump:
+ on_failure:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ PUSH_FAILURE_POINT (p + mcnt, d);
+ break;
+
+ /* The end of a smart repeat has a maybe_finalize_jump back.
+ Change it either to a finalize_jump or an ordinary jump. */
+ case maybe_finalize_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ {
+ register unsigned char *p2 = p;
+ /* Compare what follows with the beginning of the repeat.
+ If we can establish that there is nothing that they would
+ both match, we can change to finalize_jump. */
+ while (p2 + 1 != pend
+ && (*p2 == (unsigned char) stop_memory
+ || *p2 == (unsigned char) start_memory))
+ p2 += 2; /* Skip over reg number. */
+ if (p2 == pend)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (*p2 == (unsigned char) exactn
+ || *p2 == (unsigned char) endline)
+ {
+ register int c = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ register unsigned char *p1 = p + mcnt;
+ /* p1[0] ... p1[2] are an on_failure_jump.
+ Examine what follows that. */
+ if (p1[3] == (unsigned char) exactn && p1[5] != c)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (p1[3] == (unsigned char) charset
+ || p1[3] == (unsigned char) charset_not)
+ {
+ if (ismbchar (c))
+ c = c << 8 | p2[3];
+ /* `is_in_list()' is TRUE if c would match */
+ /* That means it is not safe to finalize. */
+ if (!is_in_list (c, p1 + 4))
+ p[-3] = (unsigned char) finalize_jump;
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if (p[-1] != (unsigned char) finalize_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ goto nofinalize;
+ }
+ /* Note fall through. */
+
+ /* The end of a stupid repeat has a finalize_jump back to the
+ start, where another failure point will be made which will
+ point to after all the repetitions found so far. */
+
+ /* Take off failure points put on by matching on_failure_jump
+ because didn't fail. Also remove the register information
+ put on by the on_failure_jump. */
+ case finalize_jump:
+ POP_FAILURE_POINT ();
+ /* Note fall through. */
+
+ /* Jump without taking off any failure points. */
+ case jump:
+ nofinalize:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p += mcnt;
+ break;
+
+ case dummy_failure_jump:
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at finalize_jump. We will end up at
+ finalize_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for finalize_jump to pop. */
+ PUSH_FAILURE_POINT (0, 0);
+ goto nofinalize;
+
+
+ /* Have to succeed matching what follows at least n times. Then
+ just handle like an on_failure_jump. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ p[2] = unused;
+ p[3] = unused;
+ goto on_failure;
+ }
+ else
+ {
+ fprintf (stderr, "regex: the succeed_n's n is not set.\n");
+ exit (1);
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER(p + 2, mcnt);
+ goto nofinalize; /* Do the jump without taking off
+ any failure points. */
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ register unsigned char *p1;
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case unused:
+ break;
+
+ case wordbound:
+ if (AT_WORD_BOUNDARY)
+ break;
+ goto fail;
+
+ case notwordbound:
+ if (AT_WORD_BOUNDARY)
+ goto fail;
+ break;
+
+ case wordbeg:
+ if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ /* Have to check if AT_STRINGS_BEG before looking at d - 1. */
+ if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1)
+ && (!IS_A_LETTER (d) || AT_STRINGS_END))
+ break;
+ goto fail;
+
+#ifdef emacs
+ case before_dot:
+ if (PTR_CHAR_POS (d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ if (PTR_CHAR_POS (d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ if (PTR_CHAR_POS (d) <= point)
+ goto fail;
+ break;
+
+ case wordchar:
+ mcnt = (int) Sword;
+ goto matchsyntax;
+
+ case syntaxspec:
+ mcnt = *p++;
+ matchsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail;
+ SET_REGS_MATCHED;
+ break;
+
+ case notwordchar:
+ mcnt = (int) Sword;
+ goto matchnotsyntax;
+
+ case notsyntaxspec:
+ mcnt = *p++;
+ matchnotsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail;
+ SET_REGS_MATCHED;
+ break;
+
+#else /* not emacs */
+
+ case wordchar:
+ PREFETCH;
+ if (!IS_A_LETTER (d))
+ goto fail;
+ d++;
+ SET_REGS_MATCHED;
+ break;
+
+ case notwordchar:
+ PREFETCH;
+ if (IS_A_LETTER (d))
+ goto fail;
+ d++;
+ SET_REGS_MATCHED;
+ break;
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ case syntaxspec:
+ case notsyntaxspec:
+ break;
+
+#endif /* not emacs */
+
+ case begbuf:
+ if (AT_STRINGS_BEG)
+ break;
+ goto fail;
+
+ case endbuf:
+ if (AT_STRINGS_END)
+ break;
+ goto fail;
+
+ case exactn:
+ /* Match the next few pattern characters exactly.
+ mcnt is how many characters to match. */
+ mcnt = *p++;
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ unsigned char c;
+
+ PREFETCH;
+ c = *d++;
+ if (ismbchar (c)) {
+ if (c != (unsigned char) *p++
+ || !--mcnt /* ѥѥ뤵
+ Ƥ¤, Υå
+ ĹǰΤ. */
+ || d == dend
+ || (unsigned char) *d++ != (unsigned char) *p++)
+ goto fail;
+ continue;
+ }
+ if ((unsigned char) translate[c] != (unsigned char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+#if 0
+ /* this code suppose that multi-byte chars are not splited
+ in string1 and string2. If you want to check this with
+ speed cost, change `#if 0' here and next to `#if 1'. */
+ unsigned char c;
+
+#endif
+ PREFETCH;
+#if 0
+ c = *d++;
+ if (ismbchar (c)) {
+ if (c != (unsigned char) *p++
+ || !--mcnt
+ || d == dend)
+ goto fail;
+ c = *d++;
+ }
+ if (c != (unsigned char) *p++) goto fail;
+#else
+ if (*d++ != *p++) goto fail;
+#endif
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED;
+ break;
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+ /* Jump here if any matching operation fails. */
+ fail:
+ if (stackp != stackb)
+ /* A restart point is known. Restart there and pop it. */
+ {
+ short last_used_reg, this_reg;
+
+ /* If this failure point is from a dummy_failure_point, just
+ skip it. */
+ if (!stackp[-2])
+ {
+ POP_FAILURE_POINT ();
+ goto fail;
+ }
+
+ d = *--stackp;
+ p = *--stackp;
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ /* Restore register info. */
+ last_used_reg = (long) *--stackp;
+
+ /* Make the ones that weren't saved -1 or 0 again. */
+ for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--)
+ {
+ regend[this_reg] = (unsigned char *) (-1L);
+ regstart[this_reg] = (unsigned char *) (-1L);
+ IS_ACTIVE (reg_info[this_reg]) = 0;
+ MATCHED_SOMETHING (reg_info[this_reg]) = 0;
+ }
+
+ /* And restore the rest from the stack. */
+ for ( ; this_reg > 0; this_reg--)
+ {
+ reg_info[this_reg] = *(struct register_info *) *--stackp;
+ regend[this_reg] = *--stackp;
+ regstart[this_reg] = *--stackp;
+ }
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ }
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_AND_RETURN(stackb,(-1)); /* Failure to match. */
+}
+
+
+static int
+memcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ unsigned char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2, c;
+ while (len)
+ {
+ c = *p1++;
+ if (ismbchar (c)) {
+ if (c != *p2++ || !--len || *p1++ != *p2++)
+ return 1;
+ }
+ else
+ if (translate[c] != translate[*p2++])
+ return 1;
+ len--;
+ }
+ return 0;
+}
+
+
+
+/* Entry points compatible with 4.2 BSD regex library. */
+
+#if !defined(emacs) && !defined(GAWK) && !defined(RUBY)
+
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ char *s;
+{
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ if (!(re_comp_buf.buffer = (char *) xmalloc (200)))
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+ if (!(re_comp_buf.fastmap = (char *) xmalloc (1 << BYTEWIDTH)))
+ return "Memory exhausted";
+ }
+ return re_compile_pattern (s, strlen (s), &re_comp_buf);
+}
+
+int
+re_exec (s)
+ char *s;
+{
+ int len = strlen (s);
+ return 0 <= re_search (&re_comp_buf, s, len, 0, len,
+ (struct re_registers *) 0);
+}
+#endif /* not emacs && not GAWK && not RUBY */
+
+
+
+#ifdef test
+
+#ifdef atarist
+long _stksize = 2L; /* reserve memory for stack */
+#endif
+#include <stdio.h>
+
+/* Indexed by a character, gives the upper case equivalent of the
+ character. */
+
+char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+#ifdef canned
+
+#include "tests.h"
+
+typedef enum { extended_test, basic_test } test_type;
+
+/* Use this to run the tests we've thought of. */
+
+void
+main ()
+{
+ test_type t = extended_test;
+
+ if (t == basic_test)
+ {
+ printf ("Running basic tests:\n\n");
+ test_posix_basic ();
+ }
+ else if (t == extended_test)
+ {
+ printf ("Running extended tests:\n\n");
+ test_posix_extended ();
+ }
+}
+
+#else /* not canned */
+
+/* Use this to run interactive tests. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char pat[80];
+ struct re_pattern_buffer buf;
+ int i;
+ char c;
+ char fastmap[(1 << BYTEWIDTH)];
+
+ /* Allow a command argument to specify the style of syntax. */
+ if (argc > 1)
+ obscure_syntax = atol (argv[1]);
+
+ buf.allocated = 40;
+ buf.buffer = (char *) xmalloc (buf.allocated);
+ buf.fastmap = fastmap;
+ buf.translate = upcase;
+
+ while (1)
+ {
+ gets (pat);
+
+ if (*pat)
+ {
+ re_compile_pattern (pat, strlen(pat), &buf);
+
+ for (i = 0; i < buf.used; i++)
+ printchar (buf.buffer[i]);
+
+ putchar ('\n');
+
+ printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+
+ re_compile_fastmap (&buf);
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (fastmap[i]) printchar (i);
+ putchar ('\n');
+ }
+
+ gets (pat); /* Now read the string to match against */
+
+ i = re_match (&buf, pat, strlen (pat), 0, 0);
+ printf ("Match value %d.\n", i);
+ }
+}
+
+#endif
+
+
+#ifdef NOTDEF
+print_buf (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int i;
+
+ printf ("buf is :\n----------------\n");
+ for (i = 0; i < bufp->used; i++)
+ printchar (bufp->buffer[i]);
+
+ printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->fastmap[i])
+ printchar (i);
+ printf ("\nAllowed by translate: ");
+ if (bufp->translate)
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->translate[i])
+ printchar (i);
+ printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
+ printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+}
+#endif /* NOTDEF */
+
+printchar (c)
+ char c;
+{
+ if (c < 040 || c >= 0177)
+ {
+ putchar ('\\');
+ putchar (((c >> 6) & 3) + '0');
+ putchar (((c >> 3) & 7) + '0');
+ putchar ((c & 7) + '0');
+ }
+ else
+ putchar (c);
+}
+
+error (string)
+ char *string;
+{
+ puts (string);
+ exit (1);
+}
+#endif /* test */
diff --git a/regex.h b/regex.h
new file mode 100644
index 0000000000..8481bc84b3
--- /dev/null
+++ b/regex.h
@@ -0,0 +1,276 @@
+/* Definitions for data structures callers pass the regex library.
+
+ Copyright (C) 1985, 1989-90 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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. */
+/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto)
+ Last change: May 21, 1993 by t^2 */
+
+
+#ifndef __REGEXP_LIBRARY
+#define __REGEXP_LIBRARY
+
+/* Define number of parens for which we record the beginnings and ends.
+ This affects how much space the `struct re_registers' type takes up. */
+#ifndef RE_NREGS
+#define RE_NREGS 10
+#endif
+
+#define BYTEWIDTH 8
+
+
+/* Maximum number of duplicates an interval can allow. */
+#ifndef RE_DUP_MAX
+#define RE_DUP_MAX ((1 << 15) - 1)
+#endif
+
+
+/* This defines the various regexp syntaxes. */
+extern long obscure_syntax;
+
+
+/* The following bits are used in the obscure_syntax variable to choose among
+ alternative regexp syntaxes. */
+
+/* If this bit is set, plain parentheses serve as grouping, and backslash
+ parentheses are needed for literal searching.
+ If not set, backslash-parentheses are grouping, and plain parentheses
+ are for literal searching. */
+#define RE_NO_BK_PARENS 1L
+
+/* If this bit is set, plain | serves as the `or'-operator, and \| is a
+ literal.
+ If not set, \| serves as the `or'-operator, and | is a literal. */
+#define RE_NO_BK_VBAR (1L << 1)
+
+/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are
+ literals.
+ If set, \+, \? are operators and plain +, ? are literals. */
+#define RE_BK_PLUS_QM (1L << 2)
+
+/* If this bit is set, | binds tighter than ^ or $.
+ If not set, the contrary. */
+#define RE_TIGHT_VBAR (1L << 3)
+
+/* If this bit is set, then treat newline as an OR operator.
+ If not set, treat it as a normal character. */
+#define RE_NEWLINE_OR (1L << 4)
+
+/* If this bit is set, then special characters may act as normal
+ characters in some contexts. Specifically, this applies to:
+ ^ -- only special at the beginning, or after ( or |;
+ $ -- only special at the end, or before ) or |;
+ *, +, ? -- only special when not after the beginning, (, or |.
+ If this bit is not set, special characters (such as *, ^, and $)
+ always have their special meaning regardless of the surrounding
+ context. */
+#define RE_CONTEXT_INDEP_OPS (1L << 5)
+
+/* If this bit is not set, then \ before anything inside [ and ] is taken as
+ a real \.
+ If set, then such a \ escapes the following character. This is a
+ special case for awk. */
+#define RE_AWK_CLASS_HACK (1L << 6)
+
+/* If this bit is set, then \{ and \} or { and } serve as interval operators.
+ If not set, then \{ and \} and { and } are treated as literals. */
+#define RE_INTERVALS (1L << 7)
+
+/* If this bit is not set, then \{ and \} serve as interval operators and
+ { and } are literals.
+ If set, then { and } serve as interval operators and \{ and \} are
+ literals. */
+#define RE_NO_BK_CURLY_BRACES (1L << 8)
+
+/* If this bit is set, then character classes are supported; they are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (1L << 9)
+
+/* If this bit is set, then the dot re doesn't match a null byte.
+ If not set, it does. */
+#define RE_DOT_NOT_NULL (1L << 10)
+
+/* If this bit is set, then [^...] doesn't match a newline.
+ If not set, it does. */
+#define RE_HAT_NOT_NEWLINE (1L << 11)
+
+/* If this bit is set, back references are recognized.
+ If not set, they aren't. */
+#define RE_NO_BK_REFS (1L << 12)
+
+/* If this bit is set, back references must refer to a preceding
+ subexpression. If not set, a back reference to a nonexistent
+ subexpression is treated as literal characters. */
+#define RE_NO_EMPTY_BK_REF (1L << 13)
+
+/* If this bit is set, bracket expressions can't be empty.
+ If it is set, they can be empty. */
+#define RE_NO_EMPTY_BRACKETS (1L << 14)
+
+/* If this bit is set, then *, +, ? and { cannot be first in an re or
+ immediately after a |, or a (. Furthermore, a | cannot be first or
+ last in an re, or immediately follow another | or a (. Also, a ^
+ cannot appear in a nonleading position and a $ cannot appear in a
+ nontrailing position (outside of bracket expressions, that is). */
+#define RE_CONTEXTUAL_INVALID_OPS (1L << 15)
+
+/* If this bit is set, then +, ? and | aren't recognized as operators.
+ If it's not, they are. */
+#define RE_LIMITED_OPS (1L << 16)
+
+/* If this bit is set, then an ending range point has to collate higher
+ or equal to the starting range point.
+ If it's not set, then when the ending range point collates higher
+ than the starting range point, the range is just considered empty. */
+#define RE_NO_EMPTY_RANGES (1L << 17)
+
+/* If this bit is set, then a hyphen (-) can't be an ending range point.
+ If it isn't, then it can. */
+#define RE_NO_HYPHEN_RANGE_END (1L << 18)
+
+
+/* Define combinations of bits for the standard possibilities. */
+#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INDEP_OPS)
+#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_AWK_CLASS_HACK)
+#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR)
+#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+#define RE_SYNTAX_EMACS 0
+#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \
+ | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \
+ | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \
+ | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \
+ | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END)
+
+#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \
+ | RE_NO_BK_VBAR | RE_NO_BK_PARENS \
+ | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \
+ | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \
+ | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \
+ | RE_NO_HYPHEN_RANGE_END)
+
+/* For multi-byte char support */
+#define RE_MBCTYPE_EUC (1L << 19)
+#define RE_MBCTYPE_SJIS (1L << 20)
+#define RE_MBCTYPE_MASK (RE_MBCTYPE_EUC | RE_MBCTYPE_SJIS)
+
+#undef ismbchar
+#define ismbchar(c) \
+ (obscure_syntax & RE_MBCTYPE_EUC \
+ ? ((unsigned char) (c) >= 0x80) \
+ : (obscure_syntax & RE_MBCTYPE_SJIS \
+ ? (( 0x80 <= (unsigned char) (c) \
+ && (unsigned char) (c) <= 0x9f) \
+ || (0xe0 <= (unsigned char) (c))) \
+ : 0))
+
+/* This data structure is used to represent a compiled pattern. */
+
+struct re_pattern_buffer
+ {
+ char *buffer; /* Space holding the compiled pattern commands. */
+ long allocated; /* Size of space that `buffer' points to. */
+ long used; /* Length of portion of buffer actually occupied */
+ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
+ /* re_search uses the fastmap, if there is one,
+ to skip over totally implausible characters. */
+ char *translate; /* Translate table to apply to all characters before
+ comparing, or zero for no translation.
+ The translation is applied to a pattern when it is
+ compiled and to data when it is matched. */
+ char fastmap_accurate;
+ /* Set to zero when a new pattern is stored,
+ set to one when the fastmap is updated from it. */
+ char can_be_null; /* Set to one by compiling fastmap
+ if this pattern might match the null string.
+ It does not necessarily match the null string
+ in that case, but if this is zero, it cannot.
+ 2 as value means can match null string
+ but at end of range or before a character
+ listed in the fastmap. */
+ };
+
+
+/* search.c (search_buffer) needs this one value. It is defined both in
+ regex.c and here. */
+#define RE_EXACTN_VALUE 1
+
+
+/* Structure to store register contents data in.
+
+ Pass the address of such a structure as an argument to re_match, etc.,
+ if you want this information back.
+
+ For i from 1 to RE_NREGS - 1, start[i] records the starting index in
+ the string of where the ith subexpression matched, and end[i] records
+ one after the ending index. start[0] and end[0] are analogous, for
+ the entire pattern. */
+
+struct re_registers
+ {
+ int start[RE_NREGS];
+ int end[RE_NREGS];
+ };
+
+
+
+#ifdef __STDC__
+
+extern char *re_compile_pattern (char *, size_t, struct re_pattern_buffer *);
+/* Is this really advertised? */
+extern void re_compile_fastmap (struct re_pattern_buffer *);
+extern int re_search (struct re_pattern_buffer *, char*, int, int, int,
+ struct re_registers *);
+extern int re_search_2 (struct re_pattern_buffer *, char *, int,
+ char *, int, int, int,
+ struct re_registers *, int);
+extern int re_match (struct re_pattern_buffer *, char *, int, int,
+ struct re_registers *);
+extern int re_match_2 (struct re_pattern_buffer *, char *, int,
+ char *, int, int, struct re_registers *, int);
+extern long re_set_syntax (long syntax);
+
+#ifndef RUBY
+/* 4.2 bsd compatibility. */
+extern char *re_comp (char *);
+extern int re_exec (char *);
+#endif
+
+#else /* !__STDC__ */
+
+extern char *re_compile_pattern ();
+/* Is this really advertised? */
+extern void re_compile_fastmap ();
+extern int re_search (), re_search_2 ();
+extern int re_match (), re_match_2 ();
+extern long re_set_syntax();
+
+#ifndef RUBY
+/* 4.2 bsd compatibility. */
+extern char *re_comp ();
+extern int re_exec ();
+#endif
+
+#endif /* __STDC__ */
+
+
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#endif
+
+#endif /* !__REGEXP_LIBRARY */
diff --git a/ruby.1 b/ruby.1
new file mode 100644
index 0000000000..df2e0aa6fa
--- /dev/null
+++ b/ruby.1
@@ -0,0 +1,212 @@
+.\"ruby.1 - -*- Nroff -*-
+.\" $Author$
+.\" $Date$
+.\" created at: Tue Apr 12 01:45:04 GMT 1994
+.TH RUBY 1 "\*(RP"
+.UC
+.SH NAME
+ruby \- ֥Ȼظץȸ
+.SH "SYNOPSIS "
+.B ruby
+[
+.B options
+] filename args
+.SH DESCRIPTION
+.IB Ruby
+, ڤʥ֥Ȼظץߥ󥰤¸뤿μ
+εǽĥ֥ȻظץȸǤ. ߷פ
+ܸ§, ʲ̤Ǥ.
+
+.IP ǽ
+֥Ȼظץߥ󥰤ȥץȥץߥ󥰤Τ
+ɬפʵǽʬ. ä˥ƥȽطεǽ˭
+٤˻. ޤ, ʥ֥ȻظǤʤ, ɬ
+Ǥм³ץߥ󥰤ǽǤ.
+.IP ĥ
+ɬפ˱ưפ˵ǽĥǤ. 饹ͳɲäǤ
+Ȥ, CץΥ󥯤ˤäƥ󥿥ץ꥿ˤ
+뵡ǽɲäǤ. ˥ץåȥեˤäƤ, ưŪ
+֥ȥɤ󥯤뵡ǽ󶡤.
+.IP
+θ§ΤŬѤ褦ʰΤͤ.
+ˤäơ֥ѥγڤפϸΤʤ. ,
+ΤȤ䤹ˤ뤳ȤϤʤ.
+.PP
+.IB Ruby
+shperlΤäƤͤˤȤäƤξQˤǤ¤꽾äΤ,
+θ̤ƤͤˤȤäƤϽ(¿ʬ)ưפ.
+.SH OPTIONS
+.IB ruby
+ϰʲΰդ.
+.TP 5
+.B \-a
+`\-n'`\-p'ȤȤѤ, ȥץåȥ⡼ɤONˤ.
+ȥץåȥ⡼ɤǤϳƥ롼פƬ,
+.nf
+.ne 2
+
+ $F = $_.split
+
+.fi
+¹Ԥ. `\-n'`\-p'ץ󤬻ꤵʤ¤,
+ץϰ̣ʤ.
+.TP 5
+.B \-c
+ץȤؤΥѥΤߤԤ, ¹Ԥʤ.
+ѥ뽪λ, ʸˡ顼̵, "Syntax OK"ȽϤ.
+.TP 5
+.B \-e " script"
+ޥɥ饤󤫤饹ץȤꤹ. \-eץդ
+ˤϰ饹ץȥե̾ʤ.
+.TP 5
+.B \-F " ʸ"
+ϥեɥѥ졼(`$;')ͤʸ˥åȤ. awk
+Ʊ̾ΥץƱƯ򤹤.
+.TP 5
+.B \-i " extension"
+ǻꤵ줿եƤ֤(in-place edit)
+Ȥꤹ. ΥեϳĥҤĤ¸.
+.nf
+:
+.ne 2
+
+ % echo matz > /tmp/junk
+ % cat /tmp/junk
+ matz
+ % ruby -p -i.bak -e '$_.toupper' /tmp/junk
+ % cat /tmp/junk
+ MATZ
+ % cat /tmp/junk.bak
+ matz
+
+.fi
+.TP 5
+.B \-I " directory"
+եɤѥ(ɲ). ꤵ줿ǥ쥯
+.IB ruby
+ѿ$LOAD_PATHɲä.
+.TP 5
+.B \-d, \--debug
+ǥХå⡼ɤonˤ. Υե饰åȤȥƥ
+ѿ$DEBUGåȤ.
+.TP 5
+.B \-l
+`$\\'`$/'Ʊͤꤷ, print()Ǥνϻ˲Ԥղä
+. ޤ, \-nޤ\-pȤȤѤ, Ϥ줿Ԥκ
+ʸchop.
+.TP 5
+.B \-n
+Υե饰åȤȥץΤ
+.nf
+.ne 2
+
+ while gets()
+ \.\.\.
+ end
+
+.fi
+ǰϤޤƤ褦ư.
+.TP 5
+.B \-p
+\-nե饰Ʊ, ƥ롼פκǸѿ`$_'ͤϤ.
+.nf
+:
+.ne 2
+
+ % echo matz | ruby \-p \-e '$_.tr("a-z", "A-Z")'
+ MATZ
+
+.fi
+.TP 5
+.B \-R ʸ(ޤ8ʿ)
+ϥ쥳ɥѥ졼(`$/')ͤʸ˥åȤ. ʸ
+٤ƿ(0-7ޤ)鹽Ƥˤ, 8ʿǻ
+ꤵ줿ɤʸ򥻥ѥ졼Ȥ.
+
+ξ, \-R0, ѥ饰ե⡼, \-R777(Υɤ
+ʸ¸ߤʤΤ)ե٤ɤ߹⡼ɤ
+Ǥ.
+.TP 5
+.B \-s
+ץ̾³, \-ǻϤޤᤷ, Ʊ̾ѿ
+ͤꤹ. `\-\-'ʤʹߤϲԤʤʤ.
+$ARGV.
+.nf
+:
+.ne 2
+
+ #! /usr/local/ruby -s
+ # -xyzץͿ"true"ɽ.
+ if $xyz then print("true\n") end
+
+.fi
+.TP 5
+.B \-v, \--verbose
+Ĺ⡼. ư˥СֹɽԤ, ƥѿ
+$VERBOSE򥻥åȤ. ѿåȤƤ, Ĥ
+Υ᥽åɤϼ¹Ի˾ĹʥåϤ. \-v ץ
+ꤵƤ, ץʳΰʤˤϥС
+ɽ, ¹Ԥλ(ɸϤΥץ
+Ԥʤ).
+.TP 5
+.B \--version
+.IB ruby
+ΥСɽ.
+.nf
+ɽ:
+.ne 2
+
+ ruby - version 0.47 (11 Jul 94)
+
+.fi
+.TP 5
+.B \-x
+åΥץȤФƼ¹Ԥ. #!ǻϤޤ,
+"ruby"ȤʸޤԤޤǤɤФ. ץȤν
+EOF(եν), ^D(ȥD), ^Z(ȥZ)
+ޤʸ"__END__"ǻꤹ.
+.TP 5
+.B \-X " directory"
+ץȼ¹˻ꤵ줿ǥ쥯ȥ˰ܤ.
+.TP 5
+.B \-y, \--yydebug
+ѥǥХå⡼. ѥιʸϤβɽ
+. ɽ˾ĹʤΤ, ѥ餽ΤΤǥХ
+ͰʳɽʤɤȻפ.
+.TP 5
+.B \-N, \-E, \-S
+.IB ruby
+νɤꤹ. Nϴʤ. EEUC,
+SSJISɽ. ǥեȤϴб.
+
+.SH "BUG Х(뤤Ϸ)"
+.PP
+٤. ñʽξperlawkʤɤ2,3ܤμ¹Ի֤.
+¾θȰۤʤ, 󶡤뵡ǽΤۤȤɤ᥽åɸƤӽ
+𤹤뤳Ȥ, ¾θǤؿƤӽФ¿ʤ
+褦ʽǤϥ᥽åɥåʬ
+.IB ruby
+ͭˤʤ뤷, ǡ¤ʣˤʤ, ֥Ȼظ
+åȤ褫Τ, ޤ뤫Τʤ.
+.PP
+perl국̤¿.
+.IB ruby
+ɵᤷ̤Ǥ. , η,
+.IB ruby
+ץȤperlɤߤ䤹Ϥ, 㴳ε̤
+򤷤䤹ȲƤȻפäߤ.
+.PP
+Sparcǥѥ뤵줿
+.IB ruby
+ϥƥ졼breakɾcore dump뤳Ȥ.
+setjmp()˴ϢХȻפ뤬, ¾Υƥ
+ǤϺƸ, ͤߤƤʤ. ïʬж
+ߤ.
+.PP
+ɥȤԽʬ. ɬפʾ뤿ˤϥɤ
+ߤ.
+.PP
+ƥȤԽʬ. ХˤĤä, Ǥмʬľ,
+ä˶ߤ. ̵ʤ, ƥХƸ
+ΤˤƥݡȤߤ.
diff --git a/ruby.c b/ruby.c
new file mode 100644
index 0000000000..5b98281bb7
--- /dev/null
+++ b/ruby.c
@@ -0,0 +1,374 @@
+/************************************************
+
+ ruby.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:37 $
+ created at: Tue Aug 10 12:47:31 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "re.h"
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#ifdef HAVE_GETOPT_LONG
+#include "getopt.h"
+#else
+#include "missing/getopt.h"
+#endif
+
+static struct option long_options[] =
+{
+ {"debug", 0, 0, 'd'},
+ {"yydebug", 0, 0, 'y'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+int debug = 0;
+int verbose = 0;
+static int sflag = FALSE;
+
+char *inplace = Qnil;
+char *strdup();
+char *strstr();
+char *index();
+
+extern int yydebug;
+extern int nerrs;
+
+int xflag = FALSE;
+
+#ifdef USE_DLN
+char *rb_dln_argv0;
+#endif
+
+static void load_stdin();
+void rb_load_file();
+
+static int do_loop = FALSE, do_print = FALSE;
+static int do_check = FALSE, do_line = FALSE;
+static int do_split = FALSE;
+
+static char*
+proc_options(argcp, argvp)
+ int *argcp;
+ char ***argvp;
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+ extern VALUE rb_load_path;
+ extern long reg_syntax;
+ extern char *optarg;
+ extern int optind;
+ int c, i, j, script_given, version, opt_index;
+ extern VALUE RS, ORS, FS;
+ char *script;
+ char *src;
+
+ version = FALSE;
+ script_given = FALSE;
+ script = Qnil;
+
+ optind = 0;
+ while ((c = getopt_long(argc, argv, "+acde:F:i:I:lnpR:svxX:yNES",
+ long_options, &opt_index)) != EOF) {
+ switch (c) {
+ case 0: /* long options */
+ if (strcmp(long_options[opt_index].name, "version") == 0) {
+ version = TRUE;
+ show_version();
+ }
+ break;
+
+ case 'p':
+ do_print = TRUE;
+ /* through */
+ case 'n':
+ do_loop = TRUE;
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'y':
+ yydebug = 1;
+ break;
+
+ case 'v':
+ version = verbose = TRUE;
+ show_version();
+ break;
+
+ case 'e':
+ script_given++;
+ if (script == 0) script = "-e";
+ lex_setsrc("-e", optarg, strlen(optarg));
+ yyparse();
+ break;
+
+ case 'i':
+ inplace = strdup(optarg);
+ break;
+
+ case 'c':
+ do_check = TRUE;
+ break;
+
+ case 'x':
+ xflag = TRUE;
+ break;
+
+ case 'X':
+ if (chdir(optarg) < 0)
+ Fatal("Can't chdir to %s", optarg);
+ break;
+
+ case 's':
+ sflag = TRUE;
+ break;
+
+ case 'l':
+ do_line = TRUE;
+ ORS = RS;
+ break;
+
+ case 'R':
+ {
+ char *p = optarg;
+
+ while (*p) {
+ if (*p < '0' || '7' < *p) {
+ break;
+ }
+ p++;
+ }
+ if (*p) {
+ RS = str_new2(optarg);
+ }
+ else {
+ int i = strtoul(optarg, Qnil, 8);
+
+ if (i == 0) RS = str_new(0, 0);
+ else if (i > 0xff) RS = Qnil;
+ else {
+ char c = i;
+ RS = str_new(&c, 1);
+ }
+ }
+ }
+ break;
+
+ case 'F':
+ FS = str_new2(optarg);
+ break;
+
+ case 'a':
+ do_split = TRUE;
+ break;
+
+ case 'N':
+ reg_syntax &= ~RE_MBCTYPE_MASK;
+ re_set_syntax(reg_syntax);
+ break;
+ case 'E':
+ reg_syntax &= ~RE_MBCTYPE_MASK;
+ reg_syntax |= RE_MBCTYPE_EUC;
+ re_set_syntax(reg_syntax);
+ break;
+ case 'S':
+ reg_syntax &= ~RE_MBCTYPE_MASK;
+ reg_syntax |= RE_MBCTYPE_SJIS;
+ re_set_syntax(reg_syntax);
+ break;
+
+ case 'I':
+ Fary_unshift(rb_load_path, str_new2(optarg));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (argv[0] == Qnil) return Qnil;
+
+ if (script_given == 0) {
+ if (argc == optind) { /* no more args */
+ if (version == TRUE) exit(0);
+ script = "-";
+ load_stdin();
+ }
+ else {
+ script = argv[optind];
+ rb_load_file(argv[optind]);
+ optind++;
+ }
+ }
+
+ xflag = FALSE;
+ *argvp += optind;
+ *argcp -= optind;
+
+ if (sflag) {
+ char *s;
+ VALUE v;
+
+ argc = *argcp; argv = *argvp;
+ for (; argc > 0 && argv[0][0] == '-'; argc--,argv++) {
+ if (argv[0][1] == '-') {
+ argc--,argv++;
+ break;
+ }
+ argv[0][0] = '$';
+ if (s = index(argv[0], '=')) {
+ *s++ = '\0';
+ GC_LINK;
+ GC_PRO3(v, str_new2(s));
+ rb_gvar_set2((*argvp)[0], v);
+ GC_UNLINK;
+ }
+ else {
+ rb_gvar_set2((*argvp)[0], TRUE);
+ }
+ }
+ *argcp = argc; *argvp = argv;
+ }
+
+ return script;
+}
+
+static void
+readin(fd, fname)
+ int fd;
+ char *fname;
+{
+ struct stat st;
+ char *ptr, *p, *pend;
+
+ 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) {
+ rb_sys_fail(fname);
+ }
+ p = ptr;
+ pend = p + st.st_size;
+ if (xflag) {
+ char *s = p;
+
+ *pend = '\0';
+ xflag = FALSE;
+ while (p < pend) {
+ while (s < pend && *s != '\n') s++;
+ if (*s != '\n') break;
+ *s = '\0';
+ if (p[0] == '#' && p[1] == '!' && strstr(p, "ruby")) {
+ if (p = strstr(p, "ruby -")) {
+ int argc; char *argv[2]; char **argvp = argv;
+ argc = 2; argv[0] = Qnil; argv[1] = p + 5;
+ proc_options(&argc, &argvp);
+ }
+ xflag = TRUE;
+ p = s + 1;
+ goto start_read;
+ }
+ p = s + 1;
+ }
+ Fail("No Ruby script found in input");
+ }
+ start_read:
+ lex_setsrc(fname, p, pend - p);
+ yyparse();
+ free(ptr);
+}
+
+void
+rb_load_file(fname)
+ char *fname;
+{
+ int fd;
+ char *ptr;
+
+ if (fname[0] == '\0') {
+ load_stdin();
+ return;
+ }
+
+ fd = open(fname, O_RDONLY, 0);
+ if (fd < 0) rb_sys_fail(fname);
+ readin(fd, fname);
+ close(fd);
+}
+
+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);
+
+ if (fd < 0) rb_sys_fail(buf);
+
+ readin(fd, "-");
+}
+
+void
+rb_main(argc, argv) /* real main() is in eval.c */
+ int argc;
+ char **argv;
+{
+ char *script;
+ extern VALUE errat;
+
+ rb_call_inits();
+
+ rb_define_variable("$@", &errat, Qnil, Qnil);
+ errat = str_new2(argv[0]);
+ rb_define_variable("$VERBOSE", &verbose, Qnil, Qnil);
+ rb_define_variable("$DEBUG", &debug, Qnil, Qnil);
+
+#ifdef USE_DLN
+ rb_dln_argv0 = argv[0];
+#endif
+
+ script = proc_options(&argc, &argv);
+ if (do_check && nerrs == 0) {
+ printf("Syntax OK\n");
+ exit(0);
+ }
+ if (do_print) {
+ yyappend_print();
+ }
+ if (do_loop) {
+ yywhole_loop(do_line, do_split);
+ }
+
+ if (nerrs == 0) {
+ TopLevel(script, argc, argv);
+ }
+
+ exit(nerrs);
+}
diff --git a/ruby.h b/ruby.h
new file mode 100644
index 0000000000..a454394f35
--- /dev/null
+++ b/ruby.h
@@ -0,0 +1,298 @@
+/************************************************
+
+ ruby.h -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:38 $
+ created at: Thu Jun 10 14:26:32 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#ifndef RUBY_H
+#define RUBY_H
+
+#include "defines.h"
+
+#ifdef __STDC__
+#else
+#define volatile
+#define const
+#endif
+
+#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
+#include <alloca.h>
+#endif
+
+typedef unsigned int UINT;
+typedef UINT VALUE;
+typedef UINT ID;
+typedef unsigned short USHORT;
+#ifdef __STDC__
+# include <limits.h>
+#else
+# ifndef LONG_MAX
+# define LONG_MAX ((long)((unsigned long)~0L>>1))
+# endif
+# ifndef LONG_MIN
+# if (0 != ~0)
+# define LONG_MIN (-LONG_MAX-1)
+# else
+# define LONG_MIN (-LONG_MAX)
+# endif
+# endif
+#endif
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+# define FIXNUM_MAX (LONG_MAX>>1)
+# define FIXNUM_MIN RSHIFT((long)LONG_MIN,1)
+
+#define FIXNUM_FLAG 0x01
+#define INT2FIX(i) (VALUE)(((int)(i))<<1 | FIXNUM_FLAG)
+
+#if (-1==(((-1)<<1)&FIXNUM_FLAG)>>1)
+# define RSHIFT(x,y) ((x)>>y)
+#else
+# define RSHIFT(x,y) (((x)<0) ? ~((~(x))>>y) : (x)>>y)
+#endif
+#define FIX2INT(x) RSHIFT((int)x,1)
+
+#define FIX2UINT(f) ((unsigned int)(f)>>1)
+#define FIXNUM_P(f) (((int)(f))&FIXNUM_FLAG)
+#define POSFIXABLE(f) ((f) <= FIXNUM_MAX)
+#define NEGFIXABLE(f) ((f) >= FIXNUM_MIN)
+#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f))
+
+#define POINTER(p) (p)
+#define NIL_P(p) ((p) == Qnil)
+
+#define TRUE INT2FIX(1)
+#define FALSE Qnil
+
+extern VALUE C_Object;
+extern VALUE C_Nil;
+extern VALUE C_Fixnum;
+extern VALUE C_Data;
+
+#define CLASS_OF(obj) (FIXNUM_P(obj)?C_Fixnum: NIL_P(obj)?C_Nil:\
+ RBASIC(obj)->class)
+
+#define FL_SINGLE 0x10
+#define FL_MARK 0x20
+#define FL_LITERAL 0x40
+
+#define FL_ABLE(x) (!(FIXNUM_P(x)||NIL_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);}
+
+#define T_NIL 0x0
+#define T_OBJECT 0x1
+#define T_CLASS 0x2
+#define T_ICLASS 0x3
+#define T_MODULE 0x4
+#define T_FLOAT 0x5
+#define T_STRING 0x6
+#define T_REGEXP 0x7
+#define T_ARRAY 0x8
+#define T_FIXNUM 0x9
+#define T_DICT 0xA
+#define T_DATA 0xB
+#define T_METHOD 0xC
+#define T_STRUCT 0xD
+#define T_BIGNUM 0xE
+
+#define T_MASK 0xF
+
+#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);}
+#define Need_Fixnum(x) {if (!FIXNUM_P(x)) (x) = num2fix(x);}
+#define NUM2INT(x) (FIXNUM_P(x)?FIX2INT(x):num2int(x))
+VALUE num2fix();
+int num2int();
+
+#define NEWOBJ(obj,type) type *obj = (type*)newobj(sizeof(type))
+#define OBJSETUP(obj,c,t) {\
+ RBASIC(obj)->class = (c);\
+ RBASIC(obj)->flags |= (t);\
+}
+#define CLONESETUP(obj1,obj2) \
+ OBJSETUP(obj1,RBASIC(obj2)->class,RBASIC(obj2)->flags&T_MASK);
+
+struct RBasic {
+ UINT flags;
+ struct RBasic *next;
+ VALUE class;
+ struct st_table *iv_tbl;
+};
+
+struct RObject {
+ struct RBasic basic;
+};
+
+struct RClass {
+ struct RBasic basic;
+ struct st_table *m_tbl;
+ struct st_table *c_tbl;
+ struct RClass *super;
+};
+
+struct RFloat {
+ struct RBasic basic;
+ double value;
+};
+
+struct RString {
+ struct RBasic basic;
+ UINT len;
+ char *ptr;
+ struct RString *orig;
+};
+
+struct RArray {
+ struct RBasic basic;
+ UINT len, capa;
+ VALUE *ptr;
+};
+
+struct RRegexp {
+ struct RBasic basic;
+ struct Regexp *ptr;
+ UINT len;
+ char *str;
+};
+
+struct RDict {
+ struct RBasic basic;
+ struct st_table *tbl;
+};
+
+struct RData {
+ struct RBasic basic;
+ void (*dmark)();
+ void (*dfree)();
+ VALUE data[1];
+};
+
+#define DATA_PTR(dta) &(RDATA(dta)->data[0])
+
+#define Get_Data_Struct(obj, iv, type, sval) {\
+ VALUE _data_;\
+ _data_ = rb_iv_get(obj, iv);\
+ Check_Type(_data_, T_DATA);\
+ sval = (type*)DATA_PTR(_data_);\
+}
+
+#define Make_Data_Struct(obj, iv, type, mark, free, sval) {\
+ struct RData *_new_;\
+ _new_ = (struct RData*)newobj(sizeof(struct RData)+sizeof(type));\
+ OBJSETUP(_new_, C_Data, T_DATA);\
+ _new_->dmark = (void (*)())(mark);\
+ _new_->dfree = (void (*)())(free);\
+ sval = (type*)DATA_PTR(_new_);\
+ bzero(sval, sizeof(type));\
+ rb_iv_set(obj, iv, _new_);\
+}
+
+struct RMethod {
+ struct RBasic basic;
+ struct node *node;
+ struct RClass *origin;
+ ID id;
+ enum mth_scope { MTH_METHOD, MTH_FUNC, MTH_UNDEF } scope;
+};
+
+struct RStruct {
+ struct RBasic basic;
+ UINT len;
+ struct kv_pair {
+ ID key;
+ VALUE value;
+ } *tbl;
+ char *name;
+};
+
+struct RBignum {
+ struct RBasic basic;
+ char sign;
+ UINT len;
+ USHORT *digits;
+};
+
+#define R_CAST(st) (struct st*)
+#define RBASIC(obj) (R_CAST(RBasic)(obj))
+#define ROBJECT(obj) (R_CAST(RObject)(obj))
+#define RCLASS(obj) (R_CAST(RClass)(obj))
+#define RFLOAT(obj) (R_CAST(RFloat)(obj))
+#define RSTRING(obj) (R_CAST(RString)(obj))
+#define RREGEXP(obj) (R_CAST(RRegexp)(obj))
+#define RARRAY(obj) (R_CAST(RArray)(obj))
+#define RDICT(obj) (R_CAST(RDict)(obj))
+#define RDATA(obj) (R_CAST(RData)(obj))
+#define RMETHOD(obj) (R_CAST(RMethod)(obj))
+#define RSTRUCT(obj) (R_CAST(RStruct)(obj))
+#define RBIGNUM(obj) (R_CAST(RBignum)(obj))
+
+#define Qnil (VALUE)0
+
+#define ALLOC_N(type,n) (type*)xmalloc(sizeof(type)*(n))
+#define ALLOC(type) (type*)xmalloc(sizeof(type))
+#define REALLOC_N(var,type,n) (var)=(type*)xrealloc((char*)(var),sizeof(type)*(n))
+
+extern struct gc_list {
+ int n;
+ VALUE *varptr;
+ struct gc_list *next;
+} *GC_List;
+
+#define GC_LINK { struct gc_list *_oldgc = GC_List;
+
+#define GC_PRO(var) {\
+ struct gc_list *_tmp = (struct gc_list*)alloca(sizeof(struct gc_list));\
+ _tmp->next = GC_List;\
+ _tmp->varptr = (VALUE*)&(var);\
+ _tmp->n = 1;\
+ GC_List = _tmp;\
+}
+#define GC_PRO2(var) GC_PRO3((var),Qnil)
+#define GC_PRO3(var,init) {\
+ (var) = (init);\
+ GC_PRO(var);\
+}
+#define GC_PRO4(var,nelt) {\
+ GC_PRO(var[0]);\
+ GC_List->n = nelt;\
+}
+
+#define GC_UNLINK GC_List = _oldgc; }
+
+VALUE rb_define_class();
+VALUE rb_define_module();
+
+void rb_define_variable();
+void rb_define_const();
+
+void rb_define_method();
+void rb_define_func();
+void rb_define_single_method();
+void rb_define_mfunc();
+void rb_undef_method();
+void rb_define_alias();
+void rb_define_attr();
+
+ID rb_intern();
+char *rb_id2name();
+
+VALUE rb_funcall();
+int rb_scan_args();
+
+VALUE rb_yield();
+
+extern int verbose, debug;
+
+#endif
diff --git a/sample/Artistic b/sample/Artistic
new file mode 100644
index 0000000000..fbf7989775
--- /dev/null
+++ b/sample/Artistic
@@ -0,0 +1,117 @@
+
+
+
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their corresponding
+ Standard Version executables, giving the non-standard executables
+ non-standard names, and clearly documenting the differences in manual
+ pages (or equivalent), together with instructions on where to get
+ the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package.
+You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+
+7. C subroutines supplied by you and linked into this Package in order
+to emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE 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.
+
+ The End
diff --git a/sample/aset.rb b/sample/aset.rb
new file mode 100644
index 0000000000..cfec649ac3
--- /dev/null
+++ b/sample/aset.rb
@@ -0,0 +1,3 @@
+ary = [0, 0, 4, 5]
+ary[1, 0] = [7]
+print(ary, "\n")
diff --git a/sample/attr.rb b/sample/attr.rb
new file mode 100644
index 0000000000..2db79f1ae5
--- /dev/null
+++ b/sample/attr.rb
@@ -0,0 +1,9 @@
+class Foo
+ attr("test", %TRUE)
+end
+
+foo = Foo.new
+foo.test = 10
+print(foo.test, "\n")
+foo._inspect.print
+print("\n")
diff --git a/sample/biorhythm.rb b/sample/biorhythm.rb
new file mode 100644
index 0000000000..3261c4377f
--- /dev/null
+++ b/sample/biorhythm.rb
@@ -0,0 +1,201 @@
+#!/mp/free/bin/ruby
+#
+# biorhythm.rb -
+# $Release Version: $
+# $Revision: 1.6 $
+# $Date: 1994/02/24 10:23:34 $
+# by Yasuo OHBA(STAFS Development Room)
+#
+# --
+#
+#
+#
+
+$RCS_ID="$Header: /var/ohba/RCS/biorhythm.rb,v 1.6 1994/02/24 10:23:34 ohba Exp ohba $"
+
+include Math
+load("parsearg.rb")
+
+$wochentag = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]
+monatstag1 = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+monatstag2 = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+def usage()
+ print("Usage:\n")
+ print("biorhythm.rb [options]\n")
+ print(" options...\n")
+ print(" -D YYYYMMDD(birthday) : ٤ default ͤȤ. \n")
+ print(" --sdate | --date YYYYMMDD : system date ⤷ϻꤷդȤ.\n")
+ print(" --birthday YYYYMMDD : λ򤹤. \n")
+ print(" -v | -g : Values or Graph λ. \n")
+ print(" --days DAYS : ֤λ򤹤(Graph λΤͭ). \n")
+ print(" --help : help\n")
+end
+$USAGE = 'usage'
+
+def leapyear(y)
+ ta = 0
+ if ((y % 4.0) == 0); ta = 1; end
+ if ((y % 100.0) == 0); ta = 0; end
+ if ((y % 400.0) == 0); ta = 1; end
+ return ta
+end
+
+def bcalc(t, m, j)
+ ta = 0
+ if (m <= 2)
+ ta = (m - 1) * 31
+ else
+ ta = leapyear(j)
+ ta = ta + ((306 * m - 324) / 10.0).to_i
+ end
+ ta = ta + (j - 1) * 365 + ((j - 1) / 4.0).to_i
+ ta = ta - ((j - 1) / 100) + ((j - 1) / 400.0).to_i
+ ta = ta + t
+ return ta
+end
+
+def printHeader(tg, mg, jg, gtag, tage)
+ print("\n")
+ print(" Biorhythm\n")
+ print(" =========\n")
+ print("\n")
+ printf("The birthday %04d.%02d.%02d is a %s\n", jg, mg, tg, $wochentag[gtag])
+ printf("Age in days: [%d]\n", tage)
+end
+
+def getPosition(z)
+ pi = 3.14159265
+ $phys = (50.0 * (1.0 + sin((z / 23.0 - (z / 23)) * 360.0 * pi / 180.0))).to_i
+ $emot = (50.0 * (1.0 + sin((z / 28.0 - (z / 28)) * 360.0 * pi / 180.0))).to_i
+ $geist =(50.0 * (1.0 + sin((z / 33.0 - (z / 33)) * 360.0 * pi / 180.0))).to_i
+end
+
+#
+# main program
+#
+parseArgs(0, nil, "vg", "D:", "sdate", "date:", "birthday:", "days:")
+
+printf($stderr, "\n")
+printf($stderr, "Biorhythm (c) 1987-1994 V3.0\n")
+printf($stderr, "\n")
+if ($OPT_D)
+ dtmp = Time.now.strftime("%Y%m%d")
+ jh = dtmp[0,4].to_i
+ mh = dtmp[4,2].to_i
+ th = dtmp[6,2].to_i
+ dtmp = $OPT_D
+ jg = dtmp[0,4].to_i
+ mg = dtmp[4,2].to_i
+ tg = dtmp[6,2].to_i
+ gtag = bcalc(tg, mg, jg) % 7
+ ausgabeart = "g"
+else
+ if ($OPT_birthday)
+ dtmp = $OPT_birthday
+ else
+ printf($stderr, "Birthday (YYYYMMDD) : ")
+ dtmp = $stdin.gets.chop
+ end
+ if (dtmp.length != 8)
+ printf($stderr, "BAD Input Birthday!!\n")
+ exit()
+ end
+ jg = dtmp[0,4].to_i
+ mg = dtmp[4,2].to_i
+ tg = dtmp[6,2].to_i
+
+ gtag = bcalc(tg, mg, jg) % 7
+
+ if ($OPT_sdate)
+ dtmp = Time.now.strftime("%Y%m%d")
+ elsif ($OPT_date)
+ dtmp = $OPT_date
+ else
+ printf($stderr, "Date [<RETURN> for Systemdate] (YYYYMMDD) : ")
+ dtmp = $stdin.gets.chop
+ end
+ if (dtmp.length != 8)
+ dtmp = Time.now.strftime("%Y%m%d")
+ end
+ jh = dtmp[0,4].to_i
+ mh = dtmp[4,2].to_i
+ th = dtmp[6,2].to_i
+
+ if ($OPT_v)
+ ausgabeart = "v"
+ elsif ($OPT_g)
+ ausgabeart = "g"
+ else
+ printf($stderr, "Values for today or Graph (v/g) [default g] : ")
+ ausgabeart = $stdin.gets.chop
+ end
+end
+if (ausgabeart == "v")
+ tag = bcalc(tg, mg, jg)
+ tah = bcalc(th, mh, jh)
+ tage = tah - tag
+ printHeader(tg, mg, jg, gtag, tage)
+ print("\n")
+
+ getPosition(tage)
+ printf("Biorhythm: %04d.%02d.%02d\n", jh, mh, th)
+ printf("Physical: %d%%\n", $phys)
+ printf("Emotional: %d%%\n", $emot)
+ printf("Mental: %d%%\n", $geist)
+ print("\n")
+else
+ if ($OPT_days)
+ ktage = $OPT_days.to_i
+ else
+ if ($OPT_D)
+ ktage = 9
+ else
+ printf($stderr, "Graph for how many days [default 10] : ")
+ ktage = $stdin.gets.chop
+ if (ktage == "")
+ ktage = 9
+ else
+ ktage = ktage.to_i - 1
+ end
+ end
+ end
+ tag = bcalc(tg, mg, jg)
+ tah = bcalc(th, mh, jh)
+ tage = tah - tag
+ printHeader(tg, mg, jg, gtag, tage)
+ print(" P=physical, E=emotional, M=mental\n")
+ print(" -------------------------+-------------------------\n")
+ print(" Bad Condition | Good Condition\n")
+ print(" -------------------------+-------------------------\n")
+
+ for z in tage..(tage + ktage)
+ getPosition(z)
+
+ printf("%04d.%02d.%02d : ", jh, mh, th)
+ p = ($phys / 2.0 + 0.5).to_i
+ e = ($emot / 2.0 + 0.5).to_i
+ g = ($geist / 2.0 + 0.5).to_i
+ graph = "." * 51
+ graph[25] = ?|
+ graph[p] = ?P
+ graph[e] = ?E
+ graph[g] = ?M
+ print(graph, "\n")
+ th = th + 1
+ if (leapyear(jh) == 0)
+ $MONATSTAG = monatstag1
+ else
+ $MONATSTAG = monatstag2
+ end
+ if (th > $MONATSTAG[mh - 1])
+ mh = mh + 1
+ th = 1
+ end
+ if (mh > 12)
+ jh = jh + 1
+ mh = 1
+ end
+ end
+ print(" -------------------------+-------------------------\n\n")
+end
diff --git a/sample/caller.rb b/sample/caller.rb
new file mode 100644
index 0000000000..8988733377
--- /dev/null
+++ b/sample/caller.rb
@@ -0,0 +1,15 @@
+def test
+ for i in 1..2
+ for j in 1..5
+ print(j, ": ", caller(j).join(':'), "\n")
+ end
+ end
+end
+
+def test2
+ print(1, ": ", caller(1).join(':'), "\n")
+ test()
+end
+
+test2()
+caller()
diff --git a/sample/case.rb b/sample/case.rb
new file mode 100644
index 0000000000..f456f023d3
--- /dev/null
+++ b/sample/case.rb
@@ -0,0 +1,12 @@
+
+case "t"
+when /1/
+ print(1, "\n")
+when /t/
+ print(3..5, "\n")
+when /./
+ print(2, "\n")
+else
+ print("else\n")
+end
+
diff --git a/sample/cat.rb b/sample/cat.rb
new file mode 100644
index 0000000000..93f028906a
--- /dev/null
+++ b/sample/cat.rb
@@ -0,0 +1,4 @@
+while gets()
+ if $. == 1 ... ~ /^\*/; print("--") end
+ printf("%5d: %s", $., $_)
+end
diff --git a/sample/cbreak.rb b/sample/cbreak.rb
new file mode 100644
index 0000000000..4b4cb1d3e6
--- /dev/null
+++ b/sample/cbreak.rb
@@ -0,0 +1,34 @@
+%CBREAK = 0x00000002
+%ECHO = 0x00000008
+%TIOCGETP = 0x40067408
+%TIOCSETP = 0x80067409
+
+def cbreak ()
+ set_cbreak(%TRUE)
+end
+
+def cooked ()
+ set_cbreak(%FALSE)
+end
+
+def set_cbreak (on)
+ tty = "\0" * 256
+ $stdin.ioctl(%TIOCGETP, tty)
+ ttys = tty.unpack("C4 S")
+ if on
+ ttys[4] |= %CBREAK
+ ttys[4] &= ~%ECHO
+ else
+ ttys[4] &= ~%CBREAK
+ ttys[4] |= %ECHO
+ end
+ tty = ttys.pack("C4 S")
+ $stdin.ioctl(%TIOCSETP, tty)
+end
+cbreak();
+
+print("this is echo line: ");
+readline().print
+cooked();
+print("this is non echo line: ");
+readline().print
diff --git a/sample/clnt.rb b/sample/clnt.rb
new file mode 100644
index 0000000000..639e2a0daf
--- /dev/null
+++ b/sample/clnt.rb
@@ -0,0 +1,12 @@
+host=(if $ARGV.length == 2; $ARGV.shift; else "localhost"; end)
+print("Trying ", host, " ...")
+$stdout.flush
+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)
+end
+s.close
diff --git a/sample/clone.rb b/sample/clone.rb
new file mode 100644
index 0000000000..6a752e8ab5
--- /dev/null
+++ b/sample/clone.rb
@@ -0,0 +1,12 @@
+foo = Object.new
+def foo.test
+ print("test\n")
+end
+bar = foo.clone
+def bar.test2
+ print("test2\n")
+end
+bar.test2
+bar.test
+foo.test
+foo.test2
diff --git a/sample/const.rb b/sample/const.rb
new file mode 100644
index 0000000000..d7629fe8e1
--- /dev/null
+++ b/sample/const.rb
@@ -0,0 +1,20 @@
+%test1 = 1
+%test2 = 2
+
+module Const
+ %test3 = 3
+ %test4 = 4
+end
+
+module Const2
+ %test3 = 6
+ %test4 = 8
+end
+
+include Const
+
+print(%test1,%test2,%test3,%test4,"\n")
+
+include Const2
+
+print(%test1,%test2,%test3,%test4,"\n")
diff --git a/sample/dbm.rb b/sample/dbm.rb
new file mode 100644
index 0000000000..1f2886e026
--- /dev/null
+++ b/sample/dbm.rb
@@ -0,0 +1,6 @@
+# ruby
+#
+
+d = Dbm.open("test")
+for k in d.keys; print(k, "\n"); end
+for v in d.values; print(v, "\n"); end
diff --git a/sample/dir.rb b/sample/dir.rb
new file mode 100644
index 0000000000..f4c3d17c11
--- /dev/null
+++ b/sample/dir.rb
@@ -0,0 +1,9 @@
+dirp = Dir.open(".")
+dirp.rewind
+for f in dirp
+ if (~/^\./ || ~/~$/ || ~/\.o/)
+ else
+ print(f, "\n")
+ end
+end
+dirp.close
diff --git a/sample/evaldef.rb b/sample/evaldef.rb
new file mode 100644
index 0000000000..c261b53068
--- /dev/null
+++ b/sample/evaldef.rb
@@ -0,0 +1,21 @@
+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/fib.awk b/sample/fib.awk
new file mode 100644
index 0000000000..7ebe8930f5
--- /dev/null
+++ b/sample/fib.awk
@@ -0,0 +1,5 @@
+ function fib(n) {
+ if ( n<2 ) return n; else return fib(n-2) + fib(n-1)
+ }
+
+ BEGIN { print fib(20); }
diff --git a/sample/fib.pl b/sample/fib.pl
new file mode 100644
index 0000000000..c5593764aa
--- /dev/null
+++ b/sample/fib.pl
@@ -0,0 +1,10 @@
+ sub fib {
+ local($n)=@_;
+ if( $n<2 ){
+ return $n;
+ } {
+ return &fib($n-2)+&fib($n-1)
+ }
+ }
+
+ print &fib(20), "\n";
diff --git a/sample/fib.rb b/sample/fib.rb
new file mode 100644
index 0000000000..c72f91f2f2
--- /dev/null
+++ b/sample/fib.rb
@@ -0,0 +1,8 @@
+def fib (n)
+ if n<2
+ n
+ else
+ fib(n-2)+fib(n-1)
+ end
+end
+print(fib(20), "\n");
diff --git a/sample/freq.rb b/sample/freq.rb
new file mode 100644
index 0000000000..7417e0b5a4
--- /dev/null
+++ b/sample/freq.rb
@@ -0,0 +1,13 @@
+#
+
+freq = {}
+while gets()
+ while sub(/\w+/, '')
+ word = $&
+ freq[word] +=1
+ end
+end
+
+for word in freq.keys.sort
+ printf("%s -- %d\n", word, freq[word])
+end
diff --git a/sample/fullpath.pl b/sample/fullpath.pl
new file mode 100644
index 0000000000..a07b90edd4
--- /dev/null
+++ b/sample/fullpath.pl
@@ -0,0 +1,22 @@
+#! /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
new file mode 100644
index 0000000000..b1d4b9c332
--- /dev/null
+++ b/sample/fullpath.rb
@@ -0,0 +1,24 @@
+#! /usr/local/bin/ruby
+# convert ls-lR filename into fullpath.
+
+if $ARGV[0] =~ /-p/
+ $ARGV.shift
+ path = $ARGV.shift
+end
+
+if path == nil
+ path = ""
+elsif path !~ /\/$/
+ print(path, "/\n")
+ path += "/"
+end
+
+while gets()
+ if /:$/
+ path = $_.chop.chop + "/"
+ elsif /^total/ || /^d/
+ elsif /^(.*\d )(.+)$/
+ print($1, path, $2, "\n")
+ end
+end
+
diff --git a/sample/gctest.rb b/sample/gctest.rb
new file mode 100644
index 0000000000..6067393bbb
--- /dev/null
+++ b/sample/gctest.rb
@@ -0,0 +1,69 @@
+def cons(car, cdr)
+ [car, cdr]
+end
+
+def car(x)
+ if x == nil ; nil else x[0] end
+end
+
+def cdr(x)
+ if x == nil ; nil else x[1] end
+end
+
+def reverse1(x, y)
+ if x == nil ; y else reverse1(cdr(x), cons(car(x), y)) end
+end
+
+def reverse(x)
+ reverse1(x, nil)
+end
+
+def ints(low, up)
+ if low > up
+ nil
+ else
+ cons(low, ints(low+1, up))
+ end
+end
+
+def print_int_list(x)
+ if x == nil
+ print("NIL\n")
+ else
+ print(car(x))
+ if nil != cdr(x)
+ print(", ")
+ print_int_list(cdr(x))
+ else
+ print("\n")
+ end
+ end
+end
+
+GC.threshold = 1000000
+
+print("start\n")
+print("threshold: ", GC.threshold, "\n");
+
+a = ints(1, 100)
+print_int_list(a)
+b = ints(1, 50)
+print_int_list(b)
+print_int_list(reverse(a))
+print_int_list(reverse(b))
+for i in 1 .. 100
+ b = reverse(reverse(b))
+# print(i, ": ")
+# print_int_list(b)
+end
+print("a: ")
+print_int_list(a)
+print("b: ")
+print_int_list(b)
+print("reverse(a): ")
+print_int_list(reverse(a))
+print("reverse(b): ")
+print_int_list(reverse(b))
+a = b = nil
+print("finish\n")
+GC.start()
diff --git a/sample/getopts.rb b/sample/getopts.rb
new file mode 100644
index 0000000000..57a7db424f
--- /dev/null
+++ b/sample/getopts.rb
@@ -0,0 +1,111 @@
+#
+# getopts.rb - get options
+# $Release Version: $
+# $Revision: 1.2 $
+# $Date: 1994/02/15 05:17:15 $
+# by Yasuo OHBA(STAFS Development Room)
+#
+# --
+# IvV̉͂, $OPT_?? ɒlZbg܂.
+# ŵȂIvVw肳ꂽ nil Ԃ܂.
+# Iꍇ, ZbgꂽIvV̐Ԃ܂.
+#
+# 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) ̗lȈꕶ̃IvV̎w܂.
+# ňȂƂ nil ̎w肪Kvł.
+# ȍ~:
+# Ol[̃IvV, ̔IvV̎w܂.
+# --version , --geometry 300x400 , -d host:0.0 ł.
+# 𔺂w ":" KtĂ.
+#
+# IvV̎w肪ꍇ, ϐ $OPT_?? non-nil , ̃I
+# vV̈Zbg܂.
+# -f -> $OPT_f = %TRUE
+# --geometry 300x400 -> $OPT_geometry = 300x400
+#
+# - -- , ȍ~, SăIvV̉͂܂.
+#
+
+$RCS_ID="$Header: /var/ohba/RCS/getopts.rb,v 1.2 1994/02/15 05:17:15 ohba Exp ohba $"
+
+def getopts(single_opts, *opts)
+ if (opts)
+ single_colon = ""
+ long_opts = []
+ sc = 0
+ for option in opts
+ if (option.length <= 2)
+ single_colon[sc, 0] = option[0, 1]
+ sc += 1
+ else
+ long_opts.push(option)
+ end
+ end
+ end
+
+ count = 0
+ while ($ARGV.length != 0)
+ compare = nil
+ case $ARGV[0]
+ when /^-*$/
+ $ARGV.shift
+ 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)
+ return nil
+ end
+ eval("$OPT_" + compare + " = " + '$ARGV[1]')
+ $ARGV.shift
+ count += 1
+ break
+ elsif (option == compare)
+ eval("$OPT_" + compare + " = %TRUE")
+ count += 1
+ break
+ end
+ end
+ 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")
+ count += 1
+ elsif (single_colon != "" && compare =~ "[" + single_colon + "]")
+ if ($ARGV[0][index..-1].length > 1)
+ eval("$OPT_" + compare + " = " + '$ARGV[0][(index + 1)..-1]')
+ count += 1
+ elsif ($ARGV.length <= 1)
+ return nil
+ else
+ eval("$OPT_" + compare + " = " + '$ARGV[1]')
+ $ARGV.shift
+ count = count + 1
+ end
+ break
+ end
+ end
+ else
+ break
+ end
+
+ $ARGV.shift
+ if (!defined("$OPT_" + compare))
+ return nil
+ end
+ end
+ return count
+end
diff --git a/sample/getopts.test b/sample/getopts.test
new file mode 100755
index 0000000000..16f1bb06c7
--- /dev/null
+++ b/sample/getopts.test
@@ -0,0 +1,31 @@
+#! /mp/free/bin/ruby -- -*- ruby -*-
+
+load("parsearg.rb")
+
+def usage()
+ printf("Usage:\n")
+ printf("This is Getopt test program \n")
+end
+
+$USAGE = 'usage'
+parseArgs(0, !nil, "d", "x:", "y:", "version", "geometry:")
+if ($OPT_d)
+ if ($OPT_x)
+ printf("x = %d\n", $OPT_x.atoi)
+ end
+ if ($OPT_y)
+ printf("y = %d\n", $OPT_y.atoi)
+ end
+ if ($OPT_geometry)
+ printf("geometry = %s\n", $OPT_geometry)
+ end
+end
+
+if ($OPT_version)
+ printf("version 1.00\n")
+end
+
+while ($ARGV.length != 0)
+ print ("other = ", $ARGV[0], "\n")
+ $ARGV.shift
+end
diff --git a/sample/hash.rb b/sample/hash.rb
new file mode 100644
index 0000000000..126b17dfe4
--- /dev/null
+++ b/sample/hash.rb
@@ -0,0 +1,4 @@
+print(+-1.0.hash,"\n")
+print(-1.0.hash,"\n")
+print((-1.0).hash,"\n")
+print(-(1.0.hash),"\n")
diff --git a/sample/io.rb b/sample/io.rb
new file mode 100644
index 0000000000..45d50d653a
--- /dev/null
+++ b/sample/io.rb
@@ -0,0 +1,40 @@
+home = getenv("HOME")
+print(home.sub("m", "&&"), home, "\n")
+print(home.reverse, "\n")
+
+if File.s("io.rb")
+ print(File.s("io.rb"), ": io.rb\n")
+end
+
+$/="f\n"
+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", do passwd.find using i; i =~ /\*/ end)
+
+n = 1
+for i in passwd #.grep(/^\*/)
+ printf("%6d: %s", n, i)
+ n = n + 1;
+end
+
+fp = open("|-", "r")
+
+if fp == nil
+ for i in 1..5
+ print(i, "\n")
+ end
+else
+ for line in fp
+ print(line)
+ end
+end
+
+def printUsage()
+ if $USAGE
+ apply($USAGE);
+ end
+end
diff --git a/sample/less.rb b/sample/less.rb
new file mode 100755
index 0000000000..e818e7a2ba
--- /dev/null
+++ b/sample/less.rb
@@ -0,0 +1,30 @@
+#! /mp/free/bin/ruby -- -*- ruby -*-
+#
+# less -
+# $Release Version: $
+# $Revision: 1.1 $
+# $Date: 90/09/29 15:17:59 $
+# by Yasuo OHBA(STAFS Development Room)
+#
+# --
+#
+#
+#
+
+$RCS_ID="$Header: less,v 1.1 90/09/29 15:17:59 ohba Locked $"
+
+ZCAT = "/usr/local/bin/zcat"
+LESS = "/usr/local/bin/less"
+
+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))
+elsif FILE == nil
+ exec(format("%s %s", LESS, OPTION))
+else
+ print(format("%s %s %s", LESS, OPTION, FILE), "\n")
+ exec(format("%s %s %s", LESS, OPTION, FILE))
+end
+exit()
diff --git a/sample/list.rb b/sample/list.rb
new file mode 100644
index 0000000000..e0b5b383b0
--- /dev/null
+++ b/sample/list.rb
@@ -0,0 +1,81 @@
+# Linked list program
+class MyElem
+ def MyElem.new(item)
+ super.init(item)
+ end
+
+ def init(item)
+ @data = item
+ @next = nil
+ self
+ end
+
+ def data
+ @data
+ end
+
+ def next
+ @next
+ end
+
+ def next=(new)
+ @next = new
+ end
+end
+
+class MyList
+ def add_to_list(obj)
+ elt = MyElem.new(obj)
+ if @head
+ @tail.next = elt
+ else
+ @head = elt
+ end
+ @tail = elt
+ end
+
+ def each
+ elt = @head
+ while elt
+ yield elt
+ elt = elt.next
+ end
+ end
+
+ def to_s
+ str = "<MyList:\n";
+ for elt in self
+ str += elt.data.to_s + "\n"
+ end
+ str += ">"
+ str
+ end
+end
+
+class Point
+ def Point.new(x, y)
+ super.init(x, y)
+ end
+
+ def init(x, y)
+ @x = x; @y = y
+ self
+ end
+
+ def to_s
+ sprintf("%d@%d", @x, @y)
+ end
+end
+
+list1 = MyList.new
+list1.add_to_list(10)
+list1.add_to_list(20)
+list1.add_to_list(Point.new(2, 3))
+list1.add_to_list(Point.new(4, 5))
+list2 = MyList.new
+list2.add_to_list(20)
+list2.add_to_list(Point.new(4, 5))
+list2.add_to_list(list1)
+
+print("list1:\n", list1, "\n")
+print("list2:\n", list2, "\n")
diff --git a/sample/list2.rb b/sample/list2.rb
new file mode 100644
index 0000000000..c2e9bca753
--- /dev/null
+++ b/sample/list2.rb
@@ -0,0 +1,20 @@
+# Linked list program -- short version
+class Point
+ def Point.new(x, y)
+ super.init(x, y)
+ end
+
+ def init(x, y)
+ @x = x; @y = y
+ self
+ end
+
+ def to_s
+ sprintf("%d@%d", @x, @y)
+ end
+end
+
+list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)]
+list2 = [20, Point.new(4, 5), list1]
+print("list1:\n", list1.join("\n"), "\n")
+print("list2:\n", list2.join("\n"), "\n")
diff --git a/sample/math.rb b/sample/math.rb
new file mode 100644
index 0000000000..83be1b9048
--- /dev/null
+++ b/sample/math.rb
@@ -0,0 +1,4 @@
+#load("lib/math.o")
+include Math
+sqrt(4)
+print(Math.sqrt(257), "\n")
diff --git a/sample/mpart.rb b/sample/mpart.rb
new file mode 100755
index 0000000000..c0f705ef8e
--- /dev/null
+++ b/sample/mpart.rb
@@ -0,0 +1,42 @@
+#! ./ruby
+
+lines = 1000
+
+if ($ARGV[0] =~ /^-(\d+)$/ )
+ lines = $1.to_i;
+ $ARGV.shift;
+end
+
+basename = $ARGV[0]
+extname = "part"
+
+part = 1
+line = 0
+
+fline = 0
+for i in ifp = open(basename)
+ fline = fline + 1
+end
+ifp.close
+
+parts = fline / lines + 1
+
+for i in ifp = open(basename)
+ if line == 0
+ ofp = open(sprintf("%s.%s%02d", basename, extname, part), "w")
+ printf(ofp, "%s part%02d/%02d\n", basename, part, parts)
+ ofp.write("BEGIN--cut here--cut here\n")
+ end
+ ofp.write(i)
+ line = line + 1
+ if line >= lines
+ ofp.write("END--cut here--cut here\n")
+ ofp.close
+ part = part + 1
+ line = 0
+ end
+end
+ofp.write("END--cut here--cut here\n")
+ofp.close
+
+ifp.close
diff --git a/sample/newver.rb b/sample/newver.rb
new file mode 100644
index 0000000000..bbf03aebc2
--- /dev/null
+++ b/sample/newver.rb
@@ -0,0 +1,13 @@
+#! /usr/local/bin/ruby
+
+f = open("version.h", "r")
+f.gets()
+f.close
+
+if $_ =~ /"(\d)\.(\d+)"/;
+ f = open("version.h", "w")
+ i = $2.to_i + 1
+ printf("ruby version %d.%0d\n", $1, i)
+ printf(f, "#define RUBY_VERSION \"%d.%0d\"\n", $1, i)
+ f.close
+end
diff --git a/sample/occur.pl b/sample/occur.pl
new file mode 100644
index 0000000000..1f5fcf27a4
--- /dev/null
+++ b/sample/occur.pl
@@ -0,0 +1,9 @@
+while (<>) {
+ for (split(/\W+/)) {
+ $freq{$_}++;
+ }
+}
+
+for (sort keys %freq) {
+ print "$_ -- $freq{$_}\n";
+}
diff --git a/sample/occur.rb b/sample/occur.rb
new file mode 100644
index 0000000000..12e469d20a
--- /dev/null
+++ b/sample/occur.rb
@@ -0,0 +1,10 @@
+freq = {}
+while gets()
+ for word in $_.split(/\W+/)
+ freq[word] +=1
+ end
+end
+
+for word in freq.keys.sort
+ printf("%s -- %d\n", word, freq[word])
+end
diff --git a/sample/occur2.rb b/sample/occur2.rb
new file mode 100644
index 0000000000..6e0513707c
--- /dev/null
+++ b/sample/occur2.rb
@@ -0,0 +1,14 @@
+freq = {}
+while gets()
+ for word in $_.split(/\W+/)
+ protect
+ freq[word] = freq[word] + 1
+ resque
+ freq[word] = 1
+ end
+ end
+end
+
+for word in freq.keys.sort
+ printf("%s -- %d\n", word, freq[word])
+end
diff --git a/sample/opt_s.rb b/sample/opt_s.rb
new file mode 100644
index 0000000000..4981119012
--- /dev/null
+++ b/sample/opt_s.rb
@@ -0,0 +1,8 @@
+#! ruby -s
+if ($xyz)
+ print("xyz = TRUE\n")
+end
+if ($zzz)
+ print("zzz = ", $zzz, "\n")
+end
+print($ARGV.join(", "), "\n")
diff --git a/sample/opt_x.rb b/sample/opt_x.rb
new file mode 100644
index 0000000000..4fc9a25276
--- /dev/null
+++ b/sample/opt_x.rb
@@ -0,0 +1,8 @@
+this is a forwarding header
+this is a header too.
+ #! ruby
+this is a trailing
+#! ./ruby -v
+print("tt\n")
+__END__
+this is a trailer
diff --git a/sample/parsearg.rb b/sample/parsearg.rb
new file mode 100644
index 0000000000..e7e2b7a7f3
--- /dev/null
+++ b/sample/parsearg.rb
@@ -0,0 +1,69 @@
+#
+# parseargs.rb - parse arguments
+# $Release Version: $
+# $Revision: 1.3 $
+# $Date: 1994/02/15 05:16:21 $
+# by Yasuo OHBA(STAFS Development Room)
+#
+# --
+# ̉͂, $OPT_?? ɒlZbg܂.
+# Iꍇ, ZbgꂽIvV̐Ԃ܂.
+#
+# 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:")
+#
+# :
+# IvVȊO̍Œ̐
+# :
+# IvV̕KvcKKvȂ %TRUE łȂ %FALSE.
+# O:
+# -f -x (= -fx) ̗lȈꕶ̃IvV̎w܂.
+# ňȂƂ nil ̎w肪Kvł.
+# lȍ~:
+# Ol[̃IvV, ̔IvV̎w܂.
+# --version , --geometry 300x400 , -d host:0.0 ł.
+# 𔺂w ":" KtĂ.
+#
+# IvV̎w肪ꍇ, ϐ $OPT_?? non-nil , ̃I
+# vV̈Zbg܂.
+# -f -> $OPT_f = %TRUE
+# --geometry 300x400 -> $OPT_geometry = 300x400
+#
+# usage gꍇ, $USAGE usage() w肵܂.
+# def usage()
+# c
+# end
+# $USAGE = 'usage'
+# usage , --help w肳ꂽ, Ԉwɕ\܂.
+#
+# - -- , ȍ~, SăIvV̉͂܂.
+#
+
+$RCS_ID="$Header: /var/ohba/RCS/parseargs.rb,v 1.3 1994/02/15 05:16:21 ohba Exp ohba $"
+
+load("getopts.rb")
+
+def printUsageAndExit()
+ if $USAGE
+ apply($USAGE)
+ end
+ exit()
+end
+
+def parseArgs(argc, nopt, single_opts, *opts)
+ if ((noOptions = getopts(single_opts, *opts)) == nil)
+ printUsageAndExit()
+ end
+ if (nopt && noOptions == 0)
+ printUsageAndExit()
+ end
+ if ($ARGV.length < argc)
+ printUsageAndExit()
+ end
+ return noOptions
+end
diff --git a/sample/perror.rb b/sample/perror.rb
new file mode 100644
index 0000000000..9196525ee3
--- /dev/null
+++ b/sample/perror.rb
@@ -0,0 +1,7 @@
+if File._s("io.rb")
+ print(File._z("io.rb", "io.rb\n")
+# print(File._s("io.rb"), ": io.rb\n")
+end
+
+for i in 1..5
+end
diff --git a/sample/rcs.awk b/sample/rcs.awk
new file mode 100644
index 0000000000..08979285c9
--- /dev/null
+++ b/sample/rcs.awk
@@ -0,0 +1,33 @@
+BEGIN {
+ sw = 40.0;
+ dw = 78.0;
+ hdw = dw / 2.0;
+ w = 20.0;
+ h =1.0;
+ d = 0.2;
+ ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./";
+ rnd = srand();
+}
+
+{
+ xr = -hdw; y = h * 1.0; maxxl = -999;
+ s = "";
+ while (xr < hdw) {
+ x = xr * (1 + y) - y * w / 2;
+ i = (x / (1 + h) + sw /2);
+ c = (0 < i && i < length($0)) ? substr($0, i, 1) : "0";
+ y = h - d * c;
+ xl = xr - w * y / (1 + y);
+ if (xl < -hdw || xl >= hdw || xl <= maxxl) {
+ t = rand() * length(ss);
+ c = substr(ss, t, 1);
+ }
+ else {
+ c = substr(s, xl + hdw, 1);
+ maxxl = xl;
+ }
+ s = s c;
+ xr = xr + 1;
+ }
+ print s;
+}
diff --git a/sample/rcs.dat b/sample/rcs.dat
new file mode 100644
index 0000000000..61c88bff89
--- /dev/null
+++ b/sample/rcs.dat
@@ -0,0 +1,17 @@
+0000000000000000220000000000000000
+0000000000000111221110000000000000
+0000000000111112222111110000000000
+0000000111111112222111111110000000
+0000111111111122222211111111110000
+0111111111111222222221111111111110
+2222222222222222222222222222222222
+1122222222222222222222222222222211
+0111122222222222222222222222211110
+0011111122222222222222222211111100
+0001111111222222222222221111111000
+0000111112222222222222222111110000
+0000011122222222112222222211100000
+0000001122222221111222222211000000
+0000000122221111111111222210000000
+0000000221111111111111111220000000
+0000000000000000000000000000000000
diff --git a/sample/rcs.rb b/sample/rcs.rb
new file mode 100644
index 0000000000..b14742981d
--- /dev/null
+++ b/sample/rcs.rb
@@ -0,0 +1,42 @@
+sw = 40.0 # ̃p^[̕
+dw = 78.0 # Random Character Streogram ̕
+hdw = dw / 2.0
+w = 20.0 # ̕
+h =1.0 # ʂƊʂ̋
+d = 0.2 # Pʓ̕オ
+ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./"
+rnd = srand()
+
+while gets()
+# print($_)
+ xr = -hdw; y = h * 1.0; maxxl = -999
+ s = "";
+ while xr < hdw
+ x = xr * (1 + y) - y * w / 2
+ i = (x / (1 + h) + sw /2)
+ c = if (0 < i < $_.length); $_[i, 1].to_i else 0 end
+ y = h - d * c
+ xl = xr - w * y / (1 + y);
+ if xl < -hdw || xl >= hdw || xl <= maxxl
+ t = rand(ss.length)
+ c = ss[t, 1]
+ else
+ c = s[xl + hdw, 1]
+ maxxl = xl
+ end
+ s = s + c
+ xr = xr + 1
+ end
+ print(s, "\n")
+end
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/reach.rb b/sample/reach.rb
new file mode 100644
index 0000000000..25b340887d
--- /dev/null
+++ b/sample/reach.rb
@@ -0,0 +1,5 @@
+for i in 1..10
+ print("test\n")
+ break
+ print("test2\n")
+end
diff --git a/sample/resp.rb b/sample/resp.rb
new file mode 100644
index 0000000000..84516b4c78
--- /dev/null
+++ b/sample/resp.rb
@@ -0,0 +1,2 @@
+undef kill
+print(responds_to("kill"), "\n")
diff --git a/sample/samp.rb b/sample/samp.rb
new file mode 100644
index 0000000000..b4d818faba
--- /dev/null
+++ b/sample/samp.rb
@@ -0,0 +1,12 @@
+P = 0
+while gets()
+ printf("%3d: %s", $., $_)
+ while sub(/\w+/, '')
+ if $& != "";
+ P += 1
+ end
+ end
+ if ($. >= 10); break; end
+end
+printf("line: %d\n", $.)
+printf("word: %d\n", P)
diff --git a/sample/split.rb b/sample/split.rb
new file mode 100644
index 0000000000..737173635e
--- /dev/null
+++ b/sample/split.rb
@@ -0,0 +1,12 @@
+print("1 byte string", "\n")
+print("1 byte string".reverse, "\n")
+
+print("ʸ", "\n")
+print("ʸ".reverse, "\n")
+
+print("1 byte string", "\n")
+print("1 byte string".split(//).reverse.join(":"), "\n")
+print("ʸ", "\n")
+print("ʸ".split(//).reverse.join(":"), "\n")
+print("Ѥ1byteκ", "\n")
+print("Ѥ1byteκ".split(//).reverse.join(":"), "\n")
diff --git a/sample/struct.rb b/sample/struct.rb
new file mode 100644
index 0000000000..7c0690b627
--- /dev/null
+++ b/sample/struct.rb
@@ -0,0 +1,4 @@
+foo = Struct.new("test", "a1"::1, "a2"::2)
+print(foo, "\n")
+bar = foo.clone
+print(bar.a1, "\n")
diff --git a/sample/svr.rb b/sample/svr.rb
new file mode 100644
index 0000000000..c00a03c8cf
--- /dev/null
+++ b/sample/svr.rb
@@ -0,0 +1,23 @@
+gs = TCPserver.open(0)
+printf("server port is on %d\n", gs.port)
+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
+ print(s, " is gone\n")
+ s.close
+ socks.delete(s)
+ else
+ if str = s.gets;
+ s.write(str)
+ end
+ end
+ end
+ end
+end
diff --git a/sample/system.rb b/sample/system.rb
new file mode 100644
index 0000000000..c51626d2dc
--- /dev/null
+++ b/sample/system.rb
@@ -0,0 +1 @@
+print(system2("echo foobar"))
diff --git a/sample/t1.rb b/sample/t1.rb
new file mode 100644
index 0000000000..98d3b529e9
--- /dev/null
+++ b/sample/t1.rb
@@ -0,0 +1,20 @@
+def test(a1, *a2)
+ while 1
+ switch gets()
+ case nil
+ break
+ case /^-$/
+ print("-\n")
+ return
+ case /^-help/
+ print("-help\n")
+ break
+ end
+ end
+ print(a1, a2, "\n")
+end
+
+print($ARGV, "\n")
+print("in: ")
+test(1)
+print("end\n")
diff --git a/sample/t2.rb b/sample/t2.rb
new file mode 100644
index 0000000000..7f5b9df480
--- /dev/null
+++ b/sample/t2.rb
@@ -0,0 +1,24 @@
+#print("in Print\n")
+def t2() end
+
+def println(*args)
+ for a in args
+ t2()
+ print(a)
+ end
+ print("\n")
+end def
+
+def tt
+ for i in 1..10
+ println("i:", i);
+ yield(i);
+ end
+end
+
+test =
+do tt() using i
+ if i == 3; break end
+ println("ttt: ", i);
+end
+#exit()
diff --git a/sample/test.rb b/sample/test.rb
new file mode 100644
index 0000000000..9c422cc94a
--- /dev/null
+++ b/sample/test.rb
@@ -0,0 +1,5 @@
+index = 1
+for argument in $ARGV
+ printf("%d:%s\n", index, argument)
+ index = index + 1
+end
diff --git a/sample/trap.pl b/sample/trap.pl
new file mode 100644
index 0000000000..ce022d4062
--- /dev/null
+++ b/sample/trap.pl
@@ -0,0 +1,6 @@
+$SIG{'INT'} = 'test';
+
+while (<>) {
+ print;
+}
+sub test { print "C-c handled\n"; }
diff --git a/sample/trap.rb b/sample/trap.rb
new file mode 100644
index 0000000000..e552a0fddc
--- /dev/null
+++ b/sample/trap.rb
@@ -0,0 +1,3 @@
+trap('print("C-c handled\n")', 'INT', 'HUP')
+print("---\n")
+while gets(); print($_) end
diff --git a/sample/tt.rb b/sample/tt.rb
new file mode 100644
index 0000000000..cb863e3527
--- /dev/null
+++ b/sample/tt.rb
@@ -0,0 +1,103 @@
+module Print
+ print("in Print\n")
+ def println(*args)
+ for a in args
+ print(a)
+ end
+ print("\n")
+ end def
+
+ def println2(*args)
+ print(*args)
+ print("\n")
+ end def
+end module
+
+module Print2
+ def println(*args)
+ print("pr2: ");
+ super
+ end
+end
+
+module Print3
+ include Print2
+ def println(*args)
+ print("pr3: ");
+ super
+ end
+end
+
+include Print, Print2, Print3
+
+println2("in TopLevel")
+
+print("a: ", $OPT_test, "\n")
+printf("%10.5g: %*s -> 0x%x\n", 123345, -10, Print, Print.id);
+
+println("a+ matches aaa at ", "bccAAaaa" =~ /a+/)
+ttt = "this is a "
+if offset = (ttt =~ /this ([^ ]*) (.*)/)
+ println("0 = ", $&);
+ println("1 = ", $1);
+ println("2 = ", $2);
+end
+
+class Fib:Object
+ print("in Fib:Object\n")
+
+ def Fib.test(*args)
+ println("in Fib.test")
+
+ if args; println(*args) end
+ args = args.grep(/^c/)
+ super(*args)
+ end def
+
+ def init
+ println("in Fib.init");
+ end def
+
+ def fib(n)
+ a =0; b = 1
+
+ while b <= n
+ c = a; a = b; b = c+b
+ end while
+ return b
+ end def
+end
+
+def Object.test(*args)
+ println("in Object.test")
+ if args; println(*args) end
+end
+
+Fib.test("abc", "def", "aaa", "ccc")
+println("1:", 0x3fffffffa)
+println("2:", 0x3ffffffa)
+#println("3:", 0x40000000+0x40000000)
+
+fib = Fib.new
+
+fib.init
+print(Fib, ":")
+
+#for i in 1 .. 100
+# fib.fib(90000)
+#end
+
+println(fib.fib(9000))
+
+def tt
+ for i in 1..10
+ println("i:", i);
+ yield(i);
+ end
+end
+
+test = do tt() using i
+ if i == 2; break end
+end
+
+println([1,2,3,4].join(":"))
diff --git a/socket.c b/socket.c
new file mode 100644
index 0000000000..ce0f71d3d3
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,738 @@
+/************************************************
+
+ socket.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:51 $
+ created at: Thu Mar 31 12:21:29 JST 1994
+
+************************************************/
+
+#include "ruby.h"
+#ifdef HAVE_SOCKET
+#include "io.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/un.h>
+
+extern VALUE C_IO;
+VALUE C_BasicSocket;
+VALUE C_TCPsocket;
+VALUE C_TCPserver;
+VALUE C_UNIXsocket;
+VALUE C_UNIXserver;
+VALUE C_Socket;
+
+FILE *rb_fdopen();
+char *strdup();
+
+static VALUE
+sock_new(class, fd)
+ VALUE class;
+ int fd;
+{
+ VALUE sock = obj_alloc(class);
+ OpenFile *fp;
+
+ GC_LINK;
+ GC_PRO(sock);
+ MakeOpenFile(sock, fp);
+ fp->f = rb_fdopen(fd, "r");
+ setbuf(fp->f, NULL);
+ fp->f2 = rb_fdopen(fd, "w");
+ fp->mode = FMODE_READWRITE|FMODE_SYNC;
+ GC_UNLINK;
+
+ return sock;
+}
+
+static VALUE
+Fbsock_shutdown(sock, args)
+ VALUE sock, args;
+{
+ VALUE howto;
+ int how;
+ OpenFile *fptr;
+
+ rb_scan_args(args, "01", &howto);
+ if (howto == Qnil)
+ how = 2;
+ else {
+ how = NUM2INT(howto);
+ if (how < 0 && how > 2) how = 2;
+ }
+ GetOpenFile(sock, fptr);
+ if (shutdown(fileno(fptr->f), how) == -1)
+ rb_sys_fail(Qnil);
+ return sock;
+}
+
+static VALUE
+Fbsock_setopt(sock, lev, optname, val)
+ VALUE sock, lev, optname;
+ struct RString *val;
+{
+ int level, option;
+ OpenFile *fptr;
+
+ level = NUM2INT(lev);
+ option = NUM2INT(optname);
+ Check_Type(val, T_STRING);
+
+
+ GetOpenFile(sock, fptr);
+ if (setsockopt(fileno(fptr->f), level, option, val->ptr, val->len) < 0)
+ rb_sys_fail(fptr->path);
+ return sock;
+}
+
+static VALUE
+Fbsock_getopt(sock, lev, optname)
+ VALUE sock, lev, optname;
+{
+ int level, option, len;
+ struct RString *val;
+ OpenFile *fptr;
+
+ level = NUM2INT(lev);
+ option = NUM2INT(optname);
+ len = 256;
+ val = (struct RString*)str_new(0, len);
+ Check_Type(val, T_STRING);
+
+ GetOpenFile(sock, fptr);
+ if (getsockopt(fileno(fptr->f), level, option, val->ptr, &len) < 0)
+ rb_sys_fail(fptr->path);
+ val->len = len;
+ return (VALUE)val;
+}
+
+static VALUE
+Fbsock_getsockname(sock)
+ VALUE sock;
+{
+ char buf[1024];
+ int len = sizeof buf;
+ OpenFile *fptr;
+
+ GetOpenFile(sock, fptr);
+ if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return str_new(buf, len);
+}
+
+static VALUE
+Fbsock_getpeername(sock)
+ VALUE sock;
+{
+ char buf[1024];
+ int len = sizeof buf;
+ OpenFile *fptr;
+
+ GetOpenFile(sock, fptr);
+ if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0)
+ rb_sys_fail("getpeername(2)");
+ return str_new(buf, len);
+}
+
+static VALUE
+open_inet(class, h, serv, server)
+ VALUE class, h, serv;
+ int server;
+{
+ char *host;
+ struct hostent *hostent, _hostent;
+ struct servent *servent, _servent;
+ struct protoent *protoent;
+ struct sockaddr_in sockaddr;
+ int fd, status;
+ int hostaddr, hostaddrPtr[2];
+ int servport;
+ char *syscall;
+ VALUE sock;
+
+ if (h) {
+ Check_Type(h, T_STRING);
+ host = RSTRING(h)->ptr;
+ hostent = gethostbyname(host);
+ if (hostent == NULL) {
+ hostaddr = inet_addr(host);
+ if (hostaddr == -1) {
+ if (server && !strlen(host))
+ hostaddr = INADDR_ANY;
+ else
+ rb_sys_fail(host);
+ }
+ _hostent.h_addr_list = (char **)hostaddrPtr;
+ _hostent.h_addr_list[0] = (char *)&hostaddr;
+ _hostent.h_addr_list[1] = NULL;
+ _hostent.h_length = sizeof(hostaddr);
+ _hostent.h_addrtype = AF_INET;
+ hostent = &_hostent;
+ }
+ }
+ servent = NULL;
+ if (FIXNUM_P(serv)) {
+ servport = FIX2UINT(serv);
+ goto setup_servent;
+ }
+ Check_Type(serv, T_STRING);
+ servent = getservbyname(RSTRING(serv)->ptr, "tcp");
+ if (servent == NULL) {
+ servport = strtol(RSTRING(serv)->ptr, Qnil, 0);
+ if (servport == -1) Fail("no such servce %s", RSTRING(serv)->ptr);
+ setup_servent:
+ _servent.s_port = servport;
+ _servent.s_proto = "tcp";
+ servent = &_servent;
+ }
+ protoent = getprotobyname(servent->s_proto);
+ if (protoent == NULL) Fail("no such proto %s", servent->s_proto);
+
+ fd = socket(PF_INET, SOCK_STREAM, protoent->p_proto);
+
+ sockaddr.sin_family = AF_INET;
+ if (h == Qnil) {
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ }
+ else {
+ memcpy((char *)&(sockaddr.sin_addr.s_addr),
+ (char *) hostent->h_addr_list[0],
+ (size_t) hostent->h_length);
+ }
+ sockaddr.sin_port = servent->s_port;
+
+ if (server) {
+ status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ syscall = "bind(2)";
+ }
+ else {
+ status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ syscall = "connect(2)";
+ }
+
+ if (status < 0) {
+ close (fd);
+ rb_sys_fail(syscall);
+ }
+ if (server) listen(fd, 5);
+
+ /* create new instance */
+ sock = sock_new(class, fd);
+
+ return sock;
+}
+
+static VALUE
+Ftcp_sock_open(class, host, serv)
+ VALUE class, host, serv;
+{
+ Check_Type(host, T_STRING);
+ return open_inet(class, host, serv, 0);
+}
+
+static VALUE
+Ftcp_svr_open(class, args)
+ VALUE class, args;
+{
+ VALUE arg1, arg2;
+
+ if (rb_scan_args(args, "11", &arg1, &arg2) == 2)
+ return open_inet(class, arg1, arg2, 1);
+ else
+ return open_inet(class, Qnil, arg1, 1);
+}
+
+static VALUE
+sock_accept(class, fd, sockaddr, len)
+ VALUE class;
+ int fd;
+ struct sockaddr *sockaddr;
+ int *len;
+{
+ int fd2;
+
+ retry:
+ fd2 = accept(fd, sockaddr, len);
+ if (fd2 < 0) {
+ if (errno == EINTR) goto retry;
+ rb_sys_fail(Qnil);
+ }
+ return sock_new(class, fd2);
+}
+
+static VALUE
+Ftcp_accept(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_in from;
+ int fromlen;
+
+ GetOpenFile(sock, fptr);
+ fromlen = sizeof(struct sockaddr_in);
+ return sock_accept(C_TCPsocket, fileno(fptr->f),
+ (struct sockaddr*)&from, &fromlen);
+}
+
+static VALUE
+open_unix(class, path, server)
+ VALUE class;
+ struct RString *path;
+ int server;
+{
+ struct sockaddr_un sockaddr;
+ int fd, status;
+ char *syscall;
+ VALUE sock;
+ OpenFile *fptr;
+
+ Check_Type(path, T_STRING);
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) rb_sys_fail("socket(2)");
+
+ sockaddr.sun_family = AF_UNIX;
+ strncpy(sockaddr.sun_path, path->ptr, sizeof(sockaddr.sun_path)-1);
+ sockaddr.sun_path[sizeof(sockaddr.sun_path)-1] = '\0';
+
+ if (server) {
+ status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ syscall = "bind(2)";
+ }
+ else {
+ status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ syscall = "connect(2)";
+ }
+
+ if (status < 0) {
+ close (fd);
+ rb_sys_fail(syscall);
+ }
+
+ if (server) listen(fd, 5);
+
+ GC_LINK;
+ GC_PRO3(sock, sock_new(class, fd));
+ GetOpenFile(sock, fptr);
+ fptr->path = strdup(path->ptr);
+ GC_UNLINK;
+
+ return sock;
+}
+
+static VALUE
+tcp_addr(sockaddr)
+ struct sockaddr_in *sockaddr;
+{
+ VALUE family, port, addr;
+ VALUE ary;
+ struct hostent *hostent;
+
+ GC_LINK;
+ GC_PRO3(family, str_new2("AF_INET"));
+ hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr,
+ sizeof(sockaddr->sin_addr),
+ AF_INET);
+ if (hostent) {
+ addr = str_new2(hostent->h_name);
+ }
+ else {
+ char buf[16];
+ char *a = (char*)&sockaddr->sin_addr;
+ sprintf(buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
+ addr = str_new2(buf);
+ }
+ GC_PRO(addr);
+ port = INT2FIX(sockaddr->sin_port);
+ ary = ary_new3(3, family, port, addr);
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Ftcp_addr(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_in addr;
+ int len = sizeof addr;
+
+ GetOpenFile(sock, fptr);
+
+ if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return tcp_addr(&addr);
+}
+
+static VALUE
+Ftcp_peeraddr(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_in addr;
+ int len = sizeof addr;
+
+ GetOpenFile(sock, fptr);
+
+ if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return tcp_addr(&addr);
+}
+
+static VALUE
+Funix_sock_open(sock, path)
+ VALUE sock, path;
+{
+ return open_unix(sock, path, 0);
+}
+
+static VALUE
+Funix_path(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(sock, fptr);
+ if (fptr->path == Qnil) {
+ struct sockaddr_un addr;
+ int len = sizeof(addr);
+ if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail(Qnil);
+ fptr->path = strdup(addr.sun_path);
+ }
+ return str_new2(fptr->path);
+}
+
+static VALUE
+Funix_svr_open(class, path)
+ VALUE class, path;
+{
+ return open_unix(class, path, 1);
+}
+
+static VALUE
+Funix_accept(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_un from;
+ int fromlen;
+
+ GetOpenFile(sock, fptr);
+ fromlen = sizeof(struct sockaddr_un);
+ return sock_accept(C_UNIXsocket, fileno(fptr->f),
+ (struct sockaddr*)&from, &fromlen);
+}
+
+static VALUE
+unix_addr(sockaddr)
+ struct sockaddr_un *sockaddr;
+{
+ VALUE family, path;
+ VALUE ary;
+
+ GC_LINK;
+ GC_PRO3(family, str_new2("AF_UNIX"));
+ GC_PRO3(path, str_new2(sockaddr->sun_path));
+ ary = assoc_new(family, path);
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Funix_addr(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_un addr;
+ int len = sizeof addr;
+
+ GetOpenFile(sock, fptr);
+
+ if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return unix_addr(&addr);
+}
+
+static VALUE
+Funix_peeraddr(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ struct sockaddr_un addr;
+ int len = sizeof addr;
+
+ GetOpenFile(sock, fptr);
+
+ if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return unix_addr(&addr);
+}
+
+static void
+setup_domain_and_type(domain, dv, type, tv)
+ VALUE domain, type;
+ int *dv, *tv;
+{
+ char *ptr;
+
+ if (TYPE(domain) == T_STRING) {
+ ptr = RSTRING(domain)->ptr;
+ if (strcmp(ptr, "PF_UNIX") == 0)
+ *dv = PF_UNIX;
+ else if (strcmp(ptr, "PF_INET") == 0)
+ *dv = PF_INET;
+#ifdef PF_IMPLINK
+ else if (strcmp(ptr, "PF_IMPLINK") == 0)
+ *dv = PF_IMPLINK;
+#endif
+#ifdef PF_AX25
+ else if (strcmp(ptr, "PF_AX25") == 0)
+ *dv = PF_AX25;
+#endif
+#ifdef PF_IPX
+ else if (strcmp(ptr, "PF_IPX") == 0)
+ *dv = PF_IPX;
+#endif
+ else
+ Fail("Unknown socket domain %s", ptr);
+ }
+ else {
+ *dv = NUM2INT(domain);
+ }
+ if (TYPE(type) == T_STRING) {
+ ptr = RSTRING(type)->ptr;
+ if (strcmp(ptr, "SOCK_STREAM") == 0)
+ *tv = SOCK_STREAM;
+ else if (strcmp(ptr, "SOCK_DGRAM") == 0)
+ *tv = SOCK_DGRAM;
+#ifdef SOCK_RAW
+ else if (strcmp(ptr, "SOCK_RAW") == 0)
+ *tv = SOCK_RAW;
+#endif
+#ifdef SOCK_SEQPACKET
+ else if (strcmp(ptr, "SOCK_SEQPACKET") == 0)
+ *tv = SOCK_SEQPACKET;
+#endif
+#ifdef SOCK_RDM
+ else if (strcmp(ptr, "SOCK_RDM") == 0)
+ *tv = SOCK_RDM;
+#endif
+#ifdef SOCK_PACKET
+ else if (strcmp(ptr, "SOCK_PACKET") == 0)
+ *tv = SOCK_PACKET;
+#endif
+ else
+ Fail("Unknown socket type %s", ptr);
+ }
+ else {
+ *tv = NUM2INT(type);
+ }
+}
+
+static VALUE
+Fsock_open(class, domain, type, protocol)
+ VALUE class, domain, type, protocol;
+{
+ int fd;
+ int d, t;
+
+ setup_domain_and_type(domain, &d, type, &t);
+ fd = socket(d, t, NUM2INT(protocol));
+ if (fd < 0) rb_sys_fail("socke(2)");
+ return sock_new(class, fd);
+}
+
+static VALUE
+Fsock_socketpair(class, domain, type, protocol)
+ VALUE class, domain, type, protocol;
+{
+ int fd;
+ int d, t, sp[2];
+ VALUE sock1, sock2, pair;
+
+ setup_domain_and_type(domain, &d, type, &t);
+ if (socketpair(d, t, NUM2INT(protocol), sp) < 0)
+ rb_sys_fail("socketpair(2)");
+
+ GC_LINK;
+ GC_PRO3(sock1, sock_new(class, sp[0]));
+ GC_PRO3(sock2, sock_new(class, sp[1]));
+ pair = assoc_new(sock1, sock2);
+ GC_UNLINK;
+
+ return pair;
+}
+
+static VALUE
+Fsock_connect(sock, addr)
+ VALUE sock;
+ struct RString *addr;
+{
+ OpenFile *fptr;
+
+ Check_Type(addr, T_STRING);
+ str_modify(addr);
+
+ GetOpenFile(sock, fptr);
+ if (connect(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0)
+ rb_sys_fail("connect(2)");
+ return sock;
+}
+
+static VALUE
+Fsock_bind(sock, addr)
+ VALUE sock;
+ struct RString *addr;
+{
+ OpenFile *fptr;
+
+ Check_Type(addr, T_STRING);
+ str_modify(addr);
+
+ GetOpenFile(sock, fptr);
+ if (bind(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0)
+ rb_sys_fail("bind(2)");
+ return sock;
+}
+
+static VALUE
+Fsock_listen(sock, log)
+ VALUE sock, log;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(sock, fptr);
+ if (listen(fileno(fptr->f), NUM2INT(log)) < 0)
+ rb_sys_fail("listen(2)");
+ return sock;
+}
+
+static VALUE
+Fsock_accept(sock)
+ VALUE sock;
+{
+ OpenFile *fptr;
+ VALUE addr, sock2;
+ int fd;
+ char buf[1024];
+ int len = sizeof buf;
+
+ GetOpenFile(sock, fptr);
+ if ((fd = accept(fileno(fptr->f), (struct sockaddr*)buf, &len)) < 0)
+ rb_sys_fail("listen(2)");
+
+ return sock_new(C_Socket, fd);
+}
+
+static VALUE
+Fsock_send(sock, args)
+ VALUE sock, args;
+{
+ struct RString *msg, *to;
+ VALUE flags;
+ OpenFile *fptr;
+ FILE *f;
+ int fd, n;
+
+ rb_scan_args(args, "21", &msg, &flags, &to);
+
+ Check_Type(msg, T_STRING);
+
+ GetOpenFile(sock, fptr);
+ f = fptr->f2?fptr->f2:fptr->f;
+ fd = fileno(f);
+ if (to) {
+ Check_Type(to, T_STRING);
+ n = sendto(fd, msg->ptr, msg->len, NUM2INT(flags),
+ (struct sockaddr*)to->ptr, to->len);
+ }
+ else {
+ n = send(fd, msg->ptr, msg->len, NUM2INT(flags));
+ }
+ if (n < 0) {
+ rb_sys_fail("send(2)");
+ }
+ return INT2FIX(n);
+}
+
+static VALUE
+Fsock_recv(sock, len, flags)
+ VALUE sock, len, flags;
+{
+ OpenFile *fptr;
+ FILE f;
+ struct RString *str;
+ char buf[1024];
+ int fd, alen = sizeof buf;
+ VALUE addr, result;
+
+ GC_LINK;
+ GC_PRO3(str, (struct RString*)str_new(0, NUM2INT(len)));
+
+ GetOpenFile(sock, fptr);
+ fd = fileno(fptr->f);
+ if (recvfrom(fd, str->ptr, str->len, NUM2INT(flags),
+ (struct sockaddr*)buf, &alen) < 0) {
+ rb_sys_fail("recv(2)");
+ }
+ GC_PRO3(addr, str_new(buf, alen));
+ result = assoc_new(str, addr);
+ GC_UNLINK;
+
+ return result;
+}
+
+Init_Socket ()
+{
+ C_BasicSocket = rb_define_class("BasicSocket", C_IO);
+ rb_undef_method(C_BasicSocket, "new");
+ rb_define_method(C_BasicSocket, "shutdown", Fbsock_shutdown, -2);
+ rb_define_method(C_BasicSocket, "setopt", Fbsock_setopt, 3);
+ rb_define_method(C_BasicSocket, "getopt", Fbsock_getopt, 2);
+ rb_define_method(C_BasicSocket, "getsockname", Fbsock_getsockname, 0);
+ rb_define_method(C_BasicSocket, "getpeername", Fbsock_getpeername, 0);
+
+ C_TCPsocket = rb_define_class("TCPsocket", C_BasicSocket);
+ rb_define_single_method(C_TCPsocket, "open", Ftcp_sock_open, 2);
+ rb_define_alias(C_TCPsocket, "new", "open");
+ rb_define_method(C_TCPsocket, "addr", Ftcp_addr, 0);
+ rb_define_method(C_TCPsocket, "peeraddr", Ftcp_peeraddr, 0);
+
+ C_TCPserver = rb_define_class("TCPserver", C_TCPsocket);
+ rb_define_single_method(C_TCPserver, "open", Ftcp_svr_open, -2);
+ rb_define_alias(C_TCPserver, "new", "open");
+ rb_define_method(C_TCPserver, "accept", Ftcp_accept, 0);
+
+ C_UNIXsocket = rb_define_class("UNIXsocket", C_BasicSocket);
+ rb_define_single_method(C_UNIXsocket, "open", Funix_sock_open, 1);
+ rb_define_alias(C_UNIXsocket, "new", "open");
+ rb_define_method(C_UNIXsocket, "path", Funix_path, 0);
+ rb_define_method(C_UNIXsocket, "addr", Funix_addr, 0);
+ rb_define_method(C_UNIXsocket, "peeraddr", Funix_peeraddr, 0);
+
+ C_UNIXserver = rb_define_class("UNIXserver", C_UNIXsocket);
+ rb_define_single_method(C_UNIXserver, "open", Funix_svr_open, 1);
+ rb_define_alias(C_UNIXserver, "new", "open");
+ rb_define_single_method(C_UNIXserver, "new", Funix_svr_open, 1);
+ rb_define_method(C_UNIXserver, "accept", Funix_accept, 0);
+
+ C_Socket = rb_define_class("Socket", C_BasicSocket);
+ rb_define_single_method(C_Socket, "open", Fsock_open, 3);
+ rb_define_alias(C_UNIXserver, "new", "open");
+
+ rb_define_method(C_Socket, "connect", Fsock_connect, 1);
+ rb_define_method(C_Socket, "bind", Fsock_bind, 1);
+ rb_define_method(C_Socket, "listen", Fsock_listen, 1);
+ rb_define_method(C_Socket, "accept", Fsock_accept, 0);
+
+ rb_define_method(C_Socket, "send", Fsock_send, -2);
+ rb_define_method(C_Socket, "recv", Fsock_recv, 2);
+
+ rb_define_single_method(C_Socket, "socketpair", Fsock_socketpair, 3);
+}
+#endif /* HAVE_SOCKET */
diff --git a/spec b/spec
new file mode 100644
index 0000000000..2171e958aa
--- /dev/null
+++ b/spec
@@ -0,0 +1,3244 @@
+.\" spec - -*- Indented-Text -*- created at: Tue May 25 15:18:26 JST 1993
+
+* Ϥ(ʤRuby?)
+
+RubyϡUNIXǼڤ˥֥Ȼظץߥ󥰡פ򤷤Ȥ˾
+¸뤿ޤ줿. ΤɬפȻפ줿:
+
+ * ʸˡ䵡ǽñǤ.
+ * ֥Ȼظץߥ󥰤򥵥ݡȤ뵡ǽ
+ ĸȤ߷פƤ.
+ * 󥢥饦ɥबû(󥿥ץ꥿Ǥ).
+ * OS(UNIX)εǽñѤǤ.
+ * ץȸȤƻȤ.
+ * ϤfreeǤ.
+
+ʤɤǤ. ֥ȻظȤưŪǤSmalltalkC++ʤɤ
+ξΰƤϤ뤬, ä˼ڤʥץߥ󥰤Ȥ˷
+Ƥ, ƤΤǤϤʤä. ץȸǤPerl
+Tcl˥֥Ȼظǽɲäƥ¸ߤ뤬, ϡּ
+ڤ˥ץߥ󥰡פȤƤϤƤ, դ˥֥Ȼظ
+ǽä. ä, ξ򿷤߷
+ɬפȹͤ줿. Ƨޤ߷פ줿Rubyħ:
+
+ * 󥿥ץ꥿Ǥ.
+ * ñ㳰ξʤʸˡ.
+ * ʬʥ֥Ȼظǽ.
+ * Ǥ񤭤䤹(黻ҷʤ).
+ * ١쥯.
+ * 㳰ǽ.
+ * ʬ˶Ϥʥ饹Ȥ߹ޤƤ.
+ * OS򥢥뵡ǽ󶡤.
+ * ĥ䤹.
+
+ʤɤ. RubyshperlΤäƤͤˤȤäƤξQˤʤ뤿ä
+Τ, θ줫μʰܹԤǽǤȻפ. ץޤ
+RubyΥ֥ȻظǽˤĤƳؤ٤, 궯ϤʤȤǤ褦
+ʤ.
+
+Cǥ饹򵭽Ҥ뤳ȤRuby򹹤˶Ϥˤ뤳ȤǤ.
+OSǤRubyưŪ˥֥ȥե󥯤Ǥ뤷, Ǥʤ
+ƤRubyƥѥ뤷Ȥ߹ߥ饹ɲäΤưפǤ.
+
+* Rubyδ
+
+RubyδŪʬ˾, ʸˡ㳰ʤΤǿȤˤĤΤ
+ۤ񤷤ʤ.
+
+**
+
+ץȸνˤʤ餤, ʸʳ'#'ޤǤϥ
+ȸʤ.
+
+** ڤ국
+
+ʸ(֤ȥڡ)Ӳ(\n)ڤ국Ȥʤ. ,
+
+ a +
+ b
+
+Τ褦˹Ԥ(ʸ)ǽ, ιԤ³Ȥʾʳϲ
+ʸζڤȤǧ.
+
+** ̻
+
+ե٥åȤ`_'ǻϤޤ, ե٥å, , `_'Ǥդ¤Ӥϼ
+̻ҤǤ. ̻ҤŤϤʤ.
+
+** ͽ
+
+ͽϰʲ̤Ǥ
+
+ break, case, class, continue, def, do, else,
+ elsif, end, ensure, for, func, if, in,
+ include, module, nil, protect, redo, resque, retry,
+ return, self, super, then, undef, unless, until,
+ using, when, while, yield, __END__
+
+ͽϥ饹̾, ᥽å̾, ѿ̾ʤɤѤ뤳ȤϤǤʤ.
+
+** ƥ
+
+ʲΥƥ뤬
+
+ ʸƥ
+
+ "..." # Хååβᤢ
+ '...' # Хååβʤ(\\\'ϲ᤹)
+
+ Хåå嵭ˡ
+
+ \t (0x09)
+ \n ʸ(0x0a)
+ \r ʸ(0x0d)
+ \f ڡʸ(0x0c)
+ \b Хåڡ(0x08)
+ \a ٥(0x07)
+ \e (0x1b)
+ \nnn 8ʿɽ(n0-7)
+ \xnn 16ʿɽ(n0-9,a-f)
+ \^c ȥʸ(cASCIIʸ)
+ \C-c ȥʸ(Ʊ)
+ \M-c ᥿ʸ(c|0x80)
+
+ ɽƥ
+
+ /.../
+
+ ɽ
+
+ ^ Ƭ
+ $
+ . Ǥդ1ʸ
+ \w ѿ. [0-9A-Za-z_]Ʊ
+ \W ѿ
+ \s ʸ. [ \t\n\r\f]Ʊ
+ \S ʸ
+ \d . [0-9] Ʊ
+ \D
+ \b 춭ʸ(ʸ饹)
+ \B 춭ʸ
+ \b (0x08)(ʸ饹)
+ [ ] ʸ饹
+ * ľɽ0ʾη֤
+ + ľɽ1ʾη֤
+ {m,n} m󤫤nη֤
+ ? 0ޤ1
+ |
+ ( ) ɽޤȤ
+
+ ¾˥Хåå嵭ˡͭǤ.
+
+ ͥƥ
+
+ 123
+ -123 (Ĥ)
+ 1_234 (10ʿ`_'ޤळȤǤ)
+ 123.45 ư
+ 1.2e-3 ư
+ 0xffff 16
+ 0377 8
+ ?a ʸ`a'Υ(97)
+ ?\C-a ȥaΥ(1)
+ ?\M-a ᥿aΥ(225)
+ ?\M-\C-a ᥿-ȥaΥ(129)
+
+ ?ɽǤƤΥХåå嵭ˡͭǤ.
+
+** 롼ԥ
+
+ϳ̤ˤäƥ롼ԥ󥰤뤳ȤǤ. ˳ˤϼ¤Ӥ
+񤱤. ¤Ӥ򵭽Ҥ, ȼζڤˤϲԤ ';' Ѥ.
+¤ӤͤϺǸɾͤǤ. Ĥޤ
+
+ (; ; ...)
+
+ͤϺǸɾͤˤʤ.
+
+** ƥ
+
+Ҥο, ʸ, ɽγƥƥϼǤ.
+
+**
+
+󼰤ϰʲηǤ.
+
+ [ , ... ]
+
+줾μɾ̤ޤ֤. Ǥ0뤿
+ˤ϶
+
+ []
+
+Ѥ.
+
+** Ϣ
+
+ϢȤǤդΥ֥Ȥ򥭡(ź)ȤƻƤǤ. Ruby
+ǤϢSmalltalkѸڤDict()ȤƤФ. ܺ٤ϥ
+饹Dictι򻲾Ȥ줿. ϢϢ󼰤ϰʲη
+.
+
+ { =>, ... }
+
+줾μɾ̤򥭡ͤȤϢ󥪥֥Ȥ֤.
+Ǥ0Ϣ뤿ˤ϶Ϣ
+
+ {}
+
+Ѥ.
+
+** ѿ
+
+ѿˤ4ढä, μѿ̾κǽΰʸǷꤵ.
+Ƥʤѿ򻲾ȤͤnilǤ.
+
+ 饹ѿ()
+
+ `%'ǻϤޤѿϥ饹ѿǤ, Υ饹ƤΥ֥饹
+ 󥹥󥹤黲ȤǤ. ѿؤϥȥåץ٥, ʤ
+ ᥽åɤǤ٥ǤΤ߲ǽǤ. ѿϥ饹֤
+ ͤͭ, ȥ᥽åɤͤѹ뤳ȤǤ
+ Τ,ȤѤ.
+
+ 󥹥ѿ
+
+ `@'ǻϤޤѿϥ󥹥ѿǤ, Υ饹ޤϥ֥
+ Υ᥽åɤ黲ȤǤ. μ̿ϥ֥Ȥμ̿.
+
+ Хѿ
+
+ `$'ǻϤޤѿΥפϥХǤ, μ̿ϥץ
+ μ̿.
+
+ 饹̾/⥸塼̾/ѿ
+
+ ե٥åȤޤ`_'ǻϤޤѿϼ̻ҤȤƤФ,
+ ѿ, 饹̾ޤϥ⥸塼̾Ǥ.
+
+ ֤Ǥϼ̻Ҥϥ饹/⥸塼̾Ȥߤʤ뤬(륯
+ ¸ߤʤͤnil), κդ˸줿̻Ҥ,Υ
+ ǤϥѿȤƸʤ, Ʊ̾Υ饹⥸塼
+ . ΰ̣Ǽ̻ҤؤȤƤƯ.
+
+ Array # 󥯥饹 Array
+ Array = 15 # . ʸArrayϥѿ
+ print(Array, "\n") # `15'Ϥ
+
+ ϥѥ˲ᤵ뤿, ̻Ҥؤºݤ˼
+ ԤƤ, ʤƤ, ʹߤΥǤϤμ̻Ҥϥ
+ ѿȤߤʤ.
+
+ Array # 󥯥饹 Array
+ if %FALSE
+ Array = 15 # Υɤϼ¹Ԥʤ,
+ # ʹArrayϥѿȤߤʤ.
+ end
+ print(Array, "\n") # `nil'Ϥ
+
+ Υ롼ϰ츫ʣ, 饹/⥸塼̾ȥѿ̾
+ ʣʤ¤, ̤ΥѿͤnilǤȹͤƤ⺹
+ ٤Ϥʤ.
+
+ RubyȤ߹ߴؿʸΥե٥åȤǻϤޤ̾ĤƤ,
+ 桼⥯饹/⥸塼̾ˤʸǻϤޤ뼱̻Ҥ,
+ ̾ˤϾʸޤ`_'ǻϤޤ뼱̻ҤȤȤ򶯤侩.
+
+ ѿμ̿ϤΥ᥽åɤλޤ(ȥåץ٥Υ
+ ѿϥץνλޤ)Ǥ.
+
+˵ѿȸƤФüѿȤselfnil. selfϸߤΥ
+åɤμ¹ԼΤؤѿǤ, nilNil饹ͣΥ󥹥
+(ɽ)ؤѿǤ. εѿˤäƤ
+ѹ뤳ȤϤǤʤ. ѿؤ㳰ȯ.
+
+** å
+
+֥Ȥ˥åŪʹʸåǤ, δ
+ܷϰʲ̤Ǥ.
+
+ 1.᥽å̾(...)
+
+ 1ɾ륪֥Ȥ, ̻Ҥǻꤵ᥽
+ ɤƤӽФ. ֺǸΰ'*'³(ñ)Ǥ,
+ μɾ(Ǥɬפ)Ÿ, Ȥ
+ ɲä.
+
+ Ĥʤˤϳ̤άǤ.
+
+᥽å̾ȤƤǤդμ̻ҤѤ뤳ȤǤ. ѿ̾Ȥϼ̻Ҥ
+֤̾㤦ΤǽʣƤ⹽ʤ.
+
+** ؿ
+
+å, 쥷Фselfξ, 쥷Фάƴؿǥ᥽
+ɤƤӽФȤǤ. ξ1ĤʤǤ̤ξάϤ
+ʤ.
+
+ؿǤ`@'ǻϤޤ̾ĥ᥽åɤƤӽФȤǤ. `@'
+Ϥޤ᥽åɤϴؿǤƤӽФȤǤʤ, 륯
+ޤ, Υ֥饹餷ƤФʤΤ, ץ饤١ȥ᥽åɤ
+ƤФ. ץ饤١ȥ᥽åɤC++ˤprotected member function
+.
+
+** ѡ饹Υ᥽åɸƤӽФ
+
+åüʥȤƥѡ饹Υ᥽åɤθƤӽФ
+. ηϥ᥽åɤ˥ѡ饹Ѥ뤿
+˻Ȥ.
+
+ super
+
+ ߤΥ᥽åɤͿ줿Τޤޥѥ饹Ʊ̾Υ᥽
+ ɤƤӽФ.
+
+ super(...)
+
+ ȤȤ˥ѡ饹Ʊ̾Υ᥽åɤƤӽФ. ֺǸ
+ ΰ`*'³̾Υ᥽åɸƤӽФƱ.
+
+** 黻
+
+ץߥ󥰤ؤΤ˰Υ᥽åɸƤӽФ湽¤ϱ黻ҷ
+Ȥ. Rubyˤϰʲˤ黻Ҥ. ΤΤۤɷ̤,
+Ʊα黻Ҥη̤ƱǤ.
+
+ -(unary) +(unary) ! ~
+ **
+ * / %
+ + -
+ << >>
+ > >= < <=
+ <=> == != =~ !~
+ &
+ | ^
+ &&
+ ||
+ .. ...
+ ::
+ =() (+=, -=, ..)
+ yield
+
+ۤȤɤα黻ˤϥ᥽åɸƤӽФȤƲᤵ(饹˺
+Ǥ), ǤʤüʤΤ. Ǥʤü黻
+
+ &&(), ||(), =(), ...(ϰϻ),
+ yield(֥åƤӽФ)
+
+5ĤǤ.
+
+Ǥü黻Ұʳα黻ҷϥ᥽åɸƤӽФȸʤ.
+ñ黻(+, -, !, ~)
+
+ 1."黻"()
+
+Ȥ, 2黻Ҥ
+
+ 1."黻"(2)
+
+Ȳᤵ. ¿黻(λȤ[])ϱ黻ҷ̤ʷȤ
+
+ recv[arg..]
+
+,
+
+ recv."[]"(arg..)
+
+Ȳᤵ. ǤƱͤǤ.
+
+** P
+
+if, unless, while, untilξȽμ, ü黻`&&', `||',
+`...'ξդμ, ̾黻`!'αդϾPȸƤФ. PǤʸ
+ɽƥϼ$_=~ƥפξάǤȤߤʤ.
+黻`...'ξդǤƥ뤬$.==ƥפξάȲᤵ.
+
+: 黻`!'ü黻ҤǤϤʤΤ, Ԥʤ˵Ĥ
+뤳.
+
+ ! ʸƥ
+ ! ɽƥ
+
+ηǸƤӽФ᥽åɤΰ, ƥɽ֥ȤǤϤʤ.
+`!'᥽åɤϺʤɤȻפ.
+
+**
+
+ˤѿФ(), ץñˤ뤿Υ
+å奬ȤƤ. ϰʲηǤ.
+
+ ѿ =
+
+ϼɾ, ѿͤȤ. 饹⥸塼䵿ѿ
+ˤǤʤ. 饹⥸塼ѹ뤿ˤclassʸ,
+moduleʸѤ. ϱ黻ҷȤäƤ뤬, ᥽åɤǤϤʤ
+Ǻ뤳ȤϤǤʤ.
+
+󥿥å奬ȤƤϰʲΤΤ.
+
+ Ǥؤ
+
+ 1[2,..] = n
+
+ 1ɾ륪֥Ȥ, 2鼰nޤǤȤ
+ , "[]="Ȥ᥽åɤƤӽФ.
+
+ °
+
+ 1.̻ = 2
+
+ 1ɾ륪֥ȤФ"̻="Ȥ᥽
+ ɤ, 2ȤƸƤӽФ.
+
+
+
+ 1 op= 2 # 1ǽǤʤФʤʤ.
+
+ ηŪˡּ1 = 1 op 2פŸ, ¹Ԥ.
+ ϥץޤΥ׿򸺤餹ŪΤ¸ߤǤ
+ . opȤƻȤ黻Ҥ
+
+ +, -, *, /, %, **, &, |, ^, <<, >>
+
+ 11Ǥ. 黻Ҥ`='δ֤˥ڡƤϤʤ.
+
+*** ¿
+
+ƱʣѿԤʤȤǤ. ηϰʲ̤Ǥ.
+
+ ѿ, [ѿ,...] = [, ]
+
+դμĤʤ, , ͤǤˤǤ
+ѿ. ʳξˤ, 줾μͤѿ
+. դѿοȱդǤοʤˤ­ʤѿˤ
+nil, ;äǤ̵뤵.
+
+ foo, bar = [1, 2] # foo = 1; bar = 2
+ foo, bar = 1, 2 # foo = 1; bar = 2
+ foo, bar = 1 # foo = 1; bar = nil
+
+ foo, bar, baz = 1, 2 # foo = 1; bar = 2; baz = nil
+ foo, bar = 1, 2, 3 # foo = 1; bar = 2
+
+** ʬ
+
+ if 1 [then]
+ ʸ1
+ [ elsif 2 [then]
+ ʸ2 ]...
+ [ else
+ ʸn ]
+ end [ if ]
+
+ Ƚʸ. else if ǤelifǤʤelsififϢ³ԤʤȤ
+ դ뤳.
+
+ unless 1 [then]
+ ʸ1
+ [ else
+ ʸ2 ]
+ end [ unless ]
+
+ 1(nil)֤, 2ɾ㳰ȯʸ1ɾ
+ .
+
+ 1 && 2
+
+ 1ɾ, ͤ(nilʳ)Ǥ, 2ɾ.
+
+ 1 || 2
+
+ 1ɾ, ͤǤ, 2ɾ.
+
+ 1 ... 2
+
+ 1ˤʤޤǤϵ֤, θϼ2֤ޤǤϿ֤.
+ 2ˤʤо֤ϵ
+
+ case 0
+ [ when 1 [, 2]...
+ ʸ1 ]...
+ [ else
+ ʸn ]
+ end [ case ]
+
+ ʬ, CswitchPascalcase˻Ƥ. breakæФ뤳
+ Ȥʸ˷³뤳ȤʤΤ.
+
+ ΰפϡּn =~ 0]ǹԤʤ. Ĥޤ,
+
+ case expr0
+ when expr1, expr2
+ stmt1
+ when expr3, expr4
+ stmt2
+ else
+ stmt3
+ end
+
+ ϰʲifʸǤ.
+
+ _tmp = expr0
+ if expr1 =~ _tmp || expr2 =~ _tmp
+ stmt1
+ elsif expr3 =~ _tmp || expr4 =~ _tmp
+ stmt2
+ else
+ stmt3
+ end
+
+** ֤
+
+ while
+ ʸ
+ end [ while ]
+
+ 郎δ, ʸ򷫤֤¹Ԥ.
+
+ until
+ ʸ
+ end [ until ]
+ 1 until 2
+
+ (nil)֤, ɾ㳰ȯ, ʸ򷫤֤
+ Ԥ.
+
+** ƥ졼
+
+ƥ졼Ȥ湽¤(ä˥롼)ݲΤѤ᥽åɤ
+Ǥ. ƥ졼θƤӽФϰʲιʸǹԤʤ.
+
+ do
+ ʸ1
+ using ѿ
+ ʸ2
+ end [ do ]
+
+ ʸ2פ֥åȤꤷ, ʸ1Υ᥽åɤ򥤥ƥ졼Ȥɾ
+ . ʸ1Υȥåץ٥Υ᥽åɤƥ졼ȤƸƤӽФ,
+ 쥷Фɽ, μϥƥ졼ȤƤϸƤӽФʤ. ʸ
+ 1ʣμгơƥ졼Ȥƽ˸ƤФ.
+
+ƥ졼yield value¹Ԥ, ͤdoʸǻꤵ줿ѿ
+֥å¹Ԥ. ֥åμ¹ԤλȤͤ
+yieldͤȤ֤. ᥽åɤƥ졼ȤƸƤӽФ줿
+ɤϴؿiterator_p()Τ뤳ȤǤ. ˤEnumerable⥸塼
+grep᥽åɤΤ褦˥ƥ졼ȤƸƤФ줿̤Υ᥽åɤȤ
+ƸƤФ줿Ȥưۤʤ᥽åɤ⤢.
+
+ for ѿ in
+ ʸ
+ end [ for ]
+
+ γǤФʸ¹Ԥ. ϰʲdoʸǤ.
+
+ do ().each using ѿ
+ ʸ
+ end
+
+ äƼͤΥ֥Ȥ᥽åeachʤ, for¹
+ 㳰ȯ.
+
+** 㳰
+
+ͽʤ֤ȯˤ㳰ȯ. RubyǤ㳰
+ª, ƻԤ, Ԥʤäꤹ뤳ȤǤ.
+
+ protect
+ ʸ1
+ [ resque
+ ʸ2 ]
+ [ ensure
+ ʸ3 ]
+ end [ protect ]
+
+ ʸ1¹Ԥ, μ¹㳰ȯresqueǻꤵ줿ʸ2
+ ¹Ԥ. ensure᤬¸ߤprotectʸλɬ(
+ ェλǤʤ, 㳰, return, break, continue, redoʤɤˤ
+ æФǤ)ʸ3¹Ԥ.
+
+protectʳ, unless黻, until黻Ҥϱդͤ㳰ȯ
+, դͤȤƵͿ줿ȸʤΤ, ۤ㳰
+ԤʤäƤ뤳Ȥˤʤ.
+
+** æ
+
+ return []
+
+ ᥽åɤμ¹Ԥλ.
+
+ continue
+ redo
+ break
+
+ 嵭3Ĥϥ롼ǻȤ.
+
+ continueϤäȤ¦Υ롼פμη֤Ϥ. redoϥ롼
+ Υ롼׾ΥåԤʤ鷺, ߤη֤ľ. break
+ ϥ롼פæФ. CȰ㤤, breakϤäȤ¦Υ롼פæФ
+ Ѥ, caseȴѤϻʤ.
+
+ retry
+
+ protectʸresqueǻȤ, protectʸϤᤫ¹Ԥ. 㳰
+ ԤʤäƤƻԤΤ˻Ȥ.
+
+ fail([å]) * Kernel饹Υ᥽åɤǤ.
+
+ 㳰ȯ. åͿ줿ˤȯե
+ ̾, ֹ$@˥å$!˥åȤ.
+
+** ƥ졼ǤΥ֥åƤӽФ
+
+ yield
+
+ ƥ졼ǥ֥åθƤӽФԤʤ. yield¹Ԥ᥽
+ ɤƥ졼ȤƸƤӽФƤʤˤ㳰ȯ.
+
+** 饹
+
+饹빽ʸϰʲ̤Ǥ.
+
+ class 饹̾ [ : ѡ饹̾ ]
+
+ end [ class ]
+
+ͤnil. 饹̾ʸǻϤޤ뼱̻ҤǤ. 饹ΥͥȤϤ
+ʤΤ¾ʸǤϥ饹Ǥʤ.
+
+** ⥸塼
+
+⥸塼빽ʸϰʲ̤Ǥ.
+
+ module 饹̾
+
+ end [ module ]
+
+⥸塼̾ʸǻϤޤ뼱̻ҤǤ. 饹Ʊ, ⥸塼
+ͥȤǤʤ.
+
+** 󥯥롼
+
+饹˥⥸塼򥤥󥯥롼ɤ빽ʸϰʲ̤Ǥ.
+
+ include ⥸塼̾ [, ⥸塼̾...]
+
+ߤΥ饹ޤϥ⥸塼(ȥåץ٥ǤObject)˻ꤷ
+⥸塼򥤥󥯥롼ɤ. ˤä¿ŷѾ򥨥ߥ졼ȤǤ.
+
+** ᥽å
+
+̾(ðۥ᥽åɤǤʤ)᥽åηϰʲ̤Ǥ. ̾᥽
+ϥͥȤǤʤΤ, ᥽åʸǤϥ᥽åʸƤӸ
+ӽФʤ.
+
+ def [func] ᥽å̾ [ ( [, ...][, * ] ) ]
+
+ end [ def ]
+
+᥽å̾ϼ̻ҤޤʸǤ. 黻Ҥκ򤹤ˤʸ
+ǻꤹ. ¤ӤκǸ`*', ¿Ϳ줿
+°, ǸΰȤͿ(­ʤˤϥ顼).
+
+`func'ꤵ줿ˤϤΥ᥽åɤϴؿǤƤӽФ
+ȤǤؿŪ᥽åɤˤʤ.
+
+** ðۥ᥽å
+
+᥽åˤϤ⤦ðۥ᥽åɤ. ϰʲ̤Ǥ
+.
+
+ def .᥽å̾ [ ( [, ...][, * ] ) ]
+
+ end [ def ]
+
+ηϼͤǤ륪֥Ȥðۥ᥽åɤ. ͤ
+(ӥȥ󥯥饹Ǥʤ)̾索֥Ȥ, 饹ޤϥ⥸塼Ǥ
+ɬפ. ̾᥽åȤϰۤʤ, ϥ᥽åǤ
+ȤƸƤӽФȤǤ.
+
+ðۥ᥽åɤΥ֥Ȥˤ°᥽åɤǤ. ä̾
+ϷѾʤ, 㳰Ȥƥ饹ðۥ᥽åɤϤΥ֥饹ˤ
+. ؤХ饹ðۥ᥽åɤ¾Υ֥Ȼظ
+ˤ륯饹᥽åɤƯ򤹤.
+
+** ᥽åɤ̾
+
+ʲηǥ᥽åɤ̾Ĥ뤳ȤǤ.
+
+ def ᥽å̾1 ᥽å̾2
+
+̾դ줿᥽åɤ, λǤΥ᥽åѤ, Υ
+åɤƤ, Ť᥽åɤƤӽФ줿ΤƱƯ
+.
+
+** ᥽åä
+
+᥽åɤäˤundefѤ.
+
+ undef ᥽å̾
+
+̻Ҥޤʸǻꤷ᥽åɤä.
+
+defˤ̾undefˤäȤȥ饹Υ󥿥ե
+򥹡ѥ饹Ωѹ뤳ȤǤ. , ʬȤ˥å
+äƤդʤȴ¸Υ᥽åɤưʤʤǽ
+.
+
+* RubyȤ߹ߵǽ
+
+Rubyץߥ󥰤δܤϥ饹, ⥸塼ȤΥ᥽åɤνǤ.
+RubyˤϽϤΤΤ˰ʲεǽȤ߹ޤƤ. 饤֥
+ɤ뤳ȤˤäƵǽ뤬, ˴ؤƤϤ줾Υ饤֥
+ΥɥȤ򻲾ȤƤ餤.
+
+ü`+'ΤĤ᥽åɤϴؿ᥽åɤǤ.
+
+** ؿ
+
+Rubyˤϸ̩ʰ̣ǤϴؿϤʤKernel饹Υ᥽åɤΰ(
+饹ؿǸƤӽФΤ), ؿŪѤ. ؿŪѤ
+᥽åɤʲˤ. Υ᥽åɤݤˤϸߴ
+ͤƹԤʤ٤Ǥ.
+
+ _exit(status) +
+
+ ץμ¹Ԥλ. statusλơȤ.
+ exit()Ȥϰä, 㳰ʤɤϰڹԤʤʤ. fork()θ,
+ ץλʤɤѤ.
+
+ caller +
+ caller(level) +
+
+ åե졼फ, ᥽åɤθƤӽФΥե
+ ̾, ֹ, ǤȤ֤. levelͿ줿
+ ϸߤΥåե졼levelʾξ֤.
+
+ eof() +
+
+ ޥɥ饤󤫤ϤEOFãƤ, ֤.
+
+ eval(expr) +
+
+ exprȤͿ줿ʸrubyץȤƲ,¹Ԥ.
+
+ exec(command) +
+
+ ߼¹ԤƤץλ, commandʸǻꤵ
+ ̥ץư.
+
+ exit([status]) +
+
+ ץμ¹Ԥλ. statusȤͿ줿,
+ ͤRubyޥɤνλơȤ. ǥեȤ0.
+
+ fail([message]) +
+
+ 㳰ȯ. åͿ줿ˤϤ򥷥ƥ
+ ѿ`$!'˥åȤ, ȯ֤򥷥ƥѿ`$@'˥åȤ.
+
+ fork() +
+
+ forkƥॳ¹Ԥ, ҥץ. ܺ٤
+ fork(2)򻲾ȤΤ. ƥץ¦ǤϻҥץΥץid
+ ֤, ҥץ¦Ǥnil֤. 餫θǻҥץ
+ ˼Ԥˤ㳰ȯ.
+
+ format(format, ...) +
+
+ եޥåȤȤͿ줿ʸCsprintfƱ褦
+ ᤷ, Ÿʸ֤. ᥽åsprintf()̾.
+
+ RubyˤformatҤγĥˤĤƤsprintf()ι򻲾Ȥ
+ .
+
+ getc() +
+
+ ɸϤʸФ. ͤɤ߹ʸʸ
+ (ASCII)ɽFixnumǤ.
+
+ getenv(name) +
+
+ name˳ĶѿФ.
+
+ gets(pattern, replace) +
+
+ ȤͿ줿ե(ʤɸ)ɤ߹
+ , ɤ߹ߤˤϤʸ֤. եν
+ ãˤnil֤. Ԥζڤϥƥѿ`$/'ˤä
+ ѹǤ. ɤ߹ʸϥƥѿ`$_'ˤ⥻åȤ.
+
+ gsub() +
+
+ ƥѿ`$_'λؤʸФִԤʤ. ʸ
+ pattern˥ޥåʬreplace֤. String饹
+ gsub᥽åɤβ򻲾ȤΤ.
+
+ iterator_p() +
+
+ ᥽åɤƥ졼ȤƸƤӽФ줿˿, Ǥʤ˵
+ ֤Ҹ.
+
+ kill(signal, pid...) +
+
+ pidǻꤵ줿ץ˥ʥ. ʥϥʥ
+ 椫̾ǻꤹ. ͤĥʥ(뤤ϥʥ̾
+ `-')ͿȥץǤϤʤץ롼פ˥ʥ
+ .
+
+ load(file) +
+
+ fileɤ. fileɤѥϥƥѿ$LOAD_PATH
+ Ƿꤵ.
+
+ open(file[, mode]) +
+
+ file򥪡ץ󤷤, File֥Ȥ֤. ե̾ϥ
+ 󤹤ե򼨤. ե̾`|'ǻϤޤˤ³ʸ
+ 򥳥ޥɤȤƵư, ѥץ饤.
+
+ ޥ̾"-"Ǥ, open()Rubyλҥץ,
+ λҥץȤΥѥפ֤.
+
+ modeϥեΥ⡼ɤꤹ. ϰʲΤΤ
+ 줫ʸǤ.
+
+ r ɤ߹. openեϤ餫¸ߤƤ
+ ɬפ.
+
+ r+ ɤ߽ξ. openեϤ餫¸ߤƤ
+ ɬפ.
+
+ w 񤭹. ե뤬¸ߤƤ, Ĺ0ˤ
+ . ¸ߤƤʤп˥ե.
+
+ w+ ɤ߽ξ. ɤ߹ߤԤʤ뤳Ȱʳ"w"ƱƯ
+ 򤹤.
+
+ a ɲý񤭹. եϤ餫¸ߤƤɬ
+ . 񤭹ߤϥեκǸɲä.
+
+ a+ ɤ߽ξ. ե뤬¸ߤƤʤп˺
+ . ֤ϥեκǸ˽.
+
+ ⡼ɤά줿ΥǥեȤ"r"Ǥ.
+
+ print(arg1, ..., argn)
+
+ ˽Ϥ. Ϳʤˤϥ쥷ФϤ.
+ ʸʳΥ֥ȤȤͿ줿ˤ,
+ ֥Ȥto_s᥽åɤˤäʸѴƤϤ.
+ ƥѿ`$;'(ϥեɥѥ졼)nilǤʤͤ
+ ȤƤˤ, ưδ֤ˤʸϤ. ƥ
+ ѿ`$\'(ϥեɥѥ졼)nil ǤʤͤåȤ
+ ˤ, ǸˤϤ.
+
+ printf([port, ]format, arg1, ..., argn) +
+
+ Cprintf()Ʊformat˽ʸѴ, Ϥ
+ . 1IOΥ֥饹Υ󥹥󥹤ǤäϤΥ
+ ֥ȤФƽϤԤʤ. ǥեȤ$stdout˽Ϥ.
+
+ RubyˤformatҤγĥˤĤƤsprintf()ι򻲾Ȥ
+ .
+
+ rand(max) +
+
+ 0maxۤʤϰϤȯ. ͤFixnum.
+
+ require(file)
+
+ fileɤ. loadȤưΰ㤤requireϥɤե
+ Υեѥѿ$LOAD_FILES˳ФƤ, ˥ɤե
+ ϺƥɤʤǤ. ºݤ˥ɤˤ%TRUE,
+ ˥ɤƤˤ%FALSE֤.
+
+ select(reads[, writes[, execpts[, timeout]]]) +
+
+ select(2)¹Ԥ. reads/writes/execptsˤIO(ޤϤΥ
+ 饹)Υ󥹥󥹤Ϳ. timeoutFixnum / Float
+ / TimeΤ줫ǻꤹ. ͤtimeoutΩˤ
+ nil, ǤʤȤ3Ǥ֤, γǤ//
+ 㳰ԤΥ֥ȤǤȤƻ.
+
+ setenv(name, value) +
+
+ nameǻꤵĶѿvalue˥åȤ.
+
+ sleep([sec]) +
+
+ secäץμ¹Ԥߤ. secά줿, ץ
+ SIGALRMʤ¤, ʵפ˥꡼פ. ºݤ˥꡼
+ פÿ֤.
+
+ sprintf(format, ...) +
+
+ formatʸCsprintfƱ褦˲ᤷ, Ÿ
+ ʸ֤. ᥽åformat()̾.
+
+
+ formatҤCsprintf()դ(, Ruby
+ unsignedʤΤ, %uϽ)˲ä, %b, %B, %O, %XȤ
+ ȤǤ. %bϿͤ2ɽ, %B, %O, %X, 줾, 2,
+ 8 , 16ʿɽԤʤ, ονκݤ2ɽ
+ Ϥʤ, ɽƬ`-'ĤΤɽ.
+
+ srand([]) +
+
+ νͤꤷ, Ť֤ͤ. ͤά줿
+ time(3)֤ͤǥեȤȤ.
+
+ sub() +
+
+ ƥѿ`$_'λؤʸФִԤʤ. ʸǺ
+ pattern˥ޥåʬreplace֤. String饹
+ sub᥽åɤβ򻲾ȤΤ.
+
+ system(command) +
+
+ ޥɤ¹Ԥ, νλơ֤.
+
+ system2(command) +
+
+ ޥɤ¹Ԥ, νϤʸȤ֤.
+
+ trap(command, signal...) +
+
+ signalγߤäcommand¹Ԥ. signalϥ
+ ʥ̾ʥֹ. commandȤSIG_IGNޤIGNORE
+ ˤϤΥʥ̵뤹(ǽʤ). SIG_DFLޤ
+ DEFAULTꤷϥǥեȤưԤʤ.
+
+ wait() +
+
+ ҥץλΤԤ, λҥץpid֤.
+ ҥץĤʤnil֤.
+
+ waitpid(pid, flags) +
+
+ λҥץνλԤ, Υץλ˿
+ . ҥץ¸ߤʤ, Υ֥å󥰥⡼ɤǻҥץ
+ ޤλƤʤˤnil֤. waitpid(2)wait4(2)
+ ƤʤޥǤflagsϤĤnilޤ0ǤʤФ
+ ʤ.
+
+** ƥѿ
+
+ $! 顼å. fail()ꤹ.
+
+ $@ 顼ȯΥե̾ȹֹ椬
+
+ "ե:ֹ[:᥽å̾()]"
+
+ ηdzǼ.
+
+ $& Ǹѥޥå
+
+ $1..$9 Ǹѥޥånܤγ̤˥ޥå
+ ͤǼ. ̤ʤniläƤ.
+
+ $~ ǸΥޥå˴ؤ. 򥻥åȤ$&$1..$9
+ ͤѲ.
+
+ $= ѿͤnilǤʤ, ѥޥåʸ
+ Ӥǥե٥åȤʸʸ̤ʤ. ǥե
+ Ȥnil(̤).
+
+ $/ ϥ쥳ɥѥ졼. եʸФeach
+ Ԥʤʬʸꤹ. $/˶ʸ("")ꤹ
+ ñ̤ϤԤʤ, nilꤹΤ
+ ɤ߹. $/ˤɽϻȤʤ. ǥեȤ
+ "\n".
+
+ $\ ϥ쥳ɥѥ졼. ѿʸꤹ
+ write()print()٤˺ǸˤʸղäƽϤ
+ . ǥեȤnil(ʤˤɲäʤ).
+
+ $, Array:join()ΥǥեȤζڤʸ. print()γư
+ δ֤˽Ϥʸ.
+
+ $; String:split()ΥǥեȤζڤʸ.
+
+ $. Ǹɤϥեιֹ.
+
+ $_ Ǹgets()ʤɤɤ߹ʸ.
+
+ $0 rubyץȤ̾
+
+ $* rubyץȤͿ줿. rubyȤФ
+ Ƥ.
+
+ $$ ߼¹rubyץpid.
+
+ $? Ǹ˼¹Ԥҥץstatus.
+
+ $ARGV $*Ʊ.
+
+ $ENV Ķѿ˥Ϣ(EnvDict). ѿ
+ `for'Ԥʤѿ̾ͤΥڥͿ.
+
+ $FILENAME ؿgets()Ǹɤ߹Υե̾.
+
+ $DEBUG `-d'ե饰ξ()
+
+ $LOAD_PATH եɤ˸ǥ쥯ȥؤΥѥ
+ ޤ. ưˤϥǥե(ѥ˻ꤹ
+ )˲ä, ĶѿRUBYLIBͤrubyư-Iץ
+ ǻꤵ줿ͤɲä.
+
+ $stdin ɸ
+ $stdout ɸ
+ $stderr ɸ२顼
+
+ $VERBOSE `-v'ե饰ξ()
+
+ $VERSION rubyΥС򼨤ʸ
+
+** ƥ
+
+ %TRUE 1(Fixnum)
+ %FALSE nil
+
+ 줾쿿ͤɽ. ȽǤnil, ʳƤͤ
+ ȤȽǤ뤿, ֤ͤ᥽åɤ%TRUEʳͤ
+ Τ¸ߤΤ, ӤѤΤŬڤǤʤȤդ
+ Ǥ. 㤨мμϿˤʤʤ.
+
+ (2 < 3) == %TRUE
+
+ ᥽å"<"Ͽλ֤ͤΤ, ξͤ3Ǥ,
+ %TRUE1Ȥϰۤʤ. %FALSE˴ؤƤ, Τ褦
+ ʤ.
+
+** 饹/⥸塼
+
+*** Array(饹)
+
+źȤΥ饹Ǥ. ϰŪˤ``[...]''
+Ԥʤ.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ self[nth]
+ self[start..end]
+ self[start, length]
+
+ Ǥ˥. ǽηǤnthܤǤ
+ ֤, 2ܤηǤstartܤǤendܤǤޤ
+ ʬ֤. 3ܤηǤstartܤlengthĤǤޤ
+ ʬ֤.
+
+ self[nth] = val
+ self[start..end] = val
+ self[start, length] = val
+
+ Ǥѹ. ǽηǤnthܤǤval
+ ѹ. 2ܤηstartܤǤendܤǤޤǤ
+ valѹ. 3ܤηǤstartܤlengthĤǤval
+ ѹ.
+
+ 2, 3ܤηǤvalǤʤФʤʤ.
+
+ :
+
+ ary = [1, 2, 3, 4, 5]
+ ary[0..2] = [0, 0] # Ƥ [0, 0, 4, 5]
+ ary[1, 0] = [7] # Ƥ [0, 7, 0, 6, 5]
+
+ self + other
+
+ Ϣ. selfotherξƤҤ
+ ֤.
+
+ self * times
+
+ η֤.
+
+ self << obj
+
+ objɲä. self֤ΤC++ŪϢǤ.
+
+ assoc(key)
+
+ Ϣۥꥹ(2ǤǤȤ)򸡺, 1Ǥkey
+ ("=="Ӥ)֤.
+
+ clear
+
+ 礭0ˤ.
+
+ delete(item)
+
+ itemȰפǤ.
+
+ delete_if
+
+ Ǥ륤ƥ졼. ֥åɾͤλ, б
+ Ǥ󤫤.
+
+ each
+
+ γǤͿ륤ƥ졼.
+
+ fill(val)
+ fill(val, start[, length])
+ fill(val, start..end)
+
+ (λꤵ줿ʬ)Ǥͤvalꤹ. 2֤η
+ lengthά줿νޤǤĹȤ. ꤵ줿
+ ʬ󤬸ϰϤۤϼưŪ˳ĥ.
+
+ join([sep])
+
+ ǤϢ뤷ʸ֤. ǤʸѴ,
+ sep򶴤Ϣ뤵. sepά줿ˤϥƥѿ`$,'
+ ͤѤ.
+
+ length
+
+ Ĺ(ǿ)֤.
+
+ push(obj)
+
+ objɲä.
+
+ pack(template)
+
+ Ƥtemplateʸˤä, 1Ĥʸ˥ѥå
+ . ѥåʸ֤. ƥץ졼ȤϷʸȤĹ
+ (ά1)¤٤ΤǤ. ĹȤ`*'ꤵ줿
+ ֻĤΥǡơפĹɽ.
+
+ ʸϰʲΤΤ.
+
+ a ASCIIʸ(nullʸͤ)
+ A ASCIIʸ(ڡͤ)
+ b ӥåȥȥ(̥ӥåȤ̥ӥå)
+ B ӥåȥȥ(̥ӥåȤ鲼̥ӥå)
+ h 16ʸ(̥˥֥뤬)
+ H 16ʸ(̥˥֥뤬)
+ c char
+ C unsigned char
+ s sort
+ S unsigned sort
+ i int
+ I unsigned int
+ l long
+ L unsigned int
+ n ͥåȥХȥshort
+ N ͥåȥХȥlong
+ f ñư(¸)
+ d ư(¸)
+ x ʥХ
+ X 1Хȸ
+ @ а֤ؤΰư
+
+ pop
+
+ Ǥ, ֤.
+
+ rassoc(value)
+
+ Ϣۥꥹ(2ǤǤȤ)򸡺, 2Ǥ
+ value("=="Ӥ)֤.
+
+ shift
+
+ ƬǤ, ֤.
+
+ sort
+
+ Ƥ򥽡Ȥ. ƥ졼ȤƸƤӽФ줿ϥ֥
+ ɾͤǤ羮ꤹ. 礭,
+ 0, . ̾Υ᥽åɤȤƸƤӽФ줿ϳ
+ `<=>'Ӥ.
+
+ to_a
+
+ ʬȤ֤. оΤѰդƤ᥽åɤǤޤ
+ 򤯤ʤ.
+
+ unshift(obj)
+
+ objƬɲä.
+
+*** Bignum(饹)
+
+̵¿ĹΥ饹. 黻η̤FixnumϰǤˤϼ
+ưŪ˥饹FixnumѴ. ŪRubyץǤFixnum
+BignumѴϰۤΤ˹ԤΤ, ռɬפ̵. FloatȤ
+˴ؤƤ, Bignum Floatgenericity⤤Τˤؤ餺,
+Bignum, 礭ͤɽǤΤ, Ѵ˷ǽ
+.
+
+
+SuperClass: Integer
+
+Methods:
+
+ self + other
+ self - other
+ self * other
+ self / other
+ self % other
+ self ** other
+
+ ѱ黻. 줾, , , , ;, Ѿ֤.
+
+ ~ self
+ self | other
+ self & other
+ self ^ other
+
+ ӥåȱ黻. 줾ӥåȿž, , , ¾Ū¤
+ ֤.
+
+ self << bits
+ self >> bits
+
+ եȱ黻. 줾bitsӥåȤ˥ӥåȥեȤԤʤ.
+
+ divmod(other)
+
+ Ⱦ;2Ǥ֤.
+
+*** Class(饹)
+
+饹Υ饹. 긷̩ȥ饹ðۥ᥽åɤѾ뤿
+, 줾᥿饹ȸƤФ̾Τʤ饹򥯥饹Ȥƻ,
+ClassϤΥ᥿饹Υ饹Ǥ(ʬä?). , β⤬
+ǤʤƤ, RubyȤȤ˲λپʤ.
+
+SuperClass: Module
+
+Methods:
+
+ attr(name[, public])
+
+ Υ饹Υ󥹥󥹤Фnameǻꤵ°.
+ ܤModuleattr᥽åɤι򻲾ȤΤ.
+
+ new(...)
+
+ 饹Υ󥹥󥹤. ¿ξ礳Υ᥽åɤϥ֥
+ 饹ðۥ᥽åɤˤäƥС饤ɤ, 饹ˤäư
+ ۤʤ.
+
+*** Comparable(⥸塼)
+
+ ӱ黻饹ΤMixin. Υ⥸塼򥤥󥯥롼ɤ뤳
+ Ȥˤä, `<=>'¾α黻ҤϤѤ
+ .
+
+Methods:
+
+ self > other
+
+ selfother礭֤.
+
+ self >= other
+
+ selfother礭֤.
+
+ self < other
+
+ selfother꾮֤.
+
+ self <= other
+
+ selfother꾮֤.
+
+ between(min, max)
+
+ selfminmaxϰˤ֤.
+
+*** 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_value
+
+ ƤvalueФƷ֤ƥ졼.
+
+ each_key
+
+ ƤkeyФƷ֤ƥ졼.
+
+ each_pair
+
+ [key, value]ʤͿ륤ƥ졼.
+
+ includes(key)
+ has_key(key)
+
+ keyǡ١¸ߤ, ֤
+
+ has_value(value)
+
+ valueͤȤȤǡ١¸ߤ,
+ ֤
+
+ keys
+
+ ǡ١¸ߤ륭Ƥޤ֤.
+
+ length
+
+ ǡ١Ǥο֤. (:ߤμ¸Ǥǿ
+ 뤿˥ǡ١Τ, 빽Ȥ⤤.
+ ĤƻȤ.)
+
+ to_a
+
+ ǡ١key-valueڥǤȤ֤.
+
+ values
+
+ ǡ١¸ߤƤޤ֤.
+
+Single Methods:
+
+ open(dbname[, mode])
+
+ dbnameǻꤷǡ١⡼ɤmodeꤷƥץ
+ . modeξάͤ0666Ǥ. modeȤnilꤹȥǡ
+ ١¸ߤʤˤϿ˥ץ󤻤, nil֤.
+
+*** Dict(饹)
+
+񤢤뤤Ϣ. ǤդΥ֥ȤźȤǤΥ饹Ǥ
+. HashȤ̾Ǥ⥢Ǥ. Ϣ󥪥֥Ȥϰ
+ŪˤϢ``{a=>b,..}'' ǹԤʤ.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ self [key]
+
+ key򥭡Ȥ֤ͤ.
+
+ self [key]= value
+
+ key򥭡Ȥ, valueǼ. valueȤnilꤹȤ
+ keyФܤκȤʤ. Ĥޤ, DictͤȤnil
+ ȤϤǤʤ.
+
+ clear
+
+ Ϣˤ.
+
+ delete(key)
+
+ key򥭡ȤȤ.
+
+ delete_if
+
+ Ǥ륤ƥ졼. [key, value]ȤͿ, ֥
+ ɾͤλ, ܤ.
+
+ each
+ each_value
+
+ ƤvalueФƷ֤ƥ졼.
+
+ each_key
+
+ ƤkeyФƷ֤ƥ졼.
+
+ each_pair
+
+ [key, value]ʤͿ륤ƥ졼.
+
+ includes(key)
+ has_key(key)
+
+ key¸ߤ, ֤
+
+ has_value(value)
+
+ valueͤȤȤ¸ߤ, ֤
+
+ keys
+
+ ¸ߤ륭Ƥޤ֤.
+
+ length
+
+ Ǥο֤.
+
+ to_a
+
+ key-valueڥǤȤ֤.
+
+ values
+
+ ¸ߤƤޤ֤.
+
+Single Methods:
+
+ new
+
+ ()񥪥֥Ȥ֤.
+
+*** Directory(饹)
+
+ǥ쥯ȥǤ֤ǥ쥯ȥꥹȥ꡼ΤΥ饹.
+DirȤ̾Ǥ⥢Ǥ.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ close
+
+ ǥ쥯ȥꥹȥ꡼򥯥. ʸ㳰ȯ
+ .
+
+ each
+
+ ǥ쥯ȥγǤͿ륤ƥ졼.
+
+ getwd
+ pwd
+
+ ȥǥ쥯ȥ֤.
+
+ rewind
+
+ ǥ쥯ȥꥹȥ꡼Ƭ˥ꥻåȤ.
+
+ seek(pos)
+
+ ǥ쥯ȥꥹȥ꡼ΰ֤posꤹ.
+
+ tell
+
+ ǥ쥯ȥꥹȥ꡼θߤΰ֤֤.
+
+Single Methods:
+
+ chdir(path)
+
+ ȥǥ쥯ȥpathѹ.
+
+ chroot(path)
+
+ ץΥ롼ȥǥ쥯ȥѹ, Ʊ̾Υƥॳ
+ ƱƯ򤹤. ϼ¸uidѥ桼Ǥ
+ ¤Ƥ. 롼ȥǥ쥯ȥ򸵤᤹(롼ȥǥ쥯
+ ѹ)ˡ󶡤Ƥʤ.
+
+ mkdir(path[, mode])
+
+ modeǻꤵ줿⡼ɤĥǥ쥯ȥpath. ⡼
+ umaskˤäƽ. modeΥǥեͤ0777.
+
+ open(path)
+
+ pathФǥ쥯ȥꥹȥ꡼򥪡ץ󤹤.
+
+ rmdir(path)
+
+ pathǻꤵ줿ǥ쥯ȥ. ǥ쥯ȥ϶Ǥ
+ ɬפ.
+
+*** Enumerable(⥸塼)
+
+ǤФ뷫֤Ԥʤ饹ΤMixin. Υ⥸塼򥤥
+롼ɤ뤿ˤ, ᥽å`each'ɬפ.
+
+Methods:
+
+ collect
+
+ ǤФƥ֥åɾ̤ƴޤ֤
+
+ find
+
+ ǤФƥ֥åɾͤˤʤäǽǤ֤.
+
+ find_all
+
+ ǤФƥ֥åɾͤǤäǤƴޤ
+ ֤.
+
+ grep(pattern)
+
+ pattern =~ ǡפΩƤǤޤ֤.
+ 졼ȤѤ줿Ͼ嵭ξΩǤФƥ֥
+ ¹Ԥ.
+
+ includes(val)
+
+ val`=='δطˤǤĻ, ֤.
+
+ index(val)
+
+ val`=='δطˤ륪֥Ȥܤ˸줿֤.
+ ֺǽǤ0ˤʤ. Ǥ¸ߤʤˤnil֤.
+ ʤ饹ФƤϤޤ̣ʤ.
+
+ min
+
+ ǾǤ֤. Ǥ`<=>'᥽åɤĤȤۤΤ
+ ˲ꤷƤ.
+
+ max
+
+ Ǥ֤. Ǥ`<=>'᥽åɤĤȤۤΤ
+ ˲ꤷƤ.
+
+ reverse
+
+ ƤǤս¤٤֤.
+
+ sort
+
+ ƤǤ򥽡Ȥ֤.
+
+*** EnvDict(饹)
+
+Ķѿ뤿Υ饹. ƥѿ$ENVͣΥ󥹥󥹤
+Ƥ.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ [name]
+
+ Ķѿ֤ͤ.
+
+ [name]= val
+
+ Ķѿͤvalꤹ. valȤnilꤷˤ,
+ Ķѿ.
+
+ delete(name)
+
+ Ķѿ.
+
+ each
+
+ Ķѿ̾ͤΥڥͿ륤ƥ졼.
+
+*** Etc(⥸塼)
+
+/etcǥ쥯ȥʲξ뤿Υ⥸塼. 饹˥󥯥롼
+ƻȤȤǤ.
+
+Methods:
+Single Methods:
+
+ getlogin
+
+ ʬlogin֤̾. 줬Ԥgetpwuid()Ѥ
+ ɤ.
+
+ getpwnam(name)
+
+ /etc/passwdե(뤤DBMեNISǡ١)
+ , name̾passwdȥ֤. ͤpasswd¤
+ ΤǰʲΥФ.
+
+ struct passwd
+ name # 桼̾(ʸ)
+ passwd # ѥ(ʸ)
+ uid # 桼ID()
+ gid # 롼ID()
+ gecos # gecosե(ʸ)
+ dir # ۡǥ쥯ȥ(ʸ)
+ shell # 󥷥(ʸ)
+ # ʹߤΥФϥƥˤäƤϤʤΤ⤢
+ change # ѥѹ()
+ quota # ()
+ age # ()
+ class # 桼饹(ʸ)
+ comment # (ʸ)
+ expire # ͭ()
+ end
+
+ ܺ٤getpwnam(3)򻲾ȤΤ.
+
+ getpwuid([uid])
+
+ uid桼IDȤpasswdȥ֤. ͤgetpwnam()
+ ƱͤǤ. άˤgetuid()ͤѤ. ܺ٤
+ getpwuid(3)򻲾ȤΤ.
+
+ getgrgid(gid)
+
+ /etc/groupե(뤤ϡgetpwnam)򸡺, gid򥰥롼
+ IDȤ륰롼ץȥ֤. ͤgroup¤Τǰʲ
+ Ф.
+
+ struct group
+ name # 롼̾(ʸ)
+ passwd # 롼פΥѥ(ʸ)
+ gid # 롼ID()
+ mem # 롼ץ̾
+ end
+
+ ܺ٤getgrgid(3)򻲾ȤΤ.
+
+ getgrnam(name)
+
+ nameȤ̾Υ롼ץȥ֤. ͤgetgrgid()Ʊ
+ ͤǤ. ܺ٤getgrnam(3)򻲾.
+
+ group
+
+ ƤΥ롼ץȥ˥뤿Υƥ졼.
+
+ passwd
+
+ Ƥpasswdȥ˥뤿Υƥ졼.
+
+*** File(饹)
+
+ե륢ΤΥ饹. ؿ᥽åopen(). ޤ,
+Υ饹ðۥ᥽åɤȤtestΥեƥȱ黻Υ᥽å
+Ƥ.
+
+SuperClass: IO
+
+Methods:
+
+ atime
+
+ եκǽ֤.
+
+ ctime
+
+ եκǽơѹ֤.
+
+ chmod(mode)
+
+ եΥѡߥåѹ(cf chmod(2)).
+
+ chmod(owner, group)
+
+ եνͭԤȥ롼פѹ(cf chown(2)). nil-1
+ ꤹ뤳ȤˤäƽͭԤ䥰롼פ򸽺ߤΤޤѤʤǤ
+ ȤǤ.
+
+ eof
+
+ եνüã˿֤.
+
+ lstat
+
+ ե˴ؤStat¤Τ֤. lstatϥե뤬ܥ
+ 󥯤ǤХ󥯤ΤΤ˴ؤStat¤Τ֤. ¤
+ ƤˤĤƤstat 򻲾ȤΤ.
+
+ mtime
+
+ եκǽ֤.
+
+ rewind
+
+ եΥեݥ󥿤ΰ֤Ƭ˰ư.
+
+ path
+
+ եΥѥ֤̾.
+
+ seek(offset, ptrname)
+
+ եΥեݥ󥿤ΰ֤offset˰ư. ptrname
+ 0, 1, 2Τ줫Ǥä, 줾եƬ, ߰,
+ եνüΤΤ줫Ф򼨤.
+
+ stat()
+
+ ե˴ؤStat¤Τ֤(Struct 򻲾).
+
+ struct stat
+ dev # ե¸ߤǥХ
+ ino # եi-nodeֹ
+ mode # ⡼
+ nlink # ϡɥ󥯤ο
+ uid # ͭԤΥ桼ID
+ gid # ͭԤΥ롼ID
+ rdev # ǥХID(ڥեΤ)
+ size # ե륵(byte)
+ blksize # ե륷ƥˤŬڤʥ֥å
+ blocks # ֥å
+ atime # ǽ
+ mtime # ǽ
+ ctime # ǽѹ
+ end
+
+ ܺ٤fstat(2)򻲾ȤΤ. ƥƤ
+ stat¤Τ˳Фʤ0ꤵƤ.
+
+ tell
+
+ եθߤΥեݥ󥿤ΰ֤֤.
+
+ truncate(length)
+
+ եڤΤƤƺlengthХȤˤ. եwrite⡼
+ ɤǥץ󤵤ƤʤФʤʤ.
+
+Single Methods:
+
+ atime(filename)
+
+ filenameκǽ֤.
+
+ b(filename)
+
+ filenameΥե뤬֥åڥեǤ,
+ .
+
+ c(filename)
+
+ filenameΥե뤬饯ڥեǤ,
+ ֤.
+
+ ctime(filename)
+
+ filenameκǽơѹ֤.
+
+ chmod(mode, path, file...)
+
+ եΥѡߥåѹ(cf chmod(2)).
+
+ chown(owner, group, file...)
+
+ եνͭԤȥ롼פѹ(cf chown(2)). nil-1
+ ꤹ뤳ȤˤäƽͭԤ䥰롼פ򸽺ߤΤޤѤʤǤ
+ ȤǤ.
+
+ executable(filename)
+ x(filename)
+
+ filenameΥե뤬¹Բǽλ, ֤.
+
+ exists(filename)
+ e(filename)
+ a(filename)
+
+ filenameΥե뤬¸ߤ, ֤.
+
+ G(filename)
+
+ filenameΥեgid¸롼פgidƱ, ֤.
+
+ isdirectory(filename)
+ d(filename)
+
+ filenameǥ쥯ȥλ, ֤.
+
+ isfile(filename)
+ f(filename)
+
+ filenameΥե뤬̾եλ, ֤.
+
+ islink(filename)
+ l(filename)
+
+ filenameΥե뤬ܥå󥯤Ǥ, ֤.
+
+ ispipe(filename)
+ p(filename)
+
+ filenameΥե뤬̾Ĥѥ(FIFO)Ǥ, ֤.
+
+ issocket(filename)
+ S(filename)
+
+ filenameΥե뤬åȤǤ, ֤.
+
+ link(old, new)
+
+ oldؤΥϡɥnew. link(2)Ʊ¤.
+
+ mtime(filename)
+
+ filenameκǽ֤.
+
+ owned(filename)
+ O(filename)
+
+ filenameΥե¸桼ͭƤ, ֤.
+
+ readable(filename)
+ r(filename)
+
+ filenameΥեɤߤȤǽλ, ֤.
+
+ readlink(path)
+
+ ܥåpathƤʸȤ֤.
+
+ rename(from, to)
+
+ ե̾fromtoѹ. rename(2). toȤ̾
+ Υե뤬¸ߤˤϤޤΥե뤬.
+
+ R(filename)
+
+ filenameΥեuid/gidɤߤȤǽλ, ֤.
+
+ setuid(filename)
+ u(filename)
+
+ filenameΥեsetuidӥåȤåȤƤ, ֤.
+
+ setuid(filename)
+ g(filename)
+
+ filenameΥեsetuidӥåȤåȤƤ, ֤.
+
+ size(filename)
+ s(filename)
+
+ filenameΥե뤬¸ߤ, ե礭֤. ¸ߤ
+ ʤnil֤.
+
+ sticky(filename)
+ g(filename)
+
+ filenameΥեstickyӥåȤåȤƤ, ֤.
+
+ symlink(old, new)
+
+ oldؤΥܥånew.
+
+ truncate(path, length)
+
+ pathǻꤵ줿եڤΤƤƺlengthХȤˤ.
+
+ unlink(file...)
+
+ ե. ǥ쥯ȥκˤDir.rmdirȤ.
+
+ utime(atime, mtime, file...)
+
+ եΥatime, mtimeꤹ.
+ atime, mtimeϿޤTime饹Υ󥹥󥹤ǤʤФʤ
+ .
+
+ writable(filename)
+ w(filename)
+
+ filenameΥե뤬uid/gidǽ񤭹߲ǽλ, ֤.
+
+ W(filename)
+
+ filenameΥե뤬񤭹߲ǽλ, ֤.
+
+ z(filename)
+
+ filenameΥե뤬¸ߤ, 礭0Ǥ, ֤.
+
+*** Fixnum(饹)
+
+31bitΥ饹. builtin classǤ. Υ饹pointer¨ͤ
+뤿call by valueǸƤӽФħŪǤ(¾Υ饹call
+by reference). 黻η̤31bitۤˤϼưŪBignum(̵¿
+Ĺ)˳ĥ.
+
+ƥ졼upto(), downto(), step()Ϸ֤ΤѤ, ̤
+Range饹Ѥ®Ǥ.
+
+SuperClass: Integer
+
+Methods:
+
+ self + other
+ self - other
+ self * other
+ self / other
+ self % other
+ self ** other
+
+ ѱ黻. 줾, , , , ;, Ѿ֤.
+
+ ~ self
+ self | other
+ self & other
+ self ^ other
+
+ ӥåȱ黻. 줾ӥåȿž, , , ¾Ū¤
+ ֤.
+
+ self << bits
+ self >> bits
+
+ եȱ黻. 줾bitsӥåȤ˥ӥåȥեȤԤʤ.
+
+ self .. max
+
+ ϰϻ. self maxޤǤϰϥ֥Ȥ֤.
+
+ downto(min)
+
+ ƥ졼. selfminޤDz˷֤.
+
+ id2name
+
+ ͤIDȤߤʤ, ʸ֤. ʸ
+ ¸ߤʤnil֤.
+
+ step(max, step)
+
+ ƥ졼. selfmaxޤstepѲʤ, ֤.
+
+ to_f
+
+ selfFloatѴΤ֤.
+
+ to_i
+
+ self򤽤Τޤ֤.
+
+ upto(max)
+
+ ƥ졼. selfmaxޤǷ֤.
+
+*** Float(饹)
+
+ ưΥ饹.
+
+SuperClass: Numeric
+
+Methods:
+
+ self + other
+ self - other
+ self * other
+ self / other
+ self % other
+ self ** other
+
+ ѱ黻. 줾, , , , ;, Ѿ֤.
+
+ self == other
+ self > other
+
+ ӱ黻.
+
+ coerce(num)
+
+ numfloatѴ. FloatǤ¾ο
+ FixnumǤ.
+
+ to_f
+
+ self򤽤Τޤ֤.
+
+ to_i
+
+ floatѴ̤֤.
+
+Single Methods:
+
+ new(float)
+
+ floatƱͤĿFloat֥Ȥ֤.
+
+*** GC(⥸塼)
+
+RubyȤ߹ߤgarbage collectorԤʤΥ⥸塼. Υ
+塼Υ᥽åɤѤ뤳Ȥˤä, ŪGCߤ᤿, GCε
+륿ߥ󥰤椷Ǥ.
+
+gcϻϤޤˤΥ⥸塼start_hook᥽åɤ, äˤ
+end_hook᥽åɤƤӽФ, ᥽åɤ뤳Ȥˤä
+hook򤫤뤳ȤǤ.
+
+Methods:
+
+ garbage_collect +
+
+ GC򳫻Ϥ. GC.startפƱ.
+
+Single Methods:
+
+ disable
+
+ GCػߤ.
+
+ enable
+
+ GCĤ.
+
+ start
+
+ GC򳫻Ϥ.
+
+ threshold
+
+ GCγϥߥ󥰤ꤹͤθߤ֤ͤ.
+
+ threshold=(val)
+
+ GCͤꤹ. Ť֤ͤ.
+
+ start_hook
+ end_hook
+
+ GCγϻ, λˤ줾ƤФ. ǥեȤǤϲ⤷ʤ
+ ᥽åɤƤ.
+
+*** Integer(饹)
+
+饹. ºݤϤ礭ˤäFixnumBignumĤΥ֥饹
+Ǽ¸Ƥ. IntegerϤΥѡ饹Ȥʤݥ饹Ǥ
+. RubyǤϤۤȤɤξ, FixnumBignumζ̤ɬפʤ, ߤѴ
+ϼưŪ˹Ԥʤ. ӥåȤߤʤˤ, ̵¤Ĺ
+ĥӥåȹͤƹʤ.
+
+SuperClass: Numeric
+
+Methods:
+
+ self[idx]
+
+ idxӥåܤåȤƤ1, åȤƤʤ
+ 0֤.
+
+ chr
+
+ ο򥳡ɤȤʸޤ1ʸʸ֤. ̤
+ Ĺ1ʾʸˤĤ, δطΩ.
+
+ str[0].chr == str[0,1]
+
+ ʸϰ(0..255)ˤʤ㳰ȯ.
+
+ is_integer
+
+ Ĥ⿿֤.
+
+*** IO(饹)
+
+ϤΤδܥ饹.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ self << object
+
+ objectϤ. objectʸǤʤˤϥ᥽åto_sѤ
+ ʸѴ. selfͤȤΤ, C++Τ褦`<<'
+ ϢȤ.
+
+ :
+
+ $stdout << 1 << " is a " << Fixnum
+
+ close
+
+ ϥݡȤ򥯥. ʸΤΥ֥ȤФ
+ ϥ顼ˤʤ.
+
+ each
+
+ Ԥɤ߹Ǥ뤿Υƥ졼. Ԥζڤϥƥ
+ `$/'ˤäѹǤ. ɤ߹ʸϥƥѿ`$_'
+ ⥻åȤ.
+
+ each_byte()
+
+ ʸɤ߹Ǥ뤿Υƥ졼. ʸʸɤɽ
+ FixnumǤ.
+
+ fileno
+
+ IO֥ȤȤäƤեǥץ(Fixnum)֤.
+
+ flush
+
+ Хåեեå夹.
+
+ getc
+
+ ɤ߹, ɤ߹ߤˤϤʸ֤. ե
+ νãˤnil֤. ؿŪ᥽åɤgetc()
+ $stdin.getcƱ̣Ǥ.
+
+ gets
+
+ ɤ߹, ɤ߹ߤˤϤʸ֤. ե
+ νãˤnil֤.
+
+ isatty
+
+ ϥݡȤttyǤ, ֤.
+
+ puts(obj)
+
+ objϤ. self << objפƱ̣Ǥ.
+
+ read([length])
+
+ lengthХɤ߹, ʸ֤. lengthά줿
+ ˤ, ƤΥǡɤ߹.
+
+ sync
+
+ ߤνƱ⡼ɤ򿿵֤ͤ. Ʊ⡼ɤλϽϴ
+ θƽ˥Хåեեå夵.
+
+ sync=(newstate)
+
+ Ʊ⡼ɤꤹ.
+
+ sysread(length)
+
+ stdioͳread(2)ѤϤԤʤ. Ϥ줿ǡ
+ ޤʸ֤. եνãˤnil֤.
+ read(2)ˤɬlengthХȤʸɤ߹ޤ櫓
+ ǤϤʤ. gets()getc()ʤstdioͳ᥽åɤȺѤ
+ ȤϥХåե󥰤ʤɤ, פư򤹤뤳Ȥ.
+
+ syswrite(str)
+
+ stdioͳ, wirte(2)ѤƽϤԤʤ. Υ᥽å
+ ϥХåե󥰤ʤstdioƤ뤳ȤϰڹԤʤʤ.
+ print()printf()syswrite()ѤΤϿ侩Ǥʤ.
+
+ write(str)
+
+ strϤ. ϤХȿ֤.
+
+Single Methods:
+
+ default
+
+ printprintfΥǥեȤν֤. ͤ$stdout.
+
+ default=
+
+ ǥեȤνꤹ.
+
+*** Kernel(饹)
+
+ƤΥ饹δ쥯饹. RubyȤ߹ߤƤδؿŪ᥽åɤϤΥ
+饹Ƥ.
+
+SuperClass: ʤ
+
+Methods:
+
+ ! self
+
+ . Non-nilΥ֥Ȥξ˵(nil)֤. Υ᥽
+ ɤNil饹ǤϺ쿿֤.
+
+ equal(other)
+ self == other
+
+ ֥ȤΰȽ. 쥷ФȰΰפ,
+ ֤. Kernel饹ǤΥ֥ȤƱλ
+ . "=="᥽åɤϳƥ֥Ȥ˱ƺɬפ
+ . "=="᥽åɤˤ, hash᥽åɤ⤽˹
+ ƺɬפ.
+
+ equal()᥽åɤ"=="᥽åɤ̾, "=="Ǥ
+ ֥ȤƱȽԤʤѤ. ä
+ equal()᥽åɤϥ֥饹Ǻ٤ǤϤʤ.
+
+ self != other
+
+ "==". "=="᥽åɤƤӽФƤΤ, Ϻ
+ ɬפϤʤ.
+
+ self =~ other
+
+ ޥå. ǥեȤ"=="ƱǤ. "=~"caseӤˤѤ
+ .
+
+ !~
+
+ "=~". "=~"᥽åɤƤӽФƤΤ,
+ ɬפϤʤ.
+
+ self :: other
+
+ selfotherǤȤĹ2֤. Ȥ
+ ̣ [self, other]ƱƯ򤹤뤬, 鷺˸
+ Ψɤ. α黻ҤϱǤΤ, a::b::c (a::(b::c))
+ Ȳᤵ.
+
+ is_nil
+
+ ֥ȤnilǤ뤫ɤ. Kernel饹ǤϿ
+ . Nil饹ǺƤ.
+
+ id
+
+ ƥ֥ȤФưդFixnum֤. , FixnumϼʬȤ
+ Τ, idפƤƱ֥ȤǤ뤳Ȥݾڤʤ.
+ Ĥޤ,
+
+ obj1.id == obj2.id
+
+ ΩƤ, ɤ餫FixnumǤ, obj1obj2ƱǤ
+ Ȥϸ¤ʤ. , ξFixnumǤʤȤݾڤǤ, 2
+ ĤΥ֥ȤƱǤ뤳Ȥϳμ¤Ǥ.
+
+ hash
+
+ ֥ȤΥϥå(Fixnum)֤. Dict饹ǥȤʤ
+ ֥ȤǼΤѤƤ.A == BפΩ
+ ɬA.hash == B.hashפΩɬפΤ, "=="
+ ˤϤ⤽˹碌ƺ뤳.
+
+*** Math(⥸塼)
+
+ư黻򥵥ݡȤ륯饹. Math⥸塼ƱΥ᥽å
+ðۥ᥽åɤȤξƤΤ, ðۥ᥽åɤƤӽФƻ
+Ȥ, 饹˥󥯥롼ɤƻȤȤȤξǤ.
+
+ :
+
+ pi = Math.atan2(1, 1) * 4;
+ include Math
+ pi2 = atan2(1, 1)
+
+Methods:
+Single Methods:
+
+ atan2(x, y)
+
+ С-ФϰϤX/YΥ󥸥Ȥ֤.
+
+ cos(x)
+ sin(x)
+ tan(x)
+
+ 饸ɽ줿xλѴؿ֤ͤ.
+
+ exp(x)
+
+ xλؿؿ֤ͤ.
+
+ log(x)
+
+ xμп֤.
+
+ log10(x)
+
+ xξп֤.
+
+ sqrt(x)
+
+ xʿ֤. xͤǤˤ㳰ȯ.
+
+ cbrt(x)
+
+ xΩ֤.
+
+*** Module(饹)
+
+⥸塼Υ饹.
+
+SuperClass: Object
+
+Methods:
+
+ attr(name[, public]) +
+
+ Υ⥸塼򥤥󥯥롼ɤ饹Υ󥹥󥹤Ф
+ nameǻꤵ°ղä, °Ф륢᥽åɤ
+ . attr("attr")ϰʲ˼ɤȤۤƱǤ.
+
+ def attr; @attr; end
+
+ άǽ2publicͿ, ĤͤnilǤʤ
+ , °ˤ°᥽åɤѰդ, ǽ
+ ʤ. attr("attr", %TRUE)ϰʲΥɤȤۤƱǤ.
+
+ def attr; @attr; end
+ def attr=(val); @attr = val; end
+
+ °᥽åɤ뤳Ȥˤä, ư
+ ѹǤ. 㤨
+
+ attr("test", %TRUE)
+ def test=(val)
+ print("test was ", @test, "\n")
+ print("and now is ", @test = val, "\n")
+ end
+
+ Τ褦°ͤɽ褦ʤȤǽǤ. attr
+ ϥ᥽åɤǤƤ, ǥեȤΥ
+ ᥽åɤʤ.
+
+ to_s
+
+ ⥸塼ʸɽ֤. ⥸塼ξʸɽϥ
+ 塼̾Ǥ.
+
+*** Nil(饹)
+
+ɽ֥nilΥ饹. ѿ()nilNil饹ͣΥ
+󥹥󥹤Ǥ. nil饹ӥ᥽åɤդ뤬, Ӥ
+ϢԤʤǤ. ӤϢȤ
+
+ 10 < a < 13
+
+Τ褦ʤΤǤ. ӱ黻ҥ᥽åɤϼԤnil֤,
+ϱդΥ֥Ȥ֤Τ, nilӱ黻Ҥ˵֤Ȥˤ
+ϢΩ.
+
+SuperClass: Kernel
+
+Methods:
+
+ self + other
+
+ other, ư, ʸ, Τ줫Ǥä,
+ other֤. ̤ΰФ뼫ʤ褦Ѱ
+ 줿᥽åɤǤ.
+
+ foo # ͤnil
+ foo += 1 # foo1ˤʤ.
+
+ self > other
+ self >= other
+ self < other
+ self <= other
+
+ ϢΤΥ᥽å. nil֤.
+
+ ! self
+
+ ˿֤.
+
+ is_nil
+
+ ˿֤.
+
+*** Numeric(饹)
+
+̤ɽݥ饹.
+
+SuperClass: Object
+
+Included Modules: Comparable
+
+Methods:
+
+ + self
+
+ ֥selfΤΤ֤
+
+ - self
+
+ 0 - selfפ֤ͤ. ֥饹ǤΨŪ˺뤳
+ ȤԤ.
+
+ abs
+
+ ֤ͤ.
+
+ divmod(other)
+
+ Ⱦ;2Ǥ֤.
+
+*** Object(饹)
+
+Ƥ̾說饹Υѥ饹. Υ饹Υ֥饹Ǥʤ饹
+KernelNilǤ.
+
+SuperClass: Kernel
+
+Methods:
+
+ is_member_of(class)
+
+ ֥self饹classΥ󥹥󥹤Ǥ,֤.
+
+ is_kind_of(class)
+
+ ֥self饹classΥ֥饹Υ󥹥󥹤
+ , ֤.
+
+ clone
+
+ ֥Ȥʣ. FixnumʳΥ饹ξ, 餯
+ obj.eqaul(obj.clone)פϵǤ뤬, ¿ξobj ==
+ obj.cloneפϿǤ.
+
+ to_s
+
+ ֥Ȥʸɽ֤. Υ᥽åɤŪprint()
+ format()᥽åɤѤƤ.
+
+ to_a
+
+ ֥ȤѴ. ͥ륯饹Ƥ
+ եȤ, Υ֥ȼȤޤ1Ǥ֤.
+
+*** Process(⥸塼)
+
+ץ˴ؤԤʤΥ⥸塼. Math⥸塼Ʊͤ
+ƤΥ᥽åɤðۥ᥽åɤȤƤ̾Υ᥽åɤȤƤȤ.
+Processϥץ֥ȤΥ饹ǤϤʤ, ץΥ᥽
+ɤޤȤ᤿ΤǤ뤳Ȥդ뤳.
+
+Methods:
+Single Methods:
+
+ euid
+
+ ץθߤμ¸UID֤.
+
+ euid=(uid)
+
+ ץθߤμ¸UIDuid˥åȤ.
+
+ getpgrp([pid])
+
+ pidǻꤵ줿ץ߽°Ƥץ롼פid
+ ֤. pidάpid0Ϳϸ߼¹ԤƤ
+ оݤˤ.
+
+ getpriority(which, who)
+
+ whichwhoǻꤵץ, ץ롼, 桼θ
+ ̤֤ͥ. ܺ٤getpriority(2)򻲾. Process⥸塼
+ ǤwhichȤƻǤ%PRIO_PROCESS, %PRIO_PGRP,
+ %PRIO_USERƤ.
+
+ pid
+
+ ץΥץID֤. ϥƥѿ`$$'ͤƱ
+ .
+
+ ppid
+
+ ƥץΥץΥץID֤. UNIXǤľܤοƥץ
+ λ, ƥץpid1(initpid)ˤʤ.
+
+ setpgrp(pid, pgrp)
+
+ pidǻꤵ줿ץΥץ롼פpgrpˤ. pid0
+ Ϳȸ߼¹Υץоݤˤ.
+
+ setpriority(which, who, prio)
+
+ whichwhoǻꤵץ, ץ롼, 桼θ
+ ̤ͥprioꤹ. ܺ٤setpriority(2)򻲾ȤΤ.
+
+ uid
+
+ ץθߤμUID֤.
+
+ uid=
+
+ ץθߤμUIDuid˥åȤ.
+
+*** Range(饹)
+
+ϰϥ֥ȤΥ饹. ϰϥ֥Ȥ`..'黻Ҥˤä,
+ŪˤϰʲΤ褦ʻȤ򤹤
+
+ for i in 1..5
+ ...
+ end
+
+, ξϰʲ®.
+
+ do 1.upto(5)
+ ...
+ end
+
+ϰϥ֥Ȥ`..'黻ҤξդComparableޤ९饹Υ
+󥹥󥹤ǤвǤ⹽ʤ. ϰϤϻȽޤळȤդ
+.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ self =~ other
+
+ selfotherƱ饹Фϰϥ֥Ȥ, ϰ
+ other(start <= other <= end),֤. caseʸ
+ ϰϻꤹǤ. 㤨
+
+ case i
+ when 1, 3..5
+ ...
+ end case
+
+ Τ褦ʥɤ񤯤ȤǤ.
+
+ each
+
+ ϰ¸ߤ륪֥ȤͿ륤ƥ졼. forʸΤ
+ Ѥ.
+
+ end
+
+ ϰϤν֤
+
+ start
+
+ ϰϤλ֤.
+
+*** Socket(饹)
+
+SuperClass: BasicSocket
+
+åȤΤΤФ륷ƥॳ٥Υ󶡤륯饹.
+PerlΥåȤФ륢Ʊ٥εǽ󶡤Ƥ. Υ
+Ǥϥåȥɥ쥹pack줿ʸ, ꤹ. UDPåȤϤ
+Υ饹ȤäѤ.
+
+Methods:
+
+ accept
+
+ ³դ, ³Ф륽åȤ֤.
+ accept(2)򻲾.
+
+ bind(addr)
+
+ bind(2)ƱƯ򤹤. addrpack줿åȥɥ쥹¤
+ ΤǤ.
+
+ connect(addr)
+
+ connect(2)ƱƯ򤹤. addrpack줿åȥɥ쥹
+ ¤ΤǤ.
+
+ listen(backlog)
+
+ listen(2)ƱƯ򤹤.
+
+ recv(len, flags)
+ send(mesg, flags[, to])
+ sysread(len)
+
+Single Methods:
+
+ open(domain, type, protocol)
+ new(domain, type, protocol)
+
+ åȤ. domain, type, protocolϥ󥯥롼
+ եƤͤǻꤹ. domaintype˴ؤ
+ , ʸǻǤ뤬, ٤Ƥ򥫥СƤݾڤϤʤ.
+
+ socketpair(domain, type, protocol)
+
+ åȤΥڥ, 2ǤȤ֤. λ
+ openƱǤ.
+
+*** Regexp(饹)
+
+ɽΥ饹. ɽΥƥ/.../Ȥɽ, ưŪ
+뤿ˤ
+
+ Regexp.compile(ʸ)
+
+Ȥ. , String饹`=~'ϤȤ¿Υ᥽åɤɽ
+ؤʸͿ줿ˤŪɽΤ,
+Ȥ󤷤ȻפʳŪȻפȤϾʤ
+.
+
+SuperClass: Object
+
+Methods:
+
+
+ self =~ string
+
+ ɽʸ˥ޥå, ޥå֤֤. ޥå
+ ʤnil֤.
+
+ ~ self
+
+ $_ =~ selfפƱ.
+
+Single Methods:
+
+ complie(string)
+
+ ʸɽѴ֥Ȥ֤.
+
+*** 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)
+
+ åȤΰʹߤ³λ. how0Ǥ, ʹߤμ,
+ how1Ǥ, ʹߤݤ. how2λˤ,
+ ʹߤ, Ȥ˵ݤ.
+
+*** String(饹)
+
+ʸ󥯥饹. Rubyʸϥ̥륿ߥ͡ȤǤϤʤΤ, Хʥǡ
+ⰷ. äƤɤ餫ȤñʤʸȤХǤ.
+λۤ˴Ť, ɽ˴ؤ᥽åɰʳ2byteϤʸռ
+Ƥʤ. ϺԤμȴǤϤʤտŪˤƤΤǤ(
+Ƥ).
+
+SuperClass: Object
+
+Included Modules: Comparable, Enumerable
+
+Methods:
+
+ self + other
+
+ ʸϢ. Ϣ뤵줿ʸ֤.
+
+ self * times
+
+ ʸη֤. 㤨
+
+ "x" * 4 == "xxxx"
+
+ Ǥ.
+
+ self == other
+ self > other
+
+ ʸ. ƥѿ$=nilǤʤˤʸʸ
+ ̤ӤԤʤ.
+
+ self =~ other
+
+ ʸΥޥå. otherɽʸ. otherʸξ
+ ưŪɽѴ. ޥåϥޥå,
+ ʤänil֤.
+
+ ~ self
+
+ $_ =~ selfפƱ.
+
+ self[nth]
+ self[beg..end]
+ self[beg,len]
+
+ ƤμФ. 1ܤηǤnthХܤΥǡFixnumȤ
+ ֤. 2ܤηǤbegХܤendХܤޤǤʬʸ
+ ֤(ξüޤ). 3ܤηǤbegХܤlenХ
+ ʬʬʸ֤.
+
+ self[nth] = val
+ self[beg..end] = val
+ self[beg,len] = val
+
+ Ƥι. 1ܤηǤnthХܤΥǡval ()
+ . 2ܤηbegХܤendХܤޤǤʬʸ
+ valȤͿ줿ʸ֤. 3ܤηbeg
+ ܤlenХʬʬʸvalȤͿ줿ʸ
+ ֤.
+
+ chop
+
+ ʸκǸΥХȤڤȤ.
+
+ crypt(salt)
+
+ crypt(3)ѤưŹ沽ʸ֤. salt2ХȰʾĹ
+ ǤդʸǤ.
+
+ delete(str)
+
+ ʸΤ, str˴ޤޤʸ. ʸλtr
+ ƱͤǤ, a-babޤǤϰϤ, Ƭ^ʸ(
+ ޤƤʤΤ)̣.
+
+ each
+
+ ʸ󤫤1Ԥɤ߹Ǥ륤ƥ졼.
+
+ each_byte
+
+ ʸΤ줾ΥХȤˤĤƷ֤ƥ졼.
+
+ gsub(pattern, replace)
+
+ ʸpattern˥ޥåʬreplace֤.
+ ʸreplace&\0ϥޥåʸ, \1..\9nܤ
+ ̤Ƥ֤.
+
+ hex
+
+ ʸ16ʿɽʸȲᤷ, Ѵ.
+
+ index(substr[, pos])
+
+ substrǽ˽и֤֤. posͿȤΰ֤鸡
+ 򳫻Ϥ. Ĥʤˤnil֤.
+
+ intern
+
+ ʸ˰դб֤. ʸϥʥʸޤǤϤ
+ ʤ.
+
+ lcfirst
+
+ ʸκǽʸ(줬ե٥åȤǤ), ʸ
+ Ѵ.
+
+ length
+
+ ʸĹ(Хȿ)֤.
+
+ next
+
+ ּΡʸ֤. ʸȤϿϿȤ,ʸ
+ ʸȤä, 夬νԤʤ줿ԤǤ.
+
+ "aa".next == "ab"
+ "99".next == "100"
+ "a9".next == "b0"
+
+ Υ᥽åɤRange:eachѤƤΤ, ʲΤ褦ʽ
+ ǽǤ.
+
+ for i in "a" .. "ba"
+ print(i, "\n");
+ end
+
+ a, b, c, .. aa, .. az, baޤǤƹԤ˽Ϥ.
+
+
+ դʤФʤΤ, νλȽ羮طǤϤʤ
+ `=='ȽꤵƤ뤿, `..'黻Ҥκդͤ³ʸ˱
+ դʸ󤬴ޤޤƤʤ, ʲΤ褦ʾ̵¥롼פ
+ ٤äƤޤ.
+
+ for i in "0" .. "1a"
+ print(i, "\n");
+ end
+
+ ԤϤ񤯤ȤˤäǤƨƤ褦ȤƤȹͤ
+ ⤤뤫⤷ʤ. ο¬.
+
+ oct
+
+ ʸ8ʿɽʸȲᤷ, Ѵ. 8ʿ
+ /[0-7]+/Ǥ, ƤϤޤʤʸФƤ
+ 0֤. perlȤϰäʸ0xϤޤäƤ뤫Ȥä
+ 16ʿȸʤƤ줿Ϥʤ. 8ʿǤϤʤΤ0
+ ֤.
+
+ reverse
+
+ ʸγƥХȤս¤٤ʸ֤. ʸ2ХȤ
+ ʸޤǤƤ⤪ʤ˥Хñ̤ȿž.
+ split2Хʸ򤹤Τ, 2Хʸޤʸʸ
+ ñ̤ȿžˤ
+
+ "ʸ".split(//).reverse.join("")
+
+ ȤФ褤.
+
+ rindex(substr[, pos])
+
+ ʸsubstrǸ˽и֤֤. posͿȤΰ
+ Ǹλ. Ĥʤˤnil֤. indexȤ
+ 1)ʸ鸡. 2)substrȤɽդ
+ ʤ. 2Ǥ.
+
+ split([sep[, limit]])
+
+ ʸsepǻꤵ줿ѥˤä, եɤʬ䤹.
+ sepά줿ΥǥեȤϥƥѿ`$;'ͤѤ
+ . limitꤵ줿ˤϺlimitĤΥեɤʬ䤹.
+ split()ʬ䤵줿ʸޤ֤. sepǻꤵ줿ѥ
+ 󤬶ʸȥޥåʸ1ʸĤʬ䤵.
+
+ squeeze([str])
+
+ ʸΤ, str˴ޤޤʸϢ³Ƥ, ʸ˰
+ ̤. strά줿, ٤ƤʸоݤȤ. ʸ
+ λtrƱͤǤ, `a-b'abޤǤϰϤ, Ƭ`^'
+ ʸ(ޤޤƤʤΤ)̣.
+
+ strip
+
+ ʸζ.
+
+ sub(pattern, replace)
+
+ ʸpattern˥ޥåʬreplace֤. ִʸ
+ replace&\0ϥޥåʸ, \1..\9nܤγ
+ Ƥ֤. sub()gsub()Ȱۤʤ, ǽΥޥå
+ ִ.
+
+ to_f
+
+ ʸFloatѴ.
+
+ to_i
+
+ ʸ10ʿɽʸȲᤷ, Ѵ.
+
+ toupper
+ uc
+
+ ʸΥե٥åȤʸ֤. ʸ
+ 뤳Ȥ. tr("a-z", "A-Z")꾯®.
+
+ tolower
+ lc
+
+ ʸΥե٥åȤƾʸ֤. ʸ
+ 뤳Ȥ. tr("A-Z", "a-z")꾯®.
+
+ tr(search, replace)
+
+ ʸsearchʸ˴ޤޤʸ¸ߤ, replaceʸ
+ бʸ֤. replaceʸ󤬾ά줿
+ ϶ʸͿ줿ȸʤ. replaceʸsearchʸ
+ ûreplaceʸκǸʸ֤Ƥȸ
+ . searchʸûˤбʸΤʤreplace
+ ñ̵뤵(BSDtrư).
+
+ searchʸ, replaceʸ`a-b'Ȥ줿,
+ abޤǤϰϤʸASCIIξǻꤷȤˤʤ.
+ , searchʸκǽʸ`^'Ǥ, ³ʸ*ޤ
+ ʤ*ʸִоݤˤʤ.
+
+ tr(1)εǽΤ, ʸ뵡ǽ, Ϣ³ʸ򰵽̤
+ ǽ̤Υ᥽åɤʬ䤵Ƥ. εǽˤĤƤ
+ delete, squeeze򻲾ȤΤ.
+
+ ؤΤ, str.tr(src,repl).squeeze(repl)᥽å
+ tr_s(src,repl) 󶡤Ƥ.
+
+ ucfirst
+
+ ʸκǽʸ(줬ե٥åȤǤ), ʸ
+ Ѵ.
+
+ unpack(template)
+
+ ʸtemplateʸˤäƥѥå, Ǥ
+ ޤ֤. templateʸArray饹pack᥽åɤȤ
+ ƱͤǤ.
+
+ a ASCIIʸ(³nullʸ䥹ڡĤ)
+ A ASCIIʸ(³nullʸ䥹ڡ)
+ b ӥåȥȥ(̥ӥåȤ̥ӥå)
+ B ӥåȥȥ(̥ӥåȤ鲼̥ӥå)
+ h 16ʸ(̥˥֥뤬)
+ H 16ʸ(̥˥֥뤬)
+ c char
+ C unsigned char
+ s sort
+ S unsigned sort
+ i int
+ I unsigned int
+ l long
+ L unsigned int
+ n ͥåȥХȥshort
+ N ͥåȥХȥlong
+ f ñư(¸)
+ d ư(¸)
+ x 1ХɤФ
+ X 1Хȸ
+ @ а֤ؤΰư
+
+Single Methods:
+
+ new(string)
+
+ stringƱƤĿʸ֤.
+
+*** Struct(饹)
+
+¤Υ饹. ʣΥǡޤȤѤ(: Time::times).
+ǡޤȤˤ󥯥饹Ѥ뤳Ȥ⤢뤬(: select),
+¤ΤȤ٤ϰʲΤ褦ʾǤ.
+
+ (1) Ǥο¿
+
+ ʹ֤٤ưפ˰복ǰο7ĤޤǤǤȤ⤬.
+ β˽, ǡ4İʾξѤ,
+ *2(ĤޤꥪեåȤȤΰ̣)7ۤ. ä, Τ褦
+ ˤϹ¤ΤȤä򤷤䤹Ȼפ.
+
+ (2) Ʊ̤ʤ
+
+ ¤Τ㴳Ȥ⤤Τ, ®٤ˤʤ
+ ˤ, (Ʊ̤ʤ)Ϲ¤ΤλѤŬڤǤʤ
+ ǽ.
+
+ƹ¤Τˤϥ̾Ʊ̾ΰΤʤ᥽åɤƤ.
+
+ܥɥ, ¤Τɽ뤿ˤϰʲηȤ.
+
+ struct ¤̾
+ ...
+ end
+
+, ץǤηǹ¤Τ櫓ǤϤʤ.
+
+SuperClass: Object
+
+Included Modules: Enumerable
+
+Methods:
+
+ self[idx]
+
+ idxλidxܤǤ֤. idxʸλidxƱ̾
+ ΥФǤ֤.
+
+ values
+
+ ¤ΤΥФͤǤ˻֤. 㤨аʲΥɤ
+ ʬpasswdȥϤ뤳ȤǤ.
+
+ print(Etc.getpwuid().values.join(":"), "\n")
+
+ νϤ`grep "$USER" /etc/passwd'νϤ;ʬʥեɤ
+ Ĥ(ƥˤäưۤʤ)ʳƱǤ.
+
+Single Methods:
+
+ new(name, member::value...)
+
+ nameȤ̾ŤΤ. memberϹ¤ΤΥФ
+ ɽʸǤ, valueϤͤǤ. 줿¤Τϥ
+ Фǻꤵ줿̾ðۥ᥽åɤƤ, Υ᥽å
+ ˤäƥФƤ뤳ȤǤ.
+
+*** 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)ϿƤ륵
+ ̾ݡֹǤ.
+
+*** Time(饹)
+
+֤ɽ饹. 羮ӤʤɤǤ. Time.nowǸߤλ֤뤳
+Ǥ. ޤեΥॹפ᥽åɤͤ⤳Υ
+Υ󥹥󥹤Ǥ.
+
+SuperClass: Object
+
+Included Modules: Comparable
+
+Methods:
+
+ self == other
+ self > other
+
+ otherTimeΥ󥹥󥹤. Ϳ줿
+ ˤ1970ǯ 1 1 00:00:00 GMTÿǤ
+ ƻȤӤԤʤ.
+
+ asctime
+ ctime
+ to_s
+
+ date(1)ʸѴ.
+
+ gmtime
+
+ ॾνԤʤʤGMTǤλ. Υ᥽å
+ ȤäTime饹Υ󥹥󥹤, ʸλѴGMT
+ Ԥʤ. gmtimeϼʬȤ֤.
+
+ ɥλɽˤ
+
+ print(Time.now.gmtime, "\n")
+
+ ȤФ褤.
+
+ localtime
+
+ ॾνԤʤä(ǥե). localtime
+ ⼫ʬȤ֤.
+
+ to_i
+ tv_sec
+
+ 1970ǯ 1 1 00:00:00 GMTޤǤÿ֤.
+ secondʬǤ⤢.
+
+ sec
+ min
+ hour
+ mday
+ year
+ wday
+ yday
+ zone
+ isdst
+
+ ŪݻƤtm¤ΤƤ֤. zoneʳ֤.
+ zoneϥॾɽʸ֤. (cf localtime(3))
+
+ strftime(format)
+
+ formatʸ˽äʸѴ̤֤. format
+ ʸȤƻǤΤ ʲ̤Ǥ.
+
+ %A ̾(Sunday, Monday,...)
+ %a ξά̾(Sun, Mon,...)
+ %B ̾(January, February,...)
+ %b ξά̾(Jan, Feb,...)
+ %c ɽ(cf ctime(3))
+ %d ʿǤ(01-31)
+ %H 24λ(00-23)
+ %I 12λ(01-12)
+ %j ǯ̻(001-366)
+ %M ʬ(00-59)
+ %m ɽ(01-12)
+ %p ޤϸ(AM,PM)
+ %S (00-61)
+ %U ɽ. ǽ1
+ Ϥޤ(00-53)
+ %W ɽ. ǽη1
+ Ϥޤ(00-53)
+ %w ɽ. 0(0-6)
+ %X (: 15:01:06)
+ %x (: Fri Jan 14 1994)
+ %Y ɽ
+ %y β2(00-99)
+ %Z ॾ
+ %% %
+
+ usec
+ tv_usec
+
+ micro secondʬ֤.
+
+Single Methods:
+
+ now
+
+ ߤλɽTime饹Υ󥹥󥹤.
+
+ at(time)
+
+ timeƱɽTime饹Υ󥹥󥹤. time
+ Time饹Υ󥹥󥹤뤤Ͽ(/ư)Ǥ,
+ ξ1970ǯ 1 1 00:00:00 GMTÿǤȤƻ
+ ׻.
+
+ times
+
+ ߤΥץȤλҥץ񤷤桼/ƥCPU
+ ѻ¤ΤȤ֤(Struct򻲾).
+
+ struct tms
+ utime # ץΥ桼
+ stime # ץΥƥ
+ cutime # ҥץΥ桼
+ cstime # ҥץΥƥ
+ end
+
+ ֤ñ̤äǤ, ưͿ. ܺ٤
+ times(3C)򻲾ȤΤ.
+
+*** 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/sprintf.c b/sprintf.c
new file mode 100644
index 0000000000..e63798be5e
--- /dev/null
+++ b/sprintf.c
@@ -0,0 +1,431 @@
+/************************************************
+
+ sprintf.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:40 $
+ created at: Fri Oct 15 10:39:26 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include <ctype.h>
+
+static void fmt_setup();
+
+VALUE
+Fsprintf(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ struct RString *fmt;
+ char *buf, *p, *end;
+ int i, blen, bsiz;
+ VALUE result;
+
+#define FNONE 0
+#define FSHARP 1
+#define FMINUS 2
+#define FPLUS 4
+#define FZERO 8
+#define FWIDTH 16
+#define FPREC 32
+
+ int width, prec, flags = FNONE;
+ VALUE str;
+
+ GC_LINK;
+ GC_PRO2(str);
+
+#define CHECK(l) {\
+ while (blen + (l) >= bsiz) {\
+ REALLOC_N(buf, char, bsiz*2);\
+ bsiz*=2;\
+ }\
+}
+
+#define PUSH(s, l) { \
+ CHECK(l);\
+ memmove(&buf[blen], s, l);\
+ blen += (l);\
+}
+
+#define GETARG() \
+ ((argc == 1)?Fail("too few argument."):(argc--, argv++, argv[0]))
+
+ fmt = (struct RString*)GETARG();
+ Check_Type(fmt, T_STRING);
+
+ blen = 0;
+ bsiz = 120;
+ buf = ALLOC_N(char, bsiz);
+ end = fmt->ptr + fmt->len;
+
+ for (p = fmt->ptr; p < end; p++) {
+ char *t;
+
+ for (t = p; t < end && *t != '%'; t++) ;
+ CHECK(t - p);
+ PUSH(p, t - p);
+ if (t >= end) {
+ /* end of fmt string */
+ goto sprint_exit;
+ }
+ p = t + 1; /* skip `%' */
+
+ retry:
+ switch (*p) {
+ default:
+ if (isprint(*p))
+ Fail("malformed format string - %%%c", *p);
+ else
+ Fail("malformed format string");
+ break;
+
+ case ' ':
+ p++;
+ goto retry;
+
+ case '#':
+ flags |= FSHARP;
+ p++;
+ goto retry;
+
+ case '+':
+ flags |= FPLUS;
+ p++;
+ goto retry;
+
+ case '-':
+ flags |= FMINUS;
+ p++;
+ goto retry;
+
+ case '0':
+ flags |= FZERO;
+ p++;
+ goto retry;
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ flags |= FWIDTH;
+ width = 0;
+ for (p; p < end && isdigit(*p); p++) {
+ width = 10 * width + (*p - '0');
+ }
+ if (p >= end) {
+ Fail("malformed format string - %%[0-9]");
+ }
+ goto retry;
+
+ case '*':
+ if (flags & FWIDTH) {
+ Fail("width given twice");
+ }
+
+ flags |= FWIDTH;
+ width = GETARG();
+ width = NUM2INT(width);
+ if (width < 0) {
+ flags |= FMINUS;
+ width = - width;
+ }
+ p++;
+ goto retry;
+
+ case '.':
+ if (flags & FPREC) {
+ Fail("precision given twice");
+ }
+
+ prec = 0;
+ p++;
+ if (*p == '0') flags |= FZERO;
+ if (*p == '*') {
+ prec = GETARG();
+ prec = NUM2INT(prec);
+ if (prec > 0)
+ flags |= FPREC;
+ p++;
+ goto retry;
+ }
+
+ for (p; p < end && isdigit(*p); p++) {
+ prec = 10 * prec + (*p - '0');
+ }
+ if (p >= end) {
+ Fail("malformed format string - %%.[0-9]");
+ }
+ if (prec > 0)
+ flags |= FPREC;
+ goto retry;
+
+ case '%':
+ PUSH("%", 1);
+ break;
+
+ case 'c':
+ {
+ VALUE val = GETARG();
+ char c;
+
+ c = NUM2INT(val) & 0xff;
+ PUSH(&c, 1);
+ }
+ break;
+
+ case 's':
+ {
+ VALUE arg = GETARG();
+ int len;
+ char fbuf[32];
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+ str = obj_as_string(arg);
+ fmt_setup(fbuf, 's', flags, width, prec);
+ if (flags&FPREC) {
+ CHECK(prec);
+ }
+ else if ((flags&FWIDTH) && width > RSTRING(str)->len) {
+ CHECK(width);
+ }
+ else {
+ CHECK(RSTRING(str)->len);
+ }
+ sprintf(&buf[blen], fbuf, RSTRING(str)->ptr);
+ blen += strlen(&buf[blen]);
+ }
+ break;
+
+ case 'b':
+ case 'B':
+ case 'o':
+ case 'x':
+ {
+ VALUE val = GETARG();
+ char fbuf[32], *s, *t, *end;
+ int v, base;
+
+ GC_LINK;
+ GC_PRO(val);
+ 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);
+ break;
+ case T_STRING:
+ val = str2inum(RSTRING(val)->ptr, 0);
+ goto bin_retry;
+ case T_BIGNUM:
+ val = Fbig_clone(val);
+ break;
+ default:
+ WrongType(val, T_FIXNUM);
+ 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);
+ val = big2str(val, base);
+ fmt_setup(fbuf, 's', flags, width, prec);
+
+ s = t = RSTRING(val)->ptr;
+ end = s + RSTRING(val)->len;
+ if (*s == '-' && *p != 'B') {
+ s++; t++;
+ if (base == 16) {
+ while (t<end && *t == 'f') t++;
+ if (*t == 'e') {
+ *t = '2';
+ }
+ else if (*t == 'd') {
+ *t = '5';
+ }
+ else if (*t == 'c') {
+ *t = '4';
+ }
+ else if (*t < '8') {
+ *--t = '1';
+ }
+ }
+ if (base == 8) {
+ while (t<end && *t == '7') t++;
+ if (*t == '6') {
+ *t = '2';
+ }
+ else if (*t < '4') {
+ *--t = '1';
+ }
+ }
+ if (base == 2) {
+ while (t<end && *t == '1') t++;
+ t--;
+ }
+ while (t<end) *s++ = *t++;
+ *s = '\0';
+ }
+ s = RSTRING(val)->ptr;
+ if (flags&FPREC) {
+ CHECK(prec);
+ }
+ else if ((flags&FWIDTH) && width > end - s) {
+ CHECK(width);
+ }
+ else {
+ CHECK(s - end);
+ }
+ sprintf(&buf[blen], fbuf, s);
+ blen += strlen(&buf[blen]);
+ obj_free(val);
+ GC_UNLINK;
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ case 'O':
+ case 'X':
+ {
+ VALUE val = GETARG();
+ char fbuf[32], c = *p;
+ int bignum = 0, base;
+ int v;
+
+ if (c == 'D') c = 'd';
+ GC_LINK;
+ GC_PRO(val);
+ int_retry:
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ v = FIX2INT(val);
+ break;
+ case T_FLOAT:
+ v = RFLOAT(val)->value;
+ break;
+ case T_STRING:
+ val = str2inum(RSTRING(val)->ptr, 0);
+ goto int_retry;
+ case T_BIGNUM:
+ if (c == 'd') base = 10;
+ else if (c == 'X') base = 16;
+ else if (c == 'O') base = 8;
+ val = big2str(val, base);
+ bignum = 1;
+ break;
+ default:
+ WrongType(val, T_FIXNUM);
+ break;
+ }
+
+ if (bignum) {
+ fmt_setup(fbuf, 's', flags, width, prec);
+
+ if (flags&FPREC) {
+ CHECK(prec);
+ }
+ else if ((flags&FWIDTH) && width > RSTRING(val)->len) {
+ CHECK(width);
+ }
+ else {
+ CHECK(RSTRING(val)->len);
+ }
+ sprintf(&buf[blen], fbuf, RSTRING(val)->ptr);
+ blen += strlen(&buf[blen]);
+ }
+ else {
+ fmt_setup(fbuf, c, flags, width, prec);
+
+ CHECK(11);
+ if (v < 0 && (c == 'X' || c == 'O')) {
+ v = -v;
+ PUSH("-", 1);
+ }
+ sprintf(&buf[blen], fbuf, v);
+ blen += strlen(&buf[blen]);
+ }
+ GC_UNLINK;
+ }
+ break;
+
+ case 'f':
+ case 'g':
+ case 'e':
+ {
+ VALUE val = GETARG();
+ double fval;
+ char fbuf[32];
+ double big2dbl();
+ double atof();
+
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ fval = FIX2INT(val);
+ break;
+ case T_FLOAT:
+ fval = RFLOAT(val)->value;
+ break;
+ case T_BIGNUM:
+ fval = big2dbl(val);
+ break;
+ case T_STRING:
+ fval = atof(RSTRING(val)->ptr);
+ break;
+ default:
+ WrongType(val, T_FLOAT);
+ break;
+ }
+
+ fmt_setup(fbuf, *p, flags, width, prec);
+
+ CHECK(22);
+ sprintf(&buf[blen], fbuf, fval);
+ blen += strlen(&buf[blen]);
+ }
+ break;
+ }
+ flags = FNONE;
+ }
+
+ sprint_exit:
+ GC_UNLINK;
+ if (verbose && argc > 1) {
+ Fail("too many argument for format string");
+ }
+ result = str_new(buf, blen);
+ free(buf);
+
+ return result;
+}
+
+static void
+fmt_setup(buf, c, flags, width, prec)
+ char *buf, c;
+ int flags, width, prec;
+{
+ *buf++ = '%';
+ if (flags & FSHARP) *buf++ = '#';
+ if (flags & FPLUS) *buf++ = '+';
+ if (flags & FMINUS) *buf++ = '-';
+ if (flags & FZERO) *buf++ = '0';
+
+ if (flags & FWIDTH) {
+ sprintf(buf, "%d", width);
+ buf += strlen(buf);
+ }
+
+ if (flags & FPREC) {
+ sprintf(buf, ".%d", prec);
+ buf += strlen(buf);
+ }
+
+ *buf++ = c;
+ *buf = '\0';
+}
diff --git a/st.c b/st.c
new file mode 100644
index 0000000000..fcae526853
--- /dev/null
+++ b/st.c
@@ -0,0 +1,365 @@
+/* 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: /work/cvsroot/ruby/st.c,v 1.2 1994/06/27 15:48:41 matz Exp $";
+#endif
+
+#include <stdio.h>
+#include "st.h"
+
+extern void *xmalloc();
+static 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))
+
+ /*
+ * DEFAULT_MAX_DENSITY is the default for the largest we allow the
+ * average number of items per bin before increasing the number of
+ * bins
+ *
+ * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins
+ * allocated initially
+ *
+ * DEFAULT_GROW_FACTOR is the amount the hash table is expanded after
+ * the density has reached max_density
+ */
+
+#define EQUAL(func, x, y) \
+ ((func == ST_NUMCMP) ? ((x) == (y)) : ((*func)((x), (y)) == 0))
+
+/*#define do_hash(key, table) (*table->hash)(key, table->num_bins)*/
+
+#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))
+
+st_table *st_init_table_with_params(compare, hash, size, density, grow_factor,
+ reorder_flag)
+int (*compare)();
+int (*hash)();
+int size;
+int density;
+double grow_factor;
+int reorder_flag;
+{
+ st_table *tbl;
+
+ tbl = alloc(st_table);
+ tbl->compare = compare;
+ tbl->hash = hash;
+ tbl->num_entries = 0;
+ tbl->max_density = density;
+ tbl->grow_factor = grow_factor;
+ tbl->reorder_flag = reorder_flag;
+ tbl->num_bins = size;
+ tbl->bins =
+ (st_table_entry **) Calloc((unsigned)size, sizeof(st_table_entry *));
+ return tbl;
+}
+
+st_table *st_init_table(compare, hash)
+int (*compare)();
+int (*hash)();
+{
+ return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE,
+ ST_DEFAULT_MAX_DENSITY,
+ ST_DEFAULT_GROW_FACTOR,
+ ST_DEFAULT_REORDER_FLAG);
+}
+
+st_free_table(table)
+st_table *table;
+{
+ register st_table_entry *ptr, *next;
+ int i;
+
+ for(i = 0; i < table->num_bins ; i++) {
+ ptr = table->bins[i];
+ while (ptr != nil(st_table_entry)) {
+ next = ptr->next;
+ free((char *) ptr);
+ ptr = next;
+ }
+ }
+ 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 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;\
+ }\
+}
+
+st_lookup(table, key, value)
+st_table *table;
+register char *key;
+char **value;
+{
+ int hash_val;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash(key, table);
+
+ FIND_ENTRY(table, ptr, hash_val);
+
+ if (ptr == nil(st_table_entry)) {
+ return 0;
+ } else {
+ 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) {\
+ rehash(table);\
+ hash_val = do_hash(key,table);\
+ }\
+ \
+ tbl = alloc(st_table_entry);\
+ \
+ tbl->key = key;\
+ tbl->record = value;\
+ tbl->next = table->bins[hash_val];\
+ table->bins[hash_val] = tbl;\
+ table->num_entries++;\
+}
+
+st_insert(table, key, value)
+register st_table *table;
+register char *key;
+char *value;
+{
+ int hash_val;
+ st_table_entry *tbl;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash(key, table);
+
+ FIND_ENTRY(table, ptr, hash_val);
+
+ if (ptr == nil(st_table_entry)) {
+ ADD_DIRECT(table,key,value,hash_val,tbl);
+ return 0;
+ } else {
+ ptr->record = value;
+ return 1;
+ }
+}
+
+st_add_direct(table, key, value)
+st_table *table;
+char *key;
+char *value;
+{
+ int hash_val;
+ st_table_entry *tbl;
+
+ hash_val = do_hash(key, table);
+ ADD_DIRECT(table, key, value, hash_val, tbl);
+}
+
+st_find_or_add(table, key, slot)
+st_table *table;
+char *key;
+char ***slot;
+{
+ int hash_val;
+ st_table_entry *tbl, *ptr;
+
+ hash_val = do_hash(key, table);
+
+ 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;
+ return 0;
+ } else {
+ if (slot != nil(char **)) *slot = &ptr->record;
+ return 1;
+ }
+}
+
+static rehash(table)
+register st_table *table;
+{
+ register st_table_entry *ptr, *next, **old_bins = table->bins;
+ int i, old_num_bins = table->num_bins, hash_val;
+
+ table->num_bins = table->grow_factor*old_num_bins;
+
+ 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 *));
+
+ for(i = 0; i < old_num_bins ; i++) {
+ ptr = old_bins[i];
+ while (ptr != nil(st_table_entry)) {
+ next = ptr->next;
+ hash_val = do_hash(ptr->key, table);
+ ptr->next = table->bins[hash_val];
+ table->bins[hash_val] = ptr;
+ table->num_entries++;
+ ptr = next;
+ }
+ }
+ free((char *) old_bins);
+}
+
+st_table *st_copy(old_table)
+st_table *old_table;
+{
+ st_table *new_table;
+ st_table_entry *ptr, *tbl;
+ int i, num_bins = old_table->num_bins;
+
+ new_table = alloc(st_table);
+ if (new_table == nil(st_table)) {
+ return nil(st_table);
+ }
+
+ *new_table = *old_table;
+ 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);
+ return nil(st_table);
+ }
+
+ for(i = 0; i < num_bins ; i++) {
+ new_table->bins[i] = nil(st_table_entry);
+ ptr = old_table->bins[i];
+ 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);
+ return nil(st_table);
+ }
+ *tbl = *ptr;
+ tbl->next = new_table->bins[i];
+ new_table->bins[i] = tbl;
+ ptr = ptr->next;
+ }
+ }
+ return new_table;
+}
+
+st_delete(table, key, value)
+register st_table *table;
+register char **key;
+char **value;
+{
+ int hash_val;
+ st_table_entry *tmp;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash(*key, table);
+
+ ptr = table->bins[hash_val];
+
+ if (ptr == nil(st_table_entry)) {
+ if (value != nil(char *)) *value = nil(char);
+ return 0;
+ }
+
+ if (EQUAL(table->compare, *key, ptr->key)) {
+ table->bins[hash_val] = ptr->next;
+ table->num_entries--;
+ if (value != nil(char *)) *value = ptr->record;
+ *key = ptr->key;
+ free((char *) ptr);
+ return 1;
+ }
+
+ for(; ptr->next != nil(st_table_entry); ptr = ptr->next) {
+ if (EQUAL(table->compare, ptr->next->key, *key)) {
+ tmp = ptr->next;
+ ptr->next = ptr->next->next;
+ table->num_entries--;
+ if (value != nil(char *)) *value = tmp->record;
+ *key = tmp->key;
+ free((char *) tmp);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+st_foreach(table, func, arg)
+st_table *table;
+enum st_retval (*func)();
+char *arg;
+{
+ st_table_entry *ptr, *last, *tmp;
+ enum st_retval retval;
+ int i;
+
+ for(i = 0; i < table->num_bins; i++) {
+ last = nil(st_table_entry);
+ for(ptr = table->bins[i]; ptr != nil(st_table_entry);) {
+ retval = (*func)(ptr->key, ptr->record, arg);
+ switch (retval) {
+ case ST_CONTINUE:
+ last = ptr;
+ ptr = ptr->next;
+ break;
+ case ST_STOP:
+ return;
+ case ST_DELETE:
+ tmp = ptr;
+ if (last == nil(st_table_entry)) {
+ table->bins[i] = ptr->next;
+ } else {
+ last->next = ptr->next;
+ }
+ ptr = ptr->next;
+ free((char *) tmp);
+ }
+ }
+ }
+}
+
+st_strhash(string, modulus)
+register char *string;
+int modulus;
+{
+ register int val = 0;
+ register int c;
+
+ while ((c = *string++) != '\0') {
+ val = val*997 + c;
+ }
+
+ return ((val < 0) ? -val : val)%modulus;
+}
diff --git a/st.h b/st.h
new file mode 100644
index 0000000000..04ac5c1088
--- /dev/null
+++ b/st.h
@@ -0,0 +1,57 @@
+/* This is a general purpose hash table package written by Peter Moore @ UCB. */
+
+/* @(#) st.h 5.1 89/12/14 */
+
+#ifndef ST_INCLUDED
+
+#define ST_INCLUDED
+
+typedef struct st_table_entry st_table_entry;
+
+struct st_table_entry {
+ char *key;
+ char *record;
+ st_table_entry *next;
+};
+
+typedef struct st_table st_table;
+
+struct st_table {
+ int (*compare)();
+ int (*hash)();
+ int num_bins;
+ int num_entries;
+ int max_density;
+ int reorder_flag;
+ double grow_factor;
+ st_table_entry **bins;
+};
+
+#define st_is_member(table,key) st_lookup(table,key,(char **) 0)
+
+enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE};
+
+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_GROW_FACTOR 2.0
+#define ST_DEFAULT_REORDER_FLAG 0
+
+int st_strhash();
+
+#endif ST_INCLUDED
diff --git a/string.c b/string.c
new file mode 100644
index 0000000000..57198e4e87
--- /dev/null
+++ b/string.c
@@ -0,0 +1,1552 @@
+/************************************************
+
+ string.c -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:44 $
+ created at: Mon Aug 9 17:12:58 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "re.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+VALUE C_String;
+
+#define STRLEN(s) RSTRING(s)->len
+
+VALUE
+str_new(ptr, len)
+ char *ptr;
+ UINT len;
+{
+ NEWOBJ(str, struct RString);
+ OBJSETUP(str, C_String, T_STRING);
+
+ str->len = len;
+ str->ptr = ALLOC_N(char,len+1);
+ if (ptr) {
+ memmove(str->ptr, ptr, len);
+ }
+ str->ptr[len] = '\0';
+ str->orig = Qnil;
+ return (VALUE)str;
+}
+
+VALUE
+str_new2(ptr)
+ char *ptr;
+{
+ return str_new(ptr, strlen(ptr));
+}
+
+VALUE
+str_new3(str)
+ struct RString *str;
+{
+ NEWOBJ(str2, struct RString);
+ OBJSETUP(str2, C_String, T_STRING);
+
+ str2->len = str->len;
+ str2->ptr = str->ptr;
+ str2->orig = str;
+
+ return (VALUE)str2;
+}
+
+#define as_str(str) (struct RString*)obj_as_string(str)
+
+static ID pr_str = Qnil;
+
+VALUE
+obj_as_string(obj)
+ VALUE obj;
+{
+ VALUE str;
+
+ if (TYPE(obj) == T_STRING) {
+ return obj;
+ }
+ str = rb_funcall(obj, pr_str, 0);
+ if (TYPE(str) != T_STRING)
+ return Fkrn_to_s(obj);
+ return str;
+}
+
+VALUE
+Fstr_clone(str)
+ struct RString *str;
+{
+ VALUE obj;
+
+ if (str->orig)
+ obj = str_new3(str->orig);
+ else
+ obj = str_new(str->ptr, str->len);
+ CLONESETUP(obj, str);
+ return obj;
+}
+
+static VALUE
+Fstr_new(class, str)
+ VALUE class;
+ struct RString *str;
+{
+ Check_Type(str, T_STRING);
+ {
+ NEWOBJ(str2, struct RString);
+ OBJSETUP(str2, class, T_STRING);
+
+ str2->len = str->len;
+ str2->ptr = ALLOC_N(char, str->len+1);
+ if (str2->ptr) {
+ memmove(str2->ptr, str->ptr, str->len);
+ }
+ str2->ptr[str->len] = '\0';
+ str2->orig = Qnil;
+ return (VALUE)str2;
+ }
+}
+
+static VALUE
+Fstr_length(str)
+ struct RString *str;
+{
+ return INT2FIX(str->len);
+}
+
+VALUE
+Fstr_plus(str1, str2)
+ struct RString *str1, *str2;
+{
+ struct RString *str3;
+
+ GC_LINK;
+ GC_PRO3(str2, as_str(str2));
+ str3 = (struct RString*)str_new(0, str1->len+str2->len);
+ memcpy(str3->ptr, str1->ptr, str1->len);
+ memcpy(str3->ptr+str1->len, str2->ptr, str2->len);
+ str3->ptr[str3->len] = '\0';
+ GC_UNLINK;
+
+ return (VALUE)str3;
+}
+
+VALUE
+Fstr_times(str, times)
+ struct RString *str;
+ VALUE times;
+{
+ struct RString *str2;
+ int i;
+
+ times = NUM2INT(times);
+
+ str2 = (struct RString*)str_new(0, str->len*times);
+ for (i=0; i<times; i++) {
+ memmove(str2->ptr+(i*str->len), str->ptr, str->len);
+ }
+ str2->ptr[str2->len] = '\0';
+
+ return (VALUE)str2;
+}
+
+extern VALUE C_Range;
+
+static VALUE
+Fstr_dot2(left, right)
+ VALUE left, right;
+{
+ extern VALUE C_Range;
+ VALUE str;
+
+ Check_Type(right, T_STRING);
+ str = range_new(C_Range, left, right);
+ return str;
+}
+
+VALUE
+str_substr(str, start, len)
+ struct RString *str;
+ int start, len;
+{
+ struct RString *str2;
+
+ if (start < 0) {
+ start = str->len + start;
+ }
+ if (str->len <= start) {
+ Fail("index %d out of range [0..%d]", start, str->len-1);
+ }
+ if (len < 0) {
+ Fail("Negative length %d", len);
+ }
+
+ str2 = (struct RString*)str_new(str->ptr+start, len);
+
+ return (VALUE)str2;
+}
+
+VALUE
+str_subseq(str, beg, end)
+ struct RString *str;
+ int beg, end;
+{
+ int len;
+
+ 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 (beg > end) {
+ int tmp;
+
+ if (verbose) {
+ Warning("start %d is bigger than end %d", beg, end);
+ }
+ tmp = beg; beg = end; end = tmp;
+ }
+
+ if (beg >= str->len) {
+ return str_new(0, 0);
+ }
+ if (str->len < end) {
+ end = str->len;
+ }
+
+ len = end - beg + 1;
+ if (len < 0) {
+ Fail("end %d too small(size %d)", end, str->len);
+ }
+
+ return str_substr(str, beg, len);
+}
+
+extern VALUE ignorecase;
+
+void
+str_modify(str)
+ struct RString *str;
+{
+ if (str->orig == Qnil) return;
+ str->ptr = ALLOC_N(char, str->len+1);
+ if (str->ptr) {
+ memcpy(str->ptr, str->orig->ptr, str->len+1);
+ }
+ str->orig = Qnil;
+}
+
+VALUE
+str_grow(str, len)
+ struct RString *str;
+ UINT len;
+{
+ str_modify(str);
+ if (len > 0) {
+ REALLOC_N(str->ptr, char, len + 1);
+ str->len = len;
+ str->ptr[len] = '\0'; /* sentinel */
+ }
+ return (VALUE)str;
+}
+
+VALUE
+str_cat(str, ptr, len)
+ struct RString *str;
+ char *ptr;
+ UINT len;
+{
+ str_modify(str);
+
+ if (len > 0) {
+ REALLOC_N(str->ptr, char, str->len + len + 1);
+ if (ptr)
+ memmove(str->ptr + str->len, ptr, len);
+ str->len += len;
+ str->ptr[str->len] = '\0'; /* sentinel */
+ }
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_concat(str1, str2)
+ struct RString *str1, *str2;
+{
+ str2 = as_str(str2);
+ str_cat(str1, str2->ptr, str2->len);
+ return (VALUE)str1;
+}
+
+static char
+str_next(s)
+ char *s;
+{
+ char c = *s;
+
+ /* control code */
+ if (c < ' ') return 0;
+
+ /* numerics */
+ if ('0' <= c && c < '9') (*s)++;
+ else if (c == '9') {
+ *s = '0';
+ return '1';
+ }
+ /* small alphabets */
+ else if ('a' <= c && c < 'z') (*s)++;
+ else if (c == 'z') {
+ return *s = 'a';
+ }
+ /* capital alphabets */
+ else if ('A' <= c && c < 'Z') (*s)++;
+ else if (c == 'Z') {
+ return *s = 'A';
+ }
+ return 0;
+}
+
+static VALUE
+Fstr_next(orig)
+ struct RString *orig;
+{
+ struct RString *str, *str2;
+ char *sbeg, *s;
+ char c = -1;
+
+ GC_LINK;
+ GC_PRO3(str, (struct RString*)str_new(orig->ptr, orig->len));
+
+ sbeg = str->ptr; s = sbeg + str->len - 1;
+
+ while (sbeg <= s) {
+ if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
+ s--;
+ }
+ if (s < sbeg && c != -1) {
+ GC_PRO3(str2, (struct RString*)str_new(0, str->len+1));
+ str2->ptr[0] = c;
+ memmove(str2->ptr+1, str->ptr, str->len);
+ obj_free(str);
+ str = str2;
+ }
+ GC_UNLINK;
+
+ return (VALUE)str;
+}
+
+static
+str_hash(str)
+ struct RString *str;
+{
+ int len = str->len;
+ unsigned char *p = (unsigned char*)str->ptr;
+ int key = 0;
+
+ if (ignorecase) {
+ while (len--) {
+ key = key*65599 + *p;
+ }
+ }
+ else {
+ while (len--) {
+ key = key*65599 + toupper(*p);
+ }
+ }
+ return key;
+}
+
+static VALUE
+Fstr_hash(str)
+ VALUE str;
+{
+ int key = str_hash(str);
+ return INT2FIX(key);
+}
+
+#define min(a,b) (((a)>(b))?(b):(a))
+
+int
+str_cmp(str1, str2)
+ struct RString *str1, *str2;
+{
+ UINT len;
+ int retval;
+
+ if (ignorecase != Qnil) {
+ return str_cicmp(str1, str2);
+ }
+
+ len = min(str1->len, str2->len);
+ retval = memcmp(str1->ptr, str2->ptr, len);
+ if (retval == 0) {
+ return str1->ptr[len] - str2->ptr[len];
+ }
+ return retval;
+}
+
+static VALUE
+Fstr_equal(str1, str2)
+ struct RString *str1, *str2;
+{
+ if (TYPE(str2) != T_STRING)
+ return FALSE;
+
+ if (str1->len == str2->len
+ && str_cmp(str1, str2) == 0) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+Fstr_cmp(str1, str2)
+ VALUE str1, str2;
+{
+ int result;
+
+ Check_Type(str2, T_STRING);
+ result = str_cmp(str1, str2);
+ return INT2FIX(result);
+}
+
+Regexp * make_regexp();
+VALUE Freg_match();
+
+static VALUE
+Fstr_match(this, other)
+ struct RString *this, *other;
+{
+ VALUE reg;
+ int start;
+
+ switch (TYPE(other)) {
+ case T_REGEXP:
+ return Freg_match(other, this);
+ case T_STRING:
+ reg = re_regcomp(other);
+ start = research(reg, this, 0, ignorecase);
+ if (start == -1) {
+ return FALSE;
+ }
+ return INT2FIX(start);
+ default:
+ Fail("type mismatch");
+ break;
+ }
+}
+
+static VALUE
+Fstr_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 = re_regcomp(str);
+ start = research(reg, rb_lastline, 0, ignorecase);
+ if (start == -1) {
+ return Qnil;
+ }
+ return INT2FIX(start);
+}
+
+static int
+str_index(str, sub, offset)
+ struct RString *str, *sub;
+ int offset;
+{
+ char *s, *e, *p;
+ int len;
+
+ if (str->len - offset < sub->len) return -1;
+ s = str->ptr+offset;
+ p = sub->ptr;
+ len = sub->len;
+ e = s + str->len - len + 1;
+ while (s < e) {
+ if (*s == *(sub->ptr) && memcmp(s, p, len) == 0) {
+ return (s-(str->ptr));
+ }
+ s++;
+ }
+ return -1;
+}
+
+static VALUE
+Fstr_index(str, args)
+ struct RString *str;
+ VALUE args;
+{
+ struct RString *sub;
+ VALUE initpos;
+ int pos;
+
+ if (rb_scan_args(args, "11", &sub, &initpos) == 2) {
+ pos = NUM2INT(initpos);
+ }
+ else {
+ pos = 0;
+ }
+
+ switch (TYPE(sub)) {
+ case T_REGEXP:
+ pos = research(sub, str, pos, ignorecase);
+ break;
+
+ case T_STRING:
+ pos = str_index(str, sub, pos);
+ break;
+
+ default:
+ Fail("Type mismatch: %s given", rb_class2name(CLASS_OF(sub)));
+ }
+
+ if (pos == -1) return Qnil;
+ return INT2FIX(pos);
+}
+
+static VALUE
+Fstr_rindex(str, args)
+ struct RString *str;
+ VALUE args;
+{
+ struct RString *sub;
+ VALUE initpos;
+ int pos, len;
+ char *s, *sbeg, *t;
+
+ if (rb_scan_args(args, "11", &sub, &initpos) == 2) {
+ pos = NUM2INT(initpos);
+ if (pos >= str->len) pos = str->len;
+ }
+ else {
+ pos = str->len;
+ }
+
+ Check_Type(sub, T_STRING);
+ if (pos > str->len) return Qnil; /* substring longer than string */
+ sbeg = str->ptr; s = s + pos - sub->len;
+ t = sub->ptr;
+ len = sub->len;
+ while (sbeg <= s) {
+ if (*s == *t && memcmp(s, t, len) == 0) {
+ return INT2FIX(s - sbeg);
+ }
+ s--;
+ }
+ return Qnil;
+}
+
+static VALUE
+Fstr_aref_internal(str, indx)
+ struct RString *str;
+ VALUE indx;
+{
+ int idx;
+
+ switch (TYPE(indx)) {
+ case T_FIXNUM:
+ idx = FIX2UINT(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 (VALUE)INT2FIX(str->ptr[idx] & 0xff);
+
+ case T_REGEXP:
+ if (Fstr_index(str, indx))
+ return re_last_match(0);
+ return Qnil;
+
+ case T_STRING:
+ if (str_index(str, indx, 0)) return indx;
+ return Qnil;
+
+ default:
+ /* check if indx is Range */
+ if (obj_is_kind_of(indx, C_Range)) {
+ int beg, end;
+
+ beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg);
+ end = rb_iv_get(indx, "end"); end = NUM2INT(end);
+ if (beg > end) {
+ int tmp;
+
+ if (verbose) {
+ Warning("start %d is bigger than end %d", beg, end);
+ }
+ tmp = beg; beg = end; end = tmp;
+ }
+
+ return str_subseq(str, beg, end);
+ }
+ Fail("Invalid index for string");
+ }
+}
+
+static VALUE
+Fstr_aref(str, args)
+ struct RString *str;
+ VALUE args;
+{
+ VALUE arg1, arg2;
+
+ if (rb_scan_args(args, "11", &arg1, &arg2) == 2) {
+ return str_substr(str, NUM2INT(arg1), NUM2INT(arg2));
+ }
+ return Fstr_aref_internal(str, arg1);
+}
+
+static void
+str_replace(str, beg, len, val)
+ struct RString *str, *val;
+ int beg, len;
+{
+ if (len < val->len) {
+ /* expand string */
+ REALLOC_N(str->ptr, char, str->len+val->len-len+1);
+ }
+
+ memmove(str->ptr+beg+val->len, str->ptr+beg+len, str->len-(beg+len));
+ memmove(str->ptr+beg, val->ptr, val->len);
+ str->len += val->len - len;
+ str->ptr[str->len] = '\0';
+}
+
+static void
+str_replace2(str, beg, end, val)
+ struct RString *str, *val;
+ int beg, end;
+{
+ int len;
+
+ if (beg < 0) {
+ beg = str->len + beg;
+ }
+ if (str->len <= beg) {
+ Fail("start %d too big", beg);
+ }
+ if (end < 0) {
+ end = str->len + end;
+ }
+ if (end < 0 || str->len <= end) {
+ Fail("end %d too big", end);
+ }
+ len = end - beg + 1; /* length of substring */
+ if (len < 0) {
+ Fail("end %d too small", end);
+ }
+
+ str_replace(str, beg, len, val);
+}
+
+static VALUE
+str_sub(str, pat, val, once)
+ struct RString *str;
+ struct RRegexp *pat;
+ VALUE val;
+ int once;
+{
+ VALUE sub;
+ int beg, end, offset, n;
+
+ GC_LINK;
+ GC_PRO2(sub);
+ for (offset=0, n=0;
+ (beg=research(pat, str, offset, ignorecase)) >= 0;
+ offset=RREGEXP(pat)->ptr->regs.start[0]+STRLEN(val)) {
+ end = RREGEXP(pat)->ptr->regs.end[0]-1;
+ sub = re_regsub(val);
+ str_replace2(str, beg, end, sub);
+ n++;
+ if (once) break;
+ }
+ GC_UNLINK;
+ if (n == 0) return Qnil;
+ return INT2FIX(n);
+}
+
+static VALUE
+Fstr_aset_internal(str, indx, val)
+ struct RString *str;
+ VALUE indx, val;
+{
+ int idx, beg, end, offset;
+
+ switch (TYPE(indx)) {
+ case T_FIXNUM:
+ idx = NUM2INT(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);
+ }
+ str->ptr[idx] = FIX2UINT(val) & 0xff;
+ return val;
+
+ case T_REGEXP:
+ str_sub(str, indx, val, 0);
+ return val;
+
+ case T_STRING:
+ for (offset=0;
+ (beg=str_index(str, indx, offset)) >= 0;
+ offset=beg+STRLEN(val)) {
+ end = beg + STRLEN(indx) - 1;
+ str_replace2(str, beg, end, val);
+ }
+ if (offset == 0) Fail("Not a substring");
+ return val;
+
+ default:
+ /* check if indx is Range */
+ if (obj_is_kind_of(indx, C_Range)) {
+ Check_Type(val, T_STRING);
+
+ beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg);
+ end = rb_iv_get(indx, "end"); end = NUM2INT(end);
+ if (beg > end) {
+ int tmp;
+
+ if (verbose) {
+ Warning("start %d is bigger than end %d", beg, end);
+ }
+ tmp = beg; beg = end; end = tmp;
+ }
+
+ str_replace2(str, beg, end, val);
+ return val;
+ }
+ Fail("Invalid index for string");
+ }
+}
+
+static VALUE
+Fstr_aset(str, args)
+ struct RString *str;
+ VALUE args;
+{
+ VALUE arg1, arg2, arg3;
+
+ str_modify(str);
+
+ if (rb_scan_args(args, "21", &arg1, &arg2, &arg3) == 3) {
+ int beg, len;
+
+ Check_Type(arg3, T_STRING);
+
+ beg = NUM2INT(arg1);
+ if (beg < 0) {
+ beg = str->len + beg;
+ if (beg < 0) Fail("start %d too small", beg);
+ }
+ len = NUM2INT(arg2);
+ if (len < 0) Fail("length %d too small", len);
+ if (beg + len > str->len) {
+ len = str->len - beg;
+ }
+ str_replace(str, beg, len, arg3);
+ return arg3;
+ }
+ return Fstr_aset_internal(str, arg1, arg2);
+}
+
+static VALUE
+Fstr_sub_internal(str, pat, val, once)
+ VALUE str, pat, val;
+ int once;
+{
+ VALUE reg, result;
+
+ Check_Type(val, T_STRING);
+ str_modify(str);
+
+ switch (TYPE(pat)) {
+ case T_REGEXP:
+ return str_sub(str, pat, val, once);
+
+ case T_STRING:
+ reg = re_regcomp(pat);
+ result = str_sub(str, reg, val, once);
+ return result;
+
+ default:
+ /* type failed */
+ Check_Type(pat, T_REGEXP);
+ }
+ return Qnil; /* not reached */
+}
+
+static VALUE
+Fstr_sub(str, pat, val)
+ VALUE str, pat, val;
+{
+ return Fstr_sub_internal(str, pat, val, 1);
+}
+
+static VALUE
+Fstr_gsub(str, pat, val)
+ VALUE str, pat, val;
+{
+ return Fstr_sub_internal(str, pat, val, 0);
+}
+
+extern VALUE rb_lastline;
+
+static VALUE
+Fsub(obj, pat, val)
+ VALUE obj, pat, val;
+{
+ Check_Type(rb_lastline, T_STRING);
+ return Fstr_sub_internal(rb_lastline, pat, val, 1);
+}
+
+static VALUE
+Fgsub(obj, pat, val)
+ VALUE obj, pat, val;
+{
+ Check_Type(rb_lastline, T_STRING);
+ return Fstr_sub_internal(rb_lastline, pat, val, 0);
+}
+
+static VALUE
+Fstr_reverse(str)
+ struct RString *str;
+{
+ VALUE obj = str_new(0, str->len);
+ char *s, *e, *p;
+
+ s = str->ptr; e = s + str->len - 1;
+ p = RSTRING(obj)->ptr;
+
+ while (e >= s) {
+ *p++ = *e--;
+ }
+
+ return obj;
+}
+
+static VALUE
+Fstr_to_i(str)
+ struct RString *str;
+{
+ return str2inum(str->ptr, 10);
+}
+
+static VALUE
+Fstr_to_f(str)
+ struct RString *str;
+{
+ double atof();
+ double f = atof(str->ptr);
+
+ return float_new(f);
+}
+
+static VALUE
+Fstr_to_s(str)
+ VALUE str;
+{
+ return str;
+}
+
+static VALUE
+Fstr_inspect(str)
+ struct RString *str;
+{
+ char buf[160];
+ char *p, *pend;
+ char *b, *bend;
+
+#define CHECK(n) if (b+n > bend) break;
+
+ p = str->ptr; pend = p + str->len;
+ b = buf; bend = b + sizeof buf - (str->len>150?4:2);
+ *b++ = '"';
+ while (p < pend) {
+ char c = *p++;
+ if (isprint(c)) {
+ CHECK(1);
+ *b++ = c;
+ }
+ else if (ismbchar(c)) {
+ CHECK(2);
+ *b++ = c;
+ *b++ = *p++;
+ }
+ else if (c == '\n') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'n';
+ }
+ else if (c == '\r') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'r';
+ }
+ else if (c == '\t') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 't';
+ }
+ else if (c == '\f') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'f';
+ }
+ else if (c == '\13') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'v';
+ }
+ else if (c == '\a') {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'a';
+ }
+ else if (c == 033) {
+ CHECK(2);
+ *b++ = '\\';
+ *b++ = 'e';
+ }
+ else if (iscntrl(c)) {
+ CHECK(2);
+ *b++ = '^';
+ *b++ = c;
+ }
+ else {
+ CHECK(1);
+ *b++ = c;
+ }
+ }
+ *b++ = '"';
+ if (p < pend) {
+ bend = buf + sizeof buf;
+ while (b < bend) {
+ *b++ = '.';
+ }
+ }
+ return str_new(buf, b - buf);
+}
+
+static VALUE
+Fstr_toupper(str)
+ struct RString *str;
+{
+ char *s;
+ int i;
+
+ str_modify(str);
+ s = str->ptr;
+ for (i=0; i < str->len; i++) {
+ if (islower(*s)) {
+ *s = toupper(*s);
+ }
+ *s++;
+ }
+
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_tolower(str)
+ struct RString *str;
+{
+ char *s;
+ int i;
+
+ str_modify(str);
+ s = str->ptr;
+ for (i=0; i < str->len; i++) {
+ if (isupper(*s)) {
+ *s = tolower(*s);
+ }
+ *s++;
+ }
+
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_ucfirst(str)
+ struct RString *str;
+{
+ char *s, *send;
+ int i;
+
+ str_modify(str);
+ s = str->ptr; send = s + str->len;
+ if (islower(*s))
+ *s = toupper(*s);
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_lcfirst(str)
+ struct RString *str;
+{
+ char *s, *send;
+ int i;
+
+ str_modify(str);
+ s = str->ptr; send = s + str->len;
+ if (isupper(*s))
+ *s = tolower(*s);
+ return (VALUE)str;
+}
+
+struct tr {
+ int last, max;
+ char *p, *pend;
+} trsrc, trrepl;
+
+static
+trnext(t)
+ struct tr *t;
+{
+ while (t->p < t->pend) {
+ if (t->max) {
+ if (++t->last < t->max)
+ return t->last;
+ t->last = t->max = 0;
+ }
+ else if (t->last && *t->p == '-') {
+ t->p++;
+ t->max = *t->p;
+ if (t->p == t->pend) {
+ t->p--;
+ return '-';
+ }
+ else if (t->max < t->last) {
+ t->last = t->max - 1;
+ return '-';
+ }
+ continue;
+ }
+ return t->last = *t->p++;
+ }
+ return -1;
+}
+
+static VALUE
+Fstr_tr(str, src, repl)
+ struct RString *str, *src, *repl;
+{
+ struct tr trsrc, trrepl;
+ char trans[256];
+ int cflag = 0;
+ int i, c, save;
+ char *s, *send, *t;
+
+ Check_Type(src, T_STRING);
+ trsrc.p = src->ptr; trsrc.pend = trsrc.p + src->len;
+ if (src->len > 2 && src->ptr[0] == '^') {
+ cflag++;
+ trsrc.p++;
+ }
+ Check_Type(repl, T_STRING);
+ trrepl.p = repl->ptr; trrepl.pend = trrepl.p + repl->len;
+ trsrc.last = trrepl.last = trsrc.max = trrepl.max = 0;
+
+ for (i=0; i<256; i++) {
+ trans[i] = cflag ? 1 : 0;
+ }
+
+ while ((c = trnext(&trsrc)) >= 0) {
+ trans[c & 0xff] = cflag ? 0 : 1;
+ }
+
+ c = 0;
+ for (i=0; i<256; i++) {
+ if (trans[i] == 0) {
+ trans[i] = i;
+ }
+ else {
+ c = trnext(&trrepl);
+ if (c == -1) {
+ trans[i] = trrepl.last;
+ }
+ else {
+ trans[i] = c;
+ }
+ }
+ }
+
+ str_modify(str);
+
+ t = s = str->ptr; send = s + str->len;
+ while (s < send) {
+ c = *s++ & 0xff;
+ c = trans[c] & 0xff;
+ *t++ = c;
+ }
+ *t = '\0';
+ str->len = t - str->ptr;
+
+ return (VALUE)str;
+}
+
+static void
+tr_setup_table(str, table)
+ struct RString *str;
+ char table[256];
+{
+ struct tr tr;
+ int i, cflag = 0;
+ char c;
+
+ tr.p = str->ptr; tr.pend = tr.p + str->len;
+ tr.last = tr.max = 0;
+ if (str->len > 2 && str->ptr[0] == '^') {
+ cflag++;
+ tr.p++;
+ }
+
+ for (i=0; i<256; i++) {
+ table[i] = cflag ? 1 : 0;
+ }
+ while ((c = trnext(&tr)) >= 0) {
+ table[c & 0xff] = cflag ? 0 : 1;
+ }
+}
+
+static VALUE
+Fstr_delete(str1, str2)
+ struct RString *str1, *str2;
+{
+ char *s, *send, *t;
+ char squeez[256];
+
+ Check_Type(str2, T_STRING);
+ tr_setup_table(str2, squeez);
+
+ str_modify(str1);
+
+ s = t = str1->ptr;
+ send = s + str1->len;
+ while (s < send) {
+ if (!squeez[*s & 0xff]) {
+ *t++ = *s;
+ }
+ s++;
+ }
+ *t = '\0';
+ str1->len = t - str1->ptr;
+
+ return (VALUE)str1;
+}
+
+static VALUE
+tr_squeeze(str1, str2)
+ struct RString *str1, *str2;
+{
+ char squeez[256];
+ char *s, *send, *t;
+ char c, save;
+
+ if (str2) {
+ tr_setup_table(str2, squeez);
+ }
+ else {
+ int i;
+
+ for (i=0; i<256; i++) {
+ squeez[i] = 1;
+ }
+ }
+
+ str_modify(str1);
+
+ s = t = str1->ptr;
+ send = s + str1->len;
+ save = -1;
+ while (s < send) {
+ c = *s++ & 0xff;
+ if (c != save || !squeez[c & 0xff]) {
+ *t++ = save = c;
+ }
+ }
+ *t = '\0';
+ str1->len = t - str1->ptr;
+
+ return (VALUE)str1;
+}
+
+static VALUE
+Fstr_squeeze(str1, args)
+ VALUE str1;
+ VALUE *args;
+{
+ VALUE str2;
+
+ rb_scan_args(args, "01", &str2);
+ if (str2) {
+ Check_Type(str2, T_STRING);
+ }
+ return tr_squeeze(str1, str2);
+}
+
+static VALUE
+Fstr_tr_s(str, src, repl)
+ VALUE str, src, repl;
+{
+ Check_Type(src, T_STRING);
+ Check_Type(repl, T_STRING);
+ Fstr_tr(str, src, repl);
+ tr_squeeze(str, repl);
+ return str;
+}
+
+static VALUE
+Fstr_split(str, args)
+ struct RString *str;
+ VALUE args;
+{
+ extern VALUE FS;
+ struct RRegexp *spat;
+ VALUE limit;
+ char char_sep = 0;
+ int beg, end, lim, i;
+ VALUE result, tmp;
+
+ rb_scan_args(args, "02", &spat, &limit);
+ if (limit) {
+ lim = NUM2INT(limit);
+ i = 1;
+ }
+
+ if (spat == Qnil) {
+ if (FS) {
+ spat = (struct RRegexp*)FS;
+ goto fs_set;
+ }
+ char_sep = ' ';
+ }
+ else {
+ switch (TYPE(spat)) {
+ case T_STRING:
+ fs_set:
+ if (STRLEN(spat) == 1) {
+ char_sep = RSTRING(spat)->ptr[0];
+ }
+ else {
+ spat = (struct RRegexp*)re_regcomp(spat);
+ }
+ break;
+ case T_REGEXP:
+ break;
+ default:
+ Fail("split(): bad separator");
+ }
+ }
+
+ GC_LINK;
+ GC_PRO(spat);
+ GC_PRO3(result, ary_new());
+
+ beg = 0;
+ if (char_sep != 0) {
+ char *ptr = str->ptr;
+ int len = str->len;
+ char *eptr = ptr + len;
+
+ if (char_sep == ' ') { /* AWK emulation */
+ int skip = 1;
+
+ for (end = beg = 0; ptr<eptr; ptr++) {
+ if (skip) {
+ if (isspace(*ptr)) {
+ beg++;
+ }
+ else {
+ end = beg+1;
+ skip = 0;
+ }
+ }
+ else {
+ if (isspace(*ptr)) {
+ Fary_push(result, str_substr(str, beg, end-beg));
+ if (limit && lim <= ++i) break;
+ skip = 1;
+ beg = end + 1;
+ }
+ else {
+ end++;
+ }
+ }
+ }
+ }
+ else {
+ for (end = beg = 0; ptr<eptr; ptr++) {
+ if (*ptr == char_sep) {
+ Fary_push(result, str_substr(str, beg, end-beg));
+ if (limit && lim <= ++i) break;
+ beg = end + 1;
+ }
+ end++;
+ }
+ }
+ }
+ else {
+ int start = beg;
+ int last_null = 0;
+ int idx;
+
+#define LMATCH spat->ptr->regs.start
+#define RMATCH spat->ptr->regs.end
+
+ while ((end = research(spat, str, start, ignorecase)) >= 0) {
+ if (start == end && LMATCH[0] == RMATCH[0]) {
+ if (last_null == 1) {
+ if (ismbchar(str->ptr[beg]))
+ Fary_push(result, str_substr(str, beg, 2));
+ else
+ Fary_push(result, str_substr(str, beg, 1));
+ beg = start;
+ if (limit && lim <= ++i) break;
+ }
+ else {
+ start += ismbchar(str->ptr[start])?2:1;
+ last_null = 1;
+ continue;
+ }
+ }
+ else {
+ Fary_push(result, str_substr(str, beg, end-beg));
+ beg = start = RMATCH[0];
+ if (limit && lim <= ++i) break;
+ }
+ last_null = 0;
+
+ for (idx=1; idx < 10; idx++) {
+ if (LMATCH[idx] == -1) break;
+ if (LMATCH[idx] == RMATCH[idx])
+ tmp = str_new(0, 0);
+ else
+ tmp = str_subseq(str, LMATCH[idx], RMATCH[idx]-1);
+ Fary_push(result, tmp);
+ if (limit && lim <= ++i) break;
+ }
+
+ }
+ }
+ if (str->len > beg) {
+ Fary_push(result, str_subseq(str, beg, -1));
+ }
+ else if (str->len == beg) {
+ Fary_push(result, str_new(0, 0));
+ }
+
+ GC_UNLINK;
+ return result;
+}
+
+static VALUE
+Fstr_each(str)
+ struct RString* str;
+{
+ extern VALUE RS;
+ int newline;
+ int rslen;
+ char *p = str->ptr, *pend = p + str->len, *s;
+
+ if (RS == Qnil) {
+ rb_yield(str);
+ return (VALUE)str;
+ }
+
+ rslen = RSTRING(RS)->len;
+ if (rslen == 0) {
+ newline = '\n';
+ }
+ else {
+ newline = RSTRING(RS)->ptr[rslen-1];
+ }
+
+ for (s = p, p += rslen; p < pend; p++) {
+ if (rslen == 0 && *p == '\n') {
+ if (*(p+1) != '\n') continue;
+ while (*p == '\n') p++;
+ p--;
+ }
+ 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);
+ s = p + 1;
+ }
+ }
+
+ if (s != pend) {
+ rb_lastline = str_new(s, p - s);
+ rb_yield(rb_lastline);
+ }
+
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_each_byte(str)
+ struct RString* str;
+{
+ int i;
+
+ for (i=0; str->len; i++) {
+ rb_yield(str->ptr[i] & 0xff);
+ }
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_chop(str)
+ struct RString *str;
+{
+ int result;
+
+ str_modify(str);
+
+ str->len--;
+ str->ptr[str->len] = '\0';
+
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_strip(str)
+ struct RString *str;
+{
+ char *s, *t, *e;
+
+ s = str->ptr;
+ e = t = s + str->len;
+ /* remove spaces at head */
+ while (s < t && isspace(*s)) s++;
+
+ /* remove trailing spaces */
+ t--;
+ while (s <= t && isspace(*t)) t--;
+ t++;
+
+ if (s > str->ptr || t < e) {
+ str_modify(str);
+ return str_new(s, t-s);
+ }
+ return (VALUE)str;
+}
+
+static VALUE
+Fstr_hex(str)
+ struct RString *str;
+{
+ return str2inum(str->ptr, 16);
+}
+
+static VALUE
+Fstr_oct(str)
+ struct RString *str;
+{
+ return str2inum(str->ptr, 8);
+}
+
+static VALUE
+Fstr_crypt(str, salt)
+ struct RString *str, *salt;
+{
+ Check_Type(salt, T_STRING);
+ if (salt->len < 2)
+ Fail("salt too short(need 2 byte)");
+ return str_new2(crypt(str->ptr, salt->ptr));
+}
+
+static VALUE
+Fstr_intern(str)
+ struct RString *str;
+{
+ if (strlen(str->ptr) != str->len)
+ Fail("string contains `\0'");
+
+ return rb_intern(str->ptr)|FIXNUM_FLAG;
+}
+
+extern VALUE C_Kernel;
+extern VALUE M_Comparable;
+extern VALUE M_Enumerable;
+
+Init_String()
+{
+ C_String = rb_define_class("String", C_Object);
+ rb_include_module(C_String, M_Comparable);
+ rb_include_module(C_String, M_Enumerable);
+ rb_define_single_method(C_String, "new", Fstr_new, 1);
+ rb_define_method(C_String, "clone", Fstr_clone, 0);
+ rb_define_method(C_String, "<=>", Fstr_cmp, 1);
+ rb_define_method(C_String, "==", Fstr_equal, 1);
+ rb_define_method(C_String, "hash", Fstr_hash, 0);
+ rb_define_method(C_String, "+", Fstr_plus, 1);
+ rb_define_method(C_String, "*", Fstr_times, 1);
+ rb_define_method(C_String, "..", Fstr_dot2, 1);
+ rb_define_method(C_String, "[]", Fstr_aref, -2);
+ rb_define_method(C_String, "[]=", Fstr_aset, -2);
+ rb_define_method(C_String, "length", Fstr_length, 0);
+ rb_define_method(C_String, "=~", Fstr_match, 1);
+ rb_define_method(C_String, "~", Fstr_match2, 0);
+ rb_define_method(C_String, "next", Fstr_next, 0);
+ rb_define_method(C_String, "index", Fstr_index, -2);
+ rb_define_method(C_String, "rindex", Fstr_rindex, -2);
+
+ rb_define_method(C_String, "to_i", Fstr_to_i, 0);
+ rb_define_method(C_String, "to_f", Fstr_to_f, 0);
+ rb_define_method(C_String, "to_s", Fstr_to_s, 0);
+ rb_define_method(C_String, "_inspect", Fstr_inspect, 0);
+
+ rb_define_method(C_String, "toupper", Fstr_toupper, 0);
+ rb_define_alias(C_String, "uc", "toupper");
+ rb_define_method(C_String, "tolower", Fstr_tolower, 0);
+ rb_define_alias(C_String, "lc", "tolower");
+ rb_define_method(C_String, "ucfirst", Fstr_ucfirst, 0);
+ rb_define_method(C_String, "lcfirst", Fstr_lcfirst, 0);
+ rb_define_method(C_String, "hex", Fstr_hex, 0);
+ rb_define_method(C_String, "oct", Fstr_oct, 0);
+ rb_define_method(C_String, "split", Fstr_split, -2);
+ rb_define_method(C_String, "reverse", Fstr_reverse, 0);
+ rb_define_method(C_String, "concat", Fstr_concat, 1);
+ rb_define_method(C_String, "crypt", Fstr_crypt, 1);
+ rb_define_method(C_String, "intern", Fstr_intern, 0);
+
+ rb_define_method(C_String, "sub", Fstr_sub, 2);
+ rb_define_method(C_String, "gsub", Fstr_gsub, 2);
+ rb_define_method(C_String, "chop", Fstr_chop, 0);
+ rb_define_method(C_String, "strip", Fstr_strip, 0);
+
+ rb_define_method(C_String, "tr", Fstr_tr, 2);
+ rb_define_method(C_String, "tr_s", Fstr_tr_s, 2);
+ rb_define_method(C_String, "delete", Fstr_delete, 1);
+ rb_define_method(C_String, "squeeze", Fstr_squeeze, -2);
+
+ rb_define_method(C_String, "each", Fstr_each, 0);
+ rb_define_method(C_String, "each_byte", Fstr_each_byte, 0);
+
+ rb_define_func(C_Kernel, "sub", Fsub, 2);
+ rb_define_func(C_Kernel, "gsub", Fgsub, 2);
+
+ pr_str = rb_intern("to_s");
+}
diff --git a/struct.c b/struct.c
new file mode 100644
index 0000000000..658925109d
--- /dev/null
+++ b/struct.c
@@ -0,0 +1,274 @@
+/************************************************
+
+ struct.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:51 $
+ created at: Tue Mar 22 18:44:30 JST 1994
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+
+VALUE C_Struct;
+extern VALUE M_Enumerable;
+
+char *strdup();
+
+static VALUE
+struct_alloc(class, name)
+ VALUE class;
+ char *name;
+{
+ NEWOBJ(st, struct RStruct);
+ OBJSETUP(st, class, T_STRUCT);
+
+ if (name) st->name = strdup(name);
+ else st->name = Qnil;
+ st->len = 0;
+ st->tbl = Qnil;
+
+ return (VALUE)st;
+}
+
+static VALUE
+struct_find(s, id)
+ struct RStruct *s;
+ ID id;
+{
+ struct kv_pair *t, *tend;
+
+ t = s->tbl;
+ tend = t + s->len;
+ while (t < tend) {
+ if (t->key == id) return t->value;
+ t++;
+ }
+ Fail("struct %s has no member %s", s->name, rb_id2name(id));
+}
+
+static VALUE
+Fstruct_access(s)
+ struct RStruct *s;
+{
+ return struct_find(s, the_env->last_func);
+}
+
+static VALUE
+struct_add(s, mem, val)
+ struct RStruct *s;
+ char *mem;
+ VALUE val;
+{
+ int pos = s->len;
+
+ s->len++;
+ if (s->tbl == Qnil) {
+ s->tbl = ALLOC_N(struct kv_pair, 1);
+ }
+ else {
+ REALLOC_N(s->tbl, struct kv_pair, s->len);
+ }
+
+ s->tbl[pos].key = rb_intern(mem);
+ s->tbl[pos].value = val;
+ rb_define_single_method(s, mem, Fstruct_access, 0);
+}
+
+#include <varargs.h>
+
+VALUE
+struct_new(name, va_alist)
+ char *name;
+ va_dcl
+{
+ VALUE st;
+ va_list args;
+ char *mem;
+
+ GC_LINK;
+ GC_PRO3(st, struct_alloc(C_Struct,name));
+ va_start(args);
+
+ while (mem = va_arg(args, char*)) {
+ struct_add(st, mem, va_arg(args, VALUE));
+ }
+
+ va_end(vargs);
+ GC_UNLINK;
+
+ return st;
+}
+
+#define ASSOC_KEY(a) RARRAY(a)->ptr[0]
+#define ASSOC_VAL(a) RARRAY(a)->ptr[1]
+
+static VALUE
+Fstruct_new(class, args)
+ VALUE class, args;
+{
+ VALUE name, st;
+ struct RArray *tbl;
+ int i, max;
+
+ rb_scan_args(args, "1*", &name, &tbl);
+ Check_Type(name, T_STRING);
+
+ GC_LINK;
+ GC_PRO(tbl);
+ GC_PRO3(st, struct_alloc(class, RSTRING(name)->ptr));
+
+ for (i=0, max=tbl->len; i<max; i++) {
+ VALUE assoc = tbl->ptr[i];
+
+ Check_Type(assoc, T_ARRAY);
+ if (RARRAY(assoc)->len != 2) {
+ Fail("args must be pairs");
+ }
+ Check_Type(ASSOC_KEY(assoc), T_STRING);
+ struct_add(st, RSTRING(ASSOC_KEY(assoc))->ptr, ASSOC_VAL(assoc));
+ }
+
+ GC_UNLINK;
+
+ return st;
+}
+
+static VALUE
+Fstruct_each(s)
+ struct RStruct *s;
+{
+ struct kv_pair *t, *tend;
+
+ t = s->tbl;
+ tend = t + s->len;
+ while (t < tend) {
+ rb_yield(t->value);
+ t++;
+ }
+}
+
+static VALUE
+Fstruct_values(s)
+ struct RStruct *s;
+{
+ VALUE ary;
+ struct kv_pair *t, *tend;
+
+ GC_LINK;
+ GC_PRO3(ary, ary_new());
+ t = s->tbl;
+ tend = t + s->len;
+ while (t < tend) {
+ Fary_push(ary, t->value);
+ t++;
+ }
+ GC_UNLINK;
+ return ary;
+}
+
+static VALUE
+Fstruct_aref(s, idx)
+ struct RStruct *s;
+ VALUE idx;
+{
+ struct RArray *ary;
+ int i;
+
+ if (TYPE(idx) == T_STRING)
+ return struct_find(rb_intern(RSTRING(idx)->ptr));
+
+ i = NUM2INT(idx);
+ if (s->len <= i)
+ Fail("offset %d too large for struct(size:%d)", i, s->len);
+ return s->tbl[i].value;
+}
+
+#define HDR "struct "
+
+static VALUE
+Fstruct_to_s(s)
+ struct RStruct *s;
+{
+ char *buf;
+
+ buf = (char*)alloca(strlen(s->name) + sizeof(HDR) + 1);
+ sprintf(buf, "%s%s", HDR, s->name);
+ return str_new2(buf);
+}
+
+static VALUE
+Fstruct_inspect(s)
+ struct RStruct *s;
+{
+ VALUE str, str2;
+ char buf[256], *p;
+ int i;
+ ID inspect = rb_intern("_inspect");
+
+ GC_LINK;
+ sprintf(buf, "#<%s%s: ", HDR, s->name);
+ GC_PRO3(str, str_new2(buf));
+ GC_PRO2(str2);
+ for (i=0; i<s->len; i++) {
+ if (i > 0) {
+ str_cat(str, ", ", 2);
+ }
+ p = rb_id2name(s->tbl[i].key);
+ str_cat(str, p, strlen(p));
+ str_cat(str, "=", 1);
+ str2 = rb_funcall(s->tbl[i].value, inspect, 0, Qnil);
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ }
+ str_cat(str, ">", 1);
+ GC_UNLINK;
+
+ return str;
+}
+
+static VALUE
+Fstruct_to_a(s)
+ struct RStruct *s;
+{
+ VALUE ary;
+ int i;
+
+ ary = ary_new2(s->len);
+ for (i=0; i<s->len; i++) {
+ Fary_push(ary, s->tbl[i].value);
+ }
+
+ return ary;
+}
+
+static VALUE
+Fstruct_clone(s)
+ struct RStruct *s;
+{
+ struct RStruct *st = (struct RStruct*)struct_alloc(s->name);
+
+ CLONESETUP(st, s);
+ st->len = s->len;
+ st->tbl = ALLOC_N(struct kv_pair, s->len);
+ memcpy(st->tbl, s->tbl, sizeof(struct kv_pair) * st->len);
+ RBASIC(st)->class = single_class_clone(RBASIC(s)->class);
+ return (VALUE)st;
+}
+
+Init_Struct()
+{
+ C_Struct = rb_define_class("Struct", C_Object);
+ rb_include_module(C_Struct, M_Enumerable);
+
+ rb_define_single_method(C_Struct, "new", Fstruct_new, -2);
+ rb_define_method(C_Struct, "clone", Fstruct_clone, 0);
+
+ rb_define_method(C_Struct, "to_s", Fstruct_to_s, 0);
+ rb_define_method(C_Struct, "_inspect", Fstruct_inspect, 0);
+ rb_define_method(C_Struct, "to_a", Fstruct_to_a, 0);
+
+ rb_define_method(C_Struct, "each", Fstruct_each, 0);
+ rb_define_method(C_Struct, "values", Fstruct_values, 0);
+ rb_define_method(C_Struct, "[]", Fstruct_aref, 1);
+}
diff --git a/time.c b/time.c
new file mode 100644
index 0000000000..05f477cd5c
--- /dev/null
+++ b/time.c
@@ -0,0 +1,562 @@
+/************************************************
+
+ time.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:51 $
+ created at: Tue Dec 28 14:31:59 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/times.h>
+
+static VALUE C_Time;
+extern VALUE M_Comparable;
+
+struct time_object {
+ struct timeval tv;
+ struct tm tm;
+#ifndef HAVE_TM_ZONE
+ int gmt;
+#endif
+ int tm_got;
+};
+
+#define GetTimeval(obj, tobj) \
+ Get_Data_Struct(obj, "tv", struct time_object, tobj)
+#define MakeTimeval(obj,tobj) {\
+ Make_Data_Struct(obj, "tv", struct time_object, Qnil, Qnil, tobj);\
+ tobj->tm_got=0;\
+}
+
+static VALUE
+Ftime_now(class)
+ VALUE class;
+{
+ VALUE obj = obj_alloc(class);
+ struct time_object *tobj;
+
+ GC_LINK;
+ GC_PRO(obj);
+ MakeTimeval(obj, tobj);
+
+ if (gettimeofday(&(tobj->tv), 0) == -1) {
+ rb_sys_fail("gettimeofday");
+ }
+ GC_UNLINK;
+
+ return obj;
+}
+
+static VALUE
+time_new_internal(class, sec, usec)
+ int sec, usec;
+{
+ VALUE obj = obj_alloc(class);
+ struct time_object *tobj;
+
+ GC_LINK;
+ GC_PRO(obj);
+ MakeTimeval(obj, tobj);
+ tobj->tv.tv_sec = sec;
+ tobj->tv.tv_usec =usec;
+ GC_UNLINK;
+
+ return obj;
+}
+
+VALUE
+time_new(sec, usec)
+ int sec, usec;
+{
+ return time_new_internal(C_Time, sec, usec);
+}
+
+struct timeval*
+time_timeval(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ static struct timeval t, *tp;
+
+ switch (TYPE(time)) {
+ case T_FIXNUM:
+ t.tv_sec = FIX2UINT(time);
+ if (t.tv_sec < 0)
+ Fail("time must be positive");
+ t.tv_usec = 0;
+ tp = &t;
+ break;
+
+ case T_FLOAT:
+ {
+ double floor();
+ double seconds, microseconds;
+
+ if (RFLOAT(time)->value < 0.0)
+ Fail("time must be positive");
+ seconds = floor(RFLOAT(time)->value);
+ microseconds = (RFLOAT(time)->value - seconds) * 1000000.0;
+ t.tv_sec = seconds;
+ t.tv_usec = microseconds;
+ tp = &t;
+ }
+ break;
+
+ default:
+ if (!obj_is_kind_of(time, C_Time)) {
+ Fail("Can't convert %s into Time", rb_class2name(CLASS_OF(time)));
+ }
+ GetTimeval(time, tobj);
+ tp = &(tobj->tv);
+ break;
+ }
+ return tp;
+}
+
+static VALUE
+Ftime_at(class, time)
+ VALUE class, time;
+{
+ VALUE obj;
+ int sec, usec;
+ struct time_object *tobj;
+ struct timeval *tp;
+
+ tp = time_timeval(time);
+ return time_new_internal(class, tp->tv_sec, tp->tv_usec);
+
+}
+static VALUE
+Ftime_to_i(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ return int2inum(tobj->tv.tv_sec);
+}
+
+static VALUE
+Ftime_usec(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ return INT2FIX(tobj->tv.tv_usec);
+}
+
+static VALUE
+Ftime_cmp(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj1, *tobj2;
+ int i;
+
+ GetTimeval(time1, tobj1);
+ if (obj_is_member_of(time2, C_Time)) {
+ GetTimeval(time2, tobj2);
+ if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
+ if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0);
+ if (tobj1->tv.tv_usec > tobj2->tv.tv_usec) return INT2FIX(1);
+ return FIX2INT(-1);
+ }
+ if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1);
+ return FIX2INT(-1);
+ }
+ i = NUM2INT(time2);
+ if (tobj1->tv.tv_sec == i) return INT2FIX(0);
+ if (tobj1->tv.tv_sec > i) return INT2FIX(1);
+ return FIX2INT(-1);
+}
+
+static VALUE
+Ftime_hash(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ int hash;
+
+ GetTimeval(time, tobj);
+ hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec;
+ return INT2FIX(hash);
+}
+
+static VALUE
+Ftime_localtime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm_tmp;
+
+ GetTimeval(time, tobj);
+ tm_tmp = localtime(&tobj->tv.tv_sec);
+ tobj->tm = *tm_tmp;
+ tobj->tm_got = 1;
+#ifndef HAVE_TM_ZONE
+ tobj->gmt = 0;
+#endif
+ return time;
+}
+
+static VALUE
+Ftime_gmtime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm_tmp;
+
+ GetTimeval(time, tobj);
+ tm_tmp = gmtime(&tobj->tv.tv_sec);
+ tobj->tm = *tm_tmp;
+ tobj->tm_got = 1;
+#ifndef HAVE_TM_ZONE
+ tobj->gmt = 1;
+#endif
+ return time;
+}
+
+static VALUE
+Ftime_asctime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ char *ct;
+ char buf[32];
+ int len;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+#ifndef HAVE_TM_ZONE
+ if (tobj->gmt == 1)
+ len = strftime(buf, 32, "%a %b %d %H:%M:%S GMT %Y", &(tobj->tm));
+ else
+#endif
+ {
+ len = strftime(buf, 32, "%a %b %d %H:%M:%S %Z %Y", &(tobj->tm));
+ }
+ return str_new(buf, len);
+}
+
+static VALUE
+Ftime_coerce(time1, time2)
+ VALUE time1, time2;
+{
+ return time_new(CLASS_OF(time1), NUM2INT(time2), 0);
+}
+
+static VALUE
+Ftime_plus(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj1, *tobj2;
+ int sec, usec;
+
+ GetTimeval(time1, tobj1);
+ if (obj_is_member_of(time2, C_Time)) {
+ GetTimeval(time2, tobj2);
+ sec = tobj1->tv.tv_sec + tobj2->tv.tv_sec;
+ usec = tobj1->tv.tv_usec + tobj2->tv.tv_usec;
+ }
+ else {
+ sec = tobj1->tv.tv_sec + NUM2INT(time2);
+ usec = tobj1->tv.tv_usec;
+ if (usec >= 1000000) {
+ sec++;
+ usec -= 1000000;
+ }
+ }
+ return time_new(sec, usec);
+}
+
+static VALUE
+Ftime_minus(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj1, *tobj2;
+ int sec, usec;
+
+ GetTimeval(time1, tobj1);
+ if (obj_is_member_of(time2, C_Time)) {
+ 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;
+ }
+ }
+ else {
+ sec = tobj1->tv.tv_sec - NUM2INT(time2);
+ usec = tobj1->tv.tv_usec;
+ }
+ return time_new(sec, usec);
+}
+
+static VALUE
+Ftime_sec(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_sec);
+}
+
+static VALUE
+Ftime_min(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_min);
+}
+
+static VALUE
+Ftime_hour(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_hour);
+}
+
+static VALUE
+Ftime_mday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_mday);
+}
+
+static VALUE
+Ftime_mon(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_mon);
+}
+
+static VALUE
+Ftime_year(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_year);
+}
+
+static VALUE
+Ftime_wday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_wday);
+}
+
+static VALUE
+Ftime_yday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_yday);
+}
+
+static VALUE
+Ftime_isdst(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ return INT2FIX(tobj->tm.tm_isdst);
+}
+
+static VALUE
+Ftime_zone(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ char buf[10];
+ int len;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+
+ len = strftime(buf, 10, "%Z", &(tobj->tm));
+ return str_new(buf, len);
+}
+
+static VALUE
+Ftime_to_a(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm;
+ char buf[10];
+ VALUE ary;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ ary = ary_new3(9,
+ INT2FIX(tobj->tm.tm_sec),
+ INT2FIX(tobj->tm.tm_min),
+ INT2FIX(tobj->tm.tm_hour),
+ INT2FIX(tobj->tm.tm_mday),
+ INT2FIX(tobj->tm.tm_mon),
+ INT2FIX(tobj->tm.tm_year),
+ INT2FIX(tobj->tm.tm_wday),
+ INT2FIX(tobj->tm.tm_yday),
+ INT2FIX(tobj->tm.tm_isdst));
+ return ary;
+}
+
+static VALUE
+Ftime_strftime(time, format)
+ VALUE time, format;
+{
+ struct time_object *tobj;
+ char buf[100];
+ int len;
+
+ Check_Type(format, T_STRING);
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ Ftime_localtime(time);
+ }
+ if (strlen(RSTRING(format)->ptr) < RSTRING(format)->len) {
+ /* Ruby string contains \0. */
+ VALUE str;
+ int l, total = 0;
+ char *p = RSTRING(format)->ptr, *pe = p + RSTRING(format)->len;
+
+ GC_LINK;
+ GC_PRO3(str, str_new(0, 0));
+ while (p < pe) {
+ len = strftime(buf, 100, p, &(tobj->tm));
+ str_cat(str, buf, len);
+ l = strlen(p);
+ p += l + 1;
+ }
+ GC_UNLINK;
+ return str;
+ }
+ len = strftime(buf, 100, RSTRING(format)->ptr, &(tobj->tm));
+ return str_new(buf, len);
+}
+
+static VALUE
+Ftime_times(obj)
+ VALUE obj;
+{
+ struct tms buf;
+ VALUE t1, t2, t3, t4, tm;
+
+ if (times(&buf) == -1) rb_sys_fail(Qnil);
+ GC_LINK;
+ GC_PRO3(t1, float_new((double)buf.tms_utime / 60.0));
+ GC_PRO3(t2, float_new((double)buf.tms_stime / 60.0));
+ GC_PRO3(t3, float_new((double)buf.tms_cutime / 60.0));
+ GC_PRO3(t4, float_new((double)buf.tms_cstime / 60.0));
+
+ tm = struct_new("tms",
+ "utime", t1, "stime", t2,
+ "cutime", t3, "cstime", t4,
+ Qnil);
+ GC_UNLINK;
+
+ return tm;
+}
+
+Init_Time()
+{
+ C_Time = rb_define_class("Time", C_Object);
+ rb_include_module(C_Time, M_Comparable);
+
+ rb_define_single_method(C_Time, "now", Ftime_now, 0);
+ rb_define_single_method(C_Time, "new", Ftime_now, 0);
+ rb_define_single_method(C_Time, "at", Ftime_at, 1);
+
+ rb_define_single_method(C_Time, "times", Ftime_times, 0);
+
+ rb_define_method(C_Time, "to_i", Ftime_to_i, 0);
+ rb_define_method(C_Time, "<=>", Ftime_cmp, 1);
+ rb_define_method(C_Time, "hash", Ftime_hash, 0);
+
+ rb_define_method(C_Time, "localtime", Ftime_localtime, 0);
+ rb_define_method(C_Time, "gmtime", Ftime_gmtime, 0);
+ rb_define_method(C_Time, "ctime", Ftime_asctime, 0);
+ rb_define_method(C_Time, "asctime", Ftime_asctime, 0);
+ rb_define_method(C_Time, "to_s", Ftime_asctime, 0);
+ rb_define_method(C_Time, "_inspect", Ftime_asctime, 0);
+ rb_define_method(C_Time, "to_a", Ftime_to_a, 0);
+ rb_define_method(C_Time, "coerce", Ftime_coerce, 1);
+
+ rb_define_method(C_Time, "+", Ftime_plus, 1);
+ rb_define_method(C_Time, "-", Ftime_minus, 1);
+
+ rb_define_method(C_Time, "sec", Ftime_sec, 0);
+ rb_define_method(C_Time, "min", Ftime_min, 0);
+ rb_define_method(C_Time, "hour", Ftime_hour, 0);
+ rb_define_method(C_Time, "mday", Ftime_mday, 0);
+ rb_define_method(C_Time, "year", Ftime_year, 0);
+ rb_define_method(C_Time, "wday", Ftime_wday, 0);
+ rb_define_method(C_Time, "yday", Ftime_yday, 0);
+ rb_define_method(C_Time, "isdst", Ftime_isdst, 0);
+
+ rb_define_method(C_Time, "tv_sec", Ftime_to_i, 0);
+ rb_define_method(C_Time, "tv_usec", Ftime_usec, 0);
+ rb_define_method(C_Time, "usec", Ftime_usec, 0);
+
+ rb_define_method(C_Time, "strftime", Ftime_strftime, 1);
+}
diff --git a/variable.c b/variable.c
new file mode 100644
index 0000000000..27b5630170
--- /dev/null
+++ b/variable.c
@@ -0,0 +1,416 @@
+/************************************************
+
+ variable.c -
+
+ $Author: matz $
+ $Date: 1994/06/17 14:23:51 $
+ created at: Tue Apr 19 23:55:15 JST 1994
+
+************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "ident.h"
+#include "st.h"
+
+st_table *rb_global_tbl;
+st_table *rb_class_tbl;
+#define global_tbl rb_global_tbl
+#define class_tbl rb_class_tbl
+#define instance_tbl (RBASIC(Qself)->iv_tbl)
+
+st_table *new_idhash()
+{
+ return st_init_table(ST_NUMCMP, ST_NUMHASH);
+}
+
+void
+Init_var_tables()
+{
+ global_tbl = new_idhash();
+ class_tbl = new_idhash();
+}
+
+void
+rb_name_class(class, id)
+ VALUE class;
+ ID id;
+{
+ VALUE body;
+
+ if (st_lookup(class_tbl, id, &body)) {
+ Bug("%s %s already exists",
+ TYPE(body)==T_CLASS?"class":"module", rb_id2name(id));
+ }
+ st_add_direct(class_tbl, id, class);
+}
+
+struct global_entry {
+ enum { GLOBAL_VAL, GLOBAL_VAR, GLOBAL_UNDEF } mode;
+ ID id;
+ union {
+ VALUE val;
+ VALUE *var;
+ } v;
+ VALUE (*get_hook)();
+ VALUE (*set_hook)();
+};
+
+static mark_global_entry(key, entry)
+ ID key;
+ struct global_entry *entry;
+{
+ switch (entry->mode) {
+ case GLOBAL_VAL:
+ mark(entry->v.val); /* normal global value */
+ break;
+ case GLOBAL_VAR:
+ if (entry->v.var)
+ mark(*entry->v.var); /* c variable pointer */
+ break;
+ default:
+ break;
+ }
+ return ST_CONTINUE;
+}
+
+mark_global_tbl()
+{
+ st_foreach(global_tbl, mark_global_entry, 0);
+}
+
+struct global_entry*
+rb_global_entry(id)
+ ID id;
+{
+ struct global_entry *entry;
+
+ if (!st_lookup(global_tbl, id, &entry)) {
+ entry = ALLOC(struct global_entry);
+ st_insert(global_tbl, id, entry);
+ entry->id = id;
+ entry->mode = GLOBAL_UNDEF;
+ entry->v.var = Qnil;
+ entry->get_hook = entry->set_hook = Qnil;
+ }
+ return entry;
+}
+
+void
+rb_define_variable(name, var, get_hook, set_hook)
+ char *name;
+ VALUE *var;
+ VALUE (*get_hook)();
+ VALUE (*set_hook)();
+{
+ struct global_entry *entry;
+ ID id;
+
+ if (name[0] == '$') id = rb_intern(name);
+ else {
+ char *buf = (char*)alloca(strlen(name)+2);
+ buf[0] = '$';
+ strcpy(buf+1, name);
+ id = rb_intern(buf);
+ }
+
+ if (!st_lookup(global_tbl, id, &entry)) {
+ entry = rb_global_entry(id);
+ }
+ entry->mode = GLOBAL_VAR;
+ entry->v.var = var;
+ entry->get_hook = get_hook;
+ entry->set_hook = set_hook;
+}
+
+void
+rb_define_varhook(name, get_hook, set_hook)
+ char *name;
+ VALUE (*get_hook)();
+ VALUE (*set_hook)();
+{
+ struct global_entry *entry;
+ ID id;
+
+ if (name[0] == '$') id = rb_intern(name);
+ else {
+ char *buf = (char*)alloca(strlen(name)+2);
+ buf[0] = '$';
+ strcpy(buf+1, name);
+ id = rb_intern(buf);
+ }
+
+ if (!st_lookup(global_tbl, id, &entry)) {
+ entry = ALLOC(struct global_entry);
+ entry->id = id;
+ entry->mode = GLOBAL_VAL;
+ st_insert(global_tbl, id, entry);
+ }
+ else if (entry->mode == GLOBAL_UNDEF) {
+ entry->mode = GLOBAL_VAL;
+ }
+ entry->v.val = Qnil;
+ entry->get_hook = get_hook;
+ entry->set_hook = set_hook;
+}
+
+VALUE
+rb_readonly_hook(val, id)
+ VALUE val;
+ ID id;
+{
+ Fail("Can't set variable %s", rb_id2name(id));
+ /* not reached */
+}
+
+VALUE
+rb_id2class(id)
+ ID id;
+{
+ VALUE class;
+
+ if (st_lookup(class_tbl, id, &class))
+ return class;
+ return Qnil;
+}
+
+VALUE
+rb_gvar_get(entry)
+ struct global_entry *entry;
+{
+ VALUE val;
+
+ if (entry->get_hook)
+ val = (*entry->get_hook)(entry->id);
+ switch (entry->mode) {
+ case GLOBAL_VAL:
+ return entry->v.val;
+
+ case GLOBAL_VAR:
+ if (entry->v.var == Qnil) return val;
+ return *entry->v.var;
+
+ default:
+ break;
+ }
+ return Qnil;
+}
+
+rb_ivar_get_1(obj, id)
+ struct RBasic *obj;
+ ID id;
+{
+ VALUE val;
+
+ if (obj->iv_tbl == Qnil)
+ return Qnil;
+ if (st_lookup(obj->iv_tbl, id, &val))
+ return val;
+ return Qnil;
+}
+
+VALUE
+rb_ivar_get(id)
+ ID id;
+{
+ return rb_ivar_get_1(Qself, id);
+}
+
+VALUE
+rb_mvar_get(id)
+ ID id;
+{
+ VALUE val;
+
+ if (st_lookup(class_tbl, id, &val)) return val;
+ return Qnil;
+}
+
+VALUE
+rb_const_get(id)
+ ID id;
+{
+ struct RClass *class = (struct RClass*)CLASS_OF(Qself);
+ VALUE value;
+
+ while (class) {
+ if (class->c_tbl && st_lookup(class->c_tbl, id, &value)) {
+ return value;
+ }
+ class = class->super;
+ }
+ Fail("Uninitialized constant %s", rb_id2name(id));
+ /* not reached */
+}
+
+VALUE
+rb_gvar_set(entry, val)
+ struct global_entry *entry;
+ VALUE val;
+{
+ if (entry->set_hook)
+ (*entry->set_hook)(val, entry->id);
+
+ if (entry->mode == GLOBAL_VAR && entry->v.var != Qnil)
+ return *entry->v.var = val;
+ else {
+ if (entry->mode == GLOBAL_UNDEF)
+ entry->mode = GLOBAL_VAL;
+ return entry->v.val = val;
+ }
+}
+
+VALUE
+rb_gvar_set2(name, val)
+ char *name;
+ VALUE val;
+{
+ struct global_entry *entry;
+ ID id;
+
+ id = rb_intern(name);
+ if (!st_lookup(global_tbl, id, &entry)) {
+ entry = rb_global_entry(id);
+ }
+ rb_gvar_set(entry, val);
+
+ return val;
+}
+
+rb_ivar_set_1(obj, id, val)
+ struct RBasic *obj;
+ ID id;
+ VALUE val;
+{
+ if (obj->iv_tbl == Qnil) obj->iv_tbl = new_idhash();
+ st_insert(obj->iv_tbl, id, val);
+ return val;
+}
+
+VALUE
+rb_ivar_set(id, val)
+ ID id;
+ VALUE val;
+{
+ return rb_ivar_set_1(Qself, id, val);
+}
+
+static VALUE
+const_bound(id)
+ ID id;
+{
+ struct RClass *class = (struct RClass*)CLASS_OF(Qself);
+
+ while (class) {
+ if (class->c_tbl && st_lookup(class->c_tbl, id, Qnil)) {
+ return TRUE;
+ }
+ class = class->super;
+ }
+ return FALSE;
+}
+
+static void
+rb_const_set_1(class, id, val)
+ struct RClass *class;
+ ID id;
+ VALUE val;
+{
+ if (const_bound(id))
+ Fail("already initialized constnant");
+
+ if (class->c_tbl == Qnil)
+ class->c_tbl = new_idhash();
+
+ st_insert(class->c_tbl, id, val);
+}
+
+VALUE
+rb_const_set(id, val)
+ ID id;
+ VALUE val;
+{
+ rb_const_set_1(the_class, id, val);
+ return val;
+}
+
+void
+rb_define_const(class, name, val)
+ struct RClass *class;
+ char *name;
+ VALUE val;
+{
+ rb_const_set_1(class, rb_intern(name), val);
+}
+
+VALUE
+rb_iv_get(obj, name)
+ VALUE obj;
+ char *name;
+{
+ ID id = rb_intern(name);
+
+ return rb_ivar_get_1(obj, id);
+}
+
+VALUE
+rb_iv_set(obj, name, val)
+ VALUE obj;
+ char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+
+ return rb_ivar_set_1(obj, id, val);
+}
+
+VALUE
+Fdefined(obj, name)
+ VALUE obj;
+ struct RString *name;
+{
+ ID id;
+ struct global_entry *entry;
+
+ if (FIXNUM_P(name)) {
+ id = FIX2INT(name);
+ }
+ else {
+ Check_Type(name, T_STRING);
+ id = rb_intern(name->ptr);
+ }
+
+ if (id == rb_intern("nil") || id == rb_intern("self")) return TRUE;
+
+ switch (id & ID_SCOPE_MASK) {
+ case ID_GLOBAL:
+ if (st_lookup(global_tbl, id, &entry) && entry->mode != GLOBAL_UNDEF)
+ return TRUE;
+ break;
+
+ case ID_INSTANCE:
+ if (TYPE(Qself) != T_OBJECT || instance_tbl == Qnil) break;
+ if (st_lookup(instance_tbl, id, Qnil)) return TRUE;
+ break;
+
+ case ID_CONST:
+ return const_bound(id);
+ break;
+
+ default:
+ {
+ int i, max;
+
+ if (the_env->local_tbl) {
+ for (i=1, max=the_env->local_tbl[0]+1; i<max; i++) {
+ if (the_env->local_tbl[i] == id) return TRUE;
+ }
+ }
+ }
+ if (st_lookup(class_tbl, id, Qnil)) return TRUE;
+ break;
+ }
+ return FALSE;
+}
+
diff --git a/version.c b/version.c
new file mode 100644
index 0000000000..b671b46139
--- /dev/null
+++ b/version.c
@@ -0,0 +1,31 @@
+/************************************************
+
+ version.c -
+
+ $Author: matz $
+ $Revision: 1.1.1.1 $
+ $Date: 1994/06/17 14:23:51 $
+ created at: Thu Sep 30 20:08:01 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "version.h"
+#include <stdio.h>
+
+static VALUE rb_version;
+
+VALUE rb_readonly_hook();
+
+Init_version()
+{
+ rb_version = str_new2(RUBY_VERSION);
+ rb_define_variable("$VERSION", &rb_version, Qnil, rb_readonly_hook);
+}
+
+show_version()
+{
+ printf("ruby - version %s (%s)\n", RUBY_VERSION, VERSION_DATE);
+}
diff --git a/version.h b/version.h
new file mode 100644
index 0000000000..f158e8a8ad
--- /dev/null
+++ b/version.h
@@ -0,0 +1,2 @@
+#define RUBY_VERSION "0.49"
+#define VERSION_DATE "18 Jul 94"