diff options
author | Yukihiro Matsumoto <matz@ruby-lang.org> | 1994-07-18 10:19:15 +0900 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-17 22:09:29 +0900 |
commit | 200e0ee2fd3c1c006c528874a88f684447215524 (patch) | |
tree | df2252585f3000878a15123d98ccb08124306b90 |
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-IF | 135 | ||||
-rw-r--r-- | CVS/Entries | 51 | ||||
-rw-r--r-- | CVS/Repository | 1 | ||||
-rw-r--r-- | ChangeLog | 788 | ||||
-rw-r--r-- | FAQ | 65 | ||||
-rw-r--r-- | Makefile | 202 | ||||
-rw-r--r-- | Makefile.in | 186 | ||||
-rw-r--r-- | ToDo | 6 | ||||
-rw-r--r-- | array.c | 805 | ||||
-rw-r--r-- | bignum.c | 1121 | ||||
-rw-r--r-- | bring | 57 | ||||
-rw-r--r-- | class.c | 387 | ||||
-rw-r--r-- | compar.c | 99 | ||||
-rwxr-xr-x | config.status | 72 | ||||
-rw-r--r-- | configure.in | 63 | ||||
-rw-r--r-- | dbm.c | 401 | ||||
-rw-r--r-- | defines.h | 36 | ||||
-rw-r--r-- | dict.c | 517 | ||||
-rw-r--r-- | dir.c | 257 | ||||
-rw-r--r-- | dln.c | 1088 | ||||
-rw-r--r-- | dln.h | 33 | ||||
-rw-r--r-- | enum.c | 332 | ||||
-rw-r--r-- | env.h | 40 | ||||
-rw-r--r-- | error.c | 165 | ||||
-rw-r--r-- | etc.c | 220 | ||||
-rw-r--r-- | eval.c | 1858 | ||||
-rw-r--r-- | file.c | 1059 | ||||
-rw-r--r-- | gc.c | 490 | ||||
-rw-r--r-- | ident.h | 25 | ||||
-rw-r--r-- | inits.c | 50 | ||||
-rw-r--r-- | io.c | 1221 | ||||
-rw-r--r-- | io.h | 47 | ||||
-rw-r--r-- | math.c | 125 | ||||
-rw-r--r-- | methods.c | 145 | ||||
-rw-r--r-- | missing.c | 67 | ||||
-rw-r--r-- | missing/CVS/Entries | 9 | ||||
-rw-r--r-- | missing/CVS/Repository | 1 | ||||
-rw-r--r-- | missing/getopt.c | 662 | ||||
-rw-r--r-- | missing/getopt.h | 128 | ||||
-rw-r--r-- | missing/getopt1.c | 162 | ||||
-rw-r--r-- | missing/memmove.c | 24 | ||||
-rw-r--r-- | missing/mkdir.c | 103 | ||||
-rw-r--r-- | missing/strerror.c | 19 | ||||
-rw-r--r-- | missing/strftime.c | 781 | ||||
-rw-r--r-- | missing/strstr.c | 73 | ||||
-rw-r--r-- | missing/strtol.c | 84 | ||||
-rw-r--r-- | missing/strtoul.c | 184 | ||||
-rwxr-xr-x | newver.rb | 14 | ||||
-rw-r--r-- | node.h | 216 | ||||
-rw-r--r-- | numeric.c | 962 | ||||
-rw-r--r-- | object.c | 458 | ||||
-rw-r--r-- | pack.c | 849 | ||||
-rw-r--r-- | parse.y | 2585 | ||||
-rw-r--r-- | process.c | 849 | ||||
-rw-r--r-- | random.c | 80 | ||||
-rw-r--r-- | range.c | 149 | ||||
-rw-r--r-- | re.c | 442 | ||||
-rw-r--r-- | re.h | 28 | ||||
-rw-r--r-- | regex.c | 3237 | ||||
-rw-r--r-- | regex.h | 276 | ||||
-rw-r--r-- | ruby.1 | 212 | ||||
-rw-r--r-- | ruby.c | 374 | ||||
-rw-r--r-- | ruby.h | 298 | ||||
-rw-r--r-- | sample/Artistic | 117 | ||||
-rw-r--r-- | sample/aset.rb | 3 | ||||
-rw-r--r-- | sample/attr.rb | 9 | ||||
-rw-r--r-- | sample/biorhythm.rb | 201 | ||||
-rw-r--r-- | sample/caller.rb | 15 | ||||
-rw-r--r-- | sample/case.rb | 12 | ||||
-rw-r--r-- | sample/cat.rb | 4 | ||||
-rw-r--r-- | sample/cbreak.rb | 34 | ||||
-rw-r--r-- | sample/clnt.rb | 12 | ||||
-rw-r--r-- | sample/clone.rb | 12 | ||||
-rw-r--r-- | sample/const.rb | 20 | ||||
-rw-r--r-- | sample/dbm.rb | 6 | ||||
-rw-r--r-- | sample/dir.rb | 9 | ||||
-rw-r--r-- | sample/evaldef.rb | 21 | ||||
-rw-r--r-- | sample/fib.awk | 5 | ||||
-rw-r--r-- | sample/fib.pl | 10 | ||||
-rw-r--r-- | sample/fib.rb | 8 | ||||
-rw-r--r-- | sample/freq.rb | 13 | ||||
-rw-r--r-- | sample/fullpath.pl | 22 | ||||
-rw-r--r-- | sample/fullpath.rb | 24 | ||||
-rw-r--r-- | sample/gctest.rb | 69 | ||||
-rw-r--r-- | sample/getopts.rb | 111 | ||||
-rwxr-xr-x | sample/getopts.test | 31 | ||||
-rw-r--r-- | sample/hash.rb | 4 | ||||
-rw-r--r-- | sample/io.rb | 40 | ||||
-rwxr-xr-x | sample/less.rb | 30 | ||||
-rw-r--r-- | sample/list.rb | 81 | ||||
-rw-r--r-- | sample/list2.rb | 20 | ||||
-rw-r--r-- | sample/math.rb | 4 | ||||
-rwxr-xr-x | sample/mpart.rb | 42 | ||||
-rw-r--r-- | sample/newver.rb | 13 | ||||
-rw-r--r-- | sample/occur.pl | 9 | ||||
-rw-r--r-- | sample/occur.rb | 10 | ||||
-rw-r--r-- | sample/occur2.rb | 14 | ||||
-rw-r--r-- | sample/opt_s.rb | 8 | ||||
-rw-r--r-- | sample/opt_x.rb | 8 | ||||
-rw-r--r-- | sample/parsearg.rb | 69 | ||||
-rw-r--r-- | sample/perror.rb | 7 | ||||
-rw-r--r-- | sample/rcs.awk | 33 | ||||
-rw-r--r-- | sample/rcs.dat | 17 | ||||
-rw-r--r-- | sample/rcs.rb | 42 | ||||
-rw-r--r-- | sample/reach.rb | 5 | ||||
-rw-r--r-- | sample/resp.rb | 2 | ||||
-rw-r--r-- | sample/samp.rb | 12 | ||||
-rw-r--r-- | sample/split.rb | 12 | ||||
-rw-r--r-- | sample/struct.rb | 4 | ||||
-rw-r--r-- | sample/svr.rb | 23 | ||||
-rw-r--r-- | sample/system.rb | 1 | ||||
-rw-r--r-- | sample/t1.rb | 20 | ||||
-rw-r--r-- | sample/t2.rb | 24 | ||||
-rw-r--r-- | sample/test.rb | 5 | ||||
-rw-r--r-- | sample/trap.pl | 6 | ||||
-rw-r--r-- | sample/trap.rb | 3 | ||||
-rw-r--r-- | sample/tt.rb | 103 | ||||
-rw-r--r-- | socket.c | 738 | ||||
-rw-r--r-- | spec | 3244 | ||||
-rw-r--r-- | sprintf.c | 431 | ||||
-rw-r--r-- | st.c | 365 | ||||
-rw-r--r-- | st.h | 57 | ||||
-rw-r--r-- | string.c | 1552 | ||||
-rw-r--r-- | struct.c | 274 | ||||
-rw-r--r-- | time.c | 562 | ||||
-rw-r--r-- | variable.c | 416 | ||||
-rw-r--r-- | version.c | 31 | ||||
-rw-r--r-- | version.h | 2 |
128 files changed, 34584 insertions, 0 deletions
@@ -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-moderuby-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. @@ -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 @@ -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); +} @@ -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) @@ -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); +} @@ -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"); +} @@ -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 */ @@ -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("<=>"); +} @@ -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]); +} @@ -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 = █ + 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); +} @@ -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(); +} @@ -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(); +} @@ -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"); +} @@ -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(®_cache); +} @@ -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; } + |