From 65a5162550f58047974793cdc8067a970b2435c0 Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 13 Aug 1999 05:45:20 +0000 Subject: 1.4.0 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@520 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/Env.rb | 31 +++ lib/README | 48 +++++ lib/cgi-lib.rb | 277 +++++++++++++++++++----- lib/complex.rb | 18 +- lib/date2.rb | 267 +++++++++++++++++++++++ lib/debug.rb | 387 ++++++++++++++++++++++----------- lib/delegate.rb | 10 +- lib/e2mmap.rb | 269 ++++++++++++++--------- lib/final.rb | 41 ++++ lib/finalize.rb | 2 +- lib/ftools.rb | 28 ++- lib/ftplib.rb | 87 +++++++- lib/getoptlong.rb | 473 ++++++++++++++++++++++++++++++++++++++++ lib/getopts.rb | 18 +- lib/jcode.rb | 149 +++++-------- lib/mailread.rb | 2 +- lib/mathn.rb | 4 +- lib/matrix.rb | 130 +++++------ lib/mkmf.rb | 436 +++++++++++++++++++++++++------------ lib/monitor.rb | 229 ++++++++++++++++++++ lib/mutex_m.rb | 226 +++++-------------- lib/observer.rb | 4 +- lib/open3.rb | 55 +++++ lib/parsedate.rb | 23 +- lib/profile.rb | 55 +++++ lib/rational.rb | 2 +- lib/readbytes.rb | 36 ++++ lib/shellwords.rb | 2 +- lib/singleton.rb | 37 ++++ lib/sync.rb | 272 +++++++---------------- lib/telnet.rb | 636 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/tempfile.rb | 94 ++++++++ lib/thread.rb | 68 ++++-- lib/timeout.rb | 42 ++++ lib/tracer.rb | 2 +- lib/weakref.rb | 34 +-- 36 files changed, 3461 insertions(+), 1033 deletions(-) create mode 100644 lib/Env.rb create mode 100644 lib/README create mode 100644 lib/date2.rb create mode 100644 lib/final.rb create mode 100644 lib/getoptlong.rb create mode 100644 lib/monitor.rb create mode 100644 lib/open3.rb create mode 100644 lib/profile.rb create mode 100644 lib/readbytes.rb create mode 100644 lib/singleton.rb create mode 100644 lib/telnet.rb create mode 100644 lib/tempfile.rb create mode 100644 lib/timeout.rb (limited to 'lib') diff --git a/lib/Env.rb b/lib/Env.rb new file mode 100644 index 0000000000..b3ee3bae8a --- /dev/null +++ b/lib/Env.rb @@ -0,0 +1,31 @@ +# Env.rb -- imports environment variables as global variables +# +# Usage: +# +# require 'Env' +# p $USER +# $USER = "matz" +# p ENV["USER"] + +for k,v in ENV + next unless /^[a-zA-Z][_a-zA-Z0-9]*/ =~ k + eval < + + require "cgi-lib.rb" + CGI::print{ + CGI::tag("HTML"){ + CGI::tag("HEAD"){ CGI::tag("TITLE"){"TITLE"} } + + CGI::tag("BODY"){ + CGI::tag("FORM", {"ACTION"=>"test.rb", "METHOD"=>"POST"}){ + CGI::tag("INPUT", {"TYPE"=>"submit", "VALUE"=>"submit"}) + } + + CGI::tag("HR") + } + } + } + + +== make raw cookie string + + require "cgi-lib.rb" + cookie1 = CGI::cookie({'name' => 'name', + 'value' => 'value', + 'path' => 'path', # optional + 'domain' => 'domain', # optional + 'expires' => Time.now, # optional + 'secure' => true # optional + }) + + CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" } + + +== print HTTP header and string to $> + + require "cgi-lib.rb" + CGI::print{ "string" } + # == CGI::print("Content-Type: text/html"){ "string" } + CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" } + + +=== NPH (no-parse-header) mode + + require "cgi-lib.rb" + CGI::print("nph"){ "string" } + # == CGI::print("nph", "Content-Type: text/html"){ "string" } + CGI::print("nph", "Content-Type: text/html", cookie1, cookie2){ "string" } + + +== make HTML tag string + + require "cgi-lib.rb" + CGI::tag("element", {"attribute_name"=>"attribute_value"}){"content"} + + +== make HTTP header string + + require "cgi-lib.rb" + CGI::header # == CGI::header("Content-Type: text/html") + CGI::header("Content-Type: text/html", cookie1, cookie2) + + +=== NPH (no-parse-header) mode + + CGI::header("nph") # == CGI::header("nph", "Content-Type: text/html") + CGI::header("nph", "Content-Type: text/html", cookie1, cookie2) + + +== escape url encode + + require "cgi-lib.rb" + url_encoded_string = CGI::escape("string") + + +== unescape url encoded + + require "cgi-lib.rb" + string = CGI::unescape("url encoded string") + + +== escape HTML &"<> + + require "cgi-lib.rb" + CGI::escapeHTML("string") + + +=end require "delegate" class CGI < SimpleDelegator - attr("inputs") + CR = "\015" + LF = "\012" + EOL = CR + LF - # original is CGI.pm - def read_from_cmdline - require "shellwords.rb" - words = Shellwords.shellwords(if not ARGV.empty? then - ARGV.join(' ') - else - STDERR.print "(offline mode: enter name=value pairs on standard input)\n" if STDIN.tty? - readlines.join(' ').gsub(/\n/, '') - end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26')) - - if words.find{|x| x =~ /=/} then words.join('&') else words.join('+') end - end - # escape url encode def escape(str) - str.gsub!(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) } - str + str.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) } end # unescape url encoded def unescape(str) - str.gsub! /\+/, ' ' - str.gsub!(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") } - str + str.gsub(/\+/, ' ').gsub(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") } + end + + # escape HTML + def escapeHTML(str) + str.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/" - print title - print "\n" - print msg - print "\n" - TRUE + attr("inputs") + attr("cookie") + + # make HTML tag string + def CGI::tag(element, attributes = {}) + "<" + escapeHTML(element) + attributes.collect{|name, value| + " " + escapeHTML(name) + '="' + escapeHTML(value) + '"' + }.to_s + ">" + + (iterator? ? yield.to_s + "" : "") + end + + # make raw cookie string + def CGI::cookie(options) + "Set-Cookie: " + options['name'] + '=' + escape(options['value']) + + (options['domain'] ? '; domain=' + options['domain'] : '') + + (options['path'] ? '; path=' + options['path'] : '') + + (options['expires'] ? '; expires=' + options['expires'].strftime("%a, %d %b %Y %X %Z") : '') + + (options['secure'] ? '; secure' : '') + end + + # make HTTP header string + def CGI::header(*options) + if ENV['MOD_RUBY'] + options.each{|option| + option.sub(/(.*?): (.*)/){ + Apache::request[$1] = $2 + } + } + Apache::request.send_http_header + '' + else + if options.delete("nph") or (ENV['SERVER_SOFTWARE'] =~ /IIS/) + [(ENV['SERVER_PROTOCOL'] or "HTTP/1.0") + " 200 OK", + "Date: " + Time.now.gmtime.strftime("%a, %d %b %Y %X %Z"), + "Server: " + (ENV['SERVER_SOFTWARE'] or ""), + "Connection: close"] + + (options.empty? ? ["Content-Type: text/html"] : options) + else + options.empty? ? ["Content-Type: text/html"] : options + end.join(EOL) + EOL + EOL + end + end + + # print HTTP header and string to $> + def CGI::print(*options) + $>.print CGI::header(*options) + yield.to_s + end + + # print message to $> + def CGI::message(message, title = "", header = ["Content-Type: text/html"]) + if message.kind_of?(Hash) + title = message['title'] + header = message['header'] + message = message['body'] + end + CGI::print(*header){ + CGI::tag("HTML"){ + CGI::tag("HEAD"){ CGI.tag("TITLE"){ title } } + + CGI::tag("BODY"){ message } + } + } + true end - def CGI.error - m = $!.to_s.dup - m.gsub!(/&/, '&') - m.gsub!(//, '>') - msgs = ["
ERROR: #{m}"]
-    msgs << $@
-    msgs << "
" - CGI.message(msgs.join("\n"), "ERROR") + # print error message to $> and exit + def CGI::error + CGI::message({'title'=>'ERROR', 'body'=> + CGI::tag("PRE"){ + "ERROR: " + CGI::tag("STRONG"){ escapeHTML($!.to_s) } + "\n" + escapeHTML($@.join("\n")) + } + }) exit end end diff --git a/lib/complex.rb b/lib/complex.rb index 59caad6ebc..0af8c20b89 100644 --- a/lib/complex.rb +++ b/lib/complex.rb @@ -72,6 +72,8 @@ class Complex < Numeric end def initialize(a, b = 0) + raise "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric + raise "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric @real = a @image = b end @@ -84,7 +86,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real + other, @image) else - x , y = a.coerce(self) + x , y = other.coerce(self) x + y end end @@ -97,7 +99,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real - other, @image) else - x , y = a.coerce(self) + x , y = other.coerce(self) x - y end end @@ -110,7 +112,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real * other, @image * other) else - x , y = a.coerce(self) + x , y = other.coerce(self) x * y end end @@ -121,7 +123,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real / other, @image / other) else - x , y = a.coerce(self) + x , y = other.coerce(self) x / y end end @@ -163,7 +165,7 @@ class Complex < Numeric r, theta = polar Complex.polar(r.power!(other), theta * other) else - x , y = a.coerce(self) + x , y = other.coerce(self) x / y end end @@ -174,7 +176,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real % other, @image % other) else - x , y = a.coerce(self) + x , y = other.coerce(self) x % y end end @@ -187,7 +189,7 @@ class Complex < Numeric elsif Complex.generic?(other) Complex(@real.divmod(other), @image.divmod(other)) else - x , y = a.coerce(self) + x , y = other.coerce(self) x.divmod(y) end end @@ -222,7 +224,7 @@ class Complex < Numeric elsif Complex.generic?(other) @real == other and @image == 0 else - x , y = a.coerce(self) + x , y = other.coerce(self) x == y end end diff --git a/lib/date2.rb b/lib/date2.rb new file mode 100644 index 0000000000..58bfbe5a23 --- /dev/null +++ b/lib/date2.rb @@ -0,0 +1,267 @@ +# date2.rb: Written by Tadayoshi Funaba 1998, 1999 +# $Id: date2.rb,v 1.13 1999/08/11 01:10:02 tadf Exp $ + +class Date + + include Comparable + + MONTHNAMES = [ nil, 'January', 'February', 'March', + 'April', 'May', 'June', 'July', 'August', + 'September', 'October', 'November', 'December' ] + + DAYNAMES = [ 'Sunday', 'Monday', 'Tuesday', + 'Wednesday', 'Thursday', 'Friday', 'Saturday' ] + + ITALY = 2299161 # Oct 15, 1582 + ENGLAND = 2361222 # Sept 14, 1752 + + class << self + + def os? (jd, sg) + case sg + when Numeric; jd < sg + else; not sg + end + end + + def ns? (jd, sg) not os?(jd, sg) end + + def civil_to_jd(y, m, d, sg=true) + if m <= 2 + y -= 1 + m += 12 + end + a = (y / 100).to_i + b = 2 - a + (a / 4).to_i + jd = (365.25 * (y + 4716)).to_i + + (30.6001 * (m + 1)).to_i + + d + b - 1524 + if os?(jd, sg) + jd -= b + end + jd + end + + def jd_to_civil(jd, sg=true) + if os?(jd, sg) + a = jd + else + x = ((jd - 1867216.25) / 36524.25).to_i + a = jd + 1 + x - (x / 4).to_i + end + b = a + 1524 + c = ((b - 122.1) / 365.25).to_i + d = (365.25 * c).to_i + e = ((b - d) / 30.6001).to_i + dom = b - d - (30.6001 * e).to_i + if e <= 13 + m = e - 1 + y = c - 4716 + else + m = e - 13 + y = c - 4715 + end + return y, m, dom + end + + def ordinal_to_jd(y, d, sg=true) + civil_to_jd(y, 1, d, sg) + end + + def jd_to_ordinal(jd, sg=true) + y = jd_to_civil(jd, sg)[0] + pl = civil_to_jd(y - 1, 12, 31, ns?(jd, sg)) + doy = jd - pl + return y, doy + end + + def mjd_to_jd(mjd) mjd + 2400000.5 end + def jd_to_mjd(jd) jd - 2400000.5 end + def tjd_to_jd(tjd) tjd + 2440000.5 end + def jd_to_tjd(jd) jd - 2440000.5 end + + def julian_leap? (y) y % 4 == 0 end + def gregorian_leap? (y) y % 4 == 0 and y % 100 != 0 or y % 400 == 0 end + + alias_method :leap?, :gregorian_leap? + + def exist3? (y, m, d, sg=ITALY) + if m < 0 + m += 13 + end + if d < 0 + ljd = nil + 31.downto 1 do |ld| + break if ljd = exist3?(y, m, ld, sg) + end + x = y * 12 + m + ny = x / 12 + nm = x % 12 + 1 + d = jd_to_civil(civil_to_jd(ny, nm, 1, ns?(ljd, sg)) + d, + ns?(ljd, sg))[-1] + end + jd = civil_to_jd(y, m, d, sg) + if [y, m, d] == jd_to_civil(jd, sg) + jd + end + end + + alias_method :exist?, :exist3? + + def new3(y=-4712, m=1, d=1, sg=ITALY) + unless jd = exist3?(y, m, d, sg) + fail ArgumentError, 'invalid date' + end + new(jd, sg) + end + + def exist2? (y, d, sg=ITALY) + if d < 0 + ljd = nil + 366.downto 1 do |ld| + break if ljd = exist2?(y, ld, sg) + end + ny = y + 1 + d = jd_to_ordinal(ordinal_to_jd(ny, 1, ns?(ljd, sg)) + d, + ns?(ljd, sg))[-1] + end + jd = ordinal_to_jd(y, d, sg) + if [y, d] == jd_to_ordinal(jd, sg) + jd + end + end + + def new2(y=-4712, d=1, sg=ITALY) + unless jd = exist2?(y, d, sg) + fail ArgumentError, 'invalid date' + end + new(jd, sg) + end + + def today(sg=ITALY) + new(civil_to_jd(*(Time.now.to_a[3..5].reverse << sg)), sg) + end + + end + + def initialize(jd=0, sg=ITALY) @jd, @sg = jd, sg end + + def jd() @jd end + + def mjd + def self.mjd() @mjd end + @mjd = Date.jd_to_mjd(@jd) + end + + def tjd + def self.tjd() @tjd end + @tjd = Date.jd_to_tjd(@jd) + end + + def civil + def self.year() @year end + def self.mon() @mon end + def self.month() @mon end + def self.mday() @mday end + def self.day() @mday end + @year, @mon, @mday = Date.jd_to_civil(@jd, @sg) + end + + private :civil + + def year + civil + @year + end + + def yday + def self.yday() @yday end + @yday = Date.jd_to_ordinal(@jd, @sg)[-1] + @yday + end + + def mon + civil + @mon + end + + alias_method :month, :mon + + def mday + civil + @mday + end + + alias_method :day, :mday + + def wday + def self.wday() @wday end + @wday = (@jd + 1) % 7 + end + + def os? () Date.os?(@jd, @sg) end + def ns? () Date.ns?(@jd, @sg) end + + def leap? + def self.leap?() @leap_p end + @leap_p = Date.jd_to_civil(Date.civil_to_jd(year, 3, 1, ns?) - 1, + ns?)[-1] == 29 + end + + def + (other) + case other + when Numeric; return Date.new(@jd + other, @sg) + end + fail TypeError, 'expected numeric' + end + + def - (other) + case other + when Numeric; return Date.new(@jd - other, @sg) + when Date; return @jd - other.jd + end + fail TypeError, 'expected numeric or date' + end + + def <=> (other) + case other + when Numeric; return @jd <=> other + when Date; return @jd <=> other.jd + end + fail TypeError, 'expected numeric or date' + end + + def downto(min) + @jd.downto(min.jd) do |jd| + yield Date.new(jd, @sg) + end + self + end + + def upto(max) + @jd.upto(max.jd) do |jd| + yield Date.new(jd, @sg) + end + self + end + + def step(limit, step) + @jd.step(limit.jd, step) do |jd| + yield Date.new(jd, @sg) + end + self + end + + def succ() self + 1 end + + alias_method :next, :succ + + def eql? (other) self == other end + def hash() @jd end + def inspect() format('#', @jd, @sg) end + def to_s() format('%.4d-%02d-%02d', year, mon, mday) end + + def _dump(limit) Marshal.dump([@jd, @sg], -1) end + def Date._load(str) Date.new(*Marshal.load(str)) end + +end diff --git a/lib/debug.rb b/lib/debug.rb index 90270a3fe7..d2f1da83ff 100644 --- a/lib/debug.rb +++ b/lib/debug.rb @@ -1,13 +1,29 @@ - class DEBUGGER__ + begin + require 'readline' + def readline(prompt, hist) + Readline::readline(prompt, hist) + end + rescue LoadError + def readline(prompt, hist) + STDOUT.print prompt + STDOUT.flush + line = STDIN.gets + line.chomp! + line + end + USE_READLINE = false + end + trap("INT") { DEBUGGER__::CONTEXT.interrupt } - $DEBUG = TRUE + $DEBUG = true def initialize @break_points = [] + @display = [] @stop_next = 1 @frames = [nil] - @frame_pos = nil @last_file = nil + @last = [nil, nil] @scripts = {} end @@ -23,185 +39,271 @@ class DEBUGGER__ val rescue at = caller(0) - printf "%s:%s\n", at.shift, $! + STDOUT.printf "%s:%s\n", at.shift, $! for i in at break if i =~ /`debug_(eval|command)'$/ #` - printf "\tfrom %s\n", i + STDOUT.printf "\tfrom %s\n", i end end end def debug_command(file, line, id, binding) + frame_pos = 0 + binding_file = file + binding_line = line + previus_line = nil if (ENV['EMACS'] == 't') - printf "\032\032%s:%d:\n", file, line + STDOUT.printf "\032\032%s:%d:\n", binding_file, binding_line else - printf "%s:%d:%s", file, line, line_at(file, line) + STDOUT.printf "%s:%d:%s", binding_file, binding_line, + line_at(binding_file, binding_line) end - @frames[-1] = binding - STDOUT.print "(rdb:-) " - STDOUT.flush - while input = STDIN.gets - input.chop! + @frames[0] = binding + display_expressions(binding) + while input = readline("(rdb:-) ", true) if input == "" input = DEBUG_LAST_CMD[0] else DEBUG_LAST_CMD[0] = input end + case input - when /^b(reak)?\s+(([^:\n]+:)?.+)/ - pos = $2 - if pos.index ":" + when /^b(?:reak)?\s+((?:[^:\n]+:)?.+)$/ + pos = $1 + if pos.index(":") file, pos = pos.split(":") end file = File.basename(file) if pos =~ /^\d+$/ pname = pos - pos = Integer(pos) + pos = pos.to_i else pname = pos = pos.intern.id2name end - printf "Set breakpoint %d at %s:%s\n", @break_points.size, file, pname - @break_points.push [file, pos] - when /^b(reak)?$/, /^info b(reak)?$/ - n = 0 - for f, p in @break_points - printf "%d %s:%s\n", n, f, p + @break_points.push [true, 0, file, pos] + STDOUT.printf "Set breakpoint %d at %s:%s\n", @break_points.size, file, + pname + + when /^wat(?:ch)?\s+(.+)$/ + exp = $1 + @break_points.push [true, 1, exp] + STDOUT.printf "Set watchpoint %d\n", @break_points.size, exp + + when /^b(?:reak)?$/, /^info b(?:reak)?$/ + n = 1 + STDOUT.print "breakpoints:\n" + for b in @break_points + if b[0] and (b[1] == 0) + STDOUT.printf " %d %s:%s\n", n, b[2], b[3] + end n += 1 end - when /^del(ete)?(\s+(\d+))?$/ - pos = $3 + n = 1 + STDOUT.print "\n" + STDOUT.print "watchpoints:\n" + for b in @break_points + if b[0] and (b[1] == 1) + STDOUT.printf " %d %s\n", n, b[2] + end + n += 1 + end + STDOUT.print "\n" + + when /^del(?:ete)?(?:\s+(\d+))?$/ + pos = $1 unless pos - STDOUT.print "clear all breakpoints? (y/n) " - STDOUT.flush - input = STDIN.gets.chop! + input = readline("clear all breakpoints? (y/n) ", false) if input == "y" - for n in @break_points.indexes - @break_points[n] = nil + for b in @break_points + b[0] = false end end else - pos = Integer(pos) - if @break_points[pos] - bp = @break_points[pos] - printf "Clear breakpoint %d at %s:%s\n", pos, bp[0], bp[1] - @break_points[pos] = nil + pos = pos.to_i + if @break_points[pos-1] + @break_points[pos-1][0] = false else - printf "Breakpoint %d is not defined\n", pos + STDOUT.printf "Breakpoint %d is not defined\n", pos end end - when /^c(ont)?$/ - return - when /^s(tep)?\s*(\d+)?$/ - if $1 - lev = Integer($1) + + when /^disp(?:lay)?\s+(.+)$/ + exp = $1 + @display.push.push [true, exp] + STDOUT.printf " %d: %s = %s\n", @display.size, exp, + debug_eval(exp, binding).to_s + + when /^disp(?:lay)?$/, /^info disp(?:lay)?$/ + display_expressions(binding) + + when /^undisp(?:lay)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("clear all expressions? (y/n) ", false) + if input == "y" + for d in @display + d[0] = false + end + end else - lev = 1 + pos = pos.to_i + if @display[pos-1] + @display[pos-1][0] = false + else + STDOUT.printf "display expression %d is not defined\n", pos + end end - @stop_next = lev + + when /^c(?:ont)?$/ return - when /^n(ext)?\s*(\d+)?$/ + + when /^s(?:tep)?(?:\s+(\d+))?$/ if $1 - lev = Integer($1) + lev = $1.to_i else lev = 1 end @stop_next = lev - @no_step = @frames.size return - when /^up\s*(\d+)?$/ - if $1 - lev = Integer($1) - else - lev = 1 - end - unless @frame_pos - @frame_pos = @frames.size - 1 - end - @frame_pos -= lev - if @frame_pos < 0 - STDOUT.print "at toplevel\n" - @frame_pos = 0 - else - binding = @frames[@frame_pos] - end - when /^down\s*(\d+)??$/ + + when /^n(?:ext)?(?:\s+(\d+))?$/ if $1 - lev = Integer($1) + lev = $1.to_i else lev = 1 end - if lev >= @frames.size or @frame_pos and @frame_pos+lev >= @frames.size - STDOUT.print "at stack bottom\n" - @frame_pos = nil - else - @frame_pos += lev - binding = @frames[@frame_pos] - end - when /^fin(ish)?$/ - @finish_pos = @frames.size + @stop_next = lev + @no_step = @frames.size - frame_pos return - when /^q(uit)?$/ - STDOUT.print "really quit? (y/n) " - STDOUT.flush - input = STDIN.gets.chop! - exit if input == "y" - when /^where$/ - at = caller(4) - for i in at - printf " %s\n", i - end - when /^l(ist)?(\s+(.*))?$/ - if $3 - b, e = $3.split(/[-,]/) - b = Integer(b)-1 - if e - e = Integer(e)-1 + + when /^w(?:here)?$/, /^f(?:rame)?$/ + at = caller(0) + 0.upto(@frames.size - 1) do |n| + if frame_pos == n + STDOUT.printf "--> #%d %s\n", n, at[-(@frames.size - n)] else - e = b + 10 + STDOUT.printf " #%d %s\n", n, at[-(@frames.size - n)] end end - unless b - b = line - 1 - e = line + 9 + + when /^l(?:ist)?(?:\s+(.+))?$/ + if not $1 + b = previus_line ? previus_line + 10 : binding_line - 5 + e = b + 9 + elsif $1 == '-' + b = previus_line ? previus_line - 10 : binding_line - 5 + e = b + 9 + else + b, e = $1.split(/[-,]/) + if e + b = b.to_i + e = e.to_i + else + b = b.to_i - 5 + e = b + 9 + end + end + previus_line = b + STDOUT.printf "[%d, %d] in %s\n", b, e, binding_file + line_at(binding_file, binding_line) + if lines = @scripts[binding_file] and lines != true + n = 0 + b.upto(e) do |n| + if n > 0 && lines[n-1] + if n == binding_line + STDOUT.printf "=> %d %s\n", n, lines[n-1].chomp + else + STDOUT.printf " %d %s\n", n, lines[n-1].chomp + end + end + end + else + STDOUT.printf "no sourcefile available for %s\n", binding_file + end + + when /^up(?:\s+(\d+))?$/ + previus_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos += lev + if frame_pos >= @frames.size + frame_pos = @frames.size - 1 + STDOUT.print "at toplevel\n" end - p [b,e] - line_at(file, line) - if lines = @scripts[file] and lines != TRUE - n = b+1 - for l in lines[b..e] - printf "%4d %s", n, l - n += 1 - end - else - printf "no sourcefile available for %s\n", file + binding = @frames[frame_pos] + info, binding_file, binding_line = frame_info(frame_pos) + STDOUT.printf "#%d %s\n", frame_pos, info + + when /^down(?:\s+(\d+))?$/ + previus_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos -= lev + if frame_pos < 0 + frame_pos = 0 + STDOUT.print "at stack bottom\n" end + binding = @frames[frame_pos] + info, binding_file, binding_line = frame_info(frame_pos) + STDOUT.printf "#%d %s\n", frame_pos, info + + when /^fi(?:nish)?$/ + @finish_pos = @frames.size - frame_pos + frame_pos = 0 + return + + when /^q(?:uit)?$/ + input = readline("really quit? (y/n) ", false) + exit if input == "y" + when /^p\s+/ - p debug_eval($', binding) #' + p debug_eval($', binding) + else v = debug_eval(input, binding) - p v unless v == nil + p v unless (v == nil) end - STDOUT.print "(rdb:-) " - STDOUT.flush end end + def display_expressions(binding) + n = 1 + for d in @display + if d[0] + STDOUT.printf "%d: %s = %s\n", n, d[1], debug_eval(d[1], binding).to_s + end + n += 1 + end + end + + def frame_info(pos = 0) + info = caller(0)[-(@frames.size - pos)] + info.sub(/:in `.*'$/, "") =~ /^(.*):(\d+)$/ #` + [info, $1, $2.to_i] + end + def line_at(file, line) lines = @scripts[file] if lines - return "\n" if lines == TRUE + return "\n" if lines == true line = lines[line-1] return "\n" unless line return line end save = $DEBUG begin - $DEBUG = FALSE + $DEBUG = false f = open(file) lines = @scripts[file] = f.readlines rescue $DEBUG = save - @scripts[file] = TRUE + @scripts[file] = true return "\n" end line = lines[line-1] @@ -219,19 +321,44 @@ class DEBUGGER__ def check_break_points(file, pos, binding, id) file = File.basename(file) - if @break_points.include? [file, pos] - index = @break_points.index([file, pos]) - printf "Breakpoint %d, %s at %s:%s\n", - index, debug_funcname(id), file, pos - return TRUE + n = 1 + for b in @break_points + if b[0] + if b[1] == 0 and b[2] == file and b[3] == pos + STDOUT.printf "breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), + file, pos + return true + elsif b[1] == 1 and debug_eval(b[2], binding) + STDOUT.printf "watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), + file, pos + return true + end + end + n += 1 + end + return false + end + + def excn_handle(file, line, id, binding) + fs = @frames.size + tb = caller(0)[-fs..-1] + + STDOUT.printf "%s\n", $! + for i in tb + STDOUT.printf "\tfrom %s\n", i end - return FALSE + debug_command(file, line, id, binding) end def trace_func(event, file, line, id, binding) - if event == 'line' - if @no_step == nil or @no_step >= @frames.size + case event + when 'line' + if !@no_step or @frames.size == @no_step @stop_next -= 1 + elsif @frames.size < @no_step + @stop_next = 0 # break here before leaving... + else + # nothing to do. skipped. end if @stop_next == 0 if [file, line] == @last @@ -245,21 +372,25 @@ class DEBUGGER__ if check_break_points(file, line, binding, id) debug_command(file, line, id, binding) end - end - if event == 'call' - @frames.push binding + + when 'call' + @frames.unshift binding if check_break_points(file, id.id2name, binding, id) debug_command(file, line, id, binding) end - end - if event == 'class' - @frames.push binding - end - if event == 'return' or event == 'end' - if @finish_pos == @frames.size + + when 'class' + @frames.unshift binding + + when 'return', 'end' + if @frames.size == @finish_pos @stop_next = 1 end - @frames.pop + @frames.shift + + when 'raise' + excn_handle(file, line, id, binding) + end @last_file = file end @@ -267,6 +398,6 @@ class DEBUGGER__ CONTEXT = new end -set_trace_func proc{|event, file, line, id, binding| +set_trace_func proc{|event, file, line, id, binding,*rest| DEBUGGER__::CONTEXT.trace_func event, file, line, id, binding } diff --git a/lib/delegate.rb b/lib/delegate.rb index 0771f2feeb..29f2a5ded2 100644 --- a/lib/delegate.rb +++ b/lib/delegate.rb @@ -34,14 +34,8 @@ class Delegator begin __getobj__.__send__(:#{method}, *args, &block) rescue Exception - c = -caller(0).size - if /:in `__getobj__'$/ =~ $@[c-1] #` - n = 1 - else - c -= 1 - n = 2 - end - $@[c,n] = nil + $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #` + $@.delete_if{|s| /^\\(eval\\):/ =~ s} raise end end diff --git a/lib/e2mmap.rb b/lib/e2mmap.rb index bf860dc5c1..e04ed4a5b4 100644 --- a/lib/e2mmap.rb +++ b/lib/e2mmap.rb @@ -1,17 +1,46 @@ # # e2mmap.rb - for ruby 1.1 -# $Release Version: 1.2$ -# $Revision: 1.8 $ -# $Date: 1998/08/19 15:22:22 $ +# $Release Version: 2.0$ +# $Revision: 1.10 $ +# $Date: 1999/02/17 12:33:17 $ # by Keiju ISHITSUKA # # -- # Usage: # +# U1) # class Foo # extend Exception2MassageMapper +# def_e2message ExistingExceptionClass, "message..." # def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# +# U2) +# module Error +# extend Exception2MassageMapper # def_e2meggage ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# include Exp +# ... +# end +# +# foo = Foo.new +# foo.Fail .... +# +# U3) +# module Error +# extend Exception2MassageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# extend Exception2MessageMapper +# include Error # ... # end # @@ -19,113 +48,149 @@ # Foo.Fail ExistingExceptionClass, arg... # # -if VERSION < "1.1" - require "e2mmap1_0.rb" -else +fail "Use Ruby 1.1" if VERSION < "1.1" + +module Exception2MessageMapper + @RCS_ID='-$Id: e2mmap.rb,v 1.10 1999/02/17 12:33:17 keiju Exp keiju $-' + + E2MM = Exception2MessageMapper + + def E2MM.extend_object(cl) + super + cl.bind(self) unless cl == E2MM + end - module Exception2MessageMapper - @RCS_ID='-$Id: e2mmap.rb,v 1.8 1998/08/19 15:22:22 keiju Exp keiju $-' - - E2MM = Exception2MessageMapper + # 以前との互換性のために残してある. + def E2MM.extend_to(b) + c = eval("self", b) + c.extend(self) + end - def E2MM.extend_object(cl) + def bind(cl) + self.module_eval %[ + def Raise(err = nil, *rest) + Exception2MessageMapper.Raise(self.type, err, *rest) + end + alias Fail Raise + + def self.append_features(mod) + super + mod.extend Exception2MessageMapper + end + ] + end + + # Fail(err, *rest) + # err: 例外 + # rest: メッセージに渡すパラメータ + # + def Raise(err = nil, *rest) + E2MM.Raise(self, err, *rest) + end + alias Fail Raise + + # 過去の互換性のため + alias fail! fail + def fail(err = nil, *rest) + begin + E2MM.Fail(self, err, *rest) + rescue E2MM::ErrNotRegisteredException super - cl.bind(self) end - - # backward compatibility - def E2MM.extend_to(b) - c = eval("self", b) - c.extend(self) - end - - # public :fail - alias fail! fail + end + class << self + public :fail + end - #def fail(err = nil, *rest) - # super - #end + + # def_e2message(c, m) + # c: exception + # m: message_form + # 例外cのメッセージをmとする. + # + def def_e2message(c, m) + E2MM.def_e2message(self, c, m) + end + + # def_exception(c, m) + # n: exception_name + # m: message_form + # s: 例外スーパークラス(デフォルト: StandardError) + # 例外名``c''をもつ例外を定義し, そのメッセージをmとする. + # + def def_exception(n, m, s = StandardError) + E2MM.def_exception(self, n, m, s) + end - def Fail(err = nil, *rest) - Exception2MessageMapper.Fail Exception2MessageMapper::ErrNotRegisteredException, err.inspect - end - - def bind(cl) - self.module_eval %q^ - E2MM_ErrorMSG = {} unless self.const_defined?(:E2MM_ErrorMSG) - # fail(err, *rest) - # err: Exception - # rest: Parameter accompanied with the exception - # - def self.Fail(err = nil, *rest) - if form = E2MM_ErrorMSG[err] - $! = err.new(sprintf(form, *rest)) - $@ = caller(0) if $@.nil? - $@.shift - # e2mm_fail() - raise() -# elsif self == Exception2MessageMapper -# fail Exception2MessageMapper::ErrNotRegisteredException, err.to_s - else -# print "super\n" - super - end - end + # + # Private definitions. + # + # {[class, exp] => message, ...} + @MessageMap = {} + + # E2MM.def_exception(k, e, m) + # k: 例外を定義するクラス + # e: exception + # m: message_form + # 例外cのメッセージをmとする. + # + def E2MM.def_e2message(k, c, m) + E2MM.instance_eval{@MessageMap[[k, c]] = m} + c + end + + # E2MM.def_exception(k, c, m) + # k: 例外を定義するクラス + # n: exception_name + # m: message_form + # s: 例外スーパークラス(デフォルト: StandardError) + # 例外名``c''をもつ例外を定義し, そのメッセージをmとする. + # + def E2MM.def_exception(k, n, m, s = StandardError) + n = n.id2name if n.kind_of?(Fixnum) + e = Class.new(s) + E2MM.instance_eval{@MessageMap[[k, e]] = m} + k.const_set(n, e) + end - # 過去の互換性のため - def self.fail(err = nil, *rest) - if form = E2MM_ErrorMSG[err] - $! = err.new(sprintf(form, *rest)) - $@ = caller(0) if $@.nil? - $@.shift - # e2mm_fail() - raise() -# elsif self == Exception2MessageMapper -# fail Exception2MessageMapper::ErrNotRegisteredException, err.to_s - else -# print "super\n" - super - end - end - class << self - public :fail - end - - # def_exception(c, m) - # c: exception - # m: message_form - # - def self.def_e2message(c, m) - E2MM_ErrorMSG[c] = m - end - - # def_exception(c, m) - # n: exception_name - # m: message_form - # s: superclass_of_exception (default: Exception) - # defines excaption named ``c'', whose message is ``m''. - # - #def def_exception(n, m) - def self.def_exception(n, m, s = nil) - n = n.id2name if n.kind_of?(Fixnum) - unless s - if defined?(StandardError) - s = StandardError - else - s = Exception - end - end - e = Class.new(s) + # Fail(klass, err, *rest) + # klass: 例外の定義されているクラス + # err: 例外 + # rest: メッセージに渡すパラメータ + # + def E2MM.Raise(klass = E2MM, err = nil, *rest) + if form = e2mm_message(klass, err) + $! = err.new(sprintf(form, *rest)) + $@ = caller(1) if $@.nil? + #p $@ + #p __FILE__ + $@.shift if $@[0] =~ /^#{Regexp.quote(__FILE__)}:/ + raise + else + E2MM.Fail E2MM, ErrNotRegisteredException, err.inspect + end + end + class < TOO_BIG + fmode = stat(from).mode + tpath = to + from = open(from, "r") from.binmode to = open(to, "w") @@ -47,6 +50,7 @@ class << File to.close from.close end + chmod(fmode, tpath) ret end @@ -63,13 +67,24 @@ class << File to = catname(from, to) $stderr.print from, " -> ", to, "\n" if verbose - if PLATFORM =~ /djgpp|cygwin|mswin32/ and FileTest.file? to + if RUBY_PLATFORM =~ /djgpp|cygwin|mswin32/ and FileTest.file? to unlink to end + fstat = stat(from) begin rename from, to rescue - syscopy from, to and unlink from + begin + symlink File.readlink(from), to and unlink from + rescue + from_stat = stat(from) + syscopy from, to and unlink from + utime(from_stat.atime, from_stat.mtime, to) + begin + chown(fstat.uid, fstat.gid, tpath) + rescue + end + end end end @@ -98,7 +113,8 @@ class << File if fr = from.read(fsize) tr = to.read(fr.size) else - ret = !to.read(fsize) + ret = to.read(fsize) + ret = !ret || ret.length == 0 break end end @@ -137,7 +153,9 @@ class << File parent = dirname(dir) makedirs parent unless FileTest.directory? parent $stderr.print "mkdir ", dir, "\n" if verbose - Dir.mkdir dir, mode + if basename(dir) != "" + Dir.mkdir dir, mode + end end end @@ -154,7 +172,7 @@ class << File def install(from, to, mode = nil, verbose = false) to = catname(from, to) unless FileTest.exist? to and cmp from, to - unlink to if FileTest.exist? to + safe_unlink to if FileTest.exist? to cp from, to, verbose chmod mode, to, verbose if mode end diff --git a/lib/ftplib.rb b/lib/ftplib.rb index 617d85899b..9336e0a8c2 100644 --- a/lib/ftplib.rb +++ b/lib/ftplib.rb @@ -150,16 +150,23 @@ class FTP end def sendport(host, port) - hbytes = host.split(".") - pbytes = [port / 256, port % 256] - bytes = hbytes + pbytes - cmd = "PORT " + bytes.join(",") + af = (@sock.peeraddr)[0] + if af == "AF_INET" + hbytes = host.split(".") + pbytes = [port / 256, port % 256] + bytes = hbytes + pbytes + cmd = "PORT " + bytes.join(",") + elsif af == "AF_INET6" + cmd = "EPRT |2|" + host + "|" + sprintf("%d", port) + "|" + else + raise FTPProtoError, host + end voidcmd(cmd) end private :sendport def makeport - sock = TCPserver.open(0) + sock = TCPserver.open(@sock.addr[3], 0) port = sock.addr[1] host = TCPsocket.getaddress(@sock.addr[2]) resp = sendport(host, port) @@ -167,9 +174,20 @@ class FTP end private :makeport + def makepasv + if @sock.peeraddr[0] == "AF_INET" + host, port = parse227(sendcmd("PASV")) + else + host, port = parse229(sendcmd("EPSV")) +# host, port = parse228(sendcmd("LPSV")) + end + return host, port + end + private :makepasv + def transfercmd(cmd) if @passive - host, port = parse227(sendcmd("PASV")) + host, port = makepasv conn = open_socket(host, port) resp = sendcmd(cmd) if resp[0] != ?1 @@ -293,11 +311,7 @@ class FTP buf = file.gets break if buf == nil if buf[-2, 2] != CRLF - if buf[-1] == ?\r or - buf[-1] == ?\n - buf = buf[0 .. -2] - end - buf = buf + CRLF + buf = buf.chop + CRLF end conn.write(buf) callback.call(buf) if use_callback @@ -545,6 +559,57 @@ class FTP end private :parse227 + def parse228(resp) + if resp[0, 3] != "228" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(",") + if numbers[0] == "4" + if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2" + raise FTPProtoError, resp + end + host = numbers[2, 4].join(".") + port = (numbers[7].to_i << 8) + numbers[8].to_i + elsif numbers[0] == "6" + if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2" + raise FTPProtoError, resp + end + v6 = ["", "", "", "", "", "", "", ""] + for i in 0 .. 7 + v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i, + numbers[(i * 2) + 3].to_i) + end + host = v6[0, 8].join(":") + port = (numbers[19].to_i << 8) + numbers[20].to_i + end + return host, port + end + private :parse228 + + def parse229(resp) + if resp[0, 3] != "229" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1]) + if numbers.length != 4 + raise FTPProtoError, resp + end + port = numbers[3].to_i + host = (@sock.peeraddr())[3] + return host, port + end + private :parse228 + def parse257(resp) if resp[0, 3] != "257" raise FTPReplyError, resp diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb new file mode 100644 index 0000000000..a37714cafd --- /dev/null +++ b/lib/getoptlong.rb @@ -0,0 +1,473 @@ +# -*- Ruby -*- +# Copyright (C) 1998 Motoyuki Kasahara +# +# 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. +# + +# +# Documents and latest version of `getoptlong.rb' are found at: +# http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/ +# + +# +# Parse command line options just like GNU getopt_long(). +# +class GetoptLong + # + # Orderings. + # + ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2] + + # + # Argument flags. + # + ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1, + OPTIONAL_ARGUMENT = 2] + + # + # Status codes. + # + STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0..2 + + # + # Error types. + # + class AmbigousOption < StandardError; end + class NeedlessArgument < StandardError; end + class MissingArgument < StandardError; end + class InvalidOption < StandardError; end + + # + # Initializer. + # + def initialize(*arguments) + # + # Current ordering. + # + if ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = PERMUTE + end + + # + # Hash table of option names. + # Keyes of the table are option names, and their values are canonical + # names of the options. + # + @canonical_names = Hash.new + + # + # Hash table of argument flags. + # Keyes of the table are option names, and their values are argument + # flags of the options. + # + @argument_flags = Hash.new + + # + # Whether error messages are output to stderr. + # + @quiet = FALSE + + # + # Status code. + # + @status = STATUS_YET + + # + # Error code. + # + @error = nil + + # + # Error message. + # + @error_message = nil + + # + # Rest of catinated short options. + # + @rest_singles = '' + + # + # List of non-option-arguments. + # Append them to ARGV when option processing is terminated. + # + @non_option_arguments = Array.new + + if 0 < arguments.length + set_options(*arguments) + end + end + + # + # Set ordering. + # + def ordering=(ordering) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + set_error(ArgumentError, "argument error") + raise RuntimeError, + "invoke ordering=, but option processing has already started" + end + + # + # Check ordering. + # + if !ORDERINGS.include?(ordering) + raise ArgumentError, "invalid ordering `#{ordering}'" + end + if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = ordering + end + end + + # + # Return ordering. + # + attr_reader :ordering + + # + # Set options + # + def set_options(*arguments) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + raise RuntimeError, + "invoke set_options, but option processing has already started" + end + + # + # Clear tables of option names and argument flags. + # + @canonical_names.clear + @argument_flags.clear + + arguments.each do |arg| + # + # Each argument must be an Array. + # + if !arg.is_a?(Array) + raise ArgumentError, "the option list contains non-Array argument" + end + + # + # Find an argument flag and it set to `argument_flag'. + # + argument_flag = nil + arg.each do |i| + if ARGUMENT_FLAGS.include?(i) + if argument_flag != nil + raise ArgumentError, "too many argument-flags" + end + argument_flag = i + end + end + raise ArgumentError, "no argument-flag" if argument_flag == nil + + canonical_name = nil + arg.each do |i| + # + # Check an option name. + # + next if i == argument_flag + begin + if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/ + raise ArgumentError, "an invalid option `#{i}'" + end + if (@canonical_names.include?(i)) + raise ArgumentError, "option redefined `#{i}'" + end + rescue + @canonical_names.clear + @argument_flags.clear + raise + end + + # + # Register the option (`i') to the `@canonical_names' and + # `@canonical_names' Hashes. + # + if canonical_name == nil + canonical_name = i + end + @canonical_names[i] = canonical_name + @argument_flags[i] = argument_flag + end + raise ArgumentError, "no option name" if canonical_name == nil + end + return self + end + + # + # Set/Unset `quit' mode. + # + attr_writer :quiet + + # + # Return the flag of `quiet' mode. + # + attr_reader :quiet + + # + # `quiet?' is an alias of `quiet'. + # + alias quiet? quiet + + # + # Termintate option processing. + # + def terminate + return if @status == STATUS_TERMINATED + raise RuntimeError, "an error has occured" if @error != nil + + @status = STATUS_TERMINATED + @non_option_arguments.reverse_each do |argument| + ARGV.unshift(argument) + end + + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + return self + end + + # + # Examine whether option processing is termintated or not. + # + def terminated? + return @status == STATUS_TERMINATED + end + + # + # Set an error (protected). + # + def set_error(type, message) + $stderr.print("#{$0}: #{message}\n") if !@quiet + + @error = type + @error_message = message + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + raise type, message + end + protected :set_error + + # + # Examine whether an option processing is failed. + # + attr_reader :error + + # + # `error?' is an alias of `error'. + # + alias error? error + + # + # Return an error message. + # + def error_message + return @error_message + end + + # + # Get next option name and its argument as an array. + # + def get + name, argument = nil, '' + + # + # Check status. + # + return if @error != nil + case @status + when STATUS_YET + @status = STATUS_STARTED + when STATUS_TERMINATED + return + end + + # + # Get next option argument. + # + if 0 < @rest_singles.length + $_ = '-' + @rest_singles + elsif (ARGV.length == 0) + terminate + return nil + elsif @ordering == PERMUTE + while 0 < ARGV.length && ARGV[0] !~ /^-./ + @non_option_arguments.push(ARGV.shift) + end + if ARGV.length == 0 + terminate + return + end + $_ = ARGV.shift + elsif @ordering == REQUIRE_ORDER + if (ARGV[0] !~ /^-./) + terminate + return nil + end + $_ = ARGV.shift + else + $_ = ARGV.shift + end + + # + # Check the special argument `--'. + # `--' indicates the end of the option list. + # + if $_ == '--' && @rest_singles.length == 0 + terminate + return nil + end + + # + # Check for long and short options. + # + if /^(--[^=]+)/ && @rest_singles.length == 0 + # + # This is a long style option, which start with `--'. + # + pattern = $1 + if @canonical_names.include?(pattern) + name = pattern + else + # + # The option `name' is not registered in `@canonical_names'. + # It may be an abbreviated. + # + match_count = 0 + @canonical_names.each_key do |key| + if key.index(pattern) == 0 + name = key + match_count += 1 + end + end + if 2 <= match_count + set_error(AmbigousOption, "option `#{$_}' is ambiguous") + elsif match_count == 0 + set_error(InvalidOption, "unrecognized option `#{$_}'") + end + end + + # + # Check an argument to the option. + # + if @argument_flags[name] == REQUIRED_ARGUMENT + if /=(.*)$/ + argument = $1 + elsif 0 < ARGV.length + argument = ARGV.shift + else + set_error(MissingArgument, "option `#{$_}' requires an argument") + end + elsif @argument_flags[name] == OPTIONAL_ARGUMENT + if /=(.*)$/ + argument = $1 + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + argument = ARGV.shift + else + argument = '' + end + elsif /=(.*)$/ + set_error(NeedlessArgument, + "option `#{name}' doesn't allow an argument") + end + + elsif /^(-(.))(.*)/ + # + # This is a short style option, which start with `-' (not `--'). + # Short options may be catinated (e.g. `-l -g' is equivalent to + # `-lg'). + # + name, ch, @rest_singles = $1, $2, $3 + + if @canonical_names.include?(name) + # + # The option `name' is found in `@canonical_names'. + # Check its argument. + # + if @argument_flags[name] == REQUIRED_ARGUMENT + if 0 < @rest_singles.length + argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length + argument = ARGV.shift + else + # 1003.2 specifies the format of this message. + set_error(MissingArgument, "option requires an argument -- #{ch}") + end + elsif @argument_flags[name] == OPTIONAL_ARGUMENT + if 0 < @rest_singles.length + argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + argument = ARGV.shift + else + argument = '' + end + end + else + # + # This is an invalid option. + # 1003.2 specifies the format of this message. + # + if ENV.include?('POSIXLY_CORRECT') + set_error(InvalidOption, "illegal option -- #{ch}") + else + set_error(InvalidOption, "invalid option -- #{ch}") + end + end + else + # + # This is a non-option argument. + # Only RETURN_IN_ORDER falled into here. + # + return '', $_ + end + + return @canonical_names[name], argument + end + + # + # `get_option' is an alias of `get'. + # + alias get_option get + + # + # Iterator version of `get'. + # + def each + loop do + name, argument = get_option + break if name == nil + yield name, argument + end + end + + # + # `each_option' is an alias of `each'. + # + alias each_option each +end diff --git a/lib/getopts.rb b/lib/getopts.rb index 9e1e8a2cf6..5b9562d5b2 100644 --- a/lib/getopts.rb +++ b/lib/getopts.rb @@ -15,7 +15,7 @@ $RCS_ID=%q$Header$ def isSingle(lopt) if lopt.index(":") if lopt.split(":")[0].length == 1 - return TRUE + return true end end return nil @@ -87,13 +87,13 @@ def getopts(single_opts, *options) return nil end setOption(compare, ARGV[1]) - opts[compare] = TRUE + opts[compare] = true ARGV.shift count += 1 break elsif lo == compare - setOption(compare, TRUE) - opts[compare] = TRUE + setOption(compare, true) + opts[compare] = true count += 1 break end @@ -106,19 +106,19 @@ def getopts(single_opts, *options) for idx in 1..(ARGV[0].length - 1) compare = ARGV[0][idx, 1] if single_opts && compare =~ "[" + single_opts + "]" - setOption(compare, TRUE) - opts[compare] = TRUE + setOption(compare, true) + opts[compare] = true count += 1 elsif single_colon != "" && compare =~ "[" + single_colon + "]" if ARGV[0][idx..-1].length > 1 setOption(compare, ARGV[0][(idx + 1)..-1]) - opts[compare] = TRUE + opts[compare] = true count += 1 elsif ARGV.length <= 1 return nil else setOption(compare, ARGV[1]) - opts[compare] = TRUE + opts[compare] = true ARGV.shift count += 1 end @@ -127,7 +127,7 @@ def getopts(single_opts, *options) end else compare = ARGV[0] - opts[compare] = TRUE + opts[compare] = true newargv << ARGV[0] end diff --git a/lib/jcode.rb b/lib/jcode.rb index 50b7beee9d..92b9284471 100644 --- a/lib/jcode.rb +++ b/lib/jcode.rb @@ -12,9 +12,10 @@ class String private :original_succ def mbchar? - if $KCODE =~ /^s/i + case $KCODE[0] + when ?s, ?S self =~ /[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]/n - elsif $KCODE =~ /^e/i + when ?e, ?E self =~ /[\xa1-\xfe][\xa1-\xfe]/n else false @@ -22,16 +23,15 @@ class String end def succ - if self[-2] && self[-2] & 0x80 != 0 + if self[-2] and self[-2, 2].mbchar? s = self.dup s[-1] += 1 - s[-1] += 1 if !s.mbchar? + s[-1] += 1 unless s[-2, 2].mbchar? return s else original_succ end end - alias next succ def upto(to) return if self > to @@ -58,9 +58,11 @@ class String return nil end - def _expand_ch + private + + def _expand_ch str a = [] - self.scan(/(.|\n)-(.|\n)|(.|\n)/) do |r| + str.scan(/(.|\n)-(.|\n)|(.|\n)/) do |r| if $3 a.push $3 elsif $1.length != $2.length @@ -74,32 +76,35 @@ class String a end + def expand_ch_hash from, to + h = {} + afrom = _expand_ch(from) + ato = _expand_ch(to) + afrom.each_with_index do |x,i| h[x] = ato[i] || ato[-1] end + h + end + + def bsquote(str) + str.gsub(/\\/, '\\\\\\\\') + end + + HashCache = {} + TrPatternCache = {} + DeletePatternCache = {} + SqueezePatternCache = {} + + public + def tr!(from, to) return self.delete!(from) if to.length == 0 - if from =~ /^\^/ - comp=TRUE - from = $' - end - afrom = from._expand_ch - ato = to._expand_ch - i = 0 - if comp - self.gsub!(/(.|\n)/) do |c| - unless afrom.include?(c) - ato[-1] - else - c - end - end + pattern = TrPatternCache[from] ||= /[#{bsquote(from)}]/ + if from[0] == ?^ + last = /.$/.match(to)[0] + self.gsub!(pattern, last) else - self.gsub!(/(.|\n)/) do |c| - if i = afrom.index(c) - if i < ato.size then ato[i] else ato[-1] end - else - c - end - end + h = HashCache[from + "::" + to] ||= expand_ch_hash(from, to) + self.gsub!(pattern) do |c| h[c] end end end @@ -108,22 +113,7 @@ class String end def delete!(del) - if del =~ /^\^/ - comp=TRUE - del = $' - end - adel = del._expand_ch - if comp - self.gsub!(/(.|\n)/) do |c| - next unless adel.include?(c) - c - end - else - self.gsub!(/(.|\n)/) do |c| - next if adel.include?(c) - c - end - end + self.gsub!(DeletePatternCache[del] ||= /[#{bsquote(del)}]+/, '') end def delete(del) @@ -131,27 +121,13 @@ class String end def squeeze!(del=nil) - if del - if del =~ /^\^/ - comp=TRUE - del = $' - end - adel = del._expand_ch - if comp - self.gsub!(/(.|\n)\1+/) do - next unless adel.include?($1) - $& - end + pattern = + if del + SqueezePatternCache[del] ||= /([#{bsquote(del)}])\1+/ else - for c in adel - cq = Regexp.quote(c) - self.gsub!(/#{cq}(#{cq})+/, cq) - end + /(.|\n)\1+/ end - self - else - self.gsub!(/(.|\n)\1+/, '\1') - end + self.gsub!(pattern, '\1') end def squeeze(del=nil) @@ -160,30 +136,14 @@ class String def tr_s!(from, to) return self.delete!(from) if to.length == 0 - if from =~ /^\^/ - comp=TRUE - from = $' - end - afrom = from._expand_ch - ato = to._expand_ch - i = 0 - c = nil - last = nil - self.gsub!(/(.|\n)/) do |c| - if comp - unless afrom.include?(c) - ato[-1] - else - c - end - elsif i = afrom.index(c) - c = if i < ato.size then ato[i] else ato[-1] end - next if c == last - last = c - else - last = nil - c - end + + pattern = SqueezePatternCache[from] ||= /([#{bsquote(from)}])\1+"/ + if from[0] == ?^ + last = /.$/.match(to)[0] + self.gsub!(pattern, last) + else + h = HashCache[from + "::" + to] ||= expand_ch_hash(from, to) + self.gsub!(pattern) do h[$1] end end end @@ -191,18 +151,17 @@ class String (str = self.dup).tr_s!(from,to) or str end - alias original_chop! chop! - private :original_chop! - def chop! - if self =~ /(.)$/ and $1.size == 2 - original_chop! - end - original_chop! + self.gsub!(/(?:.|\r?\n)\z/, '') end def chop (str = self.dup).chop! or str end + + def jcount(str) + self.delete("^#{str}").jlength + end + end $VERBOSE = $vsave diff --git a/lib/mailread.rb b/lib/mailread.rb index 5e46606c09..2edcca002a 100644 --- a/lib/mailread.rb +++ b/lib/mailread.rb @@ -43,6 +43,6 @@ class Mail end def [](field) - @header[field] + @header[field.capitalize] end end diff --git a/lib/mathn.rb b/lib/mathn.rb index 265ef1337f..8d92272159 100644 --- a/lib/mathn.rb +++ b/lib/mathn.rb @@ -121,7 +121,7 @@ class Bignum end class Rational - Unify = TRUE + Unify = true alias power! ** @@ -304,6 +304,6 @@ module Math end class Complex - Unify = TRUE + Unify = true end diff --git a/lib/matrix.rb b/lib/matrix.rb index 64b0738e1b..f5c8491146 100644 --- a/lib/matrix.rb +++ b/lib/matrix.rb @@ -1,8 +1,9 @@ +#!/usr/local/bin/ruby # # matrix.rb - # $Release Version: 1.0$ -# $Revision: 1.6 $ -# $Date: 1998/07/31 03:39:49 $ +# $Revision: 1.8 $ +# $Date: 1999/02/17 12:34:19 $ # Original Version from Smalltalk-80 version # on July 23, 1985 at 8:37:17 am # by Keiju ISHITSUKA @@ -17,6 +18,8 @@ # : # rown] # +# column: 列 +# row: 行 # # module ExceptionForMatrix:: # Exceptions: @@ -36,7 +39,7 @@ # creates a matrix where `rows' indicates rows. # `rows' is an array of arrays, # e.g, Matrix[[11, 12], [21, 22]] -# Matrix.rows(rows, copy = TRUE) +# Matrix.rows(rows, copy = true) # creates a matrix where `rows' indicates rows. # if optional argument `copy' is false, use the array as # internal structure of the metrix without copying. @@ -142,7 +145,7 @@ # # INSTANCE CREATION: # Vector.[](*array) -# Vector.elements(array, copy = TRUE) +# Vector.elements(array, copy = true) # ACCSESSING: # [](i) # size @@ -173,29 +176,29 @@ require "e2mmap.rb" module ExceptionForMatrix - Exception2MessageMapper.extend_to(binding) - + extend Exception2MessageMapper def_e2message(TypeError, "wrong argument type %s (expected %s)") def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)") - def_exception("ErrDimensionMismatch", "\#{self.type} dimemsion mismatch") + def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch") def_exception("ErrNotRegular", "Not Regular Matrix") def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined") end class Matrix - @RCS_ID='-$Id: matrix.rb,v 1.6 1998/07/31 03:39:49 keiju Exp keiju $-' - + @RCS_ID='-$Id: matrix.rb,v 1.8 1999/02/17 12:34:19 keiju Exp keiju $-' + +# extend Exception2MessageMapper include ExceptionForMatrix # instance creations private_class_method :new def Matrix.[](*rows) - new(:init_rows, rows, FALSE) + new(:init_rows, rows, false) end - def Matrix.rows(rows, copy = TRUE) + def Matrix.rows(rows, copy = true) new(:init_rows, rows, copy) end @@ -207,7 +210,7 @@ class Matrix columns[j][i] } } - Matrix.rows(rows, FALSE) + Matrix.rows(rows, false) end def Matrix.diagonal(*values) @@ -218,8 +221,7 @@ class Matrix row[j] = values[j] row } - self - rows(rows, FALSE) + rows(rows, false) end def Matrix.scalar(n, value) @@ -241,11 +243,11 @@ class Matrix def Matrix.row_vector(row) case row when Vector - Matrix.rows([row.to_a], FALSE) + Matrix.rows([row.to_a], false) when Array - Matrix.rows([row.dup], FALSE) + Matrix.rows([row.dup], false) else - Matrix.row([[row]], FALSE) + Matrix.row([[row]], false) end end @@ -310,13 +312,13 @@ class Matrix |i| @rows[i][j] } - Vector.elements(col, FALSE) + Vector.elements(col, false) end end def collect rows = @rows.collect{|row| row.collect{|e| yield e}} - Matrix.rows(rows, FALSE) + Matrix.rows(rows, false) end alias map collect @@ -337,14 +339,14 @@ class Matrix from_col = param[2] size_col = param[3] else - Matrix.fail ArgumentError, param.inspect + Matrix.Raise ArgumentError, param.inspect end rows = @rows[from_row, size_row].collect{ |row| row[from_col, size_col] } - Matrix.rows(rows, FALSE) + Matrix.rows(rows, false) end # TESTING @@ -362,20 +364,20 @@ class Matrix # COMPARING def ==(other) - return FALSE unless Matrix === other + return false unless Matrix === other other.compare_by_row_vectors(@rows) end alias eql? == def compare_by_row_vectors(rows) - return FALSE unless @rows.size == rows.size + return false unless @rows.size == rows.size 0.upto(@rows.size - 1) do |i| - return FALSE unless @rows[i] == rows[i] + return false unless @rows[i] == rows[i] end - TRUE + true end def clone @@ -404,13 +406,13 @@ class Matrix e * m } } - return Matrix.rows(rows, FALSE) + return Matrix.rows(rows, false) when Vector m = Matrix.column_vector(m) r = self * m return r.column(0) when Matrix - Matrix.fail ErrDimensionMismatch if column_size != m.row_size + Matrix.Raise ErrDimensionMismatch if column_size != m.row_size rows = (0 .. row_size - 1).collect { |i| @@ -424,7 +426,7 @@ class Matrix vij } } - return Matrix.rows(rows, FALSE) + return Matrix.rows(rows, false) else x, y = m.coerce(self) return x * y @@ -434,7 +436,7 @@ class Matrix def +(m) case m when Numeric - Matrix.fail ErrOperationNotDefined, "+" + Matrix.Raise ErrOperationNotDefined, "+" when Vector m = Matrix.column_vector(m) when Matrix @@ -443,7 +445,7 @@ class Matrix return x + y end - Matrix.fail ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size rows = (0 .. row_size - 1).collect { |i| @@ -452,13 +454,13 @@ class Matrix self[i, j] + m[i, j] } } - Matrix.rows(rows, FALSE) + Matrix.rows(rows, false) end def -(m) case m when Numeric - Matrix.fail ErrOperationNotDefined, "-" + Matrix.Raise ErrOperationNotDefined, "-" when Vector m = Matrix.column_vector(m) when Matrix @@ -467,7 +469,7 @@ class Matrix return x - y end - Matrix.fail ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size rows = (0 .. row_size - 1).collect { |i| @@ -476,7 +478,7 @@ class Matrix self[i, j] - m[i, j] } } - Matrix.rows(rows, FALSE) + Matrix.rows(rows, false) end def /(other) @@ -489,7 +491,7 @@ class Matrix e / other } } - return Matrix.rows(rows, FALSE) + return Matrix.rows(rows, false) when Matrix return self * other.inverse else @@ -499,7 +501,7 @@ class Matrix end def inverse - Matrix.fail ErrDimensionMismatch unless square? + Matrix.Raise ErrDimensionMismatch unless square? Matrix.I(row_size).inverse_from(self) end alias inv inverse @@ -512,7 +514,7 @@ class Matrix if (akk = a[k][k]) == 0 i = k begin - fail ErrNotRegular if (i += 1) > size + Matrix.Raise ErrNotRegular if (i += 1) > size end while a[i][k] == 0 a[i], a[k] = a[k], a[i] @rows[i], @rows[k] = @rows[k], @rows[i] @@ -568,9 +570,9 @@ class Matrix end z elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational) - fail ErrOperationNotDefined, "**" + Matrix.Raise ErrOperationNotDefined, "**" else - fail ErrOperationNotDefined, "**" + Matrix.Raise ErrOperationNotDefined, "**" end end @@ -618,10 +620,10 @@ class Matrix begin if (akk = a[k][k]) == 0 i = -1 - nothing = FALSE + nothing = false begin if (i += 1) > column_size - 1 - nothing = TRUE + nothing = true break end end while a[i][k] == 0 @@ -663,6 +665,8 @@ class Matrix case other when Numeric return Scalar.new(other), self + else + raise TypeError, "#{type} can't be coerced into #{other.type}" end end @@ -725,7 +729,7 @@ class Matrix when Numeric Scalar.new(@value + other) when Vector, Matrix - Scalar.fail WrongArgType, other.type, "Numeric or Scalar" + Scalar.Raise WrongArgType, other.type, "Numeric or Scalar" when Scalar Scalar.new(@value + other.value) else @@ -739,7 +743,7 @@ class Matrix when Numeric Scalar.new(@value - other) when Vector, Matrix - Scalar.fail WrongArgType, other.type, "Numeric or Scalar" + Scalar.Raise WrongArgType, other.type, "Numeric or Scalar" when Scalar Scalar.new(@value - other.value) else @@ -765,7 +769,7 @@ class Matrix when Numeric Scalar.new(@value / other) when Vector - Scalar.fail WrongArgType, other.type, "Numeric or Scalar or Matrix" + Scalar.Raise WrongArgType, other.type, "Numeric or Scalar or Matrix" when Matrix self * _M.inverse else @@ -779,7 +783,7 @@ class Matrix when Numeric Scalar.new(@value ** other) when Vector - Scalar.fail WrongArgType, other.type, "Numeric or Scalar or Matrix" + Scalar.Raise WrongArgType, other.type, "Numeric or Scalar or Matrix" when Matrix other.powered_by(self) else @@ -802,10 +806,10 @@ class Vector private_class_method :new def Vector.[](*array) - new(:init_elements, array, FALSE) + new(:init_elements, array, copy = false) end - def Vector.elements(array, copy = TRUE) + def Vector.elements(array, copy = true) new(:init_elements, array, copy) end @@ -833,7 +837,7 @@ class Vector # ENUMRATIONS def each2(v) - Vector.fail ErrDimensionMismatch if size != v.size + Vector.Raise ErrDimensionMismatch if size != v.size 0.upto(size - 1) do |i| yield @elements[i], v[i] @@ -841,7 +845,7 @@ class Vector end def collect2(v) - Vector.fail ErrDimensionMismatch if size != v.size + Vector.Raise ErrDimensionMismatch if size != v.size (0 .. size - 1).collect do |i| yield @elements[i], v[i] @@ -850,7 +854,7 @@ class Vector # COMPARING def ==(other) - return FALSE unless Vector === other + return false unless Vector === other other.compare_by(@elements) end @@ -870,15 +874,15 @@ class Vector # ARITHMETIC - def *(x) "is matrix or number" + def *(x) # is matrix or number case x when Numeric els = @elements.collect{|e| e * x} - Vector.elements(els, FALSE) + Vector.elements(els, false) when Matrix self.covector * x else - s, x = X.corece(self) + s, x = X.coerce(self) s * x end end @@ -886,16 +890,16 @@ class Vector def +(v) case v when Vector - Vector.fail ErrDimensionMismatch if size != v.size + Vector.Raise ErrDimensionMismatch if size != v.size els = collect2(v) { |v1, v2| v1 + v2 } - Vector.elements(els, FALSE) + Vector.elements(els, false) when Matrix Matrix.column_vector(self) + v else - s, x = v.corece(self) + s, x = v.coerce(self) s + x end end @@ -903,16 +907,16 @@ class Vector def -(v) case v when Vector - Vector.fail ErrDimensionMismatch if size != v.size + Vector.Raise ErrDimensionMismatch if size != v.size els = collect2(v) { |v1, v2| v1 - v2 } - Vector.elements(els, FALSE) + Vector.elements(els, false) when Matrix Matrix.column_vector(self) - v else - s, x = v.corece(self) + s, x = v.coerce(self) s - x end end @@ -920,7 +924,7 @@ class Vector # VECTOR FUNCTIONS def inner_product(v) - Vector.fail ErrDimensionMismatch if size != v.size + Vector.Raise ErrDimensionMismatch if size != v.size p = 0 each2(v) { @@ -935,7 +939,7 @@ class Vector |v| yield v } - Vector.elements(els, FALSE) + Vector.elements(els, false) end alias map collect @@ -944,7 +948,7 @@ class Vector |v1, v2| yield v1, v2 } - Vector.elements(els, FALSE) + Vector.elements(els, false) end def r @@ -980,6 +984,8 @@ class Vector case other when Numeric return Scalar.new(other), self + else + raise TypeError, "#{type} can't be coerced into #{other.type}" end end diff --git a/lib/mkmf.rb b/lib/mkmf.rb index 7e131fe890..50920a0cda 100644 --- a/lib/mkmf.rb +++ b/lib/mkmf.rb @@ -6,7 +6,9 @@ require 'find' include Config -$found = false; +SRC_EXT = ["c", "cc", "m", "cxx", "cpp", "C"] + +$cache_mod = false $lib_cache = {} $lib_found = {} $func_cache = {} @@ -31,14 +33,8 @@ if File.exist?($config_cache) then end $srcdir = CONFIG["srcdir"] -$libdir = CONFIG["libdir"]+"/"+CONFIG["ruby_install_name"] +$libdir = CONFIG["libdir"]+"/ruby/"+CONFIG["MAJOR"]+"."+CONFIG["MINOR"] $archdir = $libdir+"/"+CONFIG["arch"] -$install = CONFIG["INSTALL_PROGRAM"] -$install_data = CONFIG["INSTALL_DATA"] -if $install !~ /^\// then - $install = CONFIG["srcdir"]+"/"+$install - $install_data = CONFIG["srcdir"]+"/"+$install_data -end if File.exist? $archdir + "/ruby.h" $hdrdir = $archdir @@ -48,18 +44,22 @@ else STDERR.print "can't find header files for ruby.\n" exit 1 end +$topdir = $hdrdir +$hdrdir.gsub!('/', '\\') if RUBY_PLATFORM =~ /mswin32/ CFLAGS = CONFIG["CFLAGS"] -if PLATFORM == "m68k-human" +if RUBY_PLATFORM == "m68k-human" CFLAGS.gsub!(/-c..-stack=[0-9]+ */, '') +elsif RUBY_PLATFORM =~ /-nextstep|-rhapsody/ + CFLAGS.gsub!( /-arch\s\w*/, '' ) end -if /win32|djgpp|mingw32|m68k-human/i =~ PLATFORM +if /win32|djgpp|mingw32|m68k-human|i386-os2_emx/i =~ RUBY_PLATFORM $null = open("nul", "w") else $null = open("/dev/null", "w") end -LINK = "#{CONFIG['CC']} -o conftest -I#{$srcdir} -I#{CONFIG['includedir']} #{CFLAGS} %s #{CONFIG['LDFLAGS']} %s conftest.c #{CONFIG['LIBS']} %s" -CPP = "#{CONFIG['CPP']} -E -I#{$srcdir} -I#{CONFIG['includedir']} #{CFLAGS} %s conftest.c" +LINK = "#{CONFIG['CC']} -o conftest -I#{$hdrdir} #{CFLAGS} -I#{CONFIG['includedir']} %s #{CONFIG['LDFLAGS']} %s conftest.c %s %s #{CONFIG['LIBS']}" +CPP = "#{CONFIG['CPP']} -E -I#{$hdrdir} #{CFLAGS} -I#{CONFIG['includedir']} %s %s conftest.c" $orgerr = $stderr.dup $orgout = $stdout.dup @@ -76,29 +76,84 @@ def xsystem command return r end -def try_link(libs) - xsystem(format(LINK, $CFLAGS, $LDFLAGS, libs)) +def try_link0(src, opt="") + cfile = open("conftest.c", "w") + cfile.print src + cfile.close + xsystem(format(LINK, $CFLAGS, $LDFLAGS, opt, $LOCAL_LIBS)) end -def try_cpp - xsystem(format(CPP, $CFLAGS)) +def try_link(src, opt="") + begin + try_link0(src, opt) + ensure + system "rm -f conftest*" + end end -def install_rb(mfile) +def try_cpp(src, opt="") + cfile = open("conftest.c", "w") + cfile.print src + cfile.close + begin + xsystem(format(CPP, $CFLAGS, opt)) + ensure + system "rm -f conftest*" + end +end + +def egrep_cpp(pat, src, opt="") + cfile = open("conftest.c", "w") + cfile.print src + cfile.close + begin + xsystem(format(CPP+"|egrep #{pat}", $CFLAGS, opt)) + ensure + system "rm -f conftest*" + end +end + +def try_run(src, opt="") + begin + if try_link0(src, opt) + if xsystem("./conftest") + true + else + false + end + else + nil + end + ensure + system "rm -f conftest*" + end +end + +def install_rb(mfile, srcdir = nil) + libdir = "lib" + libdir = srcdir + "/" + libdir if srcdir path = [] dir = [] - Find.find("lib") do |f| + Find.find(libdir) do |f| next unless /\.rb$/ =~ f - f = f[4..-1] + f = f[libdir.length+1..-1] path.push f dir |= File.dirname(f) end for f in dir next if f == "." - mfile.printf "\t@test -d $(libdir)/%s || mkdir $(libdir)/%s\n", f, f + mfile.printf "\t@$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' $(libdir)/%s\n", f end for f in path - mfile.printf "\t$(INSTALL_DATA) lib/%s $(libdir)/%s\n", f, f + mfile.printf "\t@$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0644, true)' lib/%s $(libdir)/%s\n", f, f + end +end + +def append_library(libs, lib) + if /mswin32/ =~ RUBY_PLATFORM + lib + ".lib " + libs + else + "-l" + lib + " " + libs end end @@ -107,55 +162,75 @@ def have_library(lib, func="main") STDOUT.flush if $lib_cache[lib] if $lib_cache[lib] == "yes" - if $libs - $libs = "-l" + lib + " " + $libs - else - $libs = "-l" + lib - end + $libs = append_library($libs, lib) print "(cached) yes\n" - return TRUE + return true else print "(cached) no\n" - return FALSE + return false end end if func && func != "" - cfile = open("conftest.c", "w") - cfile.printf "\ + libs = append_library($libs, lib) + if /mswin32/ =~ RUBY_PLATFORM + r = try_link(<<"SRC", libs) +#include +#include int main() { return 0; } -int t() { %s(); return 0; } -", func - cfile.close - - begin - if $libs - libs = "-l" + lib + " " + $libs - else - libs = "-l" + lib - end - unless try_link(libs) - $lib_cache[lib] = 'no' - $cache_mod = TRUE - print "no\n" - return FALSE +int t() { #{func}(); return 0; } +SRC + unless r + r = try_link(<<"SRC", libs) +#include +#include +int main() { return 0; } +int t() { void ((*p)()); p = (void ((*)()))#{func}; return 0; } +SRC end - ensure - system "rm -f conftest*" - end - else - if $libs - libs = "-l" + lib + " " + $libs else - libs = "-l" + lib + r = try_link(<<"SRC", libs) +int main() { return 0; } +int t() { #{func}(); return 0; } +SRC + end + unless r + $lib_cache[lib] = 'no' + $cache_mod = true + print "no\n" + return false end + else + libs = append_library($libs, lib) end $libs = libs $lib_cache[lib] = 'yes' - $cache_mod = TRUE + $cache_mod = true + print "yes\n" + return true +end + +def find_library(lib, func, *paths) + printf "checking for %s() in -l%s... ", func, lib + STDOUT.flush + + ldflags = $LDFLAGS + libs = append_library($libs, lib) + until try_link(<<"SRC", libs) +int main() { return 0; } +int t() { #{func}(); return 0; } +SRC + if paths.size == 0 + $LDFLAGS = ldflags + print "no\n" + return false + end + $LDFLAGS = ldflags + " -L"+paths.shift + end + $libs = libs print "yes\n" - return TRUE + return true end def have_func(func) @@ -165,39 +240,47 @@ def have_func(func) if $func_cache[func] == "yes" $defs.push(format("-DHAVE_%s", func.upcase)) print "(cached) yes\n" - return TRUE + return true else print "(cached) no\n" - return FALSE + return false end end - cfile = open("conftest.c", "w") - cfile.printf "\ -char %s(); -int main() { return 0; } -int t() { %s(); return 0; } -", func, func - cfile.close - libs = $libs - libs = "" if libs == nil - begin - unless try_link(libs) - $func_found[func] = 'no' - $found = TRUE - print "no\n" - return FALSE + if /mswin32/ =~ RUBY_PLATFORM + r = try_link(<<"SRC", libs) +#include +#include +int main() { return 0; } +int t() { #{func}(); return 0; } +SRC + unless r + r = try_link(<<"SRC", libs) +#include +#include +int main() { return 0; } +int t() { void ((*p)()); p = (void ((*)()))#{func}; return 0; } +SRC end - ensure - system "rm -f conftest*" + else + r = try_link(<<"SRC", libs) +int main() { return 0; } +int t() { #{func}(); return 0; } +SRC + end + unless r + $func_found[func] = 'no' + $cache_mod = true + print "no\n" + return false end $defs.push(format("-DHAVE_%s", func.upcase)) $func_found[func] = 'yes' - $found = TRUE + $cache_mod = true print "yes\n" - return TRUE + return true end def have_header(header) @@ -208,35 +291,59 @@ def have_header(header) header.tr!("a-z./\055", "A-Z___") $defs.push(format("-DHAVE_%s", header)) print "(cached) yes\n" - return TRUE + return true else print "(cached) no\n" - return FALSE + return false end end - cfile = open("conftest.c", "w") - cfile.printf "\ -#include <%s> -", header - cfile.close - - begin - unless try_cpp - $hdr_found[header] = 'no' - $found = TRUE - print "no\n" - return FALSE - end - ensure - system "rm -f conftest*" + unless try_cpp(<<"SRC") +#include <#{header}> +SRC + $hdr_found[header] = 'no' + $cache_mod = true + print "no\n" + return false end $hdr_found[header] = 'yes' header.tr!("a-z./\055", "A-Z___") $defs.push(format("-DHAVE_%s", header)) - $found = TRUE + $cache_mod = true print "yes\n" - return TRUE + return true +end + +def arg_config(config, default=nil) + unless defined? $configure_args + $configure_args = {} + for arg in CONFIG["configure_args"].split + ARGV + next unless /^--/ =~ arg + if /=/ =~ arg + $configure_args[$`] = $' + else + $configure_args[arg] = true + end + end + end + $configure_args.fetch(config, default) +end + +def with_config(config, default=nil) + unless /^--with-/ =~ config + config = '--with-' + config + end + arg_config(config, default) +end + +def enable_config(config, default=nil) + if arg_config("--enable-"+config, default) + true + elsif arg_config("--disable-"+config, false) + false + else + default + end end def create_header() @@ -252,28 +359,58 @@ def create_header() end end +def dir_config(target) + dir = with_config("%s-dir"%target) + if dir + idir = " -I"+dir+"/include" + ldir = " -L"+dir+"/lib" + end + unless idir + dir = with_config("%s-include"%target) + idir = " -I"+dir if dir + end + unless ldir + dir = with_config("%s-lib"%target) + ldir = " -L"+dir if dir + end + + $CFLAGS += idir if idir + $LDFLAGS += ldir if ldir +end + def create_makefile(target) print "creating Makefile\n" + system "rm -f conftest*" STDOUT.flush - if $libs and CONFIG["DLEXT"] == "o" + if CONFIG["DLEXT"] == $OBJEXT libs = $libs.split for lib in libs lib.sub!(/-l(.*)/, '"lib\1.a"') end $defs.push(format("-DEXTLIB='%s'", libs.join(","))) end - $libs = "" unless $libs $DLDFLAGS = CONFIG["DLDFLAGS"] - if PLATFORM =~ /beos/ + if RUBY_PLATFORM =~ /beos/ $libs = $libs + " -lruby" $DLDFLAGS = $DLDFLAGS + " -L" + CONFIG["prefix"] + "/lib" end + defflag = '' + if RUBY_PLATFORM =~ /cygwin/ + if File.exist? target + ".def" + defflag = "--def=" + target + ".def" + end + $libs = $libs + " " + CONFIG["LIBRUBYARG"] + $DLDFLAGS = $DLDFLAGS + " -L$(topdir)" + end + unless $objs then - $objs = Dir["*.{c,cc}"] - for f in $objs - f.sub!(/\.(c|cc)$/, ".o") + $objs = [] + for f in Dir["*.{#{SRC_EXT.join(%q{,})}}"] + f = File.basename(f) + f.sub!(/(#{SRC_EXT.join(%q{|})})$/, $OBJEXT) + $objs.push f end end $objs = $objs.join(" ") @@ -285,15 +422,15 @@ SHELL = /bin/sh #### Start of system configuration section. #### srcdir = #{$srcdir} +topdir = #{$topdir} hdrdir = #{$hdrdir} CC = #{CONFIG["CC"]} -prefix = #{CONFIG["prefix"]} -CFLAGS = #{CONFIG["CCDLFLAGS"]} -I$(hdrdir) -I#{CONFIG["includedir"]} #{CFLAGS} #{$CFLAGS} #{$defs.join(" ")} +CFLAGS = #{CONFIG["CCDLFLAGS"]} -I$(hdrdir) #{CFLAGS} #{$CFLAGS} -I#{CONFIG["includedir"]} #{$defs.join(" ")} CXXFLAGS = $(CFLAGS) DLDFLAGS = #{$DLDFLAGS} #{$LDFLAGS} -LDSHARED = #{CONFIG["LDSHARED"]} +LDSHARED = #{CONFIG["LDSHARED"]} #{defflag} prefix = #{CONFIG["prefix"]} exec_prefix = #{CONFIG["exec_prefix"]} @@ -302,49 +439,47 @@ archdir = #{$archdir} #### End of system configuration section. #### -LOCAL_LIBS = #{$local_libs} +LOCAL_LIBS = #{$LOCAL_LIBS} #{$local_flags} LIBS = #{$libs} OBJS = #{$objs} -TARGET = #{target}.#{CONFIG["DLEXT"]} +TARGET = #{target} +DLLIB = $(TARGET).#{CONFIG["DLEXT"]} -INSTALL = #{$install} -INSTALL_DATA = #{$install_data} +RUBY = #{CONFIG["ruby_install_name"]} -binsuffix = #{CONFIG["binsuffix"]} +EXEEXT = #{CONFIG["EXEEXT"]} -all: $(TARGET) +all: $(DLLIB) -clean:; @rm -f *.o *.so *.sl +clean:; @rm -f *.#{$OBJEXT} *.so *.sl *.a $(DLLIB) + @rm -f $(TARGET).lib $(TARGET).exp @rm -f Makefile extconf.h conftest.* - @rm -f core ruby$(binsuffix) *~ + @rm -f core ruby$(EXEEXT) *~ realclean: clean -install: $(archdir)/$(TARGET) +install: $(archdir)/$(DLLIB) -$(archdir)/$(TARGET): $(TARGET) - @test -d $(libdir) || mkdir $(libdir) - @test -d $(archdir) || mkdir $(archdir) - $(INSTALL) $(TARGET) $(archdir)/$(TARGET) +$(archdir)/$(DLLIB): $(DLLIB) + @$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' $(libdir) $(archdir) + @$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0555, true)' $(DLLIB) $(archdir)/$(DLLIB) EOMF install_rb(mfile) mfile.printf "\n" - if CONFIG["DLEXT"] != "o" + if CONFIG["DLEXT"] != $OBJEXT mfile.printf < +Version: 1.2.1 + +USAGE: + + foo = Foo.new + foo.extend(MonitorMixin) + cond = foo.new_cond + + thread1: + foo.synchronize { + ... + cond.wait_until { foo.done? } + ... + } + + thread2: + foo.synchronize { + foo.do_something + cond.signal + } + +=end + +module MonitorMixin + module Accessible + protected + attr_accessor :mon_owner, :mon_count + attr_reader :mon_entering_queue, :mon_waiting_queue + end + + module Initializable + protected + def mon_initialize + @mon_owner = nil + @mon_count = 0 + @mon_entering_queue = [] + @mon_waiting_queue = [] + end + end + + class ConditionVariable + class Timeout < Exception; end + + include Accessible + + def wait(timeout = nil) + if @monitor.mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + + Thread.critical = true + count = @monitor.mon_count + @monitor.mon_count = 0 + @monitor.mon_owner = nil + if @monitor.mon_waiting_queue.empty? + t = @monitor.mon_entering_queue.shift + else + t = @monitor.mon_waiting_queue.shift + end + t.wakeup if t + @waiters.push(Thread.current) + + if timeout + t = Thread.current + timeout_thread = Thread.start { + sleep(timeout) + t.raise(Timeout.new) + } + end + begin + Thread.stop + rescue Timeout + @waiters.delete(Thread.current) + ensure + if timeout && timeout_thread.alive? + Thread.kill(timeout_thread) + end + end + + Thread.critical = true + while @monitor.mon_owner && + @monitor.mon_owner != Thread.current + @monitor.mon_waiting_queue.push(Thread.current) + Thread.stop + Thread.critical = true + end + @monitor.mon_owner = Thread.current + @monitor.mon_count = count + Thread.critical = false + end + + def wait_while + while yield + wait + end + end + + def wait_until + until yield + wait + end + end + + def signal + if @monitor.mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + Thread.critical = true + t = @waiters.shift + t.wakeup if t + Thread.critical = false + Thread.pass + end + + def broadcast + if @monitor.mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + Thread.critical = true + for t in @waiters + t.wakeup + end + @waiters.clear + Thread.critical = false + Thread.pass + end + + def count_waiters + return @waiters.length + end + + private + def initialize(monitor) + @monitor = monitor + @waiters = [] + end + end + + include Accessible + include Initializable + extend Initializable + + def self.extend_object(obj) + super(obj) + obj.mon_initialize + end + + def try_mon_enter + result = false + Thread.critical = true + if mon_owner.nil? + self.mon_owner = Thread.current + end + if mon_owner == Thread.current + self.mon_count += 1 + result = true + end + Thread.critical = false + return result + end + + def mon_enter + Thread.critical = true + while mon_owner != nil && mon_owner != Thread.current + mon_entering_queue.push(Thread.current) + Thread.stop + Thread.critical = true + end + self.mon_owner = Thread.current + self.mon_count += 1 + Thread.critical = false + end + + def mon_exit + if mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + Thread.critical = true + self.mon_count -= 1 + if mon_count == 0 + self.mon_owner = nil + if mon_waiting_queue.empty? + t = mon_entering_queue.shift + else + t = mon_waiting_queue.shift + end + end + t.wakeup if t + Thread.critical = false + Thread.pass + end + + def mon_synchronize + mon_enter + begin + yield + ensure + mon_exit + end + end + alias synchronize mon_synchronize + + def new_cond + return ConditionVariable.new(self) + end + +private + def initialize(*args) + super + mon_initialize + end +end + +class Monitor + include MonitorMixin + alias try_enter try_mon_enter + alias enter mon_enter + alias exit mon_exit + alias owner mon_owner +end + +# Local variables: +# mode: Ruby +# tab-width: 8 +# End: diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb index 4b8d64438e..e0fcf0f209 100644 --- a/lib/mutex_m.rb +++ b/lib/mutex_m.rb @@ -12,54 +12,27 @@ # obj = Object.new # obj.extend Mutex_m # ... -# 後はMutexと同じ使い方 +# extended object can be handled like Mutex # -require "finalize" - module Mutex_m - def Mutex_m.extendable_module(obj) - if Fixnum === obj or TRUE === obj or FALSE === obj or nil == obj - raise TypeError, "Mutex_m can't extend to this class(#{obj.type})" - else - begin - obj.instance_eval "@mu_locked" - For_general_object - rescue TypeError - For_primitive_object - end + def Mutex_m.append_features(cl) + super + unless cl.instance_of?(Module) + cl.module_eval %q{ + alias locked? mu_locked? + alias lock mu_lock + alias unlock mu_unlock + alias try_lock mu_try_lock + alias synchronize mu_synchronize + } end - end - - def Mutex_m.includable_module(cl) - begin - dummy = cl.new - Mutex_m.extendable_module(dummy) - rescue NameError - # newが定義されていない時は, DATAとみなす. - For_primitive_object - end - end - - def Mutex_m.extend_class(cl) - return super if cl.instance_of?(Module) - - # モジュールの時は何もしない. クラスの場合, 適切なモジュールの決定 - # とaliasを行う. - real = includable_module(cl) - cl.module_eval %q{ - include real - - alias locked? mu_locked? - alias lock mu_lock - alias unlock mu_unlock - alias try_lock mu_try_lock - alias synchronize mu_synchronize - } + return self end def Mutex_m.extend_object(obj) - obj.extend(Mutex_m.extendable_module(obj)) + super + obj.mu_extended end def mu_extended @@ -76,6 +49,7 @@ module Mutex_m alias synchronize mu_synchronize end" end + initialize end # locking @@ -88,132 +62,50 @@ module Mutex_m end end - # internal class - module For_general_object - include Mutex_m - - def For_general_object.extend_object(obj) - super - obj.mu_extended - end - - def mu_extended - super - @mu_waiting = [] - @mu_locked = FALSE; - end - - def mu_locked? - @mu_locked - end - - def mu_try_lock - result = FALSE - Thread.critical = TRUE - unless @mu_locked - @mu_locked = TRUE - result = TRUE - end - Thread.critical = FALSE - result - end - - def mu_lock - while (Thread.critical = TRUE; @mu_locked) - @mu_waiting.push Thread.current - Thread.stop - end - @mu_locked = TRUE - Thread.critical = FALSE - self - end - - def mu_unlock - return unless @mu_locked - Thread.critical = TRUE - wait = @mu_waiting - @mu_waiting = [] - @mu_locked = FALSE - Thread.critical = FALSE - for w in wait - w.run - end - self - end - + def mu_locked? + @mu_locked end - - module For_primitive_object - include Mutex_m - Mu_Locked = Hash.new - - def For_primitive_object.extend_object(obj) - super - - obj.mu_extended - Finalizer.add(obj, For_primitive_object, :mu_finalize) - end - - def mu_extended - super - initialize - end - - def For_primitive_object.mu_finalize(id) - Thread.critical = TRUE - if wait = Mu_Locked.delete(id) - # wait == [] ときだけ GCされるので, for w in wait は意味なし. - Thread.critical = FALSE - for w in wait - w.run - end - else - Thread.critical = FALSE - end - self - end - - def mu_locked? - Mu_Locked.key?(self.id) - end - - def mu_try_lock - Thread.critical = TRUE - if Mu_Locked.key?(self.id) - ret = FALSE - else - Mu_Locked[self.id] = [] - Finalizer.add(self, For_primitive_object, :mu_finalize) - ret = TRUE - end - Thread.critical = FALSE - ret - end - - def mu_lock - while (Thread.critical = TRUE; w = Mu_Locked[self.id]) - w.push Thread.current - Thread.stop - end - Mu_Locked[self.id] = [] - Finalizer.add(self, For_primitive_object, :mu_finalize) - Thread.critical = FALSE - self - end - - def mu_unlock - Thread.critical = TRUE - if wait = Mu_Locked.delete(self.id) - Finalizer.delete(self, For_primitive_object, :mu_finalize) - Thread.critical = FALSE - for w in wait - w.run - end - else - Thread.critical = FALSE - end - self - end + + def mu_try_lock + result = false + Thread.critical = true + unless @mu_locked + @mu_locked = true + result = true + end + Thread.critical = false + result + end + + def mu_lock + while (Thread.critical = true; @mu_locked) + @mu_waiting.push Thread.current + Thread.stop + end + @mu_locked = true + Thread.critical = false + self + end + + def mu_unlock + return unless @mu_locked + Thread.critical = true + wait = @mu_waiting + @mu_waiting = [] + @mu_locked = false + Thread.critical = false + for w in wait + w.run + end + self + end + + private + + def initialize(*args) + ret = super + @mu_waiting = [] + @mu_locked = false; + return ret end end - diff --git a/lib/observer.rb b/lib/observer.rb index 5928367a7d..0c74b49750 100644 --- a/lib/observer.rb +++ b/lib/observer.rb @@ -23,7 +23,7 @@ module Observable 0 end end - def changed(state=TRUE) + def changed(state=true) @observer_state = state end def changed? @@ -36,7 +36,7 @@ module Observable i.update(*arg) end end - @observer_state = FALSE + @observer_state = false end end end diff --git a/lib/open3.rb b/lib/open3.rb new file mode 100644 index 0000000000..9e34acffc9 --- /dev/null +++ b/lib/open3.rb @@ -0,0 +1,55 @@ +# Usage: +# require "open3" +# +# in, out, err = Open3.popen3('nroff -man') +# or +# include Open3 +# in, out, err = popen3('nroff -man') +# + +module Open3 + #[stdin, stdout, stderr] = popen3(command); + def popen3(cmd) + pw = IO::pipe # pipe[0] for read, pipe[1] for write + pr = IO::pipe + pe = IO::pipe + + pid = fork + if pid == nil then # child + pw[1].close + STDIN.reopen(pw[0]) + pw[0].close + + pr[0].close + STDOUT.reopen(pr[1]) + pr[1].close + + pe[0].close + STDERR.reopen(pe[1]) + pe[1].close + + exec(cmd) + exit + else + pw[0].close + pr[1].close + pe[1].close + pi = [ pw[1], pr[0], pe[0] ] + end + end + module_function :popen3 +end + +if $0 == __FILE__ + a = Open3.popen3("nroff -man") + Thread.start do + while gets + a[0].print $_ + end + a[0].close + end + while a[1].gets + print ":", $_ + end +end + diff --git a/lib/parsedate.rb b/lib/parsedate.rb index 68550c6505..6afec0ff06 100644 --- a/lib/parsedate.rb +++ b/lib/parsedate.rb @@ -8,8 +8,8 @@ module ParseDate 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3, 'thu' => 4, 'fri' => 5, 'sat' => 6 } DAYPAT = DAYS.keys.join('|') - - def parsedate(date) + + def parsedate(date, guess=false) # part of ISO 8601 # yyyy-mm-dd | yyyy-mm | yyyy # date hh:mm:ss | date Thh:mm:ss @@ -46,7 +46,7 @@ module ParseDate if $3 year = $3.to_i end - elsif date.sub!(/(#{MONTHPAT})\S*\s+(\d+)\S*\s*,?(?:\s+(\d+))?/i, ' ') + elsif date.sub!(/(#{MONTHPAT})\S*\s+(\d+)\S*,?(?:\s+(\d+))?/i, ' ') mon = MONTHS[$1.downcase] mday = $2.to_i if $3 @@ -58,6 +58,23 @@ module ParseDate if $3 year = $3.to_i end + elsif date.sub!(/(\d+)-(#{MONTHPAT})-(\d+)/i, ' ') + mday = $1.to_i + mon = MONTHS[$2.downcase] + year = $3.to_i + end + if guess + if year < 100 + if year >= 69 + year += 1900 + else + year += 2000 + end + end + elsif date.sub!(/(\d+)-(#{MONTHPAT})-(\d+)/i, ' ') + mday = $1.to_i + mon = MONTHS[$2.downcase] + year = $3.to_i end return year, mon, mday, hour, min, sec, zone, wday end diff --git a/lib/profile.rb b/lib/profile.rb new file mode 100644 index 0000000000..e4b1b4b189 --- /dev/null +++ b/lib/profile.rb @@ -0,0 +1,55 @@ + +module Profiler__ + Start = Float(Time.times[0]) + top = "toplevel".intern + Stack = [[0, 0, top]] + MAP = {top => [1, 0, 0, "#toplevel"]} + + p = proc{|event, file, line, id, binding, klass| + case event + when "call", "c-call" + now = Float(Time.times[0]) + Stack.push [now, 0.0, id] + when "return", "c-return" + now = Float(Time.times[0]) + tick = Stack.pop + data = MAP[id] + unless data + name = klass.to_s + if name.nil? then name = '' end + if klass.kind_of? Class + name += "#" + else + name += "." + end + data = [0.0, 0.0, 0.0, name+id.id2name] + MAP[id] = data + end + data[0] += 1 + cost = now - tick[0] + data[1] += cost + data[2] += cost - tick[1] + Stack[-1][1] += cost + end + } + END { + set_trace_func nil + total = Float(Time.times[0]) - Start + if total == 0 then total = 0.01 end + MAP[:toplevel][1] = total +# f = open("./rmon.out", "w") + f = STDERR + data = MAP.values.sort!{|a,b| b[2] <=> a[2]} + sum = 0 + f.printf " %% cumulative self self total\n" + f.printf " time seconds seconds calls ms/call ms/call name\n" + for d in data + sum += d[2] + f.printf "%6.2f %8.2f %8.2f %8d ", d[2]/total*100, sum, d[2], d[0] + f.printf "%8.2f %8.2f %s\n", d[2]*1000/d[0], d[1]*1000/d[0], d[3] + end + p total + f.close + } + set_trace_func p +end diff --git a/lib/rational.rb b/lib/rational.rb index 1282f56410..f976cf034f 100644 --- a/lib/rational.rb +++ b/lib/rational.rb @@ -77,7 +77,7 @@ class Rational < Numeric @denominator = den else @numerator = num.to_i - @denoninator = den.to_i + @denominator = den.to_i end end diff --git a/lib/readbytes.rb b/lib/readbytes.rb new file mode 100644 index 0000000000..d6a3b10afe --- /dev/null +++ b/lib/readbytes.rb @@ -0,0 +1,36 @@ +# readbytes.rb +# +# add IO#readbytes, which reads fixed sized data. +# it guarantees read data size. + +class TruncatedDataError + + +=== MAKE NEW TELNET OBJECT + + host = Telnet.new({"Binmode" => false, # default: false + "Host" => "localhost", # default: "localhost" + "Output_log" => "output_log", # default: not output + "Dump_log" => "dump_log", # default: not output + "Port" => 23, # default: 23 + "Prompt" => /[$%#>] \z/n, # default: /[$%#>] \z/n + "Telnetmode" => true, # default: true + "Timeout" => 10, # default: 10 + # if ignore timeout then set "Timeout" to false. + "Waittime" => 0, # default: 0 + "Proxy" => proxy}) # default: nil + # proxy is Telnet or TCPsocket object + +Telnet object has socket class methods. + +if set "Telnetmode" option to false. not telnet command interpretation. +"Waittime" is time to confirm "Prompt". There is a possibility that +the same character as "Prompt" is included in the data, and, when +the network or the host is very heavy, the value is enlarged. + +=== STATUS OUTPUT + + host = Telnet.new({"Host" => "localhost"){|c| print c } + +connection status output. + +example + + Trying localhost... + Connected to localhost. + + +=== WAIT FOR MATCH + + line = host.waitfor(/match/) + line = host.waitfor({"Match" => /match/, + "String" => "string", + "Timeout" => secs}) + # if ignore timeout then set "Timeout" to false. + +if set "String" option, then Match == Regexp.new(quote("string")) + + +==== REALTIME OUTPUT + + host.waitfor(/match/){|c| print c } + host.waitfor({"Match" => /match/, + "String" => "string", + "Timeout" => secs}){|c| print c} + +of cource, set sync=true or flush is necessary. + + +=== SEND STRING AND WAIT PROMPT + + line = host.cmd("string") + line = host.cmd({"String" => "string", + "Prompt" => /[$%#>] \z/n, + "Timeout" => 10}) + + +==== REALTIME OUTPUT + + host.cmd("string"){|c| print c } + host.cmd({"String" => "string", + "Prompt" => /[$%#>] \z/n, + "Timeout" => 10}){|c| print c } + +of cource, set sync=true or flush is necessary. + + +=== SEND STRING + + host.print("string") + + +=== TURN TELNET COMMAND INTERPRETATION + + host.telnetmode # turn on/off + host.telnetmode(true) # on + host.telnetmode(false) # off + + +=== TOGGLE NEWLINE TRANSLATION + + host.binmode # turn true/false + host.binmode(true) # no translate newline + host.binmode(false) # translate newline + + +=== LOGIN + + host.login("username", "password") + host.login({"Name" => "username", + "Password" => "password", + "Prompt" => /[$%#>] \z/n, + "Timeout" => 10}) + + +==== REALTIME OUTPUT + + host.login("username", "password"){|c| print c } + host.login({"Name" => "username", + "Password" => "password", + "Prompt" => /[$%#>] \z/n, + "Timeout" => 10}){|c| print c } + +of cource, set sync=true or flush is necessary. + + +== EXAMPLE + +=== LOGIN AND SEND COMMAND + + localhost = Telnet.new({"Host" => "localhost", + "Timeout" => 10, + "Prompt" => /[$%#>] \z/n}) + localhost.login("username", "password"){|c| print c } + localhost.cmd("command"){|c| print c } + localhost.close + + +=== CHECKS A POP SERVER TO SEE IF YOU HAVE MAIL + + pop = Telnet.new({"Host" => "your_destination_host_here", + "Port" => 110, + "Telnetmode" => false, + "Prompt" => /^\+OK/n}) + pop.cmd("user " + "your_username_here"){|c| print c} + pop.cmd("pass " + "your_password_here"){|c| print c} + pop.cmd("list"){|c| print c} + + +== HISTORY + +=== Version 0.232 + +1999/08/10 05:20:21 + +- STATUS OUTPUT sample code typo. thanks to Tadayoshi Funaba + host = Telnet.new({"Hosh" => "localhost"){|c| print c } + host = Telnet.new({"Host" => "localhost"){|c| print c } + +=== Version 0.231 + +1999/07/16 13:39:42 + +- TRUE --> true, FALSE --> false + +=== Version 0.23 + +1999/07/15 22:32:09 + +- waitfor: if end of file reached, then return nil. + +=== Version 0.22 + +1999/06/29 09:08:51 + +- new, waitfor, cmd: {"Timeout" => false} # ignore timeout + +=== Version 0.21 + +1999/06/28 18:18:55 + +- waitfor: not rescue (EOFError) + +=== Version 0.20 + +1999/06/04 06:24:58 + +- waitfor: support for divided telnet command + +=== Version 0.181 + +1999/05/22 + +- bug fix: print method + +=== Version 0.18 + +1999/05/14 + +- respond to "IAC WON'T SGA" with "IAC DON'T SGA" +- DON'T SGA : end of line --> CR + LF +- bug fix: preprocess method + +=== Version 0.17 + +1999/04/30 + +- bug fix: $! + "\n" --> $!.to_s + "\n" + +=== Version 0.163 + +1999/04/11 + +- STDOUT.write(message) --> yield(message) if iterator? + +=== Version 0.162 + +1999/03/17 + +- add "Proxy" option +- required timeout.rb + +=== Version 0.161 + +1999/02/03 + +- select --> IO::select + +=== Version 0.16 + +1998/10/09 + +- preprocess method change for the better +- add binmode method. +- change default Binmode. TRUE --> FALSE + +=== Version 0.15 + +1998/10/04 + +- add telnetmode method. + +=== Version 0.141 + +1998/09/22 + +- change default prompt. /[$%#>] $/ --> /[$%#>] \Z/ + +=== Version 0.14 + +1998/09/01 + +- IAC WILL SGA send EOL --> CR+NULL +- IAC WILL SGA IAC DO BIN send EOL --> CR +- NONE send EOL --> LF +- add Dump_log option. + +=== Version 0.13 + +1998/08/25 + +- add print method. + +=== Version 0.122 + +1998/08/05 + +- support for HP-UX 10.20 thanks to WATANABE Tetsuya +- socket.<< --> socket.write + +=== Version 0.121 + +1998/07/15 + +- string.+= --> string.concat + +=== Version 0.12 + +1998/06/01 + +- add timeout, waittime. + +=== Version 0.11 + +1998/04/21 + +- add realtime output. + +=== Version 0.10 + +1998/04/13 + +- first release. + +=end + +require "socket" +require "delegate" +require "thread" +require "timeout" +TimeOut = TimeoutError + +class Telnet < SimpleDelegator + + IAC = 255.chr # "\377" # interpret as command: + DONT = 254.chr # "\376" # you are not to use option + DO = 253.chr # "\375" # please, you use option + WONT = 252.chr # "\374" # I won't use option + WILL = 251.chr # "\373" # I will use option + SB = 250.chr # "\372" # interpret as subnegotiation + GA = 249.chr # "\371" # you may reverse the line + EL = 248.chr # "\370" # erase the current line + EC = 247.chr # "\367" # erase the current character + AYT = 246.chr # "\366" # are you there + AO = 245.chr # "\365" # abort output--but let prog finish + IP = 244.chr # "\364" # interrupt process--permanently + BREAK = 243.chr # "\363" # break + DM = 242.chr # "\362" # data mark--for connect. cleaning + NOP = 241.chr # "\361" # nop + SE = 240.chr # "\360" # end sub negotiation + EOR = 239.chr # "\357" # end of record (transparent mode) + ABORT = 238.chr # "\356" # Abort process + SUSP = 237.chr # "\355" # Suspend process + EOF = 236.chr # "\354" # End of file + SYNCH = 242.chr # "\362" # for telfunc calls + + OPT_BINARY = 0.chr # "\000" # Binary Transmission + OPT_ECHO = 1.chr # "\001" # Echo + OPT_RCP = 2.chr # "\002" # Reconnection + OPT_SGA = 3.chr # "\003" # Suppress Go Ahead + OPT_NAMS = 4.chr # "\004" # Approx Message Size Negotiation + OPT_STATUS = 5.chr # "\005" # Status + OPT_TM = 6.chr # "\006" # Timing Mark + OPT_RCTE = 7.chr # "\a" # Remote Controlled Trans and Echo + OPT_NAOL = 8.chr # "\010" # Output Line Width + OPT_NAOP = 9.chr # "\t" # Output Page Size + OPT_NAOCRD = 10.chr # "\n" # Output Carriage-Return Disposition + OPT_NAOHTS = 11.chr # "\v" # Output Horizontal Tab Stops + OPT_NAOHTD = 12.chr # "\f" # Output Horizontal Tab Disposition + OPT_NAOFFD = 13.chr # "\r" # Output Formfeed Disposition + OPT_NAOVTS = 14.chr # "\016" # Output Vertical Tabstops + OPT_NAOVTD = 15.chr # "\017" # Output Vertical Tab Disposition + OPT_NAOLFD = 16.chr # "\020" # Output Linefeed Disposition + OPT_XASCII = 17.chr # "\021" # Extended ASCII + OPT_LOGOUT = 18.chr # "\022" # Logout + OPT_BM = 19.chr # "\023" # Byte Macro + OPT_DET = 20.chr # "\024" # Data Entry Terminal + OPT_SUPDUP = 21.chr # "\025" # SUPDUP + OPT_SUPDUPOUTPUT = 22.chr # "\026" # SUPDUP Output + OPT_SNDLOC = 23.chr # "\027" # Send Location + OPT_TTYPE = 24.chr # "\030" # Terminal Type + OPT_EOR = 25.chr # "\031" # End of Record + OPT_TUID = 26.chr # "\032" # TACACS User Identification + OPT_OUTMRK = 27.chr # "\e" # Output Marking + OPT_TTYLOC = 28.chr # "\034" # Terminal Location Number + OPT_3270REGIME = 29.chr # "\035" # Telnet 3270 Regime + OPT_X3PAD = 30.chr # "\036" # X.3 PAD + OPT_NAWS = 31.chr # "\037" # Negotiate About Window Size + OPT_TSPEED = 32.chr # " " # Terminal Speed + OPT_LFLOW = 33.chr # "!" # Remote Flow Control + OPT_LINEMODE = 34.chr # "\"" # Linemode + OPT_XDISPLOC = 35.chr # "#" # X Display Location + OPT_OLD_ENVIRON = 36.chr # "$" # Environment Option + OPT_AUTHENTICATION = 37.chr # "%" # Authentication Option + OPT_ENCRYPT = 38.chr # "&" # Encryption Option + OPT_NEW_ENVIRON = 39.chr # "'" # New Environment Option + OPT_EXOPL = 255.chr # "\377" # Extended-Options-List + + NULL = "\000" + CR = "\015" + LF = "\012" + EOL = CR + LF +v = $-v +$-v = false + VERSION = "0.232" + RELEASE_DATE = "$Date: 1999/08/10 05:20:21 $" +$-v = v + + def initialize(options) + @options = options + @options["Binmode"] = false unless @options.key?("Binmode") + @options["Host"] = "localhost" unless @options.key?("Host") + @options["Port"] = 23 unless @options.key?("Port") + @options["Prompt"] = /[$%#>] \z/n unless @options.key?("Prompt") + @options["Telnetmode"] = true unless @options.key?("Telnetmode") + @options["Timeout"] = 10 unless @options.key?("Timeout") + @options["Waittime"] = 0 unless @options.key?("Waittime") + + @telnet_option = { "SGA" => false, "BINARY" => false } + + if @options.key?("Output_log") + @log = File.open(@options["Output_log"], 'a+') + @log.sync = true + @log.binmode + end + + if @options.key?("Dump_log") + @dumplog = File.open(@options["Dump_log"], 'a+') + @dumplog.sync = true + @dumplog.binmode + end + + if @options.key?("Proxy") + if @options["Proxy"].kind_of?(Telnet) + @sock = @options["Proxy"].sock + elsif @options["Proxy"].kind_of?(TCPsocket) + @sock = @options["Proxy"] + else + raise "Error; Proxy is Telnet or TCPSocket object." + end + else + message = "Trying " + @options["Host"] + "...\n" + yield(message) if iterator? + @log.write(message) if @options.key?("Output_log") + @dumplog.write(message) if @options.key?("Dump_log") + + begin + if @options["Timeout"] == false + @sock = TCPsocket.open(@options["Host"], @options["Port"]) + else + timeout(@options["Timeout"]){ + @sock = TCPsocket.open(@options["Host"], @options["Port"]) + } + end + rescue TimeoutError + raise TimeOut, "timed-out; opening of the host" + rescue + @log.write($!.to_s + "\n") if @options.key?("Output_log") + @dumplog.write($!.to_s + "\n") if @options.key?("Dump_log") + raise + end + @sock.sync = true + @sock.binmode + + message = "Connected to " + @options["Host"] + ".\n" + yield(message) if iterator? + @log.write(message) if @options.key?("Output_log") + @dumplog.write(message) if @options.key?("Dump_log") + end + + super(@sock) + end # initialize + + attr :sock + + def telnetmode(mode = 'turn') + if 'turn' == mode + @options["Telnetmode"] = @options["Telnetmode"] ? false : true + else + @options["Telnetmode"] = mode ? true : false + end + end + + def binmode(mode = 'turn') + if 'turn' == mode + @options["Binmode"] = @options["Binmode"] ? false : true + else + @options["Binmode"] = mode ? true : false + end + end + + def preprocess(string) + str = string.dup + + # combine CR+NULL into CR + str.gsub!(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + str.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"] + + # respond to "IAC DO x" + str.gsub!(/([^#{IAC}]?)#{IAC}#{DO}([#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}])/no){ + if OPT_BINARY == $2 + @telnet_option["BINARY"] = true + @sock.write(IAC + WILL + OPT_BINARY) + else + @sock.write(IAC + WONT + $2) + end + $1 + } + + # respond to "IAC DON'T x" with "IAC WON'T x" + str.gsub!(/([^#{IAC}]?)#{IAC}#{DONT}([#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}])/no){ + @sock.write(IAC + WONT + $2) + $1 + } + + # respond to "IAC WILL x" + str.gsub!(/([^#{IAC}]?)#{IAC}#{WILL}([#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}])/no){ + if OPT_ECHO == $2 + @sock.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA == $2 + @telnet_option["SGA"] = true + @sock.write(IAC + DO + OPT_SGA) + end + $1 + } + + # respond to "IAC WON'T x" + str.gsub!(/([^#{IAC}]?)#{IAC}#{WONT}([#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}])/no){ + if OPT_ECHO == $2 + @sock.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA == $2 + @telnet_option["SGA"] = false + @sock.write(IAC + DONT + OPT_SGA) + end + $1 + } + + # respond to "IAC AYT" (are you there) + str.gsub!(/([^#{IAC}]?)#{IAC}#{AYT}/no){ + @sock.write("nobody here but us pigeons" + EOL) + $1 + } + + str.gsub!(/#{IAC}#{IAC}/no, IAC) # handle escaped IAC characters + + str + end # preprocess + + def waitfor(options) + time_out = @options["Timeout"] + waittime = @options["Waittime"] + + if options.kind_of?(Hash) + prompt = if options.key?("Match") + options["Match"] + elsif options.key?("Prompt") + options["Prompt"] + elsif options.key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.key?("Timeout") + waittime = options["Waittime"] if options.key?("Waittime") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + until(not IO::select([@sock], nil, nil, waittime) and prompt === line) + unless IO::select([@sock], nil, nil, time_out) + raise TimeOut, "timed-out; wait for the next data" + end + begin + c = @sock.sysread(1024 * 1024) + @dumplog.print(c) if @options.key?("Dump_log") + buf.concat c + if @options["Telnetmode"] + buf = preprocess(buf) + if /#{IAC}.?\z/no === buf + next + end + end + @log.print(buf) if @options.key?("Output_log") + yield buf if iterator? + line.concat(buf) + buf = '' + rescue EOFError # End of file reached + if line == '' + line = nil + yield nil if iterator? + end + break + end + end + line + end + + def print(string) + str = string.dup + "\n" + + str.gsub!(/#{IAC}/no, IAC + IAC) if @options["Telnetmode"] + + unless @options["Binmode"] + if @telnet_option["BINARY"] and @telnet_option["SGA"] + # IAC WILL SGA IAC DO BIN send EOL --> CR + str.gsub!(/\n/n, CR) + elsif @telnet_option["SGA"] + # IAC WILL SGA send EOL --> CR+NULL + str.gsub!(/\n/n, CR + NULL) + else + # NONE send EOL --> CR+LF + str.gsub!(/\n/n, EOL) + end + end + + @sock.write(str) + end + + def cmd(options) + match = @options["Prompt"] + time_out = @options["Timeout"] + + if options.kind_of?(Hash) + string = options["String"] + match = options["Match"] if options.key?("Match") + time_out = options["Timeout"] if options.key?("Timeout") + else + string = options + end + + IO::select(nil, [@sock]) + self.print(string) + if iterator? + waitfor({"Prompt" => match, "Timeout" => time_out}){|c| yield c } + else + waitfor({"Prompt" => match, "Timeout" => time_out}) + end + end + + def login(options, password = '') + if options.kind_of?(Hash) + username = options["Name"] + password = options["Password"] + else + username = options + end + + if iterator? + line = waitfor(/login[: ]*\z/n){|c| yield c } + line.concat( cmd({"String" => username, + "Match" => /Password[: ]*\z/n}){|c| yield c } ) + line.concat( cmd(password){|c| yield c } ) + else + line = waitfor(/login[: ]*\z/n) + line.concat( cmd({"String" => username, + "Match" => /Password[: ]*\z/n}) ) + line.concat( cmd(password) ) + end + line + end + +end diff --git a/lib/tempfile.rb b/lib/tempfile.rb new file mode 100644 index 0000000000..11a8fba979 --- /dev/null +++ b/lib/tempfile.rb @@ -0,0 +1,94 @@ +# +# $Id$ +# +# The class for temporary files. +# o creates a temporary file, which name is "basename.pid.n" with mode "w+". +# o Tempfile objects can be used like IO object. +# o with tmpfile.close(true) created temporary files are removed. +# o created files are also removed on script termination. +# o with Tempfile#open, you can reopen the temporary file. +# o file mode of the temporary files are 0600. + +require 'delegate' +require 'final' + +class Tempfile < SimpleDelegator + Max_try = 10 + + def Tempfile.callback(path) + lambda{ + print "removing ", path, "..." if $DEBUG + if File.exist?(path) + File.unlink(path) + end + if File.exist?(path + '.lock') + Dir.rmdir(path + '.lock') + end + print "done\n" if $DEBUG + } + end + + def initialize(basename, tmpdir=ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp') + umask = File.umask(0177) + begin + n = 0 + while true + begin + tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n) + lock = tmpname + '.lock' + unless File.exist?(tmpname) or File.exist?(lock) + Dir.mkdir(lock) + break + end + rescue + raise "cannot generate tmpfile `%s'" % tmpname if n >= Max_try + #sleep(1) + end + n += 1 + end + + @clean_files = Tempfile.callback(tmpname) + ObjectSpace.define_finalizer(self, @clean_files) + + @tmpfile = File.open(tmpname, 'w+') + @tmpname = tmpname + super(@tmpfile) + Dir.rmdir(lock) + ensure + File.umask(umask) + end + end + + def Tempfile.open(*args) + Tempfile.new(*args) + end + + def open + @tmpfile.close if @tmpfile + @tmpfile = File.open(@tmpname, 'r+') + __setobj__(@tmpfile) + end + + def close(real=false) + @tmpfile.close if @tmpfile + @tmpfile = nil + if real + @clean_files.call + ObjectSpace.undefine_finalizer(self) + end + end + + def path + @tmpname + end +end + +if __FILE__ == $0 +# $DEBUG = true + f = Tempfile.new("foo") + f.print("foo\n") + f.close + f.open + p f.gets # => "foo\n" + f.close(true) +end diff --git a/lib/thread.rb b/lib/thread.rb index ec75144374..22610f2992 100644 --- a/lib/thread.rb +++ b/lib/thread.rb @@ -17,6 +17,16 @@ if $DEBUG Thread.abort_on_exception = true end +def Thread.exclusive + begin + Thread.critical = true + r = yield + ensure + Thread.critical = false + end + r +end + class Mutex def initialize @waiting = [] @@ -52,10 +62,10 @@ class Mutex def unlock return unless @locked - Thread.critical = TRUE + Thread.critical = true t = @waiting.shift - @locked = FALSE - Thread.critical = FALSE + @locked = false + Thread.critical = false t.run if t self end @@ -68,39 +78,46 @@ class Mutex unlock end end + + def exclusive_unlock + return unless @locked + Thread.exclusive do + t = @waiting.shift + @locked = false + t.wakeup if t + yield + end + self + end end class ConditionVariable def initialize @waiters = [] - @waiters_mutex = Mutex.new - @waiters.taint # enable tainted comunication - self.taint end def wait(mutex) - mutex.unlock - @waiters_mutex.synchronize { + mutex.exclusive_unlock do @waiters.push(Thread.current) - } - Thread.stop + Thread.stop + end mutex.lock end def signal - @waiters_mutex.synchronize { - t = @waiters.shift - t.run if t - } + t = @waiters.shift + t.run if t end def broadcast - @waiters_mutex.synchronize { - for t in @waiters - t.run - end + waiters0 = nil + Thread.exclusive do + waiters0 = @waiters.dup @waiters.clear - } + end + for t in waiters0 + t.run + end end end @@ -120,6 +137,7 @@ class Queue Thread.critical = false t.run if t end + alias enq push def pop non_block=false Thread.critical = true @@ -139,11 +157,17 @@ class Queue Thread.critical = false end end + alias shift pop + alias deq pop def empty? @que.length == 0 end + def clear + @que.replace([]) + end + def length @que.length end @@ -168,14 +192,14 @@ class SizedQueue= max @max = max - Thread.critical = FALSE + Thread.critical = false else diff = max - @max @max = max - Thread.critical = FALSE + Thread.critical = false diff.times do t = @queue_wait.shift t.run if t diff --git a/lib/timeout.rb b/lib/timeout.rb new file mode 100644 index 0000000000..d4ea758acd --- /dev/null +++ b/lib/timeout.rb @@ -0,0 +1,42 @@ +# +# timeout.rb -- execution timeout +# +#= SYNOPSIS +# +# require 'timeout' +# status = timeout(5) { +# # something may take time +# } +# +#= DESCRIPTION +# +# timeout executes the block. If the block execution terminates successfully +# before timeout, it returns true. If not, it terminates the execution and +# raise TimeoutError exception. +# +#== Parameters +# +# : timout +# +# The time in seconds to wait for block teminatation. +# +#=end + +class TimeoutError Raises WeakRef::RefError (because original GC'ed) +# p foo.to_s # should raise exception (recycled) require "delegate" @@ -15,27 +16,31 @@ class WeakRef [ref,...] + ID_REV_MAP = {} # ref -> obj ObjectSpace.add_finalizer(lambda{|id| - rid = ID_MAP[id] - if rid - ID_REV_MAP[rid] = nil + rids = ID_MAP[id] + if rids + for rid in rids + ID_REV_MAP[rid] = nil + end ID_MAP[id] = nil end rid = ID_REV_MAP[id] if rid ID_REV_MAP[id] = nil - ID_MAP[rid] = nil + ID_MAP[rid].delete(id) + ID_MAP[rid] = nil if ID_MAP[rid].empty? end }) - + def initialize(orig) super @__id = orig.__id__ ObjectSpace.call_finalizer orig ObjectSpace.call_finalizer self - ID_MAP[@__id] = self.__id__ + ID_MAP[@__id] = [] unless ID_MAP[@__id] + ID_MAP[@__id].concat self.__id__ ID_REV_MAP[self.id] = @__id end @@ -60,10 +65,11 @@ class WeakRef