From 200e0ee2fd3c1c006c528874a88f684447215524 Mon Sep 17 00:00:00 2001 From: Yukihiro Matsumoto Date: Mon, 18 Jul 1994 10:19:15 +0900 Subject: version 0.49 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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でもインクルードするようにした. * 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. --- io.c | 1221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1221 insertions(+) create mode 100644 io.c (limited to 'io.c') diff --git a/io.c b/io.c new file mode 100644 index 0000000000..b3ed236474 --- /dev/null +++ b/io.c @@ -0,0 +1,1221 @@ +/************************************************ + + io.c - + + $Author: matz $ + $Date: 1994/06/27 15:48:29 $ + created at: Fri Oct 15 18:08:59 JST 1993 + + Copyright (C) 1994 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include +#include +#include +#include +#include +#include + +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; i1) { + 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; ilen; 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; ilen; 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; ilen; 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(); +} -- cgit v1.2.3