summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Env.rb14
-rw-r--r--lib/README23
-rw-r--r--lib/cgi.rb240
-rw-r--r--lib/cgi/session.rb32
-rw-r--r--lib/date.rb38
-rw-r--r--lib/debug.rb210
-rw-r--r--lib/delegate.rb2
-rw-r--r--lib/forwardable.rb94
-rw-r--r--lib/ftools.rb3
-rw-r--r--lib/importenv.rb6
-rw-r--r--lib/irb.rb314
-rw-r--r--lib/irb/completion.rb152
-rw-r--r--lib/irb/context.rb289
-rw-r--r--lib/irb/extend-command.rb126
-rw-r--r--lib/irb/frame.rb2
-rw-r--r--lib/irb/help.rb33
-rw-r--r--lib/irb/init.rb232
-rw-r--r--lib/irb/input-method.rb12
-rw-r--r--lib/irb/lc/error.rb30
-rw-r--r--lib/irb/lc/help-message34
-rw-r--r--lib/irb/lc/ja/error.rb29
-rw-r--r--lib/irb/lc/ja/help-message35
-rw-r--r--lib/irb/loader.rb6
-rw-r--r--lib/irb/locale.rb187
-rw-r--r--lib/irb/main.rb867
-rw-r--r--lib/irb/multi-irb.rb54
-rw-r--r--lib/irb/ruby-lex.rb97
-rw-r--r--lib/irb/ruby-token.rb13
-rw-r--r--lib/irb/slex.rb22
-rw-r--r--lib/irb/version.rb10
-rw-r--r--lib/irb/workspace-binding-2.rb15
-rw-r--r--lib/irb/workspace-binding.rb77
-rw-r--r--lib/irb/workspace.rb106
-rw-r--r--lib/irb/ws-for-case-2.rb15
-rw-r--r--lib/irb/xmp.rb2
-rw-r--r--lib/mkmf.rb132
-rw-r--r--lib/monitor.rb96
-rw-r--r--lib/net/http.rb1118
-rw-r--r--lib/net/imap.rb487
-rw-r--r--lib/net/pop.rb13
-rw-r--r--lib/net/protocol.rb309
-rw-r--r--lib/net/smtp.rb29
-rw-r--r--lib/net/telnet.rb217
-rw-r--r--lib/observer.rb2
-rw-r--r--lib/open3.rb1
-rw-r--r--lib/parsedate.rb11
-rw-r--r--lib/ping.rb2
-rw-r--r--lib/pstore.rb7
-rw-r--r--lib/shell.rb274
-rw-r--r--lib/shell/builtin-command.rb154
-rw-r--r--lib/shell/command-processor.rb584
-rw-r--r--lib/shell/error.rb26
-rw-r--r--lib/shell/filter.rb111
-rw-r--r--lib/shell/process-controller.rb258
-rw-r--r--lib/shell/system-command.rb168
-rw-r--r--lib/shell/version.rb16
-rw-r--r--lib/thread.rb32
57 files changed, 5195 insertions, 2273 deletions
diff --git a/lib/Env.rb b/lib/Env.rb
index 7101b84c91..452a28659e 100644
--- a/lib/Env.rb
+++ b/lib/Env.rb
@@ -6,19 +6,7 @@
# $USER = "matz"
# p ENV["USER"]
-for k,v in ENV
- next unless /^[a-zA-Z][_a-zA-Z0-9]*/ =~ k
- eval <<EOS
- $#{k} = %q!#{v}!
- trace_var "$#{k}", proc{|v|
- ENV[%q!#{k}!] = v;
- $#{k} = %q!#{v}!
- if v == nil
- untrace_var "$#{k}"
- end
- }
-EOS
-end
+require 'importenv'
if __FILE__ == $0
p $TERM
diff --git a/lib/README b/lib/README
index de6a43af09..9aaf4524c9 100644
--- a/lib/README
+++ b/lib/README
@@ -2,10 +2,12 @@ English.rb access global variables by english names
Env.rb access environment variables as globals
README this file
base64.rb encode/decode base64 (obsolete)
-cgi-lib.rb decode CGI data
+cgi.rb CGI access
+cgi/session.rb CGI session
+cgi-lib.rb decode CGI data (old style)
complex.rb complex number suppor
date.rb date object
-date2.rb date object (compatible)
+date2.rb date object (obsolete; use date)
debug.rb ruby debugger
delegate.rb delegate messages to other object
e2mmap.rb exception utilities
@@ -13,11 +15,13 @@ eregex.rb extended regular expression (just a proof of concept)
final.rb add finalizer to the object (simple)
finalize.rb add finalizer to the object
find.rb traverse directory tree
+forwardable.rb explicit delegation library
ftools.rb file tools
-ftplib.rb ftp access library
+ftplib.rb ftp access library (obsolete; use net/ftp)
getoptlong.rb GNU getoptlong compatible
-getopts.rb parse command line options
+getopts.rb parse command line options (use getoptlong)
importenv.rb access environment variables as globals
+irb.rb interactive ruby
jcode.rb japanese text handling (replace String methods)
mailread.rb read mail headers
mathn.rb extended math operation
@@ -25,6 +29,13 @@ matrix.rb matrix calculation library
mkmf.rb Makefile maker
monitor.rb exclusive region monitor for thread
mutex_m.rb mutex mixin
+net/ftp.rb ftp access
+net/http.rb HTTP access
+net/imap.rb IMAP4 access
+net/pop.rb POP3 access
+net/protocol.rb abstract class for net library
+net/smtp.rb SMTP access
+net/telnet.rb telnet library
observer.rb observer desing pattern library (provides Observable)
open3.rb open subprocess connection stdin/stdout/stderr
ostruct.rb python style object
@@ -35,11 +46,11 @@ profile.rb ruby profiler
pstore.rb persistent object strage using marshal
rational.rb rational number support
readbytes.rb define IO#readbytes
-shell.rb shell like operation under Ruby (imcomplete)
+shell.rb shell like facilities under Ruby
shellwords.rb split into words like shell
singleton.rb singleton design pattern library
sync.rb 2 phase lock
-telnet.rb telnet library
+telnet.rb telnet library (obsolete; use net/telnet)
tempfile.rb temporary file that automatically removed
thread.rb thread support
thwait.rb thread syncronization class
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 7d27cecd67..cfbdab8686 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -4,7 +4,7 @@
cgi.rb - cgi support library
-Version 2.1.2
+Version 2.1.4
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
@@ -185,12 +185,13 @@ class CGI
CR = "\015"
LF = "\012"
EOL = CR + LF
- VERSION = "2.1.2"
- RELEASE_DATE = "2000-12-25"
- VERSION_CODE = 212
- RELEASE_CODE = 20001225
+ VERSION = '2.1.4'
+ RELEASE_DATE = '2001-04-18'
+ VERSION_CODE = 214
+ RELEASE_CODE = 20010418
+ REVISION = '$Id$'
- NEEDS_BINMODE = true if /WIN/ni === RUBY_PLATFORM
+ NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
HTTP_STATUS = {
@@ -350,11 +351,11 @@ class CGI
=begin
=== MAKE RFC1123 DATE STRING
CGI::rfc1123_date(Time.now)
- # Sat, 1 Jan 2000 00:00:00 GMT
+ # Sat, 01 Jan 2000 00:00:00 GMT
=end
def CGI::rfc1123_date(time)
t = time.clone.gmtime
- return format("%s, %d %s %d %.2d:%.2d:%.2d GMT",
+ return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
t.hour, t.min, t.sec)
end
@@ -423,7 +424,8 @@ status:
options["type"].concat( options.delete("charset") )
end
- if options.delete("nph") or (/IIS/n === env_table['SERVER_SOFTWARE'])
+ options.delete("nph") if defined?(MOD_RUBY)
+ if options.delete("nph") or /IIS/n.match(env_table['SERVER_SOFTWARE'])
buf.concat( (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " )
buf.concat( (HTTP_STATUS[options["status"]] or
options["status"] or
@@ -446,7 +448,9 @@ status:
end
if options.has_key?("status")
- buf.concat("Status: " + options.delete("status") + EOL)
+ status = (HTTP_STATUS[options["status"]] or options["status"])
+ buf.concat("Status: " + status + EOL)
+ options.delete("status")
end
if options.has_key?("server")
@@ -496,8 +500,21 @@ status:
}
if defined?(MOD_RUBY)
- buf.scan(/([^:]+): (.+)#{EOL}/n){
- Apache::request[$1] = $2
+ table = Apache::request.headers_out
+ buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
+ $stderr.printf("name:%s value:%s\n", name, value) if $DEBUG
+ case name
+ when 'Set-Cookie'
+ table.add($1, $2)
+ when /^status$/ni
+ Apache::request.status_line = value
+ when /^content-type$/ni
+ Apache::request.content_type = value
+ when /^content-encoding$/ni
+ Apache::request.content_encoding = value
+ else
+ Apache::request.headers_out[name] = value
+ end
}
Apache::request.send_http_header
''
@@ -626,13 +643,9 @@ convert string charset, and set language to "ja".
# simple support for IE
if options["path"]
@path = options["path"]
- elsif ENV["REQUEST_URI"]
- @path = ENV["REQUEST_URI"].sub(/\?.*/n,'')
- if ENV["PATH_INFO"]
- @path = @path[0...@path.rindex(ENV["PATH_INFO"])]
- end
else
- @path = (ENV["SCRIPT_NAME"] or "")
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
+ @path = ($1 or "")
end
@domain = options["domain"]
@expires = options["expires"]
@@ -793,9 +806,9 @@ convert string charset, and set language to "ja".
body = Tempfile.new("CGI")
body.binmode
- until head and (/#{boundary}(?:#{EOL}|--)/n === buf)
+ until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
- if (not head) and (/#{EOL}#{EOL}/n === buf)
+ if (not head) and /#{EOL}#{EOL}/n.match(buf)
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
head = $1.dup
""
@@ -834,14 +847,14 @@ convert string charset, and set language to "ja".
end
END
- /Content-Disposition:.* filename="?([^\";]*)"?/ni === head
+ /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
eval <<-END
def body.original_filename
#{
filename = ($1 or "").dup
- if (/Mac/ni === env_table['HTTP_USER_AGENT']) and
- (/Mozilla/ni === env_table['HTTP_USER_AGENT']) and
- (not /MSIE/ni === env_table['HTTP_USER_AGENT'])
+ if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
+ /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
+ (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
CGI::unescape(filename)
else
filename
@@ -850,14 +863,14 @@ convert string charset, and set language to "ja".
end
END
- /Content-Type: (.*)/ni === head
+ /Content-Type: (.*)/ni.match(head)
eval <<-END
def body.content_type
#{($1 or "").dump.untaint}.taint
end
END
- /Content-Disposition:.* name="?([^\";]*)"?/ni === head
+ /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
name = $1.dup
if params.has_key?(name)
@@ -889,7 +902,7 @@ convert string charset, and set language to "ja".
words = Shellwords.shellwords(string)
- if words.find{|x| /=/n === x }
+ if words.find{|x| /=/n.match(x) }
words.join('&')
else
words.join('+')
@@ -899,8 +912,7 @@ convert string charset, and set language to "ja".
def initialize_query()
if ("POST" == env_table['REQUEST_METHOD']) and
- (%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n ===
- env_table['CONTENT_TYPE'])
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
boundary = $1.dup
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
else
@@ -975,8 +987,8 @@ convert string charset, and set language to "ja".
cgi = CGI.new("html3") # add HTML generation methods
cgi.element
cgi.element{ "string" }
- cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" })
- cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" }){ "string" }
+ cgi.element({ "ATTRIBUTE1" => "value1", "ATTRIBUTE2" => "value2" })
+ cgi.element({ "ATTRIBUTE1" => "value1", "ATTRIBUTE2" => "value2" }){ "string" }
# add HTML generation methods
CGI.new("html3") # html3.2
@@ -1235,8 +1247,10 @@ convert string charset, and set language to "ja".
form("get", "url"){ "string" }
# <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
- form({"METHOD" => "post", ENCTYPE => "enctype"}){ "string" }
+ form({"METHOD" => "post", "ENCTYPE" => "enctype"}){ "string" }
# <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
+
+The hash keys are case sensitive. Ask the samples.
=end
def form(method = "post", action = nil, enctype = "application/x-www-form-urlencoded")
attributes = if method.kind_of?(String)
@@ -1244,7 +1258,7 @@ convert string charset, and set language to "ja".
"ENCTYPE" => enctype }
else
unless method.has_key?("METHOD")
- method["METHOD"] = method
+ method["METHOD"] = "post"
end
unless method.has_key?("ENCTYPE")
method["ENCTYPE"] = enctype
@@ -1935,161 +1949,7 @@ end
== HISTORY
-* Mon Dec 25 05:02:27 JST 2000 - wakou
- * version 2.1.2
- * bug fix: CGI::escapeElement(): didn't accept empty element.
- * bug fix: CGI::unescapeElement(): ditto.
- * bug fix: CGI::unescapeHTML(): support for "&copy;, &hearts;, ..."
- thanks to YANAGAWA Kazuhisa <kjana@os.xaxon.ne.jp>
- * bug fix: CGI::unescapeHTML(): support for "&#09;"
- thanks to OHSHIMA Ryunosuke <ryu@jaist.ac.jp>
- * Regexp::last_match[0] --> $&
- * Regexp::last_match[1] --> $1
- * Regexp::last_match[2] --> $2
- * add: CGI#param(): test implement. undocumented.
-
-* Mon Dec 11 00:16:51 JST 2000 - wakou
- * version 2.1.1
- * support -T1 on ruby 1.6.2
- * body.original_filename: eval(str.dump.untaint).taint
- * body.content_type: eval(str.dump.untaint).taint
- * $& --> Regexp::last_match[0]
- * $1 --> Regexp::last_match[1]
- * $2 --> Regexp::last_match[2]
-
-* Thu Oct 12 01:16:59 JST 2000 - wakou
- * version 2.1.0
- * bug fix: CGI::html(): PRETTY option didn't work.
- thanks to akira yamada <akira@ruby-lang.org>
-
-* Wed Sep 13 06:09:26 JST 2000 - wakou
- * version 2.0.1
- * bug fix: CGI::header(): output status header.
- thanks to Yasuhiro Fukuma <yasuf@bsdclub.org>
-
-* Tue Sep 12 06:56:51 JST 2000 - wakou
- * version 2.0.0
- * require ruby1.5.4 or later. (ruby1.4 doesn't have block_given? method.)
- * improvement: CGI::escape(), CGI::unescape().
- thanks to WATANABE Hirofumi <eban@os.rim.or.jp>
- * bug fix: CGI::escapeElement().
- * improvement: CGI::unescapeHTML().
- thanks to Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
-
-* 2000/08/09 04:32:22 - matz
- * improvement: CGI::pretty()
-
-* 2000/06/23 07:01:34 - matz
- * change: iterator? --> block_given?
-
-* Sun Jun 18 23:31:44 JST 2000 - wakou
- * version 1.7.0
- * change: version syntax. old: x.yz, now: x.y.z
-
-* 2000/06/13 15:49:27 - wakou
- * version 1.61
- * read_multipart(): if no content body then raise EOFError.
-
-* 2000/06/03 18:16:17 - wakou
- * version 1.60
- * improve: CGI::pretty()
-
-* 2000/05/30 19:04:08 - wakou
- * version 1.50
- * CGI#out(): if "HEAD" == REQUEST_METHOD then output only HTTP header.
-
-* 2000/05/24 06:58:51 - wakou
- * version 1.40
- * typo: CGI::Cookie::new()
- * bug fix: CGI::escape(): bad: " " --> "%2B"; true: " " --> "+";
- thanks to Ryunosuke Ohshima <ryu@jaist.ac.jp>
-
-* 2000/05/08 21:51:30 - wakou
- * version 1.31
- * improvement of time forming new CGI object accompanied with HTML generation methods.
-
-* 2000/05/07 21:51:14 - wakou
- * version 1.30
- * require English.rb
- * improvement of load time.
-
-* 2000/05/02 21:44:12 - wakou
- * version 1.21
- * support for ruby 1.5.3 (2000-05-01) (Array#filter --> Array#collect!)
-
-* 2000/04/03 18:31:42 - wakou
- * version 1.20
- * bug fix: CGI#image_button() can't get Hash option.
- thanks to Takashi Ikeda <ikeda@auc.co.jp>
- * CGI::unescapeHTML(): simple support for "&#12345;"
- * CGI::Cookie::new(): simple support for IE
- * CGI::escape(): ' ' replaced by '+'
-
-* 1999/12/06 20:16:34 - wakou
- * version 1.10
- * can make many CGI objects.
- * if use mod_ruby, then require ruby1.4.3 or later.
-
-* 1999/11/29 21:35:58 - wakou
- * version 1.01
- * support for ruby 1.5.0 (1999-11-20)
-
-* 1999/09/13 23:00:58 - wakou
- * version 1.00
- * COUTION! name change. CGI.rb --> cgi.rb
- * CGI#auth_type, CGI#content_length, CGI#content_type, ...
- if not ENV included it, then return nil.
- * CGI#content_length and CGI#server_port return Integer.
- * if not CGI#params.include?('name'), then CGI#params['name'] return [].
- * if not CGI#cookies.include?('name'), then CGI#cookies['name'] return [].
-
-* 1999/08/05 18:04:59 - wakou
- * version 0.41
- * typo. thanks to MJ Ray <markj@altern.org>
- HTTP_STATUS["NOT_INPLEMENTED"] --> HTTP_STATUS["NOT_IMPLEMENTED"]
-
-* 1999/07/20 20:44:31 - wakou
- * version 0.40
- * COUTION! incompatible change.
- sorry, but probably this change is last big incompatible change.
- * CGI::print --> CGI#out
- cgi = CGI.new
- cgi.out{"string"} # old: CGI::print{"string"}
- * CGI::cookie --> CGI::Cookie::new
- cookie1 = CGI::Cookie::new # old: CGI::cookie
- * CGI::header --> CGI#header
-
-* 1999/06/29 06:50:21 - wakou
- * version 0.30
- * COUTION! incompatible change.
- query = CGI.new
- cookies = query.cookies # old: query.cookie
- values = query.cookies[name] # old: query.cookie[name]
-
-* 1999/06/21 21:05:57 - wakou
- * version 0.24
- * CGI::Cookie::parse() return { name => CGI::Cookie object } pairs.
-
-* 1999/06/20 23:29:12 - wakou
- * version 0.23
- * modified a bit to clear module separation.
-
-* Mon Jun 14 17:49:32 JST 1999 - matz
- * version 0.22
- * Cookies are now CGI::Cookie objects.
- * Cookie modeled after CGI::Cookie.pm.
-
-* Fri Jun 11 11:19:11 JST 1999 - matz
- * version 0.21
- * modified a bit to clear module separation.
-
-* 1999/06/03 06:48:15 - wakou
- * version 0.20
- * support for multipart form.
-
-* 1999/05/24 07:05:41 - wakou
- * version 0.10
- * first release.
-
-$Date$
+delete. see cvs log.
+
+
=end
diff --git a/lib/cgi/session.rb b/lib/cgi/session.rb
index 48f3496939..1a3379b88a 100644
--- a/lib/cgi/session.rb
+++ b/lib/cgi/session.rb
@@ -1,3 +1,4 @@
+# Copyright (C) 2001 Yukihiro "Matz" Matsumoto
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -15,23 +16,22 @@ class CGI
}
end
- def create_new_id
+ def Session::create_new_id
require 'md5'
md5 = MD5::new
md5.update(String(Time::now))
md5.update(String(rand(0)))
md5.update(String($$))
md5.update('foobar')
- @session_id = md5.hexdigest[0,16]
+ md5.hexdigest[0,16]
end
- private :create_new_id
def initialize(request, option={})
session_key = option['session_key'] || '_session_id'
id, = option['session_id']
unless id
if option['new_session']
- id = create_new_id
+ id = Session::create_new_id
end
end
unless id
@@ -43,7 +43,7 @@ class CGI
if option.key?('new_session') and not option['new_session']
raise ArgumentError, "session_key `%s' should be supplied"%session_key
end
- id = create_new_id
+ id = Session::create_new_id
end
end
@session_id = id
@@ -54,7 +54,9 @@ class CGI
@output_cookies = [
Cookie::new("name" => session_key,
"value" => id,
- "path" => if ENV["SCRIPT_NAME"] then
+ "path" => if option['session_path'] then
+ option['session_path']
+ elsif ENV["SCRIPT_NAME"] then
File::dirname(ENV["SCRIPT_NAME"])
else
""
@@ -94,10 +96,19 @@ class CGI
end
class FileStore
+ def check_id(id)
+ /[^0-9a-zA-Z]/ =~ id.to_s ? false : true
+ end
+ module_function :check_id
+
def initialize(session, option={})
dir = option['tmpdir'] || ENV['TMP'] || '/tmp'
prefix = option['prefix'] || ''
- path = dir+"/"+prefix+session.session_id
+ id = session.session_id
+ unless check_id(id)
+ raise ArgumentError, "session_id `%s' is invalid" % id
+ end
+ path = dir+"/"+prefix+id
path.untaint
unless File::exist? path
@hash = {}
@@ -132,6 +143,7 @@ class CGI
end
def close
+ return if @f.closed?
update
@f.close
end
@@ -146,9 +158,9 @@ class CGI
class MemoryStore
GLOBAL_HASH_TABLE = {}
- def initialize(session, option={})
+ def initialize(session, option=nil)
@session_id = session.session_id
- GLOBAL_HASH_TABLE[@session_id] = {}
+ GLOBAL_HASH_TABLE[@session_id] ||= {}
end
def restore
@@ -164,7 +176,7 @@ class CGI
end
def delete
- GLOBAL_HASH_TABLE[@session_id] = nil
+ GLOBAL_HASH_TABLE.delete(@session_id)
end
end
end
diff --git a/lib/date.rb b/lib/date.rb
index 58179a7153..3422121298 100644
--- a/lib/date.rb
+++ b/lib/date.rb
@@ -1,5 +1,5 @@
-# date.rb: Written by Tadayoshi Funaba 1998-2000
-# $Id: date.rb,v 1.22 2000-07-16 10:23:40+09 tadf Exp $
+# date2.rb: Written by Tadayoshi Funaba 1998-2001
+# $Id: date2.rb,v 1.23 2001-01-18 12:09:47+09 tadf Exp $
class Date
@@ -128,16 +128,15 @@ class Date
end
if d < 0
ny, nm = clfloor(y * 12 + m, 12)
- nm, = clfloor(m + 1, 1)
- la = nil
- 31.downto 1 do |z|
- break if la = exist3?(y, m, z, sg)
- end
- ns = ns?(la, sg)
- d = jd_to_civil(civil_to_jd(ny, nm, 1, ns) + d, ns)[-1]
+ nm, = clfloor(nm + 1, 1)
+ jd = civil_to_jd(ny, nm, d + 1, sg)
+ ns = ns?(jd, sg)
+ return unless [y, m] == jd_to_civil(jd, sg)[0..1]
+ return unless [ny, nm, 1] == jd_to_civil(jd - d, ns)
+ else
+ jd = civil_to_jd(y, m, d, sg)
+ return unless [y, m, d] == jd_to_civil(jd, sg)
end
- jd = civil_to_jd(y, m, d, sg)
- return unless [y, m, d] == jd_to_civil(jd, sg)
jd
end
@@ -154,16 +153,15 @@ class Date
def exist2? (y, d, sg=ITALY)
if d < 0
- ny = y + 1
- la = nil
- 366.downto 1 do |z|
- break if la = exist2?(y, z, sg)
- end
- ns = ns?(la, sg)
- d = jd_to_ordinal(ordinal_to_jd(ny, 1, ns) + d, ns)[-1]
+ ny, = clfloor(y + 1, 1)
+ jd = ordinal_to_jd(ny, d + 1, sg)
+ ns = ns?(jd, sg)
+ return unless [y] == jd_to_ordinal(jd, sg)[0..0]
+ return unless [ny, 1] == jd_to_ordinal(jd - d, ns)
+ else
+ jd = ordinal_to_jd(y, d, sg)
+ return unless [y, d] == jd_to_ordinal(jd, sg)
end
- jd = ordinal_to_jd(y, d, sg)
- return unless [y, d] == jd_to_ordinal(jd, sg)
jd
end
diff --git a/lib/debug.rb b/lib/debug.rb
index b6968cc338..220b68d2c9 100644
--- a/lib/debug.rb
+++ b/lib/debug.rb
@@ -91,22 +91,70 @@ class DEBUGGER__
@finish_pos = 0
@trace = false
@catch = "StandardError"
+ @suspend_next = false
end
def stop_next(n=1)
@stop_next = n
end
+ def set_suspend
+ @suspend_next = true
+ end
+
+ def clear_suspend
+ @suspend_next = false
+ end
+
+ def suspend_all
+ DEBUGGER__.suspend
+ end
+
+ def resume_all
+ DEBUGGER__.resume
+ end
+
+ def check_suspend
+ while (Thread.critical = true; @suspend_next)
+ DEBUGGER__.waiting.push Thread.current
+ @suspend_next = false
+ Thread.stop
+ end
+ Thread.critical = false
+ end
+
+ def trace?
+ @trace
+ end
+
+ def set_trace(arg)
+ @trace = arg
+ end
+
def stdout
DEBUGGER__.stdout
end
+
def break_points
DEBUGGER__.break_points
end
+
def display
DEBUGGER__.display
end
+ def context(th)
+ DEBUGGER__.context(th)
+ end
+
+ def set_trace_all(arg)
+ DEBUGGER__.set_trace(arg)
+ end
+
+ def set_last_thread(th)
+ DEBUGGER__.set_last_thread(th)
+ end
+
def debug_eval(str, binding)
begin
val = eval(str, binding)
@@ -205,7 +253,7 @@ class DEBUGGER__
def debug_command(file, line, id, binding)
MUTEX.lock
- DEBUGGER__.set_last_thread(Thread.current)
+ set_last_thread(Thread.current)
frame_pos = 0
binding_file = file
binding_line = line
@@ -218,7 +266,8 @@ class DEBUGGER__
end
@frames[0] = [binding, file, line, id]
display_expressions(binding)
- while input = readline("(rdb:%d) "%thnum(), true)
+ prompt = true
+ while prompt and input = readline("(rdb:%d) "%thnum(), true)
catch(:debug_error) do
if input == ""
input = DEBUG_LAST_CMD[0]
@@ -228,18 +277,24 @@ class DEBUGGER__
end
case input
- when /^\s*tr(?:ace)?(?:\s+(on|off))?$/
- if defined?( $1 )
+ when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
+ if defined?( $2 )
if $1 == 'on'
- @trace = true
+ set_trace_all true
else
- @trace = false
+ set_trace_all false
+ end
+ elsif defined?( $1 )
+ if $1 == 'on'
+ set_trace true
+ else
+ set_trace false
end
end
- if @trace
- stdout.print "Trace on\n"
+ if trace?
+ stdout.print "Trace on.\n"
else
- stdout.print "Trace off\n"
+ stdout.print "Trace off.\n"
end
when /^\s*b(?:reak)?\s+((?:.*?+:)?.+)$/
@@ -336,8 +391,7 @@ class DEBUGGER__
end
when /^\s*c(?:ont)?$/
- MUTEX.unlock
- return
+ prompt = false
when /^\s*s(?:tep)?(?:\s+(\d+))?$/
if $1
@@ -346,7 +400,7 @@ class DEBUGGER__
lev = 1
end
@stop_next = lev
- return
+ prompt = false
when /^\s*n(?:ext)?(?:\s+(\d+))?$/
if $1
@@ -356,7 +410,7 @@ class DEBUGGER__
end
@stop_next = lev
@no_step = @frames.size - frame_pos
- return
+ prompt = false
when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
display_frames(frame_pos)
@@ -417,8 +471,7 @@ class DEBUGGER__
else
@finish_pos = @frames.size - frame_pos
frame_pos = 0
- MUTEX.unlock
- return
+ prompt = false
end
when /^\s*cat(?:ch)?(?:\s+(.+))?$/
@@ -440,8 +493,10 @@ class DEBUGGER__
end
when /^\s*q(?:uit)?$/
- input = readline("really quit? (y/n) ", false)
- exit if input == "y"
+ input = readline("Really quit? (y/n) ", false)
+ if input == "y"
+ exit! # exit -> exit!: No graceful way to stop threads...
+ end
when /^\s*v(?:ar)?\s+/
debug_variable_info($', binding)
@@ -451,8 +506,7 @@ class DEBUGGER__
when /^\s*th(?:read)?\s+/
if DEBUGGER__.debug_thread_info($', binding) == :cont
- MUTEX.unlock
- return
+ prompt = false
end
when /^\s*p\s+/
@@ -467,6 +521,8 @@ class DEBUGGER__
end
end
end
+ MUTEX.unlock
+ resume_all
end
def debug_print_help
@@ -492,7 +548,8 @@ Commands
up[ nn] move to higher frame
down[ nn] move to lower frame
fin[ish] return to outer frame
- tr[ace][ (on|off)] set trace mode
+ tr[ace] (on|off) set trace mode of current thread
+ tr[ace] (on|off) all set trace mode of all threads
q[uit] exit from debugger
v[ar] g[lobal] show global variables
v[ar] l[ocal] show local variables
@@ -501,11 +558,10 @@ Commands
m[ethod] i[nstance] <obj> show methods of object
m[ethod] <class|module> show instance methods of class or module
th[read] l[ist] list all threads
- th[read] c[ur[rent]] show current threads
- th[read] <nnn> stop thread nnn
- th[read] stop <nnn> alias for th[read] <nnn>
- th[read] c[ur[rent]] <nnn> alias for th[read] <nnn>
- th[read] resume <nnn> run thread nnn
+ th[read] c[ur[rent]] show current thread
+ th[read] [sw[itch]] <nnn> switch thread context to nnn
+ th[read] stop <nnn> stop thread nnn
+ th[read] resume <nnn> resume thread nnn
p expression evaluate expression and print its value
h[elp] print this help
<everything else> evaluate
@@ -587,7 +643,7 @@ EOHELP
end
def check_break_points(file, pos, binding, id)
- MUTEX.lock # Stop all threads before 'line' and 'call'.
+ return false if break_points.empty?
file = File.basename(file)
n = 1
for b in break_points
@@ -604,7 +660,6 @@ EOHELP
end
n += 1
end
- MUTEX.unlock
return false
end
@@ -616,7 +671,6 @@ EOHELP
end
if @catch and ($!.type.ancestors.find { |e| e.to_s == @catch })
- MUTEX.lock
fs = @frames.size
tb = caller(0)[-fs..-1]
if tb
@@ -624,12 +678,14 @@ EOHELP
stdout.printf "\tfrom %s\n", i
end
end
+ suspend_all
debug_command(file, line, id, binding)
end
end
def trace_func(event, file, line, id, binding, klass)
- Tracer.trace_func(event, file, line, id, binding) if @trace
+ Tracer.trace_func(event, file, line, id, binding, klass) if trace?
+ context(Thread.current).check_suspend
@file = file
@line = line
case event
@@ -647,6 +703,7 @@ EOHELP
@stop_next = 1
else
@no_step = nil
+ suspend_all
debug_command(file, line, id, binding)
@last = [file, line]
end
@@ -656,6 +713,7 @@ EOHELP
@frames.unshift [binding, file, line, id]
if check_break_points(file, id.id2name, binding, id) or
check_break_points(klass.to_s, id.id2name, binding, id)
+ suspend_all
debug_command(file, line, id, binding)
end
@@ -668,6 +726,7 @@ EOHELP
when 'return', 'end'
if @frames.size == @finish_pos
@stop_next = 1
+ @finish_pos = 0
end
@frames.shift
@@ -682,19 +741,20 @@ EOHELP
end
end
- trap("INT") { DEBUGGER__.interrupt }
-# $DEBUG = true
+ trap("INT") { DEBUGGER__.interrupt }
@last_thread = Thread::main
@max_thread = 1
@thread_list = {Thread::main => 1}
@break_points = []
@display = []
+ @waiting = []
@stdout = STDOUT
class <<DEBUGGER__
def stdout
@stdout
end
+
def stdout=(s)
@stdout = s
end
@@ -707,10 +767,51 @@ EOHELP
@break_points
end
+ def waiting
+ @waiting
+ end
+
+ def set_trace( arg )
+ Thread.critical = true
+ make_thread_list
+ for th in @thread_list
+ context(th[0]).set_trace arg
+ end
+ Thread.critical = false
+ end
+
def set_last_thread(th)
@last_thread = th
end
+ def suspend
+ Thread.critical = true
+ make_thread_list
+ for th in @thread_list
+ next if th[0] == Thread.current
+ context(th[0]).set_suspend
+ end
+ Thread.critical = false
+ # Schedule other threads to suspend as soon as possible.
+ Thread.pass
+ end
+
+ def resume
+ Thread.critical = true
+ make_thread_list
+ for th in @thread_list
+ next if th[0] == Thread.current
+ context(th[0]).clear_suspend
+ end
+ waiting.each do |th|
+ th.run
+ end
+ waiting.clear
+ Thread.critical = false
+ # Schedule other threads to restart as soon as possible.
+ Thread.pass
+ end
+
def context(thread=Thread.current)
c = thread[:__debugger_data__]
unless c
@@ -726,7 +827,7 @@ EOHELP
def get_thread(num)
th = @thread_list.index(num)
unless th
- @stdout.print "no thread no.", num, "\n"
+ @stdout.print "No thread ##{num}\n"
throw :debug_error
end
th
@@ -773,31 +874,52 @@ EOHELP
make_thread_list
thread_list_all
- when /^c(?:ur(?:rent)?)?\s+(\d+)/, /^stop\s+(\d+)/, /^(\d+)/
+ when /^c(?:ur(?:rent)?)?$/
+ make_thread_list
+ thread_list(@thread_list[Thread.current])
+
+ when /^(?:sw(?:itch)?\s+)?(\d+)/
make_thread_list
th = get_thread($1.to_i)
- thread_list(@thread_list[th])
- context(th).stop_next
- th.run
- return :cont
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).stop_next
+ th.run
+ return :cont
+ end
- when /^c(?:ur(?:rent)?)?$/
+ when /^stop\s+(\d+)/
make_thread_list
- thread_list(@thread_list[Thread.current])
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif th.stop?
+ @stdout.print "Already stopped.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).suspend
+ end
when /^resume\s+(\d+)/
make_thread_list
th = get_thread($1.to_i)
- thread_list(@thread_list[th])
- th.run
- return :cont
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif !th.stop?
+ @stdout.print "Already running."
+ else
+ thread_list(@thread_list[th])
+ th.run
+ end
end
end
end
stdout.printf "Debug.rb\n"
stdout.printf "Emacs support available.\n\n"
- set_trace_func proc{|event, file, line, id, binding,klass,*rest|
- DEBUGGER__.context.trace_func event, file, line, id, binding,klass
+ set_trace_func proc { |event, file, line, id, binding, klass, *rest|
+ DEBUGGER__.context.trace_func event, file, line, id, binding, klass
}
end
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 480e1ef6b8..a72ea943ba 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -8,7 +8,7 @@
# Usage:
# foo = Object.new
# foo2 = SimpleDelegator.new(foo)
-# foo.hash == foo2.hash # => true
+# foo.hash == foo2.hash # => false
#
# Foo = DelegateClass(Array)
#
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
new file mode 100644
index 0000000000..7f57f77f53
--- /dev/null
+++ b/lib/forwardable.rb
@@ -0,0 +1,94 @@
+#
+# forwardable.rb -
+# $Release Version: 1.1$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+# original definition by delegator.rb
+# --
+# Usage:
+#
+# class Foo
+# extend Forwardable
+#
+# def_delegators("@out", "printf", "print")
+# def_delegators(:@in, :gets)
+# def_delegator(:@contents, :[], "content_at")
+# end
+# f = Foo.new
+# f.printf ...
+# f.gets
+# f.content_at(1)
+#
+# g = Goo.new
+# g.extend SingleForwardable
+# g.def_delegator("@out", :puts)
+# g.puts ...
+#
+#
+
+module Forwardable
+
+ @debug = nil
+ class<<self
+ attr_accessor :debug
+ end
+
+ def def_instance_delegators(accessor, *methods)
+ for method in methods
+ def_instance_delegator(accessor, method)
+ end
+ end
+
+ def def_instance_delegator(accessor, method, ali = method)
+ accessor = accessor.id2name if accessor.kind_of?(Integer)
+ method = method.id2name if method.kind_of?(Integer)
+ ali = ali.id2name if ali.kind_of?(Integer)
+
+ module_eval(<<-EOS, "(__FORWARDABLE__)", 1)
+ def #{ali}(*args, &block)
+ begin
+ #{accessor}.__send__(:#{method}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
+ raise
+ end
+ end
+ EOS
+ end
+
+ alias def_delegators def_instance_delegators
+ alias def_delegator def_instance_delegator
+end
+
+module SingleForwardable
+ def def_singleton_delegators(accessor, *methods)
+ for method in methods
+ def_singleton_delegator(accessor, method)
+ end
+ end
+
+ def def_singleton_delegator(accessor, method, ali = method)
+ accessor = accessor.id2name if accessor.kind_of?(Integer)
+ method = method.id2name if method.kind_of?(Integer)
+ ali = ali.id2name if ali.kind_of?(Integer)
+
+ instance_eval(<<-EOS, "(__FORWARDABLE__)", 1)
+ def #{ali}(*args, &block)
+ begin
+ #{accessor}.__send__(:#{method}, *args,&block)
+ rescue Exception
+ $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
+ raise
+ end
+ end
+ EOS
+ end
+
+ alias def_delegators def_singleton_delegators
+ alias def_delegator def_singleton_delegator
+end
+
+
+
+
diff --git a/lib/ftools.rb b/lib/ftools.rb
index 369a6177d2..5e6203b1a3 100644
--- a/lib/ftools.rb
+++ b/lib/ftools.rb
@@ -26,6 +26,7 @@ class << File
fmode = stat(from).mode
tpath = to
+ not_exist = !exist?(tpath)
from = open(from, "r")
from.binmode
@@ -50,7 +51,7 @@ class << File
to.close
from.close
end
- chmod(fmode, tpath)
+ chmod(fmode, tpath) if not_exist
ret
end
diff --git a/lib/importenv.rb b/lib/importenv.rb
index fcf306a9ab..586f37661b 100644
--- a/lib/importenv.rb
+++ b/lib/importenv.rb
@@ -10,10 +10,10 @@
for k,v in ENV
next unless /^[a-zA-Z][_a-zA-Z0-9]*/ =~ k
eval <<EOS
- $#{k} = %q!#{v}!
+ $#{k} = v
trace_var "$#{k}", proc{|v|
- ENV[%q!#{k}!] = v;
- $#{k} = %q!#{v}!
+ ENV[%q!#{k}!] = v
+ $#{k} = v
if v == nil
untrace_var "$#{k}"
end
diff --git a/lib/irb.rb b/lib/irb.rb
new file mode 100644
index 0000000000..1b8444b5b3
--- /dev/null
+++ b/lib/irb.rb
@@ -0,0 +1,314 @@
+#
+# irb.rb - irb main module
+# $Release Version: 0.7.4 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+require "irb/init"
+require "irb/context"
+require "irb/extend-command"
+require "irb/workspace"
+
+require "irb/ruby-lex"
+require "irb/input-method"
+require "irb/locale"
+
+STDOUT.sync = true
+
+module IRB
+ @RCS_ID='-$Id$-'
+
+ class Abort < Exception;end
+
+ #
+ @CONF = {}
+
+ def IRB.conf
+ @CONF
+ end
+
+ # IRB version method
+ def IRB.version
+ if v = @CONF[:VERSION] then return v end
+
+ require "irb/version"
+ rv = @RELEASE_VERSION.sub(/\.0/, "")
+ @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
+ end
+
+ # initialize IRB and start TOP_LEVEL irb
+ def IRB.start(ap_path = nil)
+ $0 = File::basename(ap_path, ".rb") if ap_path
+
+ IRB.initialize(ap_path)
+ IRB.parse_opts
+ IRB.load_modules
+
+ if @CONF[:SCRIPT]
+ irb = Irb.new(nil, @CONF[:SCRIPT])
+ else
+ irb = Irb.new
+ end
+
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
+ @CONF[:MAIN_CONTEXT] = irb.context
+
+ trap("SIGINT") do
+ irb.signal_handle
+ end
+
+ catch(:IRB_EXIT) do
+ irb.eval_input
+ end
+ print "\n"
+ end
+
+ def IRB.irb_exit(irb, ret)
+ throw :IRB_EXIT, ret
+ end
+
+ def IRB.irb_abort(irb, exception = Abort)
+ if defined? Thread
+ irb.context.thread.raise exception, "abort then interrupt!!"
+ else
+ raise exception, "abort then interrupt!!"
+ end
+ end
+
+ #
+ # irb interpriter main routine
+ #
+ class Irb
+ def initialize(workspace = nil, input_method = nil)
+ @context = Context.new(self, workspace, input_method)
+ @context.main.extend ExtendCommand
+ @signal_status = :IN_IRB
+
+ @scanner = RubyLex.new
+ @scanner.exception_on_syntax_error = false
+ end
+ attr_reader :context
+ attr_accessor :scanner
+
+ def eval_input
+ @scanner.set_input(@context.io) do
+ signal_status(:IN_INPUT) do
+ unless l = @context.io.gets
+ if @context.ignore_eof? and @context.io.readable_atfer_eof?
+ l = "\n"
+ if @context.verbose?
+ printf "Use \"exit\" to leave %s\n", @context.ap_name
+ end
+ end
+ end
+ l
+ end
+ end
+
+ @scanner.set_prompt do
+ |ltype, indent, continue, line_no|
+ if ltype
+ f = @context.prompt_s
+ elsif continue
+ f = @context.prompt_c
+ else @context.prompt_i
+ f = @context.prompt_i
+ end
+ f = "" unless f
+ @context.io.prompt = p = prompt(f, ltype, indent, line_no)
+ if @context.auto_indent_mode
+ unless ltype
+ ind = prompt(@context.prompt_i, ltype, indent, line_no).size +
+ indent * 2 - p.size
+ ind += 2 if continue
+ @context.io.prompt = p + " " * ind if ind > 0
+ end
+ end
+ end
+
+ @scanner.each_top_level_statement do
+ |line, line_no|
+ signal_status(:IN_EVAL) do
+ begin
+ trace_in do
+ @context._ = @context.workspace.evaluate(line,
+ @context.irb_path,
+ line_no)
+# @context._ = irb_eval(line, @context.bind, @context.irb_path, line_no)
+ end
+
+ if @context.inspect?
+ printf @context.return_format, @context._.inspect
+ else
+ printf @context.return_format, @context._
+ end
+ rescue StandardError, ScriptError, Abort
+ $! = RuntimeError.new("unknown exception raised") unless $!
+ print $!.type, ": ", $!, "\n"
+ if $@[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && $!.type.to_s !~ /^IRB/
+ irb_bug = true
+ else
+ irb_bug = false
+ end
+
+ messages = []
+ lasts = []
+ levels = 0
+ for m in $@
+ m = @context.workspace.filter_backtrace(m) unless irb_bug
+ if m
+ if messages.size < @context.back_trace_limit
+ messages.push "\tfrom "+m
+ else
+ lasts.push "\tfrom "+m
+ if lasts.size > @context.back_trace_limit
+ lasts.shift
+ levels += 1
+ end
+ end
+ end
+ end
+ print messages.join("\n"), "\n"
+ unless lasts.empty?
+ printf "... %d levels...\n", levels if levels > 0
+ print lasts.join("\n")
+ end
+ print "Maybe IRB bug!!\n" if irb_bug
+ end
+ end
+ end
+ end
+
+# def irb_eval(line, bind, path, line_no)
+# id, str = catch(:IRB_TOPLEVEL_EVAL){
+# return eval(line, bind, path, line_no)
+# }
+# case id
+# when :EVAL_TOPLEVEL
+# eval(str, bind, "(irb_internal)", 1)
+# when :EVAL_CONTEXT
+# @context.instance_eval(str)
+# else
+# IRB.fail IllegalParameter
+# end
+# end
+
+ def signal_handle
+ unless @context.ignore_sigint?
+ print "\nabort!!\n" if @context.verbose?
+ exit
+ end
+
+ case @signal_status
+ when :IN_INPUT
+ print "^C\n"
+ raise RubyLex::TerminateLineInput
+ when :IN_EVAL
+ IRB.irb_abort(self)
+ when :IN_LOAD
+ IRB.irb_abort(self, LoadAbort)
+ when :IN_IRB
+ # ignore
+ else
+ # ignore other cases as well
+ end
+ end
+
+ def signal_status(status)
+ return yield if @signal_status == :IN_LOAD
+
+ signal_status_back = @signal_status
+ @signal_status = status
+ begin
+ yield
+ ensure
+ @signal_status = signal_status_back
+ end
+ end
+
+ def trace_in
+ Tracer.on if @context.use_tracer?
+ begin
+ yield
+ ensure
+ Tracer.off if @context.use_tracer?
+ end
+ end
+
+ def prompt(prompt, ltype, indent, line_no)
+ p = prompt.dup
+ p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
+ case $2
+ when "N"
+ @context.irb_name
+ when "m"
+ @context.main.to_s
+ when "M"
+ @context.main.inspect
+ when "l"
+ ltype
+ when "i"
+ if $1
+ format("%" + $1 + "d", indent)
+ else
+ indent.to_s
+ end
+ when "n"
+ if $1
+ format("%" + $1 + "d", line_no)
+ else
+ line_no.to_s
+ end
+ when "%"
+ "%"
+ end
+ end
+ p
+ end
+
+ def inspect
+ ary = []
+ for iv in instance_variables
+ case iv
+ when "@signal_status"
+ ary.push format("%s=:%s", iv, @signal_status.id2name)
+ when "@context"
+ ary.push format("%s=%s", iv, eval(iv).__to_s__)
+ else
+ ary.push format("%s=%s", iv, eval(iv))
+ end
+ end
+ format("#<%s: %s>", type, ary.join(", "))
+ end
+ end
+
+ # Singleton method
+ def @CONF.inspect
+ IRB.version unless self[:VERSION]
+
+ array = []
+ for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
+ case k
+ when :MAIN_CONTEXT
+ next
+ when :PROMPT
+ s = v.collect{
+ |kk, vv|
+ ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
+ format(":%s=>{%s}", kk.id2name, ss.join(", "))
+ }
+ array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
+ else
+ array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
+ end
+ end
+ array.join("\n")
+ end
+end
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index cbd4012773..01dcbd2219 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -1,8 +1,19 @@
+#
+# irb/completor.rb -
+# $Release Version: 0.7.1$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+# From Original Idea of shugo@ruby-lang.org
+#
require "readline"
module IRB
- module InputCompletion
+ module InputCompletor
+
+ @RCS_ID='-$Id$-'
+
ReservedWords = [
"BEGIN", "END",
"alias", "and",
@@ -20,28 +31,147 @@ module IRB
"then", "true",
"undef", "unless", "until",
"when", "while",
- "yield"
+ "yield",
]
CompletionProc = proc { |input|
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
case input
- when /^([^.]+)\.([^.]*)$/
+ when /^(\/[^\/]*\/)\.([^.]*)$/
+ # Regexp
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Regexp.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^([^\]]*\])\.([^.]*)$/
+ # Array
receiver = $1
- message = $2
- if eval("(local_variables|#{receiver}.type.constants).include?('#{receiver}')",
- IRB.conf[:MAIN_CONTEXT].bind)
- candidates = eval("#{receiver}.methods", IRB.conf[:MAIN_CONTEXT].bind)
+ message = Regexp.quote($2)
+
+ candidates = Array.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^([^\}]*\})\.([^.]*)$/
+ # Proc or Hash
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^(:[^:]*)$/
+ # Symbol
+ if Symbol.respond_to?(:all_symbols)
+ sym = $1
+ candidates = Symbol.all_symbols.collect{|s| s.id2name}
+ candidates.grep(/^#{sym}/)
else
+ []
+ end
+
+ when /^::([A-Z][^:\.\(]*)$/
+ # Absolute Constant or class methods
+ receiver = $1
+ candidates = Object.constants
+ candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
+
+ when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
+ # Constant or class methods
+ receiver = $1
+ message = Regexp.quote($4)
+ begin
+ candidates = eval("#{receiver}.constants | #{receiver}.methods", bind)
+ rescue Exception
candidates = []
end
- candidates.grep(/^#{Regexp.quote(message)}/).collect{|e| receiver + "." + e}
+ candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
+
+ when /^(:[^.]+)\.([^.]*)$/
+ # Symbol
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Symbol.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/
+ # Numeric
+ receiver = $1
+ message = Regexp.quote($4)
+
+ begin
+ candidates = eval(receiver, bind).methods
+ rescue Exception
+ candidates
+ end
+ select_message(receiver, message, candidates)
+
+# when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
+ when /^((\.?[^.]+)+)\.([^.]*)$/
+ # variable
+ receiver = $1
+ message = Regexp.quote($3)
+
+ gv = eval "global_variables", bind
+ lv = eval "local_variables", bind
+ cv = eval "type.constants", bind
+
+ if (gv | lv | cv).include?(receiver)
+ # foo.func and foo is local var.
+ candidates = eval("#{receiver}.methods", bind)
+ elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
+ # Foo::Bar.func
+ begin
+ candidates = eval("#{receiver}.methods", bind)
+ rescue Exception
+ candidates = []
+ end
+ else
+ # func1.func2
+ candidates = []
+ ObjectSpace.each_object(Module){|m|
+ next if /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
+ candidates.concat m.instance_methods
+ }
+ candidates.sort!
+ candidates.uniq!
+ end
+ select_message(receiver, message, candidates)
+
+ when /^\.([^.]*)$/
+ # unknown(maybe String)
+
+ receiver = ""
+ message = Regexp.quote($1)
+
+ candidates = String.instance_methods(true)
+ select_message(receiver, message, candidates)
+
else
- candidates = eval("methods | private_methods | local_variables | type.constants",
- IRB.conf[:MAIN_CONTEXT].bind)
+ candidates = eval("methods | private_methods | local_variables | type.constants", bind)
+
(candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
end
}
+
+ Operators = ["%", "&", "*", "**", "+", "-", "/",
+ "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
+ "[]", "[]=", "^",]
+
+ def self.select_message(receiver, message, candidates)
+ candidates.grep(/^#{message}/).collect do |e|
+ case e
+ when /^[a-zA-Z_]/
+ receiver + "." + e
+ when /^[0-9]/
+ when *Operators
+ #receiver + " " + e
+ end
+ end
+ end
end
end
-Readline.completion_proc = IRB::InputCompletion::CompletionProc
+Readline.completion_proc = IRB::InputCompletor::CompletionProc
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
new file mode 100644
index 0000000000..ffc77de875
--- /dev/null
+++ b/lib/irb/context.rb
@@ -0,0 +1,289 @@
+#
+# irb/context.rb - irb context
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+module IRB
+ class Context
+ #
+ # Arguments:
+ # input_method: nil -- stdin or readline
+ # String -- File
+ # other -- using this as InputMethod
+ #
+ def initialize(irb, workspace = nil, input_method = nil)
+ @irb = irb
+ if workspace
+ @workspace = workspace
+ else
+ @workspace = WorkSpace.new unless workspace
+ end
+ @thread = Thread.current if defined? Thread
+ @irb_level = 0
+
+ # copy of default configuration
+ @ap_name = IRB.conf[:AP_NAME]
+ @rc = IRB.conf[:RC]
+ @load_modules = IRB.conf[:LOAD_MODULES]
+
+ self.math_mode = IRB.conf[:MATH_MODE]
+ @use_readline = IRB.conf[:USE_READLINE]
+ @inspect_mode = IRB.conf[:INSPECT_MODE]
+ self.use_tracer = IRB.conf[:USE_TRACER]
+# @use_loader = IRB.conf[:USE_LOADER]
+
+ self.prompt_mode = IRB.conf[:PROMPT_MODE]
+
+ @ignore_sigint = IRB.conf[:IGNORE_SIGINT]
+ @ignore_eof = IRB.conf[:IGNORE_EOF]
+
+ @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT]
+
+ debug_level = IRB.conf[:DEBUG_LEVEL]
+ @verbose = IRB.conf[:VERBOSE]
+
+ @tracer_initialized = false
+
+ if IRB.conf[:SINGLE_IRB] or !defined?(JobManager)
+ @irb_name = IRB.conf[:IRB_NAME]
+ else
+ @irb_name = "irb#"+IRB.JobManager.n_jobs.to_s
+ end
+ @irb_path = "(" + @irb_name + ")"
+
+ case input_method
+ when nil
+ if (use_readline.nil? && IRB.conf[:PROMPT_MODE] != :INF_RUBY ||
+ use_readline?)
+ @io = ReadlineInputMethod.new
+ else
+ @io = StdioInputMethod.new
+ end
+ when String
+ @io = FileInputMethod.new(input_method)
+ @irb_name = File.basename(input_method)
+ @irb_path = input_method
+ else
+ @io = input_method
+ end
+ end
+
+ def main
+ @workspace.main
+ end
+
+ attr_accessor :workspace
+ attr_reader :thread
+ attr_accessor :io
+
+ attr_reader :_
+
+ attr_accessor :irb
+ attr_accessor :ap_name
+ attr_accessor :rc
+ attr_accessor :load_modules
+ attr_accessor :irb_name
+ attr_accessor :irb_path
+
+ attr_accessor :math_mode
+ attr_accessor :use_readline
+ attr_reader :inspect_mode
+ attr_reader :use_tracer
+# attr :use_loader
+
+ attr_reader :debug_level
+ attr_accessor :verbose
+
+ attr_reader :prompt_mode
+ attr_accessor :prompt_i
+ attr_accessor :prompt_s
+ attr_accessor :prompt_c
+ attr_accessor :auto_indent_mode
+ attr_accessor :return_format
+
+ attr_accessor :ignore_sigint
+ attr_accessor :ignore_eof
+
+ attr_accessor :back_trace_limit
+
+# alias use_loader? use_loader
+ alias use_tracer? use_tracer
+ alias use_readline? use_readline
+ alias rc? rc
+ alias math? math_mode
+ alias verbose? verbose
+ alias ignore_sigint? ignore_sigint
+ alias ignore_eof? ignore_eof
+
+ def _=(value)
+ @_ = value
+ @workspace.evaluate "_ = IRB.conf[:MAIN_CONTEXT]._"
+ end
+
+ def irb_name
+ if @irb_level == 0
+ @irb_name
+ elsif @irb_name =~ /#[0-9]*$/
+ @irb_name + "." + @irb_level.to_s
+ else
+ @irb_name + "#0." + @irb_level.to_s
+ end
+ end
+
+ def prompt_mode=(mode)
+ @prompt_mode = mode
+ pconf = IRB.conf[:PROMPT][mode]
+ @prompt_i = pconf[:PROMPT_I]
+ @prompt_s = pconf[:PROMPT_S]
+ @prompt_c = pconf[:PROMPT_C]
+ @return_format = pconf[:RETURN]
+ if ai = pconf.include?(:AUTO_INDENT)
+ @auto_indent_mode = ai
+ else
+ @auto_indent_mode = IRB.conf[:AUTO_INDENT]
+ end
+ end
+
+ def inspect?
+ @inspect_mode.nil? && !@math_mode or @inspect_mode
+ end
+
+ def file_input?
+ @io.type == FileInputMethod
+ end
+
+ def use_tracer=(opt)
+ if opt
+ IRB.initialize_tracer
+ unless @tracer_initialized
+ Tracer.set_get_line_procs(@irb_path) {
+ |line_no|
+ @io.line(line_no)
+ }
+ @tracer_initialized = true
+ end
+ elsif !opt && @use_tracer
+ Tracer.off
+ end
+ @use_tracer=opt
+ end
+
+ def use_loader
+ IRB.conf[:USE_LOADER]
+ end
+
+ def use_loader=(opt)
+ IRB.conf[:USE_LOADER] = opt
+ if opt
+ IRB.initialize_loader
+ end
+ print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose?
+ opt
+ end
+
+ def inspect_mode=(opt)
+ if opt
+ @inspect_mode = opt
+ else
+ @inspect_mode = !@inspect_mode
+ end
+ print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose?
+ @inspect_mode
+ end
+
+ def math_mode=(opt)
+ if @math_mode == true && opt == false
+ IRB.fail CantRetuenNormalMode
+ return
+ end
+
+ @math_mode = opt
+ if math_mode
+ IRB.initialize_mathn
+ main.instance_eval("include Math")
+ print "start math mode\n" if verbose?
+ end
+ end
+
+ def use_readline=(opt)
+ @use_readline = opt
+ print "use readline module\n" if @use_readline
+ end
+
+ def debug_level=(value)
+ @debug_level = value
+ RubyLex.debug_level = value
+ SLex.debug_level = value
+ end
+
+ def debug?
+ @debug_level > 0
+ end
+
+ def change_binding(*_main)
+ back = @workspace
+ @workspace = WorkSpace.new(*_main)
+ unless _main.empty?
+ begin
+ main.extend ExtendCommand
+ rescue
+ print "can't change binding to: ", main.inspect, "\n"
+ @workspace = back
+ return nil
+ end
+ end
+ @irb_level += 1
+ begin
+ catch(:SU_EXIT) do
+ @irb.eval_input
+ end
+ ensure
+ @irb_level -= 1
+ @workspace = back
+ end
+ end
+ alias change_workspace change_binding
+
+
+ alias __exit__ exit
+ def exit(ret = 0)
+ if @irb_level == 0
+ IRB.irb_exit(@irb, ret)
+ else
+ throw :SU_EXIT, ret
+ end
+ end
+
+ NOPRINTING_IVARS = ["@_"]
+ NO_INSPECTING_IVARS = ["@irb", "@io"]
+ IDNAME_IVARS = ["@prompt_mode"]
+
+ alias __inspect__ inspect
+ def inspect
+ array = []
+ for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
+ name = ivar.sub(/^@(.*)$/){$1}
+ val = instance_eval(ivar)
+ case ivar
+ when *NOPRINTING_IVARS
+ next
+ when *NO_INSPECTING_IVARS
+ array.push format("conf.%s=%s", name, val.to_s)
+ when *IDNAME_IVARS
+ array.push format("conf.%s=:%s", name, val.id2name)
+ else
+ array.push format("conf.%s=%s", name, val.inspect)
+ end
+ end
+ array.join("\n")
+ end
+ alias __to_s__ to_s
+ alias to_s inspect
+ end
+end
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
new file mode 100644
index 0000000000..3f92707d01
--- /dev/null
+++ b/lib/irb/extend-command.rb
@@ -0,0 +1,126 @@
+#
+# irb/extend-command.rb - irb command extend
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+module IRB
+ #
+ # IRB extended command
+ #
+ module ExtendCommand
+# include Loader
+
+ def irb_exit(ret = 0)
+ irb_context.exit(ret)
+ end
+ alias irb_quit irb_exit
+
+ def irb_fork(&block)
+ pid = send ExtendCommand.irb_original_method_name("fork")
+ unless pid
+ class<<self
+ alias_method :exit, ExtendCommand.irb_original_method_name('exit')
+ end
+ if iterator?
+ begin
+ yield
+ ensure
+ exit
+ end
+ end
+ end
+ pid
+ end
+
+ def irb_change_binding(*main)
+ irb_context.change_binding(*main)
+ end
+ alias irb_change_workspace irb_change_binding
+
+ def irb_source(file)
+ irb_context.source(file)
+ end
+
+ def irb(*obj)
+ require "irb/multi-irb"
+ IRB.irb(nil, *obj)
+ end
+
+ def irb_context
+ IRB.conf[:MAIN_CONTEXT]
+ end
+
+ def irb_jobs
+ require "irb/multi-irb"
+ IRB.JobManager
+ end
+
+ def irb_fg(key)
+ require "irb/multi-irb"
+ IRB.JobManager.switch(key)
+ end
+
+ def irb_kill(*keys)
+ require "irb/multi-irb"
+ IRB.JobManager.kill(*keys)
+ end
+
+ # extend command functions
+ def ExtendCommand.extend_object(obj)
+ super
+ unless (class<<obj;ancestors;end).include?(ExtendCommand)
+ obj.install_aliases
+ end
+ end
+
+ OVERRIDE_NOTHING = 0
+ OVERRIDE_PRIVATE_ONLY = 0x01
+ OVERRIDE_ALL = 0x02
+
+ def install_aliases(override = OVERRIDE_NOTHING)
+
+ install_alias_method(:exit, :irb_exit, override | OVERRIDE_PRIVATE_ONLY)
+ install_alias_method(:quit, :irb_quit, override | OVERRIDE_PRIVATE_ONLY)
+ install_alias_method(:fork, :irb_fork, override | OVERRIDE_PRIVATE_ONLY)
+ install_alias_method(:kill, :irb_kill, override | OVERRIDE_PRIVATE_ONLY)
+
+ install_alias_method(:irb_cb, :irb_change_binding, override)
+ install_alias_method(:irb_ws, :irb_change_workspace, override)
+ install_alias_method(:source, :irb_source, override)
+ install_alias_method(:conf, :irb_context, override)
+ install_alias_method(:jobs, :irb_jobs, override)
+ install_alias_method(:fg, :irb_fg, override)
+ end
+
+ # override = {OVERRIDE_NOTHING, OVERRIDE_PRIVATE_ONLY, OVERRIDE_ALL}
+ def install_alias_method(to, from, override = OVERRIDE_NOTHING)
+ to = to.id2name unless to.kind_of?(String)
+ from = from.id2name unless from.kind_of?(String)
+
+ if override == OVERRIDE_ALL or
+ (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or
+ (override == OVERRIDE_NOTHING) && !respond_to?(to, true)
+ target = self
+ (class<<self;self;end).instance_eval{
+ if target.respond_to?(to, true) &&
+ !target.respond_to?(ExtendCommand.irb_original_method_name(to), true)
+ alias_method(ExtendCommand.irb_original_method_name(to), to)
+ end
+ alias_method to, from
+ }
+ else
+ print "irb: warn: can't alias #{to} from #{from}.\n"
+ end
+ end
+
+ def self.irb_original_method_name(method_name)
+ "irb_" + method_name + "_org"
+ end
+ end
+end
diff --git a/lib/irb/frame.rb b/lib/irb/frame.rb
index dcfa5c1d13..1090cd9684 100644
--- a/lib/irb/frame.rb
+++ b/lib/irb/frame.rb
@@ -1,6 +1,6 @@
#
# frame.rb -
-# $Release Version: 0.6$
+# $Release Version: 0.7.1$
# $Revision$
# $Date$
# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
diff --git a/lib/irb/help.rb b/lib/irb/help.rb
new file mode 100644
index 0000000000..0821f68d13
--- /dev/null
+++ b/lib/irb/help.rb
@@ -0,0 +1,33 @@
+#
+# irb/help.rb - print usase module
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+module IRB
+ def IRB.print_usage
+ lc = IRB.conf[:LC_MESSAGES]
+ path = lc.find("irb/help-message")
+ space_line = false
+ File.foreach(path) do
+ |l|
+ if /^\s*$/ =~ l
+ lc.puts l unless space_line
+ space_line = true
+ next
+ end
+ space_line = false
+
+ l.sub!(/#.*$/, "")
+ next if /^\s*$/ =~ l
+ lc.puts l
+ end
+ end
+end
+
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
new file mode 100644
index 0000000000..f34a51b345
--- /dev/null
+++ b/lib/irb/init.rb
@@ -0,0 +1,232 @@
+#
+# irb/init.rb - irb initialize module
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+module IRB
+
+ # initialize config
+ def IRB.initialize(ap_path)
+ IRB.init_config(ap_path)
+ IRB.init_error
+ IRB.run_config
+ end
+
+ # @CONF default setting
+ def IRB.init_config(ap_path)
+ # class instance variables
+ @TRACER_INITIALIZED = false
+ @MATHN_INITIALIZED = false
+
+ # default configurations
+ unless ap_path and @CONF[:AP_NAME]
+ ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
+ end
+ @CONF[:AP_NAME] = File::basename(ap_path, ".rb")
+
+ @CONF[:IRB_NAME] = "irb"
+ @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__)
+
+ @CONF[:RC] = true
+ @CONF[:LOAD_MODULES] = []
+ @CONF[:IRB_RC] = nil
+
+ @CONF[:MATH_MODE] = false
+ @CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod)
+ @CONF[:INSPECT_MODE] = nil
+ @CONF[:USE_TRACER] = false
+ @CONF[:USE_LOADER] = false
+ @CONF[:IGNORE_SIGINT] = true
+ @CONF[:IGNORE_EOF] = false
+
+ @CONF[:BACK_TRACE_LIMIT] = 16
+
+ @CONF[:PROMPT] = {
+ :NULL => {
+ :PROMPT_I => nil,
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => "%s\n"
+ },
+ :DEFAULT => {
+ :PROMPT_I => "%N(%m):%03n:%i> ",
+ :PROMPT_S => "%N(%m):%03n:%i%l ",
+ :PROMPT_C => "%N(%m):%03n:%i* ",
+ :RETURN => "%s\n"
+ },
+ :SIMPLE => {
+ :PROMPT_I => ">> ",
+ :PROMPT_S => nil,
+ :PROMPT_C => "?> ",
+ :RETURN => "=> %s\n"
+ },
+ :INF_RUBY => {
+ :PROMPT_I => "%N(%m):%03n:%i> ",
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => "%s\n",
+ :AUTO_INDENT => true
+ },
+ :XMP => {
+ :PROMPT_I => nil,
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => " ==>%s\n"
+ }
+ }
+
+ @CONF[:PROMPT_MODE] = :DEFAULT
+ @CONF[:AUTO_INDENT] = false
+
+ @CONF[:CONTEXT_MODE] = 3 # use binding in function on TOPLEVEL_BINDING
+ @CONF[:SINGLE_IRB] = false
+
+# @CONF[:LC_MESSAGES] = "en"
+ @CONF[:LC_MESSAGES] = Locale.new
+
+ @CONF[:DEBUG_LEVEL] = 1
+ @CONF[:VERBOSE] = true
+ end
+
+ def IRB.init_error
+ @CONF[:LC_MESSAGES].load("irb/error.rb")
+ end
+
+ # option analyzing
+ def IRB.parse_opts
+ while opt = ARGV.shift
+ case opt
+ when "-f"
+ opt = ARGV.shift
+ @CONF[:RC] = false
+ when "-m"
+ @CONF[:MATH_MODE] = true
+ when "-d"
+ $DEBUG = true
+ when "-r"
+ opt = ARGV.shift
+ @CONF[:LOAD_MODULES].push opt if opt
+ when "--inspect"
+ @CONF[:INSPECT_MODE] = true
+ when "--noinspect"
+ @CONF[:INSPECT_MODE] = false
+ when "--readline"
+ @CONF[:USE_READLINE] = true
+ when "--noreadline"
+ @CONF[:USE_READLINE] = false
+
+ when "--prompt-mode", "--prompt"
+ prompt_mode = ARGV.shift.upcase.tr("-", "_").intern
+ IRB.fail(UndefinedPromptMode,
+ prompt_mode.id2name) unless @CONF[:PROMPT][prompt_mode]
+ @CONF[:PROMPT_MODE] = prompt_mode
+ when "--noprompt"
+ @CONF[:PROMPT_MODE] = :NULL
+ when "--inf-ruby-mode"
+ @CONF[:PROMPT_MODE] = :INF_RUBY
+ when "--sample-book-mode", "--simple-prompt"
+ @CONF[:PROMPT_MODE] = :SIMPLE
+
+ when "--tracer"
+ @CONF[:USE_TRACER] = true
+ when "--back-trace-limit"
+ @CONF[:BACK_TRACE_LIMIT] = ARGV.shift.to_i
+ when "--context-mode"
+ @CONF[:CONTEXT_MODE] = ARGV.shift.to_i
+ when "--single-irb"
+ @CONF[:SINGLE_IRB] = true
+ when "--irb_debug"
+ @CONF[:DEBUG_LEVEL] = ARGV.shift.to_i
+ when "-v", "--version"
+ print IRB.version, "\n"
+ exit 0
+ when "-h", "--help"
+ require "irb/help"
+ IRB.print_usage
+ exit 0
+ when /^-/
+ IRB.fail UnrecognizedSwitch, opt
+ else
+ @CONF[:USE_READLINE] = false
+ @CONF[:SCRIPT] = opt
+ $0 = opt
+ break
+ end
+ end
+ end
+
+ # running config
+ def IRB.run_config
+ if @CONF[:RC]
+ rcs = []
+ rcs.push File.expand_path("~/.irbrc") if ENV.key?("HOME")
+ rcs.push ".irbrc"
+ rcs.push "irb.rc"
+ rcs.push "_irbrc"
+ rcs.push "$irbrc"
+ catch(:EXIT) do
+ for rc in rcs
+ begin
+ load rc
+ throw :EXIT
+ rescue LoadError, Errno::ENOENT
+ rescue
+ print "load error: #{rc}\n"
+ print $!.type, ": ", $!, "\n"
+ for err in $@[0, $@.size - 2]
+ print "\t", err, "\n"
+ end
+ throw :EXIT
+ end
+ end
+ end
+ end
+ end
+
+ # loading modules
+ def IRB.load_modules
+ for m in @CONF[:LOAD_MODULES]
+ begin
+ require m
+ rescue
+ print $@[0], ":", $!.type, ": ", $!, "\n"
+ end
+ end
+ end
+
+ # initialize tracing function
+ def IRB.initialize_tracer
+ unless @TRACER_INITIALIZED
+ require("tracer")
+ Tracer.verbose = false
+ Tracer.add_filter {
+ |event, file, line, id, binding|
+ File::dirname(file) != @CONF[:IRB_LIB_PATH]
+ }
+ @TRACER_INITIALIZED = true
+ end
+ end
+
+ # initialize mathn function
+ def IRB.initialize_mathn
+ unless @MATHN_INITIALIZED
+ require "mathn"
+ @MATHN_INITIALIZED = true
+ end
+ end
+
+ # initialize loader function
+ def IRB.initialize_loader
+ unless @LOADER_INITIALIZED
+ require "irb/loader"
+ @LOADER_INITIALIZED = true
+ end
+ end
+end
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index 19df0eb073..ffbc6d9edc 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -1,9 +1,9 @@
#
-# input-method.rb - input methods using irb
-# $Release Version: 0.6$
+# irb/input-method.rb - input methods using irb
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
@@ -23,9 +23,9 @@ module IRB
def initialize(file = STDIN_FILE_NAME)
@file_name = file
end
- attr :file_name
+ attr_reader :file_name
- attr :prompt, true
+ attr_accessor :prompt
def gets
IRB.fail NotImplementError, "gets"
@@ -67,7 +67,7 @@ module IRB
super
@io = open(file)
end
- attr :file_name
+ attr_reader :file_name
def eof?
@io.eof?
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
new file mode 100644
index 0000000000..de38f29978
--- /dev/null
+++ b/lib/irb/lc/error.rb
@@ -0,0 +1,30 @@
+#
+# irb/lc/error.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+module IRB
+
+ # exceptions
+ extend Exception2MessageMapper
+ def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
+ def_exception :NotImplementError, "Need to define `%s'"
+ def_exception :CantRetuenNormalMode, "Can't return normal mode."
+ def_exception :IllegalParameter, "Illegal parameter(%s)."
+ def_exception :IrbAlreadyDead, "Irb is already dead."
+ def_exception :IrbSwitchToCurrentThread, "Change to current thread."
+ def_exception :NoSuchJob, "No such job(%s)."
+ def_exception :CanNotGoMultiIrbMode, "Can't go multi irb mode."
+ def_exception :CanNotChangeBinding, "Can't change binding to (%s)."
+ def_exception :UndefinedPromptMode, "Undefined prompt mode(%s)."
+
+end
+
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
new file mode 100644
index 0000000000..8a3d63803b
--- /dev/null
+++ b/lib/irb/lc/help-message
@@ -0,0 +1,34 @@
+#
+# irb/lc/help-message.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+Usage: irb.rb [options] [programfile] [arguments]
+ -f suppress read ~/.irbrc
+ -m bc mode (load mathn, fraction or matrix are available)
+ -d set $DEBUG to true (same as `ruby -d')
+ -r load-module same as `ruby -r'
+ --inspect uses `inspect' for output (the default except bc mode)
+ --noinspect doesn't uses inspect for output
+ --readline uses Readline extension module
+ --noreadline doesn't use Readline extension module
+ --prompt prompt-mode
+ --prompt-mode prompt-mode
+ switches prompt mode. Pre-defined prompt modes are
+ `defalut', `simple', `xmp' and `inf-ruby'
+ --inf-ruby-mode uses prompt appreciate for inf-ruby-mode on emacs.
+ Suppresses --readline.
+ --simple-prompt simple prompt mode
+ --noprompt no prompt
+ --tracer display trace for each execution of commands.
+ --back-trace-limit n
+ displayes backtrace top n and tail n. The default
+ value is 16.
+ --irb_debug n sets internal debug level to n (It shouldn't be used)
+ -v, --version prints the version of irb
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
new file mode 100644
index 0000000000..d5aef0a7c8
--- /dev/null
+++ b/lib/irb/lc/ja/error.rb
@@ -0,0 +1,29 @@
+#
+# irb/lc/ja/error.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+module IRB
+ # exceptions
+ extend Exception2MessageMapper
+ def_exception :UnrecognizedSwitch, '$B%9%$%C%A(B(%s)$B$,J,$j$^$;$s(B'
+ def_exception :NotImplementError, '`%s\'$B$NDj5A$,I,MW$G$9(B'
+ def_exception :CantRetuenNormalMode, 'Normal$B%b!<%I$KLa$l$^$;$s(B.'
+ def_exception :IllegalParameter, '$B%Q%i%a!<%?(B(%s)$B$,4V0c$C$F$$$^$9(B.'
+ def_exception :IrbAlreadyDead, 'Irb$B$O4{$K;`$s$G$$$^$9(B.'
+ def_exception :IrbSwitchToCurrentThread, 'Change to current thread.'
+ def_exception :NoSuchJob, '$B$=$N$h$&$J%8%g%V(B(%s)$B$O$"$j$^$;$s(B.'
+ def_exception :CanNotGoMultiIrbMode, 'multi-irb mode$B$K0\$l$^$;$s(B.'
+ def_exception :CanNotChangeBinding, '$B%P%$%s%G%#%s%0(B(%s)$B$KJQ99$G$-$^$;$s(B.'
+ def_exception :UndefinedPromptMode, '$B%W%m%s%W%H%b!<%I(B(%s)$B$ODj5A$5$l$F$$$^$;$s(B.'
+end
+
+
diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message
new file mode 100644
index 0000000000..59f5c7a72b
--- /dev/null
+++ b/lib/irb/lc/ja/help-message
@@ -0,0 +1,35 @@
+#
+# irb/lc/ja/help-message.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+Usage: irb.rb [options] [programfile] [arguments]
+ -f ~/.irbrc $B$rFI$_9~$^$J$$(B.
+ -m bc$B%b!<%I(B($BJ,?t(B, $B9TNs$N7W;;$,$G$-$k(B)
+ -d $DEBUG $B$r(Btrue$B$K$9$k(B(ruby -d $B$HF1$8(B)
+ -r load-module ruby -r $B$HF1$8(B.
+ --inspect $B7k2L=PNO$K(Binspect$B$rMQ$$$k(B(bc$B%b!<%I0J30$O%G%U%)%k%H(B).
+ --noinspect $B7k2L=PNO$K(Binspect$B$rMQ$$$J$$(B.
+ --readline readline$B%i%$%V%i%j$rMxMQ$9$k(B.
+ --noreadline readline$B%i%$%V%i%j$rMxMQ$7$J$$(B.
+ --prompt prompt-mode/--prompt-mode prompt-mode
+ $B%W%m%s%W%H%b!<%I$r@ZBX$($^$9(B. $B8=:_Dj5A$5$l$F$$$k%W(B
+ $B%m%s%W%H%b!<%I$O(B, defalut, simple, xmp, inf-ruby$B$,(B
+ $BMQ0U$5$l$F$$$^$9(B.
+ --inf-ruby-mode emacs$B$N(Binf-ruby-mode$BMQ$N%W%m%s%W%HI=<($r9T$J$&(B. $BFC(B
+ $B$K;XDj$,$J$$8B$j(B, readline$B%i%$%V%i%j$O;H$o$J$/$J$k(B.
+ --simple-prompt $BHs>o$K%7%s%W%k$J%W%m%s%W%H$rMQ$$$k%b!<%I$G$9(B.
+ --noprompt $B%W%m%s%W%HI=<($r9T$J$o$J$$(B.
+ --tracer $B%3%^%s%I<B9T;~$K%H%l!<%9$r9T$J$&(B.
+ --back-trace-limit n
+ $B%P%C%/%H%l!<%9I=<($r%P%C%/%H%l!<%9$NF,$+$i(B n, $B8e$m(B
+ $B$+$i(Bn$B$@$19T$J$&(B. $B%G%U%)%k%H$O(B16
+ --irb_debug n irb$B$N%G%P%C%0%G%P%C%0%l%Y%k$r(Bn$B$K@_Dj$9$k(B($BMxMQ$7$J(B
+ $B$$J}$,L5Fq$G$7$g$&(B).
+ -v, --version irb$B$N%P!<%8%g%s$rI=<($9$k(B
diff --git a/lib/irb/loader.rb b/lib/irb/loader.rb
index 83b10a55a0..6e7a89e454 100644
--- a/lib/irb/loader.rb
+++ b/lib/irb/loader.rb
@@ -1,9 +1,9 @@
#
-# irb-loader.rb -
-# $Release Version: 0.6$
+# irb/loader.rb - irb loader
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb
new file mode 100644
index 0000000000..ef92ea1377
--- /dev/null
+++ b/lib/irb/locale.rb
@@ -0,0 +1,187 @@
+#
+# irb/locale.rb - internationalization module
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+require "kconv"
+
+module IRB
+ class Locale
+ @RCS_ID='-$Id$-'
+
+ JPDefaultLocale = "ja"
+ LOCALE_DIR = "/lc/"
+
+ LC2KCONV = {
+# "ja" => Kconv::JIS,
+# "ja_JP" => Kconv::JIS,
+ "ja_JP.ujis" => Kconv::EUC,
+ "ja_JP.euc" => Kconv::EUC,
+ "ja_JP.eucJP" => Kconv::EUC,
+ "ja_JP.sjis" => Kconv::SJIS,
+ "ja_JP.SJIS" => Kconv::SJIS,
+ }
+
+ def initialize(locale = nil)
+ @lang = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"]
+ @lang = "C" unless @lang
+ end
+
+ attr_reader :lang
+
+ def String(mes)
+ mes = super(mes)
+ case @lang
+ when /^ja/
+ mes = Kconv::kconv(mes, LC2KCONV[@lang])
+ else
+ mes
+ end
+ mes
+ end
+
+ def format(*opts)
+ String(super(*opts))
+ end
+
+ def gets(*rs)
+ String(super(*rs))
+ end
+
+ def readline(*rs)
+ String(super(*rs))
+ end
+
+ def print(*opts)
+ ary = opts.collect{|opt| String(opt)}
+ super *ary
+ end
+
+ def printf(*opts)
+ s = format(*opts)
+ print s
+ end
+
+ def puts(*opts)
+ ary = opts.collect{|opt| String(opts)}
+ super *ary
+ end
+
+ autoload :Tempfile, "tempfile"
+
+ def require(file, priv = nil)
+ rex = Regexp.new("lc/#{Regexp.quote(file)}\.(so|o|sl|rb)?")
+ return false if $".find{|f| f =~ rex}
+
+ case file
+ when /\.rb$/
+ begin
+ load(file, priv)
+ $".push file
+ return true
+ rescue LoadError
+ end
+ when /\.(so|o|sl)$/
+ return super
+ end
+
+ begin
+ load(f = file + ".rb")
+ $".push f #"
+ return true
+ rescue LoadError
+ return ruby_require(file)
+ end
+ end
+
+ alias toplevel_load load
+
+ def load(file, priv=nil)
+ dir = File.dirname(file)
+ dir = "" if dir == "."
+ base = File.basename(file)
+
+ if /^ja(_JP)?$/ =~ @lang
+ back, @lang = @lang, "C"
+ end
+ begin
+ if dir[0] == ?/ #/
+ lc_path = search_file(dir, base)
+ return real_load(lc_path, priv) if lc_path
+ end
+
+ for path in $:
+ lc_path = search_file(path + "/" + dir, base)
+ return real_load(lc_path, priv) if lc_path
+ end
+ ensure
+ @lang = back if back
+ end
+ raise LoadError, "No such file to load -- #{file}"
+ end
+
+ def real_load(path, priv)
+ tmp_base = path.tr("./:", "___")
+ lc_file = Tempfile.new(tmp_base)
+ File.foreach(path) do |line|
+ line = self.String(line)
+ lc_file.print(line)
+ end
+ lc_file.close
+ toplevel_load lc_file.path, priv
+ end
+ private :real_load
+
+ def find(file , paths = $:)
+ dir = File.dirname(file)
+ dir = "" if dir == "."
+ base = File.basename(file)
+ if dir[0] == ?/ #/
+ return lc_path = search_file(dir, base)
+ else
+ for path in $:
+ if lc_path = search_file(path + "/" + dir, base)
+ return lc_path
+ end
+ end
+ end
+ nil
+ end
+
+ def search_file(path, file)
+ if File.exists?(p1 = path + lc_path(file, "C"))
+ if File.exists?(p2 = path + lc_path(file))
+ return p2
+ else
+ end
+ return p1
+ else
+ end
+ nil
+ end
+ private :search_file
+
+ def lc_path(file = "", lc = @lang)
+ case lc
+ when "C"
+ LOCALE_DIR + file
+ when /^ja/
+ LOCALE_DIR + "ja/" + file
+ else
+ LOCALE_DIR + @lang + "/" + file
+ end
+ end
+ private :lc_path
+ end
+end
+
+
+
+
diff --git a/lib/irb/main.rb b/lib/irb/main.rb
deleted file mode 100644
index 4c7dac240b..0000000000
--- a/lib/irb/main.rb
+++ /dev/null
@@ -1,867 +0,0 @@
-#
-# main.rb - irb main module
-# $Release Version: 0.6 $
-# $Revision$
-# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
-#
-# --
-#
-#
-#
-require "e2mmap"
-require "irb/ruby-lex"
-require "irb/input-method"
-require "irb/workspace-binding"
-
-STDOUT.sync = true
-
-module IRB
- @RCS_ID='-$Id$-'
-
- # exceptions
- extend Exception2MessageMapper
- def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
- def_exception :NotImplementError, "Need to define `%s'"
- def_exception :CantRetuenNormalMode, "Can't return normal mode."
- def_exception :IllegalParameter, "Illegal parameter(%s)."
- def_exception :IrbAlreadyDead, "Irb is already dead."
- def_exception :IrbSwitchToCurrentThread, "Change to current thread."
- def_exception :NoSuchJob, "No such job(%s)."
- def_exception :CanNotGoMultiIrbMode, "Can't go multi irb mode."
- def_exception :CanNotChangeBinding, "Can't change binding to (%s)."
- def_exception :UndefinedPromptMode, "Undefined prompt mode(%s)."
-
- class Abort < Exception;end
-
- # initialize IRB and start TOP_LEVEL irb
- def IRB.start(ap_path = nil)
- $0 = File::basename(ap_path, ".rb") if ap_path
-
- IRB.initialize(ap_path)
- IRB.parse_opts
- IRB.load_modules
-
- bind = workspace_binding
- main = eval("self", bind)
-
- if @CONF[:SCRIPT]
- irb = Irb.new(main, bind, @CONF[:SCRIPT])
- else
- irb = Irb.new(main, bind)
- end
-
- @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
- @CONF[:MAIN_CONTEXT] = irb.context
-
- trap("SIGINT") do
- irb.signal_handle
- end
-
- catch(:IRB_EXIT) do
- irb.eval_input
- end
- print "\n"
- end
-
- # initialize config
- def IRB.initialize(ap_path)
- IRB.init_config(ap_path)
- IRB.run_config
- end
-
- #
- # @CONF functions
- #
- @CONF = {}
- # @CONF default setting
- def IRB.init_config(ap_path)
- # class instance variables
- @TRACER_INITIALIZED = false
- @MATHN_INITIALIZED = false
-
- # default configurations
- unless ap_path and @CONF[:AP_NAME]
- ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
- end
- @CONF[:AP_NAME] = File::basename(ap_path, ".rb")
-
- @CONF[:IRB_NAME] = "irb"
- @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__)
-
- @CONF[:RC] = true
- @CONF[:LOAD_MODULES] = []
- @CONF[:IRB_RC] = nil
-
- @CONF[:MATH_MODE] = false
- @CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod)
- @CONF[:INSPECT_MODE] = nil
- @CONF[:USE_TRACER] = false
- @CONF[:USE_LOADER] = false
- @CONF[:IGNORE_SIGINT] = true
- @CONF[:IGNORE_EOF] = false
-
- @CONF[:BACK_TRACE_LIMIT] = 16
-
- @CONF[:PROMPT] = {
- :NULL => {
- :PROMPT_I => nil,
- :PROMPT_S => nil,
- :PROMPT_C => nil,
- :RETURN => "%s\n"
- },
- :DEFAULT => {
- :PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_S => "%N(%m):%03n:%i%l ",
- :PROMPT_C => "%N(%m):%03n:%i* ",
- :RETURN => "%s\n"
- },
- :SIMPLE => {
- :PROMPT_I => ">> ",
- :PROMPT_S => nil,
- :PROMPT_C => "?> ",
- :RETURN => "=> %s\n"
- },
- :INF_RUBY => {
- :PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_S => nil,
- :PROMPT_C => nil,
- :RETURN => "%s\n",
- :AUTO_INDENT => true
- },
- :XMP => {
- :PROMPT_I => nil,
- :PROMPT_S => nil,
- :PROMPT_C => nil,
- :RETURN => " ==>%s\n"
- }
- }
-
- @CONF[:PROMPT_MODE] = :DEFAULT
- @CONF[:AUTO_INDENT] = false
-
- @CONF[:CONTEXT_MODE] = 3
- @CONF[:SINGLE_IRB] = false
-
- @CONF[:DEBUG_LEVEL] = 1
- @CONF[:VERBOSE] = true
- end
-
- # IRB version method
- def IRB.version
- if v = @CONF[:VERSION] then return v end
-
- require "irb/version"
- rv = @RELEASE_VERSION.sub(/\.0/, "")
- @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
- end
-
- def IRB.conf
- @CONF
- end
-
- # option analyzing
- def IRB.parse_opts
- while opt = ARGV.shift
- case opt
- when "-f"
- opt = ARGV.shift
- @CONF[:RC] = false
- when "-m"
- @CONF[:MATH_MODE] = true
- when "-d"
- $DEBUG = true
- when "-r"
- opt = ARGV.shift
- @CONF[:LOAD_MODULES].push opt if opt
- when "--inspect"
- @CONF[:INSPECT_MODE] = true
- when "--noinspect"
- @CONF[:INSPECT_MODE] = false
- when "--readline"
- @CONF[:USE_READLINE] = true
- when "--noreadline"
- @CONF[:USE_READLINE] = false
- when "--prompt-mode", "--prompt"
- prompt_mode = ARGV.shift.upcase.tr("-", "_").intern
- IRB.fail(UndefinedPromptMode,
- prompt_mode.id2name) unless @CONF[:PROMPT][prompt_mode]
- @CONF[:PROMPT_MODE] = prompt_mode
- when "--noprompt"
- @CONF[:PROMPT_MODE] = :NULL
- when "--inf-ruby-mode"
- @CONF[:PROMPT_MODE] = :INF_RUBY
- when "--sample-book-mode", "--simple-prompt"
- @CONF[:PROMPT_MODE] = :SIMPLE
- when "--tracer"
- @CONF[:USE_TRACER] = true
- when "--back-trace-limit"
- @CONF[:BACK_TRACE_LIMIT] = ARGV.shift.to_i
- when "--context-mode"
- @CONF[:CONTEXT_MODE] = ARGV.shift.to_i
- when "--single-irb"
- @CONF[:SINGLE_IRB] = true
- when "--irb_debug"
- @CONF[:DEBUG_LEVEL] = ARGV.shift.to_i
- when "-v", "--version"
- print IRB.version, "\n"
- exit(0)
- when /^-/
- IRB.fail UnrecognizedSwitch, opt
- else
- @CONF[:USE_READLINE] = false
- @CONF[:SCRIPT] = opt
- $0 = opt
- break
- end
- end
- end
-
- # running config
- def IRB.run_config
- if @CONF[:RC]
- rcs = []
- rcs.push File.expand_path("~/.irbrc") if ENV.key?("HOME")
- rcs.push ".irbrc"
- rcs.push "irb.rc"
- rcs.push "_irbrc"
- rcs.push "$irbrc"
- catch(:EXIT) do
- for rc in rcs
- begin
- load rc
- throw :EXIT
- rescue LoadError, Errno::ENOENT
- rescue
- print "load error: #{rc}\n"
- print $!.type, ": ", $!, "\n"
- for err in $@[0, $@.size - 2]
- print "\t", err, "\n"
- end
- throw :EXIT
- end
- end
- end
- end
- end
-
- # loading modules
- def IRB.load_modules
- for m in @CONF[:LOAD_MODULES]
- begin
- require m
- rescue
- print $@[0], ":", $!.type, ": ", $!, "\n"
- end
- end
- end
-
- # initialize tracing function
- def IRB.initialize_tracer
- unless @TRACER_INITIALIZED
- require("tracer")
- Tracer.verbose = false
- Tracer.add_filter {
- |event, file, line, id, binding|
- File::dirname(file) != @CONF[:IRB_LIB_PATH]
- }
- @TRACER_INITIALIZED = true
- end
- end
-
- # initialize mathn function
- def IRB.initialize_mathn
- unless @MATHN_INITIALIZED
- require "mathn"
- @MATHN_INITIALIZED = true
- end
- end
-
- # initialize loader function
- def IRB.initialize_loader
- unless @LOADER_INITIALIZED
- require "irb/loader"
- @LOADER_INITIALIZED = true
- end
- end
-
- def IRB.irb_exit(irb, ret)
- throw :IRB_EXIT, ret
- end
-
- def IRB.irb_abort(irb, exception = Abort)
- if defined? Thread
- irb.context.thread.raise exception, "abort then interrupt!!"
- else
- raise exception, "abort then interrupt!!"
- end
- end
-
- #
- # irb interpriter main routine
- #
- class Irb
- def initialize(main, bind, input_method = nil)
- @context = Context.new(self, main, bind, input_method)
- main.extend ExtendCommand
- @signal_status = :IN_IRB
-
- @scanner = RubyLex.new
- @scanner.exception_on_syntax_error = false
- end
- attr :context
- attr :scanner, true
-
- def eval_input
-# @scanner = RubyLex.new
- @scanner.set_input(@context.io) do
- signal_status(:IN_INPUT) do
- unless l = @context.io.gets
- if @context.ignore_eof? and @context.io.readable_atfer_eof?
- l = "\n"
- if @context.verbose?
- printf "Use \"exit\" to leave %s\n", @context.ap_name
- end
- end
- end
- l
- end
- end
-
- @scanner.set_prompt do
- |ltype, indent, continue, line_no|
- if ltype
- f = @context.prompt_s
- elsif continue
- f = @context.prompt_c
- else @context.prompt_i
- f = @context.prompt_i
- end
- f = "" unless f
- @context.io.prompt = p = prompt(f, ltype, indent, line_no)
- if @context.auto_indent_mode
- unless ltype
- ind = prompt(@context.prompt_i, ltype, indent, line_no).size +
- indent * 2 - p.size
- ind += 2 if continue
- @context.io.prompt = p + " " * ind if ind > 0
- end
- end
- end
-
- @scanner.each_top_level_statement do
- |line, line_no|
- signal_status(:IN_EVAL) do
- begin
- trace_in do
- @context._ = eval(line, @context.bind, @context.irb_path, line_no)
-# @context._ = irb_eval(line, @context.bind, @context.irb_path, line_no)
- end
-
- if @context.inspect?
- printf @context.return_format, @context._.inspect
- else
- printf @context.return_format, @context._
- end
- rescue StandardError, ScriptError, Abort
- $! = RuntimeError.new("unknown exception raised") unless $!
- print $!.type, ": ", $!, "\n"
- if $@[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && $!.type.to_s !~ /^IRB/
- irb_bug = true
- else
- irb_bug = false
- end
-
- messages = []
- lasts = []
- levels = 0
- for m in $@
- if m !~ /irb2?(\/.*|-.*|\.rb)?:/ or irb_bug
- if messages.size < @context.back_trace_limit
- messages.push m
- else
- lasts.push m
- if lasts.size > @context.back_trace_limit
- lasts.shift
- levels += 1
- end
- end
- end
- end
- print messages.join("\n"), "\n"
- unless lasts.empty?
- printf "... %d levels...\n", levels if levels > 0
- print lasts.join("\n")
- end
- print "Maybe IRB bug!!\n" if irb_bug
- end
- end
- end
- end
-
-# def irb_eval(line, bind, path, line_no)
-# id, str = catch(:IRB_TOPLEVEL_EVAL){
-# return eval(line, bind, path, line_no)
-# }
-# case id
-# when :EVAL_TOPLEVEL
-# eval(str, bind, "(irb_internal)", 1)
-# when :EVAL_CONTEXT
-# @context.instance_eval(str)
-# else
-# IRB.fail IllegalParameter
-# end
-# end
-
- def signal_handle
- unless @context.ignore_sigint?
- print "\nabort!!\n" if @context.verbose?
- exit
- end
-
- case @signal_status
- when :IN_INPUT
- print "^C\n"
- @scanner.initialize_input
- print @context.io.prompt
- when :IN_EVAL
- IRB.irb_abort(self)
- when :IN_LOAD
- IRB.irb_abort(self, LoadAbort)
- when :IN_IRB
- # ignore
- else
- # ignore
- end
- end
-
- def signal_status(status)
- return yield if @signal_status == :IN_LOAD
-
- signal_status_back = @signal_status
- @signal_status = status
- begin
- yield
- ensure
- @signal_status = signal_status_back
- end
- end
-
- def trace_in
- Tracer.on if @context.use_tracer?
- begin
- yield
- ensure
- Tracer.off if @context.use_tracer?
- end
- end
-
- def prompt(prompt, ltype, indent, line_no)
- p = prompt.dup
- p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
- case $2
- when "N"
- @context.irb_name
- when "m"
- @context.main.to_s
- when "M"
- @context.main.inspect
- when "l"
- ltype
- when "i"
- if $1
- format("%" + $1 + "d", indent)
- else
- indent.to_s
- end
- when "n"
- if $1
- format("%" + $1 + "d", line_no)
- else
- line_no.to_s
- end
- when "%"
- "%"
- end
- end
- p
- end
-
- def inspect
- ary = []
- for iv in instance_variables
- case iv
- when "@signal_status"
- ary.push format("%s=:%s", iv, @signal_status.id2name)
- when "@context"
- ary.push format("%s=%s", iv, eval(iv).__to_s__)
- else
- ary.push format("%s=%s", iv, eval(iv))
- end
- end
- format("#<%s: %s>", type, ary.join(", "))
- end
- end
-
- #
- # irb context
- #
- class Context
- #
- # Arguments:
- # input_method: nil -- stdin or readline
- # String -- File
- # other -- using this as InputMethod
- #
- def initialize(irb, main, bind, input_method = nil)
- @irb = irb
- @main = main
- @bind = bind
- @thread = Thread.current if defined? Thread
- @irb_level = 0
-
- # copy of default configuration
- @ap_name = IRB.conf[:AP_NAME]
- @rc = IRB.conf[:RC]
- @load_modules = IRB.conf[:LOAD_MODULES]
-
- self.math_mode = IRB.conf[:MATH_MODE]
- @use_readline = IRB.conf[:USE_READLINE]
- @inspect_mode = IRB.conf[:INSPECT_MODE]
- @use_tracer = IRB.conf[:USE_TRACER]
-# @use_loader = IRB.conf[:USE_LOADER]
-
- self.prompt_mode = IRB.conf[:PROMPT_MODE]
-
- @ignore_sigint = IRB.conf[:IGNORE_SIGINT]
- @ignore_eof = IRB.conf[:IGNORE_EOF]
-
- @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT]
-
- debug_level = IRB.conf[:DEBUG_LEVEL]
- @verbose = IRB.conf[:VERBOSE]
-
- @tracer_initialized = false
-
- if IRB.conf[:SINGLE_IRB] or !defined?(JobManager)
- @irb_name = IRB.conf[:IRB_NAME]
- else
- @irb_name = "irb#"+IRB.JobManager.n_jobs.to_s
- end
- @irb_path = "(" + @irb_name + ")"
-
- case input_method
- when nil
- if (use_readline.nil? && IRB.conf[:PROMPT_MODE] != :INF_RUBY ||
- use_readline?)
- @io = ReadlineInputMethod.new
- else
- @io = StdioInputMethod.new
- end
- when String
- @io = FileInputMethod.new(input_method)
- @irb_name = File.basename(input_method)
- @irb_path = input_method
- else
- @io = input_method
- end
- end
-
- attr :bind, true
- attr :main, true
- attr :thread
- attr :io, true
-
- attr :_
-
- attr :irb
- attr :ap_name
- attr :rc
- attr :load_modules
- attr :irb_name
- attr :irb_path
-
- attr :math_mode, true
- attr :use_readline, true
- attr :inspect_mode
- attr :use_tracer
-# attr :use_loader
-
- attr :debug_level
- attr :verbose, true
-
- attr :prompt_mode
- attr :prompt_i, true
- attr :prompt_s, true
- attr :prompt_c, true
- attr :auto_indent_mode, true
- attr :return_format, true
-
- attr :ignore_sigint, true
- attr :ignore_eof, true
-
- attr :back_trace_limit
-
-# alias use_loader? use_loader
- alias use_tracer? use_tracer
- alias use_readline? use_readline
- alias rc? rc
- alias math? math_mode
- alias verbose? verbose
- alias ignore_sigint? ignore_sigint
- alias ignore_eof? ignore_eof
-
- def _=(value)
- @_ = value
- eval "_ = IRB.conf[:MAIN_CONTEXT]._", @bind
- end
-
- def irb_name
- if @irb_level == 0
- @irb_name
- elsif @irb_name =~ /#[0-9]*$/
- @irb_name + "." + @irb_level.to_s
- else
- @irb_name + "#0." + @irb_level.to_s
- end
- end
-
- def prompt_mode=(mode)
- @prompt_mode = mode
- pconf = IRB.conf[:PROMPT][mode]
- @prompt_i = pconf[:PROMPT_I]
- @prompt_s = pconf[:PROMPT_S]
- @prompt_c = pconf[:PROMPT_C]
- @return_format = pconf[:RETURN]
- if ai = pconf.include?(:AUTO_INDENT)
- @auto_indent_mode = ai
- else
- @auto_indent_mode = IRB.conf[:AUTO_INDENT]
- end
- end
-
- def inspect?
- @inspect_mode.nil? && !@math_mode or @inspect_mode
- end
-
- def file_input?
- @io.type == FileInputMethod
- end
-
- def use_tracer=(opt)
- if opt
- IRB.initialize_tracer
- unless @tracer_initialized
- Tracer.set_get_line_procs(@irb_path) {
- |line_no|
- @io.line(line_no)
- }
- @tracer_initialized = true
- end
- elsif !opt && @use_tracer
- Tracer.off
- end
- @use_tracer=opt
- end
-
- def use_loader
- IRB.conf[:USE_LOADER]
- end
-
- def use_loader=(opt)
- IRB.conf[:USE_LOADER] = opt
- if opt
- IRB.initialize_loader
- end
- print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose?
- opt
- end
-
- def inspect_mode=(opt)
- if opt
- @inspect_mode = opt
- else
- @inspect_mode = !@inspect_mode
- end
- print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose?
- @inspect_mode
- end
-
- def math_mode=(opt)
- if @math_mode == true && opt == false
- IRB.fail CantRetuenNormalMode
- return
- end
-
- @math_mode = opt
- if math_mode
- IRB.initialize_mathn
- @main.instance_eval("include Math")
- print "start math mode\n" if verbose?
- end
- end
-
- def use_readline=(opt)
- @use_readline = opt
- print "use readline module\n" if @use_readline
- end
-
- def debug_level=(value)
- @debug_level = value
- RubyLex.debug_level = value
- SLex.debug_level = value
- end
-
- def debug?
- @debug_level > 0
- end
-
- def change_binding(*main)
- back = [@bind, @main]
- @bind = IRB.workspace_binding(*main)
- unless main.empty?
- @main = eval("self", @bind)
- begin
- @main.extend ExtendCommand
- rescue
- print "can't change binding to: ", @main.inspect, "\n"
- @bind, @main = back
- return nil
- end
- end
- @irb_level += 1
- begin
- catch(:SU_EXIT) do
- @irb.eval_input
- end
- ensure
- @irb_level -= 1
- @bind, @main = back
- end
- end
-
- alias __exit__ exit
- def exit(ret = 0)
- if @irb_level == 0
- IRB.irb_exit(@irb, ret)
- else
- throw :SU_EXIT, ret
- end
- end
-
- NOPRINTING_IVARS = ["@_"]
- NO_INSPECTING_IVARS = ["@irb", "@io"]
- IDNAME_IVARS = ["@prompt_mode"]
-
- alias __inspect__ inspect
- def inspect
- array = []
- for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
- name = ivar.sub(/^@(.*)$/){$1}
- val = instance_eval(ivar)
- case ivar
- when *NOPRINTING_IVARS
- next
- when *NO_INSPECTING_IVARS
- array.push format("conf.%s=%s", name, val.to_s)
- when *IDNAME_IVARS
- array.push format("conf.%s=:%s", name, val.id2name)
- else
- array.push format("conf.%s=%s", name, val.inspect)
- end
- end
- array.join("\n")
- end
- alias __to_s__ to_s
- alias to_s inspect
-
- end
-
- #
- # IRB extended command
- #
- module Loader; end
- module ExtendCommand
- include Loader
-
- alias irb_exit_org exit
- def irb_exit(ret = 0)
- irb_context.exit(ret)
- end
- alias exit irb_exit
- alias quit irb_exit
-
- alias irb_fork fork
- def fork(&block)
- unless irb_fork
- eval "alias exit irb_exit_org"
- instance_eval "alias exit irb_exit_org"
- if iterator?
- yield
- exit
- end
- end
- end
-
- def irb_change_binding(*main)
- irb_context.change_binding(*main)
- end
- alias cb irb_change_binding
-
- def irb_source(file)
- irb_context.source(file)
- end
- alias source irb_source
-
- def irb(*obj)
- require "irb/multi-irb"
- IRB.irb(nil, *obj)
- end
-
- def irb_context
- IRB.conf[:MAIN_CONTEXT]
- end
- alias conf irb_context
-
- def irb_jobs
- require "irb/multi-irb"
- IRB.JobManager
- end
- alias jobs irb_jobs
-
- def irb_fg(key)
- require "irb/multi-irb"
- IRB.JobManager.switch(key)
- end
- alias fg irb_fg
-
- def irb_kill(*keys)
- require "irb/multi-irb"
- IRB.JobManager.kill(*keys)
- end
- alias kill irb_kill
- end
-
- # Singleton method
- def @CONF.inspect
- IRB.version unless self[:VERSION]
-
- array = []
- for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
- case k
- when :MAIN_CONTEXT
- next
- when :PROMPT
- s = v.collect{
- |kk, vv|
- ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
- format(":%s=>{%s}", kk.id2name, ss.join(", "))
- }
- array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
- else
- array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
- end
- end
- array.join("\n")
- end
-end
diff --git a/lib/irb/multi-irb.rb b/lib/irb/multi-irb.rb
index 39dbcbae3c..6e97512e27 100644
--- a/lib/irb/multi-irb.rb
+++ b/lib/irb/multi-irb.rb
@@ -1,9 +1,9 @@
#
-# multi-irb.rb - multiple irb module
-# $Release Version: 0.6$
+# irb/multi-irb.rb - multiple irb module
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
@@ -23,7 +23,7 @@ module IRB
@current_job = nil
end
- attr :current_job, true
+ attr_accessor :current_job
def n_jobs
@jobs.size
@@ -31,7 +31,7 @@ module IRB
def thread(key)
th, irb = search(key)
- irb
+ th
end
def irb(key)
@@ -74,7 +74,7 @@ module IRB
when Integer
@jobs[key]
when Irb
- @jobs.find{|k, v| v.equal?(irb)}
+ @jobs.find{|k, v| v.equal?(key)}
when Thread
@jobs.assoc(key)
else
@@ -140,20 +140,15 @@ module IRB
@JobManager
end
- # invoke multiple irb
+ # invoke multi-irb
def IRB.irb(file = nil, *main)
- workspace = IRB.workspace_binding(*main)
- if main.empty?
- main = eval("self", workspace)
- else
- main = main[0]
- end
+ workspace = WorkSpace.new(*main)
parent_thread = Thread.current
Thread.start do
begin
- irb = Irb.new(main, workspace, file)
+ irb = Irb.new(workspace, file)
rescue
- print "Subirb can't start with context(self): ", main.inspect, "\n"
+ print "Subirb can't start with context(self): ", workspace.main.inspect, "\n"
print "return to main irb\n"
Thread.pass
Thread.main.wakeup
@@ -161,6 +156,7 @@ module IRB
end
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
@JobManager.insert(irb)
+ @JobManager.current_job = irb
begin
system_exit = false
catch(:IRB_EXIT) do
@@ -190,7 +186,7 @@ module IRB
class Context
def _=(value)
@_ = value
- eval "_ = IRB.JobManager.irb(Thread.current).context._", @bind
+ @workspace.evaluate "_ = IRB.JobManager.irb(Thread.current).context._"
end
end
@@ -198,15 +194,39 @@ module IRB
def irb_context
IRB.JobManager.irb(Thread.current).context
end
- alias conf irb_context
+# alias conf irb_context
end
@CONF[:SINGLE_IRB_MODE] = false
@JobManager.insert(@CONF[:MAIN_CONTEXT].irb)
@JobManager.current_job = @CONF[:MAIN_CONTEXT].irb
+ class Irb
+ def signal_handle
+ unless @context.ignore_sigint?
+ print "\nabort!!\n" if @context.verbose?
+ exit
+ end
+
+ case @signal_status
+ when :IN_INPUT
+ print "^C\n"
+ IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput
+ when :IN_EVAL
+ IRB.irb_abort(self)
+ when :IN_LOAD
+ IRB.irb_abort(self, LoadAbort)
+ when :IN_IRB
+ # ignore
+ else
+ # ignore
+ end
+ end
+ end
+
trap("SIGINT") do
@JobManager.current_job.signal_handle
+ Thread.stop
end
end
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 4c7a3b1002..3a862002a6 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -1,9 +1,9 @@
#
-# ruby-lex.rb - ruby lexcal analizer
-# $Release Version: 0.6$
+# irb/ruby-lex.rb - ruby lexcal analizer
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
@@ -24,11 +24,13 @@ class RubyLex
def_exception(:TkReading2TokenDuplicateError,
"key duplicate(token_n='%s', key='%s')")
def_exception(:SyntaxError, "%s")
+
+ def_exception(:TerminateLineInput, "Terminate Line Input")
include RubyToken
class << self
- attr :debug_level, TRUE
+ attr_accessor :debug_level
def debug?
@debug_level > 0
end
@@ -54,14 +56,14 @@ class RubyLex
@exception_on_syntax_error = true
end
- attr :skip_space, true
- attr :readed_auto_clean_up, true
- attr :exception_on_syntax_error, true
+ attr_accessor :skip_space
+ attr_accessor :readed_auto_clean_up
+ attr_accessor :exception_on_syntax_error
- attr :seek
- attr :char_no
- attr :line_no
- attr :indent
+ attr_reader :seek
+ attr_reader :char_no
+ attr_reader :line_no
+ attr_reader :indent
# io functions
def set_input(io, p = nil)
@@ -202,8 +204,8 @@ class RubyLex
@space_seen = false
@here_header = false
+ @continue = false
prompt
- @continue = FALSE
@line = ""
@exp_line_no = @line_no
@@ -211,27 +213,35 @@ class RubyLex
def each_top_level_statement
initialize_input
- loop do
- @continue = FALSE
- prompt
- unless l = lex
- break if @line == ''
- else
- # p l
- @line.concat l
- if @ltype or @continue or @indent > 0
- next
+ catch(:TERM_INPUT) do
+ loop do
+ begin
+ @continue = false
+ prompt
+ unless l = lex
+ throw :TERM_INPUT if @line == ''
+ else
+ #p l
+ @line.concat l
+ if @ltype or @continue or @indent > 0
+ next
+ end
+ end
+ if @line != "\n"
+ yield @line, @exp_line_no
+ end
+ break unless l
+ @line = ''
+ @exp_line_no = @line_no
+
+ @indent = 0
+ prompt
+ rescue TerminateLineInput
+ initialize_input
+ prompt
+ get_readed
end
end
- if @line != "\n"
- yield @line, @exp_line_no
- end
- break unless l
- @line = ''
- @exp_line_no = @line_no
-
- @indent = 0
- prompt
end
end
@@ -239,8 +249,8 @@ class RubyLex
until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
!@continue or
tk.nil?)
- # p tk
- # p self
+ #p tk
+ #p self
end
line = get_readed
# print self.inspect
@@ -315,7 +325,7 @@ class RubyLex
end
@OP.def_rules(" ", "\t", "\f", "\r", "\13") do
- @space_seen = TRUE
+ @space_seen = true
while getc =~ /[ \t\f\r\13]/; end
ungetc
Token(TkSPACE)
@@ -333,7 +343,7 @@ class RubyLex
until peek_equal?("=end") && peek(4) =~ /\s/
until getc == "\n"; end
end
- getc; getc; getc; getc
+ gets
@ltype = nil
Token(TkRD_COMMENT)
end
@@ -342,9 +352,9 @@ class RubyLex
print "\\n\n" if RubyLex.debug?
case @lex_state
when EXPR_BEG, EXPR_FNAME, EXPR_DOT
- @continue = TRUE
+ @continue = true
else
- @continue = FALSE
+ @continue = false
@lex_state = EXPR_BEG
end
@here_header = false
@@ -458,7 +468,7 @@ class RubyLex
ungetc
identify_number
else
- # for obj.if
+ # for "obj.if" etc.
@lex_state = EXPR_DOT
Token(TkDOT)
end
@@ -608,7 +618,7 @@ class RubyLex
identify_quotation
elsif peek(0) == '='
getc
- Token(OP_ASGIN, "%")
+ Token(TkOPASGN, :%)
elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
identify_quotation
else
@@ -691,7 +701,7 @@ class RubyLex
if ch == "!" or ch == "?"
token.concat getc
end
- # fix token
+ # almost fix token
case token
when /^\$/
@@ -752,6 +762,7 @@ class RubyLex
def identify_here_document
ch = getc
+# if lt = PERCENT_LTYPE[ch]
if ch == "-"
ch = getc
indent = true
@@ -835,8 +846,8 @@ class RubyLex
end
type = TkINTEGER
- allow_point = TRUE
- allow_e = TRUE
+ allow_point = true
+ allow_e = true
while ch = getc
case ch
when /[0-9_]/
@@ -954,7 +965,7 @@ class RubyLex
read_escape(chrs)
end
else
- # other characters
+ # other characters
end
end
end
diff --git a/lib/irb/ruby-token.rb b/lib/irb/ruby-token.rb
index 1532dc78eb..2e5715c9f7 100644
--- a/lib/irb/ruby-token.rb
+++ b/lib/irb/ruby-token.rb
@@ -1,9 +1,9 @@
#
-# ruby-token.rb - ruby tokens
-# $Release Version: 0.6$
+# irb/ruby-token.rb - ruby tokens
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
@@ -17,6 +17,11 @@ module RubyToken
EXPR_FNAME = :EXPR_FNAME
EXPR_DOT = :EXPR_DOT
EXPR_CLASS = :EXPR_CLASS
+
+ # for ruby 1.4X
+ if !defined?(Symbol)
+ Symbol = Integer
+ end
class Token
def initialize(seek, line_no, char_no)
@@ -241,7 +246,7 @@ module RubyToken
TkSymbol2Token = {}
def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts)
- token_n = token_n.id2name unless token_n.kind_of?(String)
+ token_n = token_n.id2name if token_n.kind_of?(Symbol)
if RubyToken.const_defined?(token_n)
IRB.fail AlreadyDefinedToken, token_n
end
diff --git a/lib/irb/slex.rb b/lib/irb/slex.rb
index 85aa92bd73..26008906e5 100644
--- a/lib/irb/slex.rb
+++ b/lib/irb/slex.rb
@@ -1,9 +1,9 @@
#
-# irb-slex.rb - symple lex analizer
-# $Release Version: 0.6$
+# irb/slex.rb - symple lex analizer
+# $Release Version: 0.7.3$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# by Keiju ISHITSUKA(keiju@ishituska.com)
#
# --
#
@@ -20,7 +20,7 @@ class SLex
def_exception :ErrNodeAlreadyExists, "node already exists"
class << self
- attr :debug_level, TRUE
+ attr_accessor :debug_level
def debug?
debug_level > 0
end
@@ -88,16 +88,16 @@ class SLex
#
#----------------------------------------------------------------------
class Node
- # if postproc no exist, this node is abstract node.
- # if postproc isn't nil, this node is real node.
+ # if postproc is nil, this node is an abstract node.
+ # if postproc is non-nil, this node is a real node.
def initialize(preproc = nil, postproc = nil)
@Tree = {}
@preproc = preproc
@postproc = postproc
end
- attr :preproc, TRUE
- attr :postproc, TRUE
+ attr_accessor :preproc
+ attr_accessor :postproc
def search(chrs, opt = nil)
return self if chrs.empty?
@@ -159,8 +159,8 @@ class SLex
#
# chrs: String
# character array
- # io It must have getc()/ungetc(), and ungetc() can be
- # called any number of times.
+ # io must have getc()/ungetc(); and ungetc() must be
+ # able to be called arbitrary number of times.
#
def match(chrs, op = "")
print "match>: ", chrs, "op:", op, "\n" if SLex.debug?
@@ -265,7 +265,7 @@ if $0 == __FILE__
print "0: ", tr.inspect, "\n"
tr.def_rule("=") {print "=\n"}
print "1: ", tr.inspect, "\n"
- tr.def_rule("==", proc{FALSE}) {print "==\n"}
+ tr.def_rule("==", proc{false}) {print "==\n"}
print "2: ", tr.inspect, "\n"
print "case 1:\n"
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 7179d1c163..367cc21046 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -1,9 +1,9 @@
#
-# version.rb - irb version definition file
-# $Release Version: 0.6.1$
+# irb/version.rb - irb version definition file
+# $Release Version: 0.7.4$
# $Revision$
# $Date$
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
# --
#
@@ -11,6 +11,6 @@
#
module IRB
- @RELEASE_VERSION = "0.6.1"
- @LAST_UPDATE_DATE = "99/09/16"
+ @RELEASE_VERSION = "0.7.4"
+ @LAST_UPDATE_DATE = "01/05/08"
end
diff --git a/lib/irb/workspace-binding-2.rb b/lib/irb/workspace-binding-2.rb
deleted file mode 100644
index d005296f6e..0000000000
--- a/lib/irb/workspace-binding-2.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# bind.rb -
-# $Release Version: $
-# $Revision$
-# $Date$
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
-#
-# --
-#
-#
-#
-
-while true
- IRB::BINDING_QUEUE.push b = binding
-end
diff --git a/lib/irb/workspace-binding.rb b/lib/irb/workspace-binding.rb
deleted file mode 100644
index d58088d9dd..0000000000
--- a/lib/irb/workspace-binding.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# workspace-binding.rb -
-# $Release Version: $
-# $Revision$
-# $Date$
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
-#
-# --
-#
-#
-#
-
-
-module IRB
- # create new workspace.
- def IRB.workspace_binding(*main)
- if @CONF[:SINGLE_IRB]
- bind = TOPLEVEL_BINDING
- else
- case @CONF[:CONTEXT_MODE]
- when 0
- bind = eval("proc{binding}.call",
- TOPLEVEL_BINDING,
- "(irb_local_binding)",
- 1)
- when 1
- require "tempfile"
- f = Tempfile.open("irb-binding")
- f.print <<EOF
- $binding = binding
-EOF
- f.close
- load f.path
- bind = $binding
-
- when 2
- unless defined? BINDING_QUEUE
- require "thread"
-
- IRB.const_set("BINDING_QUEUE", SizedQueue.new(1))
- Thread.abort_on_exception = true
- Thread.start do
- eval "require \"irb/workspace-binding-2\"", TOPLEVEL_BINDING, __FILE__, __LINE__
- end
- Thread.pass
-
- end
-
- bind = BINDING_QUEUE.pop
-
- when 3
- bind = eval("def irb_binding; binding; end; irb_binding",
- TOPLEVEL_BINDING,
- __FILE__,
- __LINE__ - 3)
- end
- end
- unless main.empty?
- @CONF[:__MAIN__] = main[0]
- case main[0]
- when Module
- bind = eval("IRB.conf[:__MAIN__].module_eval('binding')", bind)
- else
- begin
- bind = eval("IRB.conf[:__MAIN__].instance_eval('binding')", bind)
- rescue TypeError
- IRB.fail CanNotChangeBinding, main[0].inspect
- end
- end
- end
- eval("_=nil", bind)
- bind
- end
-
- def IRB.delete_caller
- end
-end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
new file mode 100644
index 0000000000..68559a1173
--- /dev/null
+++ b/lib/irb/workspace.rb
@@ -0,0 +1,106 @@
+#
+# irb/workspace-binding.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+module IRB
+ class WorkSpace
+ # create new workspace.
+ # set self to main if specified, otherwise inherit main
+ # from TOPLEVEL_BINDING.
+ def initialize(*main)
+ if IRB.conf[:SINGLE_IRB]
+ @binding = TOPLEVEL_BINDING
+ else
+ case IRB.conf[:CONTEXT_MODE]
+ when 0 # binding in proc on TOPLEVEL_BINDING
+ @binding = eval("proc{binding}.call",
+ TOPLEVEL_BINDING,
+ __FILE__,
+ __LINE__)
+ when 1 # binding in loaded file
+ require "tempfile"
+ f = Tempfile.open("irb-binding")
+ f.print <<EOF
+ $binding = binding
+EOF
+ f.close
+ load f.path
+ @binding = $binding
+
+ when 2 # binding in loaded file(thread use)
+ unless defined? BINDING_QUEUE
+ require "thread"
+
+ IRB.const_set("BINDING_QUEUE", SizedQueue.new(1))
+ Thread.abort_on_exception = true
+ Thread.start do
+ eval "require \"irb/ws-for-case-2\"", TOPLEVEL_BINDING, __FILE__, __LINE__
+ end
+ Thread.pass
+ end
+ @binding = BINDING_QUEUE.pop
+
+ when 3 # binging in function on TOPLEVEL_BINDING(default)
+ @binding = eval("def irb_binding; binding; end; irb_binding",
+ TOPLEVEL_BINDING,
+ __FILE__,
+ __LINE__ - 3)
+ end
+ end
+ if main.empty?
+ @main = eval "self", @binding
+ else
+ @main = main[0]
+ IRB.conf[:__MAIN__] = @main
+ case @main
+ when Module
+ @binding = eval("IRB.conf[:__MAIN__].module_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
+ else
+ begin
+ @binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
+ rescue TypeError
+ IRB.fail CanNotChangeBinding, @main.inspect
+ end
+ end
+ end
+ eval("_=nil", @binding)
+ end
+
+ attr_reader :binding
+ attr_reader :main
+
+ def evaluate(statements, file = __FILE__, line = __LINE__)
+ eval statements, @binding, file, line
+ end
+
+ # error message manupilator
+ def filter_backtrace(bt)
+ case IRB.conf[:CONTEXT_MODE]
+ when 0
+ return nil if bt =~ /\(irb_local_binding\)/
+ when 1
+ if(bt =~ %r!/tmp/irb-binding! or
+ bt =~ %r!irb/.*\.rb! or
+ bt =~ /irb\.rb/)
+ return nil
+ end
+ when 2
+ return nil if bt =~ /irb\/.*\.rb/
+ when 3
+ return nil if bt =~ /irb\/.*\.rb/
+ bt.sub!(/:\s*in `irb_binding'/){""}
+ end
+ bt
+ end
+
+ def IRB.delete_caller
+ end
+ end
+end
diff --git a/lib/irb/ws-for-case-2.rb b/lib/irb/ws-for-case-2.rb
new file mode 100644
index 0000000000..8cfa87ae3d
--- /dev/null
+++ b/lib/irb/ws-for-case-2.rb
@@ -0,0 +1,15 @@
+#
+# irb/ws-for-case-2.rb -
+# $Release Version: 0.7.3$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+while true
+ IRB::BINDING_QUEUE.push b = binding
+end
diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb
index fc745a2757..e0bcee4bdb 100644
--- a/lib/irb/xmp.rb
+++ b/lib/irb/xmp.rb
@@ -1,6 +1,6 @@
#
# xmp.rb - irb version of gotoken xmp
-# $Release Version: 0.6$
+# $Release Version: 0.7.1$
# $Revision$
# $Date$
# by Keiju ISHITSUKA(Nippon Rational Inc.)
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 317200ba79..e9c78dace7 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -12,12 +12,14 @@ SRC_EXT = ["c", "cc", "m", "cxx", "cpp", "C"]
$config_cache = CONFIG["compile_dir"]+"/ext/config.cache"
$srcdir = CONFIG["srcdir"]
-$libdir = CONFIG["libdir"]+"/ruby/"+CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
-$archdir = $libdir+"/"+CONFIG["arch"]
-$sitelibdir = CONFIG["sitedir"]+"/"+CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
-$sitearchdir = $sitelibdir+"/"+CONFIG["arch"]
-
-if File.exist? $archdir + "/ruby.h"
+$libdir = CONFIG["libdir"]
+$rubylibdir = CONFIG["rubylibdir"]
+$archdir = CONFIG["archdir"]
+$sitedir = CONFIG["sitedir"]
+$sitelibdir = CONFIG["sitelibdir"]
+$sitearchdir = CONFIG["sitearchdir"]
+
+if File.exist? Config::CONFIG["archdir"] + "/ruby.h"
$hdrdir = $archdir
elsif File.exist? $srcdir + "/ruby.h"
$hdrdir = $srcdir
@@ -43,7 +45,7 @@ else
$null = open('test.log', 'w')
end
-LINK = "#{CONFIG['CC']} -o conftest -I#{$hdrdir} #{CFLAGS} -I#{CONFIG['includedir']} %s #{CONFIG['LDFLAGS']} %s conftest.c %s %s #{CONFIG['LIBS']}"
+LINK = "#{CONFIG['CC']} -o conftest -I#{$hdrdir} #{CFLAGS} -I#{CONFIG['includedir']} %s %s #{CONFIG['LDFLAGS']} %s conftest.c %s %s #{CONFIG['LIBS']}"
CPP = "#{CONFIG['CPP']} -E %s -I#{$hdrdir} #{CFLAGS} -I#{CONFIG['includedir']} %s %s conftest.c"
def rm_f(*files)
@@ -60,6 +62,7 @@ end
$orgerr = $stderr.dup
$orgout = $stdout.dup
def xsystem command
+ Config.expand(command)
if $DEBUG
print command, "\n"
return system(command)
@@ -155,7 +158,9 @@ def install_rb(mfile, dest, srcdir = nil)
mfile.printf "\t@$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' %s/%s\n", dest, f
end
for f in path
- mfile.printf "\t@$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0644, true)' lib/%s %s/%s\n", f, dest, f
+ d = '/' + File::dirname(f)
+ d = '' if d == '/.'
+ mfile.printf "\t@$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0644, true)' %s/%s %s%s\n", libdir, f, dest, d
end
end
@@ -334,24 +339,32 @@ def dir_config(target, idefault=nil, ldefault=nil)
idefault = default + "/include"
ldefault = default + "/lib"
end
- dir = with_config("%s-dir"%target, default)
- if dir
- idir = " -I"+dir+"/include"
- ldir = dir+"/lib"
- end
- unless idir
- dir = with_config("%s-include"%target, idefault)
- idir = " -I"+dir if dir
+
+ dir = with_config(target + "-dir", default)
+
+ idir, ldir = if dir then [
+ dir + "/include",
+ dir + "/lib"
+ ] else [
+ with_config(target + "-include", idefault),
+ with_config(target + "-lib", ldefault)
+ ] end
+
+ if idir
+ idircflag = "-I" + idir
+ $CPPFLAGS += " " + idircflag unless $CPPFLAGS.split.include?(idircflag)
end
- unless ldir
- ldir = with_config("%s-lib"%target, ldefault)
+
+ if ldir
+ $LIBPATH << ldir unless $LIBPATH.include?(ldir)
end
- $CPPFLAGS += idir if idir
- $LIBPATH |= [ldir] if ldir
+ [idir, ldir]
end
def create_makefile(target, srcdir = File.dirname($0))
+ save_libs = $libs.dup
+ save_libpath = $LIBPATH.dup
print "creating Makefile\n"
rm_f "conftest*"
STDOUT.flush
@@ -370,15 +383,16 @@ def create_makefile(target, srcdir = File.dirname($0))
end
$DLDFLAGS = CONFIG["DLDFLAGS"]
- if $configure_args['--enable-shared'] or CONFIG['LIBRUBY'] != CONFIG['LIBRUBY_A']
- $libs = CONFIG["LIBRUBYARG"] + " " + $libs
- $LIBPATH |= ["$(topdir)", CONFIG["libdir"]]
- end
+ $libs = CONFIG["LIBRUBYARG"] + " " + $libs
+ $configure_args['--enable-shared'] or $LIBPATH |= [$topdir]
+ $LIBPATH |= [CONFIG["libdir"]]
defflag = ''
if RUBY_PLATFORM =~ /cygwin|mingw/
- open(target + '.def', 'wb') do |f|
- f.print "EXPORTS\n", "Init_", target, "\n"
+ if not File.exist? target + '.def'
+ open(target + '.def', 'wb') do |f|
+ f.print "EXPORTS\n", "Init_", target, "\n"
+ end
end
defflag = "--def=" + target + ".def"
end
@@ -427,6 +441,8 @@ LIBPATH = #{libpath}
RUBY_INSTALL_NAME = #{CONFIG["RUBY_INSTALL_NAME"]}
RUBY_SO_NAME = #{CONFIG["RUBY_SO_NAME"]}
+arch = #{CONFIG["arch"]}
+ruby_version = #{Config::CONFIG["ruby_version"]}
#{
if destdir = CONFIG["prefix"].scan(drive)[0] and !destdir.empty?
"\nDESTDIR = " + destdir
@@ -435,11 +451,13 @@ else
end
}
prefix = $(DESTDIR)#{CONFIG["prefix"].sub(drive, '')}
-exec_prefix = $(DESTDIR)#{CONFIG["exec_prefix"].sub(drive, '')}
-libdir = $(DESTDIR)#{$libdir.sub(drive, '')}#{target_prefix}
-archdir = $(DESTDIR)#{$archdir.sub(drive, '')}#{target_prefix}
-sitelibdir = $(DESTDIR)#{$sitelibdir.sub(drive, '')}#{target_prefix}
-sitearchdir = $(DESTDIR)#{$sitearchdir.sub(drive, '')}#{target_prefix}
+exec_prefix = #{CONFIG["exec_prefix"].sub(drive, '')}
+libdir = #{$libdir.sub(drive, '')}#{target_prefix}
+rubylibdir = #{$rubylibdir.sub(drive, '')}#{target_prefix}
+archdir = #{$archdir.sub(drive, '')}#{target_prefix}
+sitedir = #{$sitedir.sub(drive, '')}#{target_prefix}
+sitelibdir = #{$sitelibdir.sub(drive, '')}#{target_prefix}
+sitearchdir = #{$sitearchdir.sub(drive, '')}#{target_prefix}
#### End of system configuration section. ####
@@ -471,10 +489,10 @@ install: $(archdir)/$(DLLIB)
site-install: $(sitearchdir)/$(DLLIB)
$(archdir)/$(DLLIB): $(DLLIB)
- @$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' $(libdir) $(archdir)
+ @$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' $(rubylibdir) $(archdir)
@$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0555, true)' $(DLLIB) $(archdir)/$(DLLIB)
EOMF
- install_rb(mfile, "$(libdir)")
+ install_rb(mfile, "$(rubylibdir)", srcdir)
mfile.printf "\n"
mfile.printf <<EOMF
@@ -482,21 +500,42 @@ $(sitearchdir)/$(DLLIB): $(DLLIB)
@$(RUBY) -r ftools -e 'File::makedirs(*ARGV)' $(libdir) $(sitearchdir)
@$(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0555, true)' $(DLLIB) $(sitearchdir)/$(DLLIB)
EOMF
- install_rb(mfile, "$(sitelibdir)")
+ install_rb(mfile, "$(sitelibdir)", srcdir)
mfile.printf "\n"
if /mswin32/ !~ RUBY_PLATFORM
mfile.print "
.c.#{$OBJEXT}:
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+
+.cc.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+.cpp.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+.cxx.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+.C.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
"
elsif /nmake/i =~ $make
mfile.print "
{$(srcdir)}.c.#{$OBJEXT}:
$(CC) $(CFLAGS) -I$(<D) $(CPPFLAGS) -c $(<:/=\\)
-
.c.#{$OBJEXT}:
$(CC) $(CFLAGS) -I$(<D) $(CPPFLAGS) -c $(<:/=\\)
+
+{$(srcdir)}.cc{}.#{$OBJEXT}:
+ $(CXX) -I. -I$(<D) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
+.cc.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
+{$(srcdir)}.cpp{}.#{$OBJEXT}:
+ $(CXX) -I. -I$(<D) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
+.cpp.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
+{$(srcdir)}.cxx{}.#{$OBJEXT}:
+ $(CXX) -I. -I$(<D) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
+.cxx.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<:/=\\)
"
else
mfile.print "
@@ -504,6 +543,9 @@ EOMF
.c.#{$OBJEXT}:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $(subst /,\\\\,$<)
+
+.cc.#{$OBJEXT} .cpp.#{$OBJEXT} .cxx.#{$OBJEXT} .C.#{$OBJEXT}:
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(subst /,\\\\,$<)
"
end
@@ -536,6 +578,8 @@ EOMF
dfile.close
end
mfile.close
+ $libs = save_libs
+ $LIBPATH = save_libpath
end
$OBJEXT = CONFIG["OBJEXT"]
@@ -550,20 +594,10 @@ $LOCAL_LIBS = ""
$defs = []
$make = with_config("make-prog", ENV["MAKE"] || "make")
-dir = with_config("opt-dir")
-if dir
- idir = "-I"+dir+"/include"
- ldir = dir+"/lib"
-end
-unless idir
- dir = with_config("opt-include")
- idir = "-I"+dir if dir
-end
-unless ldir
- ldir = with_config("opt-lib")
-end
$CFLAGS = with_config("cflags", "")
-$CPPFLAGS = [with_config("cppflags", ""), idir].compact.join(" ")
+$CPPFLAGS = with_config("cppflags", "")
$LDFLAGS = with_config("ldflags", "")
-$LIBPATH = [ldir].compact
+$LIBPATH = []
+
+dir_config("opt")
diff --git a/lib/monitor.rb b/lib/monitor.rb
index 75d9c35821..721c51a9f5 100644
--- a/lib/monitor.rb
+++ b/lib/monitor.rb
@@ -1,27 +1,44 @@
=begin
-monitor.rb
-Author: Shugo Maeda <shugo@netlab.co.jp>
-Version: 1.2.1
+= monitor.rb
-USAGE:
+Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
- foo = Foo.new
- foo.extend(MonitorMixin)
- cond = foo.new_cond
+This library is distributed under the terms of the Ruby license.
+You can freely distribute/modify this library.
- thread1:
- foo.synchronize {
- ...
- cond.wait_until { foo.done? }
- ...
- }
+== example
- thread2:
- foo.synchronize {
- foo.do_something
- cond.signal
- }
+This is a simple example.
+
+ require 'monitor.rb'
+
+ buf = []
+ buf.extend(MonitorMixin)
+ empty_cond = buf.new_cond
+
+ # consumer
+ Thread.start do
+ loop do
+ buf.synchronize do
+ empty_cond.wait_while { buf.empty? }
+ print buf.shift
+ end
+ end
+ end
+
+ # producer
+ while line = ARGF.gets
+ buf.synchronize do
+ buf.push(line)
+ empty_cond.signal
+ end
+ end
+
+The consumer thread waits for the producer thread to push a line
+to buf while buf.empty?, and the producer thread (main thread)
+reads a line from ARGF and push it to buf, then call
+empty_cond.signal.
=end
@@ -52,6 +69,15 @@ module MonitorMixin
raise ThreadError, "current thread not owner"
end
+ if timeout
+ ct = Thread.current
+ timeout_thread = Thread.start {
+ Thread.pass
+ sleep(timeout)
+ ct.raise(Timeout.new)
+ }
+ end
+
Thread.critical = true
count = @monitor.mon_count
@monitor.mon_count = 0
@@ -63,34 +89,28 @@ module MonitorMixin
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
+ Thread.critical = true
if timeout && timeout_thread.alive?
Thread.kill(timeout_thread)
end
+ if @waiters.include?(Thread.current) # interrupted?
+ @waiters.delete(Thread.current)
+ end
+ 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
-
- 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
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 3900ed6c68..26e5285525 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1,8 +1,9 @@
=begin
-= net/http.rb version 1.1.32
+= net/http.rb version 1.1.34
+
+written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
-maintained by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This file is derived from "http-access.rb".
This program is free software.
@@ -14,9 +15,34 @@ You can get it from RAA
(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html).
-= class HTTP
+== http.rb version 1.2 features
+
+You can use 1.2 features by calling HTTP.version_1_2. And
+calling Net::HTTP.version_1_1 allows to use 1.1 features.
+
+ # example
+ HTTP.start {|http1| ...(http1 has 1.1 features)... }
+
+ HTTP.version_1_2
+ HTTP.start {|http2| ...(http2 has 1.2 features)... }
+
+ HTTP.version_1_1
+ HTTP.start {|http3| ...(http3 has 1.1 features)... }
+
+Changes are:
-== Class Methods
+ * HTTP#get, head, post does not raise ProtocolError
+ * HTTP#get, head, post returns only one object, a HTTPResponse object
+ * HTTPResponseReceiver is joined into HTTPResponse
+ * request object: HTTP::Get, Head, Post; and HTTP#request(req)
+
+WARNING: These features are not definite yet.
+They will change without notice!
+
+
+== class HTTP
+
+=== Class Methods
: new( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil )
creates a new Net::HTTP object.
@@ -49,7 +75,7 @@ You can get it from RAA
HTTP default port (80).
-== Methods
+=== Methods
: start
: start {|http| .... }
@@ -72,7 +98,7 @@ You can get it from RAA
get data from "path" on connecting host.
"header" must be a Hash like { 'Accept' => '*/*', ... }.
Data is written to "dest" by using "<<" method.
- This method returns Net::HTTPResponse object, and "dest".
+ This method returns HTTPResponse object, and "dest".
# example
response, body = http.get( '/index.html' )
@@ -95,7 +121,7 @@ You can get it from RAA
: head( path, header = nil )
gets only header from "path" on connecting host.
"header" is a Hash like { 'Accept' => '*/*', ... }.
- This method returns a Net::HTTPResponse object.
+ This method returns a HTTPResponse object.
You can http header from this object like:
response['content-length'] #-> '2554'
@@ -109,7 +135,7 @@ You can get it from RAA
If the body exists, also gets entity body.
Data is written to "dest" by using "<<" method.
"header" must be a Hash like { 'Accept' => '*/*', ... }.
- This method returns Net::HTTPResponse object and "dest".
+ This method returns HTTPResponse object and "dest".
If called with block, gives a part of entity body string.
@@ -181,21 +207,21 @@ You can get it from RAA
response.body
-= class HTTPResponse
+== class HTTPResponse
HTTP response object.
All "key" is case-insensitive.
-== Methods
+=== Methods
: body
- the entity body. ("dest" argument for HTTP#get, post, put)
+ the entity body (String).
: self[ key ]
returns header field for "key".
for HTTP, value is a string like 'text/plain'(for Content-Type),
- '2045'(for Content-Length), 'bytes 0-1024/10024'(for Content-Range).
- Multiple header had be joined by HTTP1.1 scheme.
+ '2045'(for Content-Length), 'bytes 0-1023/10024'(for Content-Range).
+ If there's some fields which has same name, they are joined with ','.
: self[ key ] = val
set field value for "key".
@@ -204,88 +230,92 @@ All "key" is case-insensitive.
true if key exists
: each {|name,value| .... }
- iterates for each field name and value pair
+ iterates for each field name and value pair.
: code
- HTTP result code string. For example, '302'
+ HTTP result code string. For example, '302'.
: message
- HTTP result message. For example, 'Not Found'
+ HTTP result message. For example, 'Not Found'.
-= class HTTPResponseReceiver
+== class HTTPResponseReceiver
-== Methods
+=== Methods
: header
: response
- Net::HTTPResponse object
+ HTTPResponse object
-: body( dest = '' )
-: entity( dest = '' )
- entity body. A body is written to "dest" using "<<" method.
+: read_body( dest = '' )
+ reads entity body into DEST by calling "<<" method and
+ returns DEST.
-: body {|str| ... }
- gets entity body with block.
- If this method is called twice, block is not executed and
- returns first "dest".
+: read_body {|string| ... }
+ reads entity body little by little and gives it to block
+ until entity ends.
+: body
+: entity
+ entity body. If #read_body is called already, returns its
+ argument DEST. Else returns entity body as String.
-= http.rb version 1.2 features
+ Calling this method any times causes returning same
+ object (does not read entity again).
-You can use 1.2 features by calling HTTP.version_1_2. And
-calling Net::HTTP.version_1_1 allows to use 1.1 features.
+=end
- # example
- HTTP.start {|http1| ...(http1 has 1.1 features)... }
+require 'net/protocol'
- HTTP.version_1_2
- HTTP.start {|http2| ...(http2 has 1.2 features)... }
- HTTP.version_1_1
- HTTP.start {|http3| ...(http3 has 1.1 features)... }
+module Net
-== Method (only diff to 1.1)
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
-: get( path, u_header = nil )
-: get( path, u_header = nil ) {|str| .... }
- gets document from "path".
- returns HTTPResponse object.
-: head( path, u_header = nil )
- gets only document header from "path".
- returns HTTPResponse object.
+ class HTTP < Protocol
-: post( path, data, u_header = nil )
-: post( path, data, u_header = nil ) {|str| .... }
- posts "data" to "path" entity and gets document.
- returns HTTPResponse object.
+ HTTPVersion = '1.1'
-=end
+ #
+ # connection
+ #
-require 'net/protocol'
+ protocol_param :port, '80'
-module Net
+ def initialize( addr = nil, port = nil )
+ super
- class HTTPBadResponse < StandardError; end
+ @proxy_address = nil
+ @proxy_port = nil
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ end
- class HTTP < Protocol
- protocol_param :port, '80'
- protocol_param :command_type, '::Net::NetPrivate::HTTPCommand'
+ private
+ def conn_command( sock )
+ end
+
+ def do_finish
+ end
+
+
+ #
+ # proxy
+ #
+
+ public
- ###
- ### proxy
- ###
class << self
def Proxy( p_addr, p_port = nil )
- ::Net::NetPrivate::HTTPProxy.create_proxy_class(
- p_addr, p_port || self.port )
+ ProxyMod.create_proxy_class( p_addr, p_port || self.port )
end
alias orig_new new
@@ -293,7 +323,7 @@ module Net
def new( address = nil, port = nil, p_addr = nil, p_port = nil )
c = p_addr ? self::Proxy(p_addr, p_port) : self
i = c.orig_new( address, port )
- setimplv i
+ setvar i
i
end
@@ -332,385 +362,230 @@ module Net
end
- ###
- ### 1.2 implementation
- ###
+ module ProxyMod
- @@newimpl = false
+ class << self
- #class << self
+ def create_proxy_class( p_addr, p_port )
+ mod = self
+ klass = Class.new( HTTP )
+ klass.module_eval {
+ include mod
+ @proxy_address = p_addr
+ @proxy_port = p_port
+ }
+ def klass.proxy_class?
+ true
+ end
- def self.version_1_2
- @@newimpl = true
- end
+ def klass.proxy_address
+ @proxy_address
+ end
- def self.version_1_1
- @@newimpl = false
- end
+ def klass.proxy_port
+ @proxy_port
+ end
- #private
+ klass
+ end
- def self.setimplv( obj )
- f = @@newimpl
- obj.instance_eval { @newimpl = f }
end
- #end
-
-
- ###
- ### http operations
- ###
-
- def get( path, u_header = nil, dest = nil, &block )
- resp = get2( path, u_header ) {|f| f.body( dest, &block ) }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+ def initialize( addr, port )
+ super
+ @proxy_address = type.proxy_address
+ @proxy_port = type.proxy_port
end
- end
-
- def get2( path, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.get edit_path(path), uh
- }
- end
+
+ attr_reader :proxy_address, :proxy_port
+ alias proxyaddr proxy_address
+ alias proxyport proxy_port
- def head( path, u_header = nil )
- resp = head2( path, u_header )
- unless @newimpl then
- resp.value
+ def proxy?
+ true
end
- resp
- end
-
- def head2( path, u_header = nil, &block )
- common_oper( u_header, false, block ) {|uh|
- @command.head edit_path(path), uh
- }
- end
-
-
- def post( path, data, u_header = nil, dest = nil, &block )
- resp = post2( path, data, u_header ) {|f| f.body( dest, &block ) }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+
+ private
+
+ def conn_socket( addr, port )
+ super @proxy_address, @proxy_port
end
- end
-
- def post2( path, data, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.post edit_path(path), uh, data
- }
- end
-
- # not tested because I could not setup apache (__;;;
- def put( path, src, u_header = nil )
- resp = put2( path, src, u_header ) {|f| f.body }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+ def edit_path( path )
+ 'http://' + addr_port + path
end
- end
+
+ end # module ProxyMod
- def put2( path, src, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.put path, uh, src
- }
- end
-
-
- private
-
-
- def common_oper( u_header, body_exist, block )
- header = procheader( u_header )
- recv = err = nil
-
- connecting( header ) {
- recv = HTTPResponseReceiver.new( @command, body_exist )
- yield header
- begin
- block.call recv if block
- rescue Exception => err
- ;
- end
- recv.terminate
-
- recv.response
- }
- raise err if err
- recv.response
- end
+ #
+ # for backward compatibility
+ #
- def connecting( header )
- if not @socket then
- header['Connection'] = 'close'
- start
- elsif @socket.closed? then
- @socket.reopen
- end
+ @@newimpl = false
- resp = yield
+ class << self
- unless keep_alive? header, resp then
- @socket.close
+ def version_1_2
+ @@newimpl = true
end
- end
- def keep_alive?( header, resp )
- if resp.key? 'connection' then
- if /keep-alive/i === resp['connection'] then
- return true
- end
- elsif resp.key? 'proxy-connection' then
- if /keep-alive/i === resp['proxy-connection'] then
- return true
- end
- elsif header.key? 'Connection' then
- if /keep-alive/i === header['Connection'] then
- return true
- end
- else
- if @command.http_version == '1.1' then
- return true
- end
+ def version_1_1
+ @@newimpl = false
end
- false
- end
-
- def procheader( h )
- ret = {}
- ret[ 'Host' ] = address +
- ((port == HTTP.port) ? '' : ":#{port}")
- ret[ 'Connection' ] = 'Keep-Alive'
- ret[ 'Accept' ] = '*/*'
+ private
- return ret unless h
- tmp = {}
- h.each do |k,v|
- key = k.split('-').collect {|i| i.capitalize }.join('-')
- if tmp[key] then
- $stderr.puts "'#{key}' http header appered twice" if $VERBOSE
- end
- tmp[key] = v
+ def setvar( obj )
+ f = @@newimpl
+ obj.instance_eval { @newimpl = f }
end
- ret.update tmp
- ret
end
- def do_finish
- end
+ #
+ # http operations
+ #
- end
-
- HTTPSession = HTTP
-
-
- module NetPrivate
-
- module HTTPProxy
-
- class << self
-
- def create_proxy_class( p_addr, p_port )
- klass = Class.new( HTTP )
- klass.module_eval {
- include HTTPProxy
- @proxy_address = p_addr
- @proxy_port = p_port
- }
- def klass.proxy_class?
- true
- end
+ public
- def klass.proxy_address
- @proxy_address
+ def self.def_http_method( nm, hasdest, hasdata )
+ name = nm.id2name.downcase
+ cname = nm.id2name
+ lineno = __LINE__ + 2
+ src = <<" ----"
+
+ def #{name}( path, #{hasdata ? 'data,' : ''}
+ u_header = nil #{hasdest ? ',dest = nil, &block' : ''} )
+ resp = nil
+ request(
+ #{cname}.new( path, u_header ) #{hasdata ? ',data' : ''}
+ ) do |resp|
+ resp.read_body( #{hasdest ? 'dest, &block' : ''} )
+ end
+ if @newimpl then
+ resp
+ else
+ resp.value
+ #{hasdest ? 'return resp, resp.body' : 'resp'}
+ end
end
- def klass.proxy_port
- @proxy_port
+ def #{name}2( path, #{hasdata ? 'data,' : ''}
+ u_header = nil, &block )
+ request( #{cname}.new(path, u_header),
+ #{hasdata ? 'data,' : ''} &block )
end
-
- klass
- end
-
- end
-
-
- def initialize( addr, port )
- super
- @proxy_address = type.proxy_address
- @proxy_port = type.proxy_port
+ ----
+ module_eval src, __FILE__, lineno
end
- attr_reader :proxy_address, :proxy_port
+ def_http_method :Get, true, false
+ def_http_method :Head, false, false
+ def_http_method :Post, true, true
+ def_http_method :Put, false, true
- alias proxyaddr proxy_address
- alias proxyport proxy_port
-
- def proxy?
- true
- end
-
- def connect( addr = nil, port = nil )
- super @proxy_address, @proxy_port
- end
-
- def edit_path( path )
- 'http://' + address + (port == type.port ? '' : ":#{port}") + path
+ def request( req, *args )
+ common_oper( req ) {
+ req.__send__( :exec,
+ @socket, @curr_http_version, edit_path(req.path), *args )
+ yield req.response if block_given?
+ }
+ req.response
end
-
- end
-
- end # net private
+ private
- class HTTPResponseReceiver
-
- def initialize( command, body_exist )
- @command = command
- @body_exist = body_exist
- @header = @body = nil
- end
-
- def inspect
- "#<#{type}>"
- end
- def read_header
- unless @header then
- stream_check
- @header = @command.get_response
+ def common_oper( req )
+ req['connection'] ||= 'keep-alive'
+ if not @socket then
+ start
+ req['connection'] = 'close'
+ elsif @socket.closed? then
+ re_connect
end
- @header
- end
-
- alias header read_header
- alias response read_header
-
- def read_body( dest = nil, &block )
- unless @body then
- read_header
-
- to = procdest( dest, block )
- stream_check
-
- if @body_exist and @header.code_type.body_exist? then
- @command.get_body @header, to
- @header.body = @body = to
- else
- @command.no_body
- @header.body = nil
- @body = 1
- end
+ if not req.body_exist? or @seems_1_0_server then
+ req['connection'] = 'close'
end
- @body == 1 ? nil : @body
- end
-
- alias body read_body
- alias entity read_body
+ req['host'] = addr_port
- def terminate
- read_header
- read_body
- @command = nil
- end
+ yield req
+ req.response.__send__ :terminate
+ @curr_http_version = req.response.http_version
-
- private
-
- def stream_check
- unless @command then
- raise IOError, 'receiver was used out of block'
- end
- end
-
- def procdest( dest, block )
- if dest and block then
- raise ArgumentError,
- 'both of arg and block are given for HTTP method'
- end
- if block then
- NetPrivate::ReadAdapter.new block
+ if not req.response.body then
+ @socket.close
+ elsif keep_alive? req, req.response then
+ D 'Conn keep-alive'
+ if @socket.closed? then # (only) read stream had been closed
+ D 'Conn (but seems 1.0 server)'
+ @seems_1_0_server = true
+ @socket.close
+ end
else
- dest or ''
+ D 'Conn close'
+ @socket.close
end
- end
-
- end
-
- HTTPReadAdapter = HTTPResponseReceiver
-
- class HTTPResponse < Response
-
- def initialize( code_type, code, msg )
- super
- @data = {}
- @body = nil
+ req.response
end
- attr_accessor :body
+ def keep_alive?( req, res )
+ /close/i === req['connection'].to_s and return false
+ @seems_1_0_server and return false
- def inspect
- "#<#{type.name} #{code}>"
- end
+ /keep-alive/i === res['connection'].to_s and return true
+ /close/i === res['connection'].to_s and return false
+ /keep-alive/i === res['proxy-connection'].to_s and return true
+ /close/i === res['proxy-connection'].to_s and return false
- def []( key )
- @data[ key.downcase ]
+ @curr_http_version == '1.1' and return true
+ false
end
- def []=( key, val )
- @data[ key.downcase ] = val
- end
- def each( &block )
- @data.each( &block )
- end
+ #
+ # utils
+ #
- def each_key( &block )
- @data.each_key( &block )
- end
+ public
- def each_value( &block )
- @data.each_value( &block )
+ def self.get( addr, path, port = nil )
+ req = Get.new( path )
+ resp = nil
+ new( addr, port || HTTP.port ).start {|http|
+ resp = http.request( req )
+ }
+ resp.body
end
- def delete( key )
- @data.delete key.downcase
+ def self.get_print( addr, path, port = nil )
+ print get( addr, path, port )
end
- def key?( key )
- @data.key? key.downcase
- end
- def to_hash
- @data.dup
+ private
+
+ def addr_port
+ address + (port == HTTP.port ? '' : ":#{port}")
end
- def value
- unless SuccessCode === self then
- error! self
+ def D( msg )
+ if @dout then
+ @dout << msg
+ @dout << "\n"
end
end
end
+ HTTPSession = HTTP
+
+
class Code
@@ -776,101 +651,246 @@ module Net
HTTPVersionNotSupported = HTTPServerErrorCode.http_mkchild
- module NetPrivate
+ ###
+ ### header
+ ###
- class HTTPCommand < Command
+ net_private {
- HTTPVersion = '1.1'
+ module HTTPHeader
- def initialize( sock )
- @http_version = HTTPVersion
- super sock
+ def size
+ @header.size
end
- attr_reader :http_version
+ alias length size
- def inspect
- "#<Net::HTTPCommand>"
+ def []( key )
+ @header[ key.downcase ]
end
+ def []=( key, val )
+ @header[ key.downcase ] = val
+ end
- ###
- ### request
- ###
+ def each( &block )
+ @header.each( &block )
+ end
- public
+ def each_key( &block )
+ @header.each_key( &block )
+ end
- def get( path, u_header )
- return unless begin_critical
- request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header
+ def each_value( &block )
+ @header.each_value( &block )
end
-
- def head( path, u_header )
- return unless begin_critical
- request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header
+
+ def delete( key )
+ @header.delete key.downcase
+ end
+
+ def key?( key )
+ @header.key? key.downcase
+ end
+
+ def to_hash
+ @header.dup
end
- def post( path, u_header, data )
- return unless begin_critical
- u_header[ 'Content-Length' ] = data.size.to_s
- request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header
- @socket.write data
+ def canonical_each
+ @header.each do |k,v|
+ yield canonical(k), v
+ end
end
- def put( path, u_header, src )
- return unless begin_critical
- request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header
- @socket.write_bin src
+ def canonical( k )
+ k.split('-').collect {|i| i.capitalize }.join('-')
end
- # def delete
+ def range
+ s = @header['range']
+ s or return nil
- # def trace
+ arr = []
+ s.split(',').each do |spec|
+ m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match( spec )
+ m or raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
- # def options
+ d1 = m[1].to_i
+ d2 = m[2].to_i
+ if m[1] and m[2] then arr.push (d1 .. d2)
+ elsif m[1] then arr.push (d1 .. -1)
+ elsif m[2] then arr.push (-d2 .. -1)
+ else
+ raise HTTPHeaderSyntaxError, 'range is not specified'
+ end
+ end
- def quit
+ return *arr
end
+ def range=( r, fin = nil )
+ if fin then
+ r = r ... r+fin
+ end
- private
+ case r
+ when Numeric
+ s = r > 0 ? "0-#{r - 1}" : "-#{-r}"
+ when Range
+ first = r.first
+ last = r.last
+ if r.exclude_end? then
+ last -= 1
+ end
- def request( req, u_header )
- @socket.writeline req
- u_header.each do |n,v|
- @socket.writeline n + ': ' + v
+ if last == -1 then
+ s = first > 0 ? "#{first}-" : "-#{-first}"
+ else
+ first >= 0 or raise HTTPHeaderSyntaxError, 'range.first is negative'
+ last > 0 or raise HTTPHeaderSyntaxError, 'range.last is negative'
+ first < last or raise HTTPHeaderSyntaxError, 'must be .first < .last'
+ s = "#{first}-#{last}"
+ end
+ else
+ raise TypeError, 'Range/Integer is required'
end
- @socket.writeline ''
+
+ @header['range'] = "bytes=#{s}"
+ r
end
+ alias set_range range=
- ###
- ### response line & header
- ###
+ def content_length
+ s = @header['content-length']
+ s or return nil
- public
+ m = /\d+/.match(s)
+ m or raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
+ m[0].to_i
+ end
- def get_response
- resp = get_resp0
- resp = get_resp0 while ContinueCode === resp
- resp
+ def chunked?
+ s = @header['transfer-encoding']
+ (s and /(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i === s) ? true : false
+ end
+
+ def content_range
+ s = @header['content-range']
+ s or return nil
+
+ m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>i.match( s )
+ m or raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
+
+ m[1].to_i .. m[2].to_i + 1
+ end
+
+ def range_length
+ r = content_range
+ r and r.length
+ end
+
+ def basic_auth( acc, pass )
+ @header['authorization'] =
+ 'Basic ' + ["#{acc}:#{pass}"].pack('m').gsub(/\s+/, '')
+ end
+
+ end
+
+ }
+
+
+ ###
+ ### request
+ ###
+
+ net_private {
+
+ class HTTPRequest
+
+ include ::Net::NetPrivate::HTTPHeader
+
+ def initialize( path, uhead = nil )
+ @path = path
+ @header = tmp = {}
+ return unless uhead
+ uhead.each do |k,v|
+ key = k.downcase
+ if tmp.key? key then
+ $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
+ end
+ tmp[ key ] = v.strip
+ end
+ tmp['accept'] ||= '*/*'
+
+ @socket = nil
+ @response = nil
+ end
+
+ attr_reader :path
+ attr_reader :response
+
+ def inspect
+ "\#<#{type}>"
+ end
+
+ def body_exist?
+ type::HAS_BODY
end
private
- def get_resp0
- resp = get_reply
+ #
+ # write
+ #
+
+ def exec( sock, ver, path )
+ ready( sock ) {
+ request ver, path
+ }
+ @response
+ end
+
+ def ready( sock )
+ @response = nil
+ @socket = sock
+ yield
+ @response = get_response
+ @socket = nil
+ end
+
+ def request( ver, path )
+ @socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver)
+ canonical_each do |k,v|
+ @socket.writeline k + ': ' + v
+ end
+ @socket.writeline ''
+ end
+
+ #
+ # read
+ #
+
+ def get_response
+ begin
+ resp = read_response
+ end while ContinueCode === resp
+ resp
+ end
+
+ def read_response
+ resp = get_resline
while true do
- line = @socket.readline
+ line = @socket.readuntil( "\n", true ) # ignore EOF
+ line.sub!( /\s+\z/, '' ) # don't use chop!
break if line.empty?
m = /\A([^:]+):\s*/.match( line )
- unless m then
- raise HTTPBadResponse, 'wrong header line format'
- end
+ m or raise HTTPBadResponse, 'wrong header line format'
nm = m[1]
line = m.post_match
if resp.key? nm then
@@ -883,23 +903,117 @@ module Net
resp
end
- def get_reply
+ def get_resline
str = @socket.readline
- m = /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i.match( str )
- unless m then
- raise HTTPBadResponse, "wrong status line: #{str}"
- end
- @http_version = m[1]
+ m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/i.match( str )
+ m or raise HTTPBadResponse, "wrong status line: #{str}"
+ httpver = m[1]
status = m[2]
discrip = m[3]
- code = HTTPCODE_TO_OBJ[status] ||
- HTTPCODE_CLASS_TO_OBJ[status[0,1]] ||
- UnknownCode
- HTTPResponse.new( code, status, discrip )
+ ::Net::NetPrivate::HTTPResponse.new(
+ status, discrip, @socket, type::HAS_BODY, httpver )
+ end
+
+ end
+
+
+ class HTTPRequestWithBody < HTTPRequest
+
+ private
+
+ def exec( sock, ver, path, str = nil )
+ check_arg str, block_given?
+
+ if block_given? then
+ ac = Accumulator.new
+ yield ac # must be yield, DO NOT USE block.call
+ data = ac.terminate
+ else
+ data = str
+ end
+ @header['content-length'] = data.size.to_s
+ @header.delete 'transfer-encoding'
+
+ ready( sock ) {
+ request ver, path
+ @socket.write data
+ }
+ @response
+ end
+
+ def check_arg( data, blkp )
+ if data and blkp then
+ raise ArgumentError, 'both of data and block given'
+ end
+ unless data or blkp then
+ raise ArgumentError, 'str or block required'
+ end
+ end
+
+ end
+
+
+ class Accumulator
+
+ def initialize
+ @buf = ''
+ end
+
+ def write( s )
+ @buf.concat s
+ end
+
+ alias << write
+
+ def terminate
+ ret = @buf
+ @buf = nil
+ ret
+ end
+
+ end
+
+ }
+
+
+ class HTTP
+
+ class Get < ::Net::NetPrivate::HTTPRequest
+ HAS_BODY = true
+ METHOD = 'GET'
+ end
+
+ class Head < ::Net::NetPrivate::HTTPRequest
+ HAS_BODY = false
+ METHOD = 'HEAD'
+ end
+
+ class Post < ::Net::NetPrivate::HTTPRequestWithBody
+ HAS_BODY = true
+ METHOD = 'POST'
+ end
+
+ class Put < ::Net::NetPrivate::HTTPRequestWithBody
+ HAS_BODY = true
+ METHOD = 'PUT'
end
- HTTPCODE_CLASS_TO_OBJ = {
+ end
+
+
+
+ ###
+ ### response
+ ###
+
+ net_private {
+
+ class HTTPResponse < Response
+
+ include ::Net::NetPrivate::HTTPHeader
+
+ CODE_CLASS_TO_OBJ = {
'1' => HTTPInformationCode,
'2' => HTTPSuccessCode,
'3' => HTTPRedirectionCode,
@@ -907,7 +1021,7 @@ module Net
'5' => HTTPServerErrorCode
}
- HTTPCODE_TO_OBJ = {
+ CODE_TO_OBJ = {
'100' => ContinueCode,
'101' => HTTPSwitchProtocol,
@@ -951,22 +1065,87 @@ module Net
'505' => HTTPVersionNotSupported
}
+ def initialize( stat, msg, sock, be, hv )
+ code = CODE_TO_OBJ[stat] ||
+ CODE_CLASS_TO_OBJ[stat[0,1]] ||
+ UnknownCode
+ super code, stat, msg
+ @socket = sock
+ @body_exist = be
+ @http_version = hv
- ###
- ### body
- ###
+ @header = {}
+ @body = nil
+ @read = false
+ end
- public
+ attr_reader :http_version
+
+ def inspect
+ "#<#{type} #{code}>"
+ end
+
+ def value
+ SuccessCode === self or error! self
+ end
- def get_body( resp, dest )
- if chunked? resp then
+
+ #
+ # header (for backward compatibility)
+ #
+
+ def read_header
+ self
+ end
+
+ alias header read_header
+ alias response read_header
+
+ #
+ # body
+ #
+
+ def read_body( dest = nil, &block )
+ if @read and (dest or block) then
+ raise IOError, "#{type}\#read_body called twice with argument"
+ end
+
+ unless @read then
+ to = procdest( dest, block )
+ stream_check
+
+ if @body_exist and code_type.body_exist? then
+ read_body_0 to
+ @body = to
+ else
+ @body = nil
+ end
+ @read = true
+ end
+
+ @body
+ end
+
+ alias body read_body
+ alias entity read_body
+
+
+ private
+
+
+ def terminate
+ read_body
+ end
+
+ def read_body_0( dest )
+ if chunked? then
read_chunked dest
else
- clen = content_length( resp )
+ clen = content_length
if clen then
- @socket.read clen, dest
+ @socket.read clen, dest, true # ignore EOF
else
- clen = range_length( resp )
+ clen = range_length
if clen then
@socket.read clen, dest
else
@@ -974,16 +1153,8 @@ module Net
end
end
end
- end_critical
- end
-
- def no_body
- end_critical
end
-
- private
-
def read_chunked( dest )
len = nil
total = 0
@@ -991,9 +1162,7 @@ module Net
while true do
line = @socket.readline
m = /[0-9a-fA-F]+/.match( line )
- unless m then
- raise HTTPBadResponse, "wrong chunk size line: #{line}"
- end
+ m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
len = m[0].hex
break if len == 0
@socket.read( len, dest ); total += len
@@ -1004,44 +1173,27 @@ module Net
end
end
- def content_length( resp )
- if resp.key? 'content-length' then
- m = /\d+/.match( resp['content-length'] )
- unless m then
- raise HTTPBadResponse, 'wrong Content-Length format'
- end
- m[0].to_i
- else
- nil
- end
+ def stream_check
+ @socket.closed? and raise IOError, 'try to read body out of block'
end
- def chunked?( resp )
- tmp = resp['transfer-encoding']
- tmp and /(?:\A|\s+)chunked(?:\s+|\z)/i === tmp
- end
-
- def range_length( resp )
- if resp.key? 'content-range' then
- m = %r<bytes\s+(\d+)-(\d+)/\d+>.match( resp['content-range'] )
- unless m then
- raise HTTPBadResponse, 'wrong Content-Range format'
- end
- l = m[2].to_i
- u = m[1].to_i
- if l > u then
- nil
- else
- u - l
- end
+ def procdest( dest, block )
+ if dest and block then
+ raise ArgumentError, 'both of arg and block are given for HTTP method'
+ end
+ if block then
+ ::Net::NetPrivate::ReadAdapter.new block
else
- nil
+ dest || ''
end
end
end
+ }
+
- end # module Net::NetPrivate
+ HTTPResponse = NetPrivate::HTTPResponse
+ HTTPResponseReceiver = NetPrivate::HTTPResponse
end # module Net
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 34d324ed12..ea064d2552 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -7,9 +7,10 @@ Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
-== class Net::IMAP
+== Net::IMAP
Net::IMAP implements Internet Message Access Protocol (IMAP) clients.
+(The protocol is described in ((<[IMAP]>)).)
=== Super Class
@@ -102,21 +103,24 @@ Object
: list(refname, mailbox)
Sends a LIST command, and returns a subset of names from
the complete set of all names available to the client.
+ The return value is an array of ((<Net::IMAP::MailboxList>)).
ex).
imap.create("foo/bar")
imap.create("foo/baz")
p imap.list("", "foo/%")
- #=> [#<Net::IMAP::MailboxList attr=[:NoSelect], delim="/", name="foo/">, #<Net::IMAP::MailboxList attr=[:NoInferiors, :Marked], delim="/", name="foo/bar">, #<Net::IMAP::MailboxList attr=[:NoInferiors], delim="/", name="foo/baz">]
+ #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
: lsub(refname, mailbox)
Sends a LSUB command, and returns a subset of names from the set
of names that the user has declared as being "active" or
"subscribed".
+ The return value is an array of ((<Net::IMAP::MailboxList>)).
: status(mailbox, attr)
Sends a STATUS command, and returns the status of the indicated
mailbox.
+ The return value is a hash of attributes.
ex).
p imap.status("inbox", ["MESSAGES", "RECENT"])
@@ -166,6 +170,7 @@ Object
in the mailbox. the set parameter is a number or an array of
numbers or a Range object. the number is a message sequence
number (fetch) or a unique identifier (uid_fetch).
+ The return value is an array of ((<Net::IMAP::FetchData>)).
ex).
p imap.fetch(6..8, "UID")
@@ -188,6 +193,7 @@ Object
in the mailbox. the set parameter is a number or an array of
numbers or a Range object. the number is a message sequence
number (store) or a unique identifier (uid_store).
+ The return value is an array of ((<Net::IMAP::FetchData>)).
ex).
p imap.store(6..8, "+FLAGS", [:Deleted])
@@ -210,6 +216,443 @@ Object
p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
#=> [6, 7, 8, 1]
+== Net::IMAP::ContinuationRequest
+
+Net::IMAP::ContinuationRequest represents command continuation requests.
+
+The command continuation request response is indicated by a "+" token
+instead of a tag. This form of response indicates that the server is
+ready to accept the continuation of a command from the client. The
+remainder of this response is a line of text.
+
+ continue_req ::= "+" SPACE (resp_text / base64)
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: data
+ Returns the data (Net::IMAP::ResponseText).
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::UntaggedResponse
+
+Net::IMAP::UntaggedResponse represents untagged responses.
+
+Data transmitted by the server to the client and status responses
+that do not indicate command completion are prefixed with the token
+"*", and are called untagged responses.
+
+ response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
+ mailbox_data / message_data / capability_data)
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: name
+ Returns the name such as "FLAGS", "LIST", "FETCH"....
+
+: data
+ Returns the data such as an array of flag symbols,
+ a ((<Net::IMAP::MailboxList>)) object....
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::TaggedResponse
+
+Net::IMAP::TaggedResponse represents tagged responses.
+
+The server completion result response indicates the success or
+failure of the operation. It is tagged with the same tag as the
+client command which began the operation.
+
+ response_tagged ::= tag SPACE resp_cond_state CRLF
+
+ tag ::= 1*<any ATOM_CHAR except "+">
+
+ resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: tag
+ Returns the tag.
+
+: name
+ Returns the name. the name is one of "OK", "NO", "BAD".
+
+: data
+ Returns the data. See ((<Net::IMAP::ResponseText>)).
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::ResponseText
+
+Net::IMAP::ResponseText represents texts of responses.
+The text may be prefixed by the response code.
+
+ resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
+ ;; text SHOULD NOT begin with "[" or "="
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: code
+ Returns the response code. See ((<Net::IMAP::ResponseCode>)).
+
+: text
+ Returns the text.
+
+== Net::IMAP::ResponseCode
+
+Net::IMAP::ResponseCode represents response codes.
+
+ resp_text_code ::= "ALERT" / "PARSE" /
+ "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
+ "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
+ "UIDVALIDITY" SPACE nz_number /
+ "UNSEEN" SPACE nz_number /
+ atom [SPACE 1*<any TEXT_CHAR except "]">]
+
+=== SuperClass
+
+Struct
+
+=== Methods
+
+: name
+ Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY"....
+
+: data
+ Returns the data if it exists.
+
+== Net::IMAP::MailboxList
+
+Net::IMAP::MailboxList represents contents of the LIST response.
+
+ mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
+ "\Noselect" / "\Unmarked" / flag_extension) ")"
+ SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: attr
+ Returns the name attributes. Each name attribute is a symbol
+ capitalized by String#capitalize, such as :Noselect (not :NoSelect).
+
+: delim
+ Returns the hierarchy delimiter
+
+: name
+ Returns the mailbox name.
+
+== Net::IMAP::StatusData
+
+Net::IMAP::StatusData represents contents of the STATUS response.
+
+=== Super Class
+
+Object
+
+=== Methods
+
+: mailbox
+ Returns the mailbox name.
+
+: attr
+ Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT",
+ "UIDVALIDITY", "UNSEEN". Each value is a number.
+
+== Net::IMAP::FetchData
+
+Net::IMAP::FetchData represents contents of the FETCH response.
+
+=== Super Class
+
+Object
+
+=== Methods
+
+: seqno
+ Returns the message sequence number.
+ (Note: not the unique identifier, even for the UID command response.)
+
+: attr
+ Returns a hash. Each key is a data item name, and each value is
+ its value.
+
+ The current data items are:
+
+ : BODY
+ A form of BODYSTRUCTURE without extension data.
+ : BODY[<section>]<<origin_octet>>
+ A string expressing the body contents of the specified section.
+ : BODYSTRUCTURE
+ An object that describes the ((<[MIME-IMB]>)) body structure of a message.
+ See ((<Net::IMAP::BodyTypeBasic>)), ((<Net::IMAP::BodyTypeText>)),
+ ((<Net::IMAP::BodyTypeMessage>)), ((<Net::IMAP::BodyTypeMultipart>)).
+ : ENVELOPE
+ A ((<Net::IMAP::Envelope>)) object that describes the envelope
+ structure of a message.
+ : FLAGS
+ A array of flag symbols that are set for this message. flag symbols
+ are capitalized by String#capitalize.
+ : INTERNALDATE
+ A string representing the internal date of the message.
+ : RFC822
+ Equivalent to BODY[].
+ : RFC822.HEADER
+ Equivalent to BODY.PEEK[HEADER].
+ : RFC822.SIZE
+ A number expressing the ((<[RFC-822]>)) size of the message.
+ : RFC822.TEXT
+ Equivalent to BODY[TEXT].
+ : UID
+ A number expressing the unique identifier of the message.
+
+== Net::IMAP::Envelope
+
+Net::IMAP::Envelope represents envelope structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: date
+ Retunns a string that represents the date.
+
+: subject
+ Retunns a string that represents the subject.
+
+: from
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the from.
+
+: sender
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the sender.
+
+: reply_to
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the reply-to.
+
+: to
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the to.
+
+: cc
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the cc.
+
+: bcc
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the bcc.
+
+: in_reply_to
+ Retunns a string that represents the in-reply-to.
+
+: message_id
+ Retunns a string that represents the message-id.
+
+== Net::IMAP::Address
+
+((<Net::IMAP::Address>)) represents electronic mail addresses.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: name
+ Returns the phrase from ((<[RFC-822]>)) mailbox.
+
+: route
+ Returns the route from ((<[RFC-822]>)) route-addr.
+
+: mailbox
+ nil indicates end of ((<[RFC-822]>)) group.
+ If non-nil and host is nil, returns ((<[RFC-822]>)) group name.
+ Otherwise, returns ((<[RFC-822]>)) local-part
+
+: host
+ nil indicates ((<[RFC-822]>)) group syntax.
+ Otherwise, returns ((<[RFC-822]>)) domain name.
+
+== Net::IMAP::ContentDisposition
+
+Net::IMAP::ContentDisposition represents Content-Disposition fields.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: dsp_type
+ Returns the disposition type.
+
+: param
+ Returns a hash that represents parameters of the Content-Disposition
+ field.
+
+== Net::IMAP::BodyTypeBasic
+
+Net::IMAP::BodyTypeBasic represents basic body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: media_type
+ Returns the content media type name as defined in ((<[MIME-IMB]>)).
+
+: subtype
+ Returns the content subtype name as defined in ((<[MIME-IMB]>)).
+
+: param
+ Returns a hash that represents parameters as defined in
+ ((<[MIME-IMB]>)).
+
+: content_id
+ Returns a string giving the content id as defined in ((<[MIME-IMB]>)).
+
+: description
+ Returns a string giving the content description as defined in
+ ((<[MIME-IMB]>)).
+
+: encoding
+ Returns a string giving the content transfer encoding as defined in
+ ((<[MIME-IMB]>)).
+
+: size
+ Returns a number giving the size of the body in octets.
+
+: md5
+ Returns a string giving the body MD5 value as defined in ((<[MD5]>)).
+
+: disposition
+ Returns a ((<Net::IMAP::ContentDisposition>)) object giving
+ the content disposition.
+
+: language
+ Returns a string or an array of strings giving the body
+ language value as defined in [LANGUAGE-TAGS].
+
+: extension
+ Returns extension data.
+
+: multipart?
+ Returns false.
+
+== Net::IMAP::BodyTypeText
+
+Net::IMAP::BodyTypeText represents TEXT body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: lines
+ Returns the size of the body in text lines.
+
+And Net::IMAP::BodyTypeText has all methods of ((<Net::IMAP::BodyTypeBasic>)).
+
+== Net::IMAP::BodyTypeMessage
+
+Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: envelope
+ Returns a ((<Net::IMAP::Envelope>)) giving the envelope structure.
+
+: body
+ Returns an object giving the body structure.
+
+And Net::IMAP::BodyTypeMessage has all methods of ((<Net::IMAP::BodyTypeText>)).
+
+== Net::IMAP::BodyTypeText
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: media_type
+ Returns the content media type name as defined in ((<[MIME-IMB]>)).
+
+: subtype
+ Returns the content subtype name as defined in ((<[MIME-IMB]>)).
+
+: parts
+ Returns multiple parts.
+
+: param
+ Returns a hash that represents parameters as defined in
+ ((<[MIME-IMB]>)).
+
+: disposition
+ Returns a ((<Net::IMAP::ContentDisposition>)) object giving
+ the content disposition.
+
+: language
+ Returns a string or an array of strings giving the body
+ language value as defined in [LANGUAGE-TAGS].
+
+: extension
+ Returns extension data.
+
+: multipart?
+ Returns true.
+
+== References
+
+: [IMAP]
+ M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1",
+ RFC 2060, December 1996.
+
+: [LANGUAGE-TAGS]
+ Alvestrand, H., "Tags for the Identification of
+ Languages", RFC 1766, March 1995.
+
+: [MD5]
+ Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC
+ 1864, October 1995.
+
+: [MIME-IMB]
+ Freed, N., and N. Borenstein, "MIME (Multipurpose Internet
+ Mail Extensions) Part One: Format of Internet Message Bodies", RFC
+ 2045, November 1996.
+
+: [RFC-822]
+ Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, University of Delaware, August 1982.
+
=end
require "socket"
@@ -702,7 +1145,7 @@ module Net
Address = Struct.new(:name, :route, :mailbox, :host)
ContentDisposition = Struct.new(:dsp_type, :param)
- class BodyTypeBasic < Struct.new(:media_type, :media_subtype,
+ class BodyTypeBasic < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:md5, :disposition, :language,
@@ -710,9 +1153,15 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeText < Struct.new(:media_type, :media_subtype,
+ class BodyTypeText < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:lines,
@@ -721,9 +1170,15 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeMessage < Struct.new(:media_type, :media_subtype,
+ class BodyTypeMessage < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:envelope, :body, :lines,
@@ -732,15 +1187,27 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeMultipart < Struct.new(:media_type, :media_subtype,
+ class BodyTypeMultipart < Struct.new(:media_type, :subtype,
:parts,
:param, :disposition, :language,
:extension)
def multipart?
return true
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
class ResponseParser
@@ -1475,6 +1942,12 @@ module Net
when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n
match(T_SPACE)
result = ResponseCode.new(name, number)
+ else
+ match(T_SPACE)
+ @lex_state = EXPR_CTEXT
+ token = match(T_TEXT)
+ @lex_state = EXPR_BEG
+ result = ResponseCode.new(name, token.value)
end
match(T_RBRA)
@lex_state = EXPR_RTEXT
@@ -1579,7 +2052,7 @@ module Net
if @str.index(/\(([^)]*)\)/ni, @pos)
@pos = $~.end(0)
return $1.scan(FLAG_REGEXP).collect { |flag, atom|
- atom || flag.intern
+ atom || flag.capitalize.intern
}
else
parse_error("invalid flag list")
diff --git a/lib/net/pop.rb b/lib/net/pop.rb
index 4f6eb930a4..8f3f978e8c 100644
--- a/lib/net/pop.rb
+++ b/lib/net/pop.rb
@@ -1,6 +1,6 @@
=begin
-= net/pop.rb version 1.1.32
+= net/pop.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -184,6 +184,7 @@ module Net
protocol_param :port, '110'
protocol_param :command_type, '::Net::NetPrivate::POP3Command'
+ protocol_param :apop_command_type, '::Net::NetPrivate::APOPCommand'
protocol_param :mail_type, '::Net::POPMail'
@@ -206,9 +207,10 @@ module Net
end
- def initialize( addr = nil, port = nil )
- super
+ def initialize( addr = nil, port = nil, apop = false )
+ super addr, port
@mails = nil
+ @apop = false
end
attr :mails
@@ -238,6 +240,11 @@ module Net
private
+ def conn_command( sock )
+ @command =
+ (@apop ? type.apop_command_type : type.command_type).new(sock)
+ end
+
def do_start( acnt, pwd )
@command.auth( acnt, pwd )
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 161024cfe2..343721add3 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -1,6 +1,6 @@
=begin
-= net/protocol.rb version 1.1.32
+= net/protocol.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -59,13 +59,22 @@ Object
=end
require 'socket'
+require 'timeout'
module Net
+ module NetPrivate
+ end
+
+ def self.net_private( &block )
+ ::Net::NetPrivate.module_eval( &block )
+ end
+
+
class Protocol
- Version = '1.1.32'
+ Version = '1.1.34'
class << self
@@ -116,8 +125,12 @@ module Net
@command = nil
@socket = nil
- @active = false
- @pipe = nil
+ @active = false
+
+ @open_timeout = nil
+ @read_timeout = nil
+
+ @dout = nil
end
attr_reader :address
@@ -126,10 +139,26 @@ module Net
attr_reader :command
attr_reader :socket
+ attr_accessor :open_timeout
+ attr_accessor :read_timeout
+
+ def active?
+ @active
+ end
+
+ def set_debug_output( arg ) # un-documented
+ @dout = arg
+ end
+
+ alias set_pipe set_debug_output
+
def inspect
"#<#{type} #{address}:#{port} open=#{active?}>"
end
+ #
+ # open session
+ #
def start( *args )
return false if active?
@@ -146,45 +175,59 @@ module Net
end
end
+ private
+
def _start( args )
connect
do_start( *args )
@active = true
end
- private :_start
-
- def finish
- return false unless active?
- do_finish unless @command.critical?
- disconnect
- @active = false
- true
+ def connect
+ conn_socket @address, @port
+ conn_command @socket
+ on_connect
end
- def active?
- @active
+ def re_connect
+ @socket.reopen @open_timeout
+ on_connect
end
- def set_pipe( arg ) # un-documented
- @pipe = arg
+ def conn_socket( addr, port )
+ @socket = type.socket_type.open(
+ addr, port, @open_timeout, @read_timeout, @dout )
end
+ def conn_command( sock )
+ @command = type.command_type.new( sock )
+ end
- private
-
+ def on_connect
+ end
def do_start
end
- def do_finish
- @command.quit
+ #
+ # close session
+ #
+
+ public
+
+ def finish
+ return false unless active?
+
+ do_finish if @command and not @command.critical?
+ disconnect
+ @active = false
+ true
end
+ private
- def connect( addr = @address, port = @port )
- @socket = type.socket_type.open( addr, port, @pipe )
- @command = type.command_type.new( @socket )
+ def do_finish
+ @command.quit
end
def disconnect
@@ -192,7 +235,11 @@ module Net
if @socket and not @socket.closed? then
@socket.close
end
- @socket = nil
+ @socket = nil
+ on_disconnect
+ end
+
+ def on_disconnect
end
end
@@ -200,6 +247,7 @@ module Net
Session = Protocol
+ net_private {
class Response
@@ -223,6 +271,8 @@ module Net
end
+ }
+
class ProtocolError < StandardError; end
class ProtoSyntaxError < ProtocolError; end
@@ -294,8 +344,7 @@ module Net
- module NetPrivate
-
+ net_private {
class WriteAdapter
@@ -311,7 +360,11 @@ module Net
def write( str )
@sock.__send__ @mid, str
end
- alias << write
+
+ def <<( str )
+ @sock.__send__ @mid, str
+ self
+ end
end
@@ -407,6 +460,7 @@ module Net
@critical = false
end
+
private
def critical
@@ -431,22 +485,30 @@ module Net
class Socket
- def initialize( addr, port, pipe = nil )
+ def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
@addr = addr
@port = port
- @pipe = pipe
- @prepipe = nil
- @closed = true
- @ipaddr = ''
+ @read_timeout = rtime
+
+ @debugout = dout
+
+ @socket = nil
@sending = ''
@buffer = ''
- @socket = TCPsocket.new( addr, port )
- @closed = false
- @ipaddr = @socket.addr[3]
+ connect otime
+ D 'opened'
end
+ def connect( otime )
+ D "opening connection to #{@addr}..."
+ timeout( otime ) {
+ @socket = TCPsocket.new( @addr, @port )
+ }
+ end
+ private :connect
+
attr :pipe, true
class << self
@@ -454,27 +516,31 @@ module Net
end
def inspect
- "#<#{type} open=#{!@closed}>"
+ "#<#{type} #{closed? ? 'closed' : 'opened'}>"
end
- def reopen
- unless closed? then
- close
- @buffer = ''
- end
- @socket = TCPsocket.new( @addr, @port )
- @closed = false
+ def reopen( otime = nil )
+ D 'reopening...'
+ close
+ connect otime
+ D 'reopened'
end
attr :socket, true
def close
- @socket.close
- @closed = true
+ if @socket then
+ @socket.close
+ D 'closed'
+ else
+ D 'close call for already closed socket'
+ end
+ @socket = nil
+ @buffer = ''
end
def closed?
- @closed
+ not @socket
end
def address
@@ -486,7 +552,8 @@ module Net
attr_reader :port
def ip_address
- @ipaddr.dup
+ @socket or return ''
+ @socket.addr[3]
end
alias ipaddr ip_address
@@ -494,57 +561,64 @@ module Net
attr_reader :sending
- ###
- ### read
- ###
+ #
+ # read
+ #
+
+ public
CRLF = "\r\n"
- def read( len, dest = '' )
- @pipe << "reading #{len} bytes...\n" if @pipe; pipeoff
+ def read( len, dest = '', ignerr = false )
+ D_off "reading #{len} bytes..."
rsize = 0
- while rsize + @buffer.size < len do
- rsize += writeinto( dest, @buffer.size )
- fill_rbuf
+ begin
+ while rsize + @buffer.size < len do
+ rsize += rbuf_moveto( dest, @buffer.size )
+ rbuf_fill
+ end
+ rbuf_moveto dest, len - rsize
+ rescue EOFError
+ raise unless igneof
end
- writeinto( dest, len - rsize )
- @pipe << "read #{len} bytes\n" if pipeon
+ D_on "read #{len} bytes"
dest
end
-
def read_all( dest = '' )
- @pipe << "reading all...\n" if @pipe; pipeoff
+ D_off 'reading all...'
rsize = 0
begin
while true do
- rsize += writeinto( dest, @buffer.size )
- fill_rbuf
+ rsize += rbuf_moveto( dest, @buffer.size )
+ rbuf_fill
end
rescue EOFError
;
end
- @pipe << "read #{rsize} bytes\n" if pipeon
+ D_on "read #{rsize} bytes"
dest
end
-
- def readuntil( target )
- while true do
- idx = @buffer.index( target )
- break if idx
- fill_rbuf
- end
-
+ def readuntil( target, igneof = false )
dest = ''
- writeinto( dest, idx + target.size )
+ begin
+ while true do
+ idx = @buffer.index( target )
+ break if idx
+ rbuf_fill
+ end
+ rbuf_moveto dest, idx + target.size
+ rescue EOFError
+ raise unless igneof
+ rbuf_moveto dest, @buffer.size
+ end
dest
end
-
def readline
ret = readuntil( "\n" )
@@ -552,9 +626,8 @@ module Net
ret
end
-
def read_pendstr( dest )
- @pipe << "reading text...\n" if @pipe; pipeoff
+ D_off 'reading text...'
rsize = 0
while (str = readuntil("\r\n")) != ".\r\n" do
@@ -563,14 +636,13 @@ module Net
dest << str
end
- @pipe << "read #{rsize} bytes\n" if pipeon
+ D_on "read #{rsize} bytes"
dest
end
-
# private use only (can not handle 'break')
def read_pendlist
- @pipe << "reading list...\n" if @pipe; pipeoff
+ D_off 'reading list...'
str = nil
i = 0
@@ -580,55 +652,59 @@ module Net
yield str
end
- @pipe << "read #{i} items\n" if pipeon
+ D_on "read #{i} items"
end
private
- READ_BLOCK = 1024 * 8
+ READ_SIZE = 1024 * 4
- def fill_rbuf
- @buffer << @socket.sysread( READ_BLOCK )
+ def rbuf_fill
+ unless IO.select [@socket], nil, nil, @read_timeout then
+ on_read_timeout
+ end
+ @buffer << @socket.sysread( READ_SIZE )
end
- def writeinto( dest, len )
+ def on_read_timeout
+ raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
+ end
+
+ def rbuf_moveto( dest, len )
bsi = @buffer.size
- dest << @buffer[ 0, len ]
+ s = @buffer[ 0, len ]
+ dest << s
@buffer = @buffer[ len, bsi - len ]
- @pipe << %{read "#{Net.quote dest}"\n} if @pipe
+ @debugout << %<read "#{Net.quote s}"\n> if @debugout
len
end
- ###
- ### write
- ###
+ #
+ # write interfece
+ #
public
-
def write( str )
writing {
do_write str
}
end
-
def writeline( str )
writing {
- do_write str
- do_write "\r\n"
+ do_write str + "\r\n"
}
end
-
def write_bin( src, block )
writing {
if block then
- block.call WriteAdapter.new( self, :do_write )
+ block.call ::Net::NetPrivate::WriteAdapter.new( self, :do_write )
else
src.each do |bin|
do_write bin
@@ -637,19 +713,18 @@ module Net
}
end
-
def write_pendstr( src, block )
- @pipe << "writing text from #{src.type}\n" if @pipe; pipeoff
+ D_off "writing text from #{src.type}"
wsize = use_each_crlf_line {
if block then
- block.call WriteAdapter.new( self, :wpend_in )
+ block.call ::Net::NetPrivate::WriteAdapter.new( self, :wpend_in )
else
wpend_in src
end
}
- @pipe << "wrote #{wsize} bytes text\n" if pipeon
+ D_on "wrote #{wsize} bytes text"
wsize
end
@@ -696,17 +771,17 @@ module Net
beg = 0
buf = @wbuf
while buf.index( /\n|\r\n|\r/, beg ) do
- m = $~
+ m = Regexp.last_match
if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then
# "...\r" : can follow "\n..."
break
end
- str = buf[ beg, m.begin(0) - beg ]
+ str = buf[ beg ... m.begin(0) ]
str.concat "\r\n"
yield str
beg = m.end(0)
end
- @wbuf = buf[ beg, buf.size - beg ]
+ @wbuf = buf[ beg ... buf.size ]
end
end
@@ -736,6 +811,7 @@ module Net
yield
end
end
+ yield unless @wbuf.empty?
end
end
@@ -746,17 +822,17 @@ module Net
yield
- if @pipe then
- @pipe << 'write "'
- @pipe << @sending
- @pipe << "\"\n"
+ if @debugout then
+ @debugout << 'write "'
+ @debugout << @sending
+ @debugout << "\"\n"
end
@socket.flush
@writtensize
end
def do_write( arg )
- if @pipe or @sending.size < 128 then
+ if @debugout or @sending.size < 128 then
@sending << Net.quote( arg )
else
@sending << '...' unless @sending[-1] == ?.
@@ -768,22 +844,25 @@ module Net
end
- def pipeoff
- @prepipe = @pipe
- @pipe = nil
- @prepipe
+ def D_off( msg )
+ D msg
+ @savedo, @debugout = @debugout, nil
end
- def pipeon
- @pipe = @prepipe
- @prepipe = nil
- @pipe
+ def D_on( msg )
+ @debugout = @savedo
+ D msg
end
- end
+ def D( msg )
+ @debugout or return
+ @debugout << msg
+ @debugout << "\n"
+ end
+ end
- end # module Net::NetPrivate
+ }
def Net.quote( str )
diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb
index 9679984e2c..befc1adf03 100644
--- a/lib/net/smtp.rb
+++ b/lib/net/smtp.rb
@@ -1,6 +1,6 @@
=begin
-= net/smtp.rb version 1.1.32
+= net/smtp.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -30,10 +30,8 @@ Net::Protocol
=== Methods
-: start( helo_domain = Socket.gethostname, \
- account = nil, password = nil, authtype = nil )
-: start( helo_domain = Socket.gethostname, \
- account = nil, password = nil, authtype = nil ) {|smtp| .... }
+: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil )
+: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... }
opens TCP connection and starts SMTP session.
If protocol had been started, do nothing and return false.
@@ -53,10 +51,10 @@ Net::Protocol
to_addrs must be a String(s) or an Array of String.
Exceptions which SMTP raises are:
- * Net::ProtoSyntaxError: syntax error (errno.500)
- * Net::ProtoFatalError: fatal error (errno.550)
- * Net::ProtoUnknownError: unknown error
- * Net::ProtoServerBusy: temporary error (errno.420/450)
+ * Net::ProtoSyntaxError: syntax error (errno.500)
+ * Net::ProtoFatalError: fatal error (errno.550)
+ * Net::ProtoUnknownError: unknown error
+ * Net::ProtoServerBusy: temporary error (errno.420/450)
# usage example
@@ -153,12 +151,15 @@ module Net
end
end
- if user and secret then
+ if user or secret then
+ (user and secret) or
+ raise ArgumentError, 'both of account and password are required'
+
mid = 'auth_' + (authtype || 'cram_md5').to_s
- unless @command.respond_to? mid then
- raise ArgumentError, "wrong auth type #{authtype.to_s}"
- end
- @command.send mid, user, secret
+ @command.respond_to? mid or
+ raise ArgumentError, "wrong auth type #{authtype.to_s}"
+
+ @command.__send__ mid, user, secret
end
end
diff --git a/lib/net/telnet.rb b/lib/net/telnet.rb
index 87790c0300..380e834bea 100644
--- a/lib/net/telnet.rb
+++ b/lib/net/telnet.rb
@@ -4,7 +4,7 @@
net/telnet.rb - simple telnet client library
-Version 1.6.2
+Version 1.6.3
Wakou Aoyama <wakou@fsinet.or.jp>
@@ -239,10 +239,11 @@ module Net
CR = "\015"
LF = "\012"
EOL = CR + LF
- VERSION = "1.6.2"
- RELEASE_DATE = "2000-12-25"
- VERSION_CODE = 162
- RELEASE_CODE = 20001225
+ VERSION = '1.6.3'
+ RELEASE_DATE = '2001-02-26'
+ VERSION_CODE = 163
+ RELEASE_CODE = 20010226
+ REVISION = '$Id$'
def initialize(options)
@options = options
@@ -346,14 +347,13 @@ module Net
attr :sock
def telnetmode(mode = nil)
- if mode
- if (true == mode or false == mode)
- @options["Telnetmode"] = mode
- else
- raise ArgumentError, "required true or false"
- end
- else
+ case mode
+ when nil
@options["Telnetmode"]
+ when true, false
+ @options["Telnetmode"] = mode
+ else
+ raise ArgumentError, "required true or false"
end
end
@@ -366,14 +366,13 @@ module Net
end
def binmode(mode = nil)
- if mode
- if (true == mode or false == mode)
- @options["Binmode"] = mode
- else
- raise ArgumentError, "required true or false"
- end
- else
+ case mode
+ when nil
@options["Binmode"]
+ when true, false
+ @options["Binmode"] = mode
+ else
+ raise ArgumentError, "required true or false"
end
end
@@ -599,181 +598,7 @@ end
== HISTORY
-* Mon Dec 25 01:37:43 JST 2000 - wakou
- * version 1.6.2
- * Regexp::last_match[1] --> $1
-
-* Mon Dec 11 00:16:51 JST 2000 - wakou
- * version 1.6.1
- * $1 --> Regexp::last_match[1]
-
-* 2000/09/12 05:37:35 - matz
- * change: iterator? --> block_given?
-
-* Tue Sep 12 06:52:48 JST 2000 - wakou
- * version 1.6.0
- * correct: document.
- thanks to Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
- * add: Telnet#puts().
-
-* Sun Jun 18 23:31:44 JST 2000 - wakou
- * version 1.5.0
- * change: version syntax. old: x.yz, now: x.y.z
-
-* 2000/05/24 06:57:38 - wakou
- * version 1.40
- * improve: binmode(), telnetmode() interface.
- thanks to Dave Thomas <Dave@thomases.com>
-
-* 2000/05/09 22:02:56 - wakou
- * version 1.32
- * require English.rb
-
-* 2000/05/02 21:48:39 - wakou
- * version 1.31
- * Proxy option: can receive IO object.
-
-* 2000/04/03 18:27:02 - wakou
- * version 1.30
- * telnet.rb --> net/telnet.rb
-
-* 2000/01/24 17:02:57 - wakou
- * version 1.20
- * respond to "IAC WILL x" with "IAC DONT x"
- * respond to "IAC WONT x" with "IAC DONT x"
- * better dumplog format.
- thanks to WATANABE Hirofumi <Hirofumi.Watanabe@jp.sony.com>
-
-* 2000/01/18 17:47:31 - wakou
- * version 1.10
- * bug fix: write method
- * respond to "IAC WILL BINARY" with "IAC DO BINARY"
-
-* 1999/10/04 22:51:26 - wakou
- * version 1.00
- * bug fix: waitfor(preprocess) method.
- thanks to Shin-ichiro Hara <sinara@blade.nagaokaut.ac.jp>
- * add simple support for AO, DM, IP, NOP, SB, SE
- * COUTION! TimeOut --> TimeoutError
-
-* 1999/09/21 21:24:07 - wakou
- * version 0.50
- * add write method
-
-* 1999/09/17 17:41:41 - wakou
- * version 0.40
- * bug fix: preprocess method
-
-* 1999/09/14 23:09:05 - wakou
- * version 0.30
- * change prompt check order.
- not IO::select([@sock], nil, nil, waittime) and prompt === line
- --> prompt === line and not IO::select([@sock], nil, nil, waittime)
-
-* 1999/09/13 22:28:33 - wakou
- * version 0.24
- * Telnet#login: if ommit password, then not require password prompt.
-
-* 1999/08/10 05:20:21 - wakou
- * version 0.232
- * STATUS OUTPUT sample code typo.
- thanks to Tadayoshi Funaba <tadf@kt.rim.or.jp>
- host = Telnet.new({"Hosh" => "localhost"){|c| print c }
- --> host = Telnet.new({"Host" => "localhost"){|c| print c }
-
-* 1999/07/16 13:39:42 - wakou
- * version 0.231
- * TRUE --> true, FALSE --> false
-
-* 1999/07/15 22:32:09 - wakou
- * version 0.23
- * waitfor: if end of file reached, then return nil.
-
-* 1999/06/29 09:08:51 - wakou
- * version 0.22
- * new, waitfor, cmd: {"Timeout" => false} # ignore timeout
-
-* 1999/06/28 18:18:55 - wakou
- * version 0.21
- * waitfor: not rescue (EOFError)
-
-* 1999/06/04 06:24:58 - wakou
- * version 0.20
- * waitfor: support for divided telnet command
-
-* 1999/05/22 - wakou
- * version 0.181
- * bug fix: print method
-
-* 1999/05/14 - wakou
- * version 0.18
- * respond to "IAC WON'T SGA" with "IAC DON'T SGA"
- * DON'T SGA : end of line --> CR + LF
- * bug fix: preprocess method
-
-* 1999/04/30 - wakou
- * version 0.17
- * bug fix: $! + "\n" --> $!.to_s + "\n"
-
-* 1999/04/11 - wakou
- * version 0.163
- * STDOUT.write(message) --> yield(message) if iterator?
-
-* 1999/03/17 - wakou
- * version 0.162
- * add "Proxy" option
- * required timeout.rb
-
-* 1999/02/03 - wakou
- * version 0.161
- * select --> IO::select
-
-* 1998/10/09 - wakou
- * version 0.16
- * preprocess method change for the better
- * add binmode method.
- * change default Binmode. TRUE --> FALSE
-
-* 1998/10/04 - wakou
- * version 0.15
- * add telnetmode method.
-
-* 1998/09/22 - wakou
- * version 0.141
- * change default prompt. /[$%#>] $/ --> /[$%#>] \Z/
-
-* 1998/09/01 - wakou
- * version 0.14
- * IAC WILL SGA send EOL --> CR+NULL
- * IAC WILL SGA IAC DO BIN send EOL --> CR
- * NONE send EOL --> LF
- * add Dump_log option.
-
-* 1998/08/25 - wakou
- * version 0.13
- * add print method.
-
-* 1998/08/05 - wakou
- * version 0.122
- * support for HP-UX 10.20.
- thanks to WATANABE Tetsuya <tetsu@jpn.hp.com>
- * socket.<< --> socket.write
-
-* 1998/07/15 - wakou
- * version 0.121
- * string.+= --> string.concat
-
-* 1998/06/01 - wakou
- * version 0.12
- * add timeout, waittime.
-
-* 1998/04/21 - wakou
- * version 0.11
- * add realtime output.
-
-* 1998/04/13 - wakou
- * version 0.10
- * first release.
-
-$Date$
+delete. see cvs log.
+
+
=end
diff --git a/lib/observer.rb b/lib/observer.rb
index 08e75f5125..e1b249e885 100644
--- a/lib/observer.rb
+++ b/lib/observer.rb
@@ -5,7 +5,7 @@
module Observable
def add_observer(observer)
@observer_peers = [] unless defined? @observer_peers
- unless defined? observer.update
+ unless observer.respond_to? :update
raise NameError, "observer needs to respond to `update'"
end
@observer_peers.push observer
diff --git a/lib/open3.rb b/lib/open3.rb
index 58de740393..33701bbfc0 100644
--- a/lib/open3.rb
+++ b/lib/open3.rb
@@ -32,6 +32,7 @@ module Open3
exec(*cmd)
}
+ exit!
}
pw[0].close
diff --git a/lib/parsedate.rb b/lib/parsedate.rb
index eee114acb2..7fc75cf0c2 100644
--- a/lib/parsedate.rb
+++ b/lib/parsedate.rb
@@ -1,5 +1,5 @@
-# parsedate.rb: Written by Tadayoshi Funaba 2000
-# $Id: parsedate.rb,v 1.2 2000-04-01 12:16:56+09 tadf Exp $
+# parsedate3.rb: Written by Tadayoshi Funaba 2000, 2001
+# $Id: parsedate3.rb,v 1.3 2001-01-18 12:09:47+09 tadf Exp $
module ParseDate
@@ -46,7 +46,12 @@ module ParseDate
hour = $1.to_i
min = $2.to_i
sec = $3.to_i if $3
- hour += 12 if $4 and $4.downcase == 'p'
+ if $4
+ hour %= 12
+ if $4.downcase == 'p'
+ hour += 12
+ end
+ end
zone = $5
end
diff --git a/lib/ping.rb b/lib/ping.rb
index 48657818cc..d698dd0c52 100644
--- a/lib/ping.rb
+++ b/lib/ping.rb
@@ -47,6 +47,8 @@ module Ping
s = TCPsocket.new(host, service)
s.close
end
+ rescue Errno::ECONNREFUSED
+ return true
rescue
return false
end
diff --git a/lib/pstore.rb b/lib/pstore.rb
index b3e1df8284..d5334efda4 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -41,11 +41,10 @@ class PStore
def [](name)
in_transaction
- value = @table[name]
- if value == nil
+ unless @table.key? name
raise PStore::Error, format("undefined root name `%s'", name)
end
- value
+ @table[name]
end
def []=(name, value)
in_transaction
@@ -69,10 +68,12 @@ class PStore
end
def commit
+ in_transaction
@abort = false
throw :pstore_abort_transaction
end
def abort
+ in_transaction
@abort = true
throw :pstore_abort_transaction
end
diff --git a/lib/shell.rb b/lib/shell.rb
new file mode 100644
index 0000000000..1d28834213
--- /dev/null
+++ b/lib/shell.rb
@@ -0,0 +1,274 @@
+#
+# shell.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.8 $
+# $Date: 2001/03/19 09:01:11 $
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "thread"
+
+require "shell/error"
+require "shell/command-processor"
+require "shell/process-controller"
+
+class Shell
+ @RCS_ID='-$Id: shell.rb,v 1.8 2001/03/19 09:01:11 keiju Exp keiju $-'
+
+ include Error
+ extend Exception2MessageMapper
+
+# @cascade = true
+ # debug: true -> normal debug
+ # debug: 1 -> eval definition debug
+ # debug: 2 -> detail inspect debug
+ @debug = false
+ @verbose = true
+
+ class << Shell
+ attr :cascade, true
+ attr :debug, true
+ attr :verbose, true
+
+# alias cascade? cascade
+ alias debug? debug
+ alias verbose? verbose
+ @verbose = true
+
+ def debug=(val)
+ @debug = val
+ @verbose = val if val
+ end
+
+ def cd(path)
+ sh = new
+ sh.cd path
+ sh
+ end
+
+ def default_system_path
+ if @default_system_path
+ @default_system_path
+ else
+ ENV["PATH"].split(":")
+ end
+ end
+
+ def default_system_path=(path)
+ @default_system_path = path
+ end
+
+ def default_record_separator
+ if @default_record_separator
+ @default_record_separator
+ else
+ $/
+ end
+ end
+
+ def default_record_separator=(rs)
+ @default_record_separator = rs
+ end
+ end
+
+ def initialize
+ @cwd = Dir.pwd
+ @dir_stack = []
+ @umask = nil
+
+ @system_path = Shell.default_system_path
+ @record_separator = Shell.default_record_separator
+
+ @command_processor = CommandProcessor.new(self)
+ @process_controller = ProcessController.new(self)
+
+ @verbose = Shell.verbose
+ @debug = Shell.debug
+ end
+
+ attr_reader :system_path
+
+ def system_path=(path)
+ @system_path = path
+ rehash
+ end
+
+ attr :umask, true
+ attr :record_separator, true
+
+ attr :verbose, true
+ attr :debug, true
+
+ def debug=(val)
+ @debug = val
+ @verbose = val if val
+ end
+
+ alias verbose? verbose
+ alias debug? debug
+
+ attr_reader :command_processor
+ attr_reader :process_controller
+
+ def expand_path(path)
+ if /^\// =~ path
+ File.expand_path(path)
+ else
+ File.expand_path(File.join(@cwd, path))
+ end
+ end
+
+ # Most Shell commands are defined via CommandProcessor
+
+ #
+ # Dir related methods
+ #
+ # Shell#cwd/dir/getwd/pwd
+ # Shell#chdir/cd
+ # Shell#pushdir/pushd
+ # Shell#popdir/popd
+ # Shell#mkdir
+ # Shell#rmdir
+
+ attr :cwd
+ alias dir cwd
+ alias getwd cwd
+ alias pwd cwd
+
+ attr :dir_stack
+ alias dirs dir_stack
+
+ # If called as iterator, it restores the current directory when the
+ # block ends.
+ def chdir(path = nil)
+ if iterator?
+ cwd_old = @cwd
+ begin
+ chdir(path)
+ yield
+ ensure
+ chdir(cwd_old)
+ end
+ else
+ path = "~" unless path
+ @cwd = expand_path(path)
+ notify "current dir: #{@cwd}"
+ rehash
+ self
+ end
+ end
+ alias cd chdir
+
+ def pushdir(path = nil)
+ if iterator?
+ pushdir(path)
+ begin
+ yield
+ ensure
+ popdir
+ end
+ elsif path
+ @dir_stack.push @cwd
+ chdir path
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ if pop = @dir_stack.pop
+ @dir_stack.push @cwd
+ chdir pop
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ Shell.Fail DirStackEmpty
+ end
+ end
+ end
+ alias pushd pushdir
+
+ def popdir
+ if pop = @dir_stack.pop
+ chdir pop
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ Shell.Fail DirStackEmpty
+ end
+ end
+ alias popd popdir
+
+
+ #
+ # process management
+ #
+ def jobs
+ @process_controller.jobs
+ end
+
+ def kill(sig, command)
+ @process_controller.kill_job(sig, command)
+ end
+
+ #
+ # command definitions
+ #
+ def Shell.def_system_command(command, path = command)
+ CommandProcessor.def_system_command(command, path)
+ end
+
+ def Shell.undef_system_command(command)
+ CommandProcessor.undef_system_command(command)
+ end
+
+ def Shell.alias_command(ali, command, *opts, &block)
+ CommandProcessor.alias_command(ali, command, *opts, &block)
+ end
+
+ def Shell.unalias_command(ali)
+ CommandProcessor.unalias_command(ali)
+ end
+
+ def Shell.install_system_commands(pre = "sys_")
+ CommandProcessor.install_system_commands(pre)
+ end
+
+ #
+ def inspect
+ if debug.kind_of?(Integer) && debug > 2
+ super
+ else
+ to_s
+ end
+ end
+
+ def self.notify(*opts, &block)
+ Thread.exclusive do
+ if opts[-1].kind_of?(String)
+ yorn = verbose?
+ else
+ yorn = opts.pop
+ end
+ return unless yorn
+
+ _head = true
+ print *opts.collect{|mes|
+ mes = mes.dup
+ yield mes if iterator?
+ if _head
+ _head = false
+ "shell: " + mes
+ else
+ " " + mes
+ end
+ }.join("\n")+"\n"
+ end
+ end
+
+ CommandProcessor.initialize
+ CommandProcessor.run_config
+end
+
diff --git a/lib/shell/builtin-command.rb b/lib/shell/builtin-command.rb
new file mode 100644
index 0000000000..db1adfa48b
--- /dev/null
+++ b/lib/shell/builtin-command.rb
@@ -0,0 +1,154 @@
+#
+# shell/builtin-command.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "shell/filter"
+
+class Shell
+ class BuiltInCommand<Filter
+ def wait?
+ false
+ end
+ def active?
+ true
+ end
+ end
+
+ class Echo < BuiltInCommand
+ def initialize(sh, *strings)
+ super sh
+ @strings = strings
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ for str in @strings
+ yield str + rs
+ end
+ end
+ end
+
+ class Cat < BuiltInCommand
+ def initialize(sh, *filenames)
+ super sh
+ @cat_files = filenames
+ end
+
+ def each(rs = nil)
+ if @cat_files.empty?
+ super
+ else
+ for src in @cat_files
+ @shell.foreach(src, rs){|l| yield l}
+ end
+ end
+ end
+ end
+
+ class Glob < BuiltInCommand
+ def initialize(sh, pattern)
+ super sh
+
+ @pattern = pattern
+ Thread.critical = true
+ back = Dir.pwd
+ begin
+ Dir.chdir @shell.cwd
+ @files = Dir[pattern]
+ ensure
+ Dir.chdir back
+ Thread.critical = false
+ end
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ for f in @files
+ yield f+rs
+ end
+ end
+ end
+
+# class Sort < Cat
+# def initialize(sh, *filenames)
+# super
+# end
+#
+# def each(rs = nil)
+# ary = []
+# super{|l| ary.push l}
+# for l in ary.sort!
+# yield l
+# end
+# end
+# end
+
+ class AppendIO < BuiltInCommand
+ def initialize(sh, io, filter)
+ super sh
+ @input = filter
+ @io = io
+ end
+
+ def input=(filter)
+ @input.input=filter
+ for l in @input
+ @io << l
+ end
+ end
+
+ end
+
+ class AppendFile < AppendIO
+ def initialize(sh, to_filename, filter)
+ @file_name = to_filename
+ io = sh.open(to_filename, "a")
+ super(sh, io, filter)
+ end
+
+ def input=(filter)
+ begin
+ super
+ ensure
+ @io.close
+ end
+ end
+ end
+
+ class Tee < BuiltInCommand
+ def initialize(sh, filename)
+ super sh
+ @to_filename = filename
+ end
+
+ def each(rs = nil)
+ to = @shell.open(@to_filename, "w")
+ begin
+ super{|l| to << l; yield l}
+ ensure
+ to.close
+ end
+ end
+ end
+
+ class Concat < BuiltInCommand
+ def initialize(sh, *jobs)
+ super(sh)
+ @jobs = jobs
+ end
+
+ def each(rs = nil)
+ while job = @jobs.shift
+ job.each{|l| yield l}
+ end
+ end
+ end
+end
diff --git a/lib/shell/command-processor.rb b/lib/shell/command-processor.rb
new file mode 100644
index 0000000000..fa253b3705
--- /dev/null
+++ b/lib/shell/command-processor.rb
@@ -0,0 +1,584 @@
+#
+# shell/command-controller.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "ftools"
+require "thread"
+
+require "shell/error"
+require "shell/filter"
+require "shell/system-command"
+require "shell/builtin-command"
+
+class Shell
+ class CommandProcessor
+
+ #
+ # initialize of Shell and related classes.
+ #
+ NoDelegateMethods = ["initialize", "expand_path"]
+ def self.initialize
+
+ install_builtin_commands
+
+ # define CommandProccessor#methods to Shell#methods and Filter#methods
+ for m in CommandProcessor.instance_methods - NoDelegateMethods
+ add_delegate_command_to_shell(m)
+ end
+
+ def self.method_added(id)
+ add_delegate_command_to_shell(id)
+ end
+ end
+
+ #
+ # include run file.
+ #
+ def self.run_config
+ begin
+ load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
+ rescue LoadError, Errno::ENOENT
+ rescue
+ print "load error: #{rc}\n"
+ print $!.type, ": ", $!, "\n"
+ for err in $@[0, $@.size - 2]
+ print "\t", err, "\n"
+ end
+ end
+ end
+
+ def initialize(shell)
+ @shell = shell
+ @system_commands = {}
+ end
+
+ #
+ # CommandProcessor#expand_path(path)
+ # path: String
+ # return: String
+ # returns the absolute path for <path>
+ #
+ def expand_path(path)
+ @shell.expand_path(path)
+ end
+
+ #
+ # File related commands
+ # Shell#foreach
+ # Shell#open
+ # Shell#unlink
+ # Shell#test
+ #
+ # -
+ #
+ # CommandProcessor#foreach(path, rs)
+ # path: String
+ # rs: String - record separator
+ # iterator
+ # Same as:
+ # File#foreach (when path is file)
+ # Dir#foreach (when path is directory)
+ # path is relative to pwd
+ #
+ def foreach(path = nil, *rs)
+ path = "." unless path
+ path = expand_path(path)
+
+ if File.directory?(path)
+ Dir.foreach(path){|fn| yield fn}
+ else
+ IO.foreach(path, *rs){|l| yield l}
+ end
+ end
+
+ #
+ # CommandProcessor#open(path, mode)
+ # path: String
+ # mode: String
+ # return: File or Dir
+ # Same as:
+ # File#open (when path is file)
+ # Dir#open (when path is directory)
+ # mode has an effect only when path is a file
+ #
+ def open(path, mode)
+ path = expand_path(path)
+ if File.directory?(path)
+ Dir.open(path)
+ else
+ effect_umask do
+ File.open(path, mode)
+ end
+ end
+ end
+ # public :open
+
+ #
+ # CommandProcessor#unlink(path)
+ # same as:
+ # Dir#unlink (when path is directory)
+ # File#unlink (when path is file)
+ #
+ def unlink(path)
+ path = expand_path(path)
+ if File.directory?(path)
+ Dir.unlink(path)
+ else
+ IO.unlink(path)
+ end
+ end
+
+ #
+ # CommandProcessor#test(command, file1, file2)
+ # CommandProcessor#[command, file1, file2]
+ # command: char or String or Symbol
+ # file1: String
+ # file2: String(optional)
+ # return: Boolean
+ # same as:
+ # test() (when command is char or length 1 string or sumbol)
+ # FileTest.command (others)
+ # example:
+ # sh[?e, "foo"]
+ # sh[:e, "foo"]
+ # sh["e", "foo"]
+ # sh[:exists?, "foo"]
+ # sh["exists?", "foo"]
+ #
+ def test(command, file1, file2=nil)
+ file1 = expand_path(file1)
+ file2 = expand_path(file2) if file2
+ command = command.id2name if command.kind_of?(Symbol)
+
+ case command
+ when Integer
+ top_level_test(command, file1, file2)
+ when String
+ if command.size == 1
+ if file2
+ top_level_test(command, file1, file2)
+ else
+ top_level_test(command, file1)
+ end
+ else
+ if file2
+ FileTest.send(command, file1, file2)
+ else
+ FileTest.send(command, file1)
+ end
+ end
+ end
+ end
+ alias [] test
+
+ #
+ # Dir related methods
+ #
+ # Shell#mkdir
+ # Shell#rmdir
+ #
+ #--
+ #
+ # CommandProcessor#mkdir(*path)
+ # path: String
+ # same as Dir.mkdir()
+ #
+ def mkdir(*path)
+ for dir in path
+ Dir.mkdir(expand_path(dir))
+ end
+ end
+
+ #
+ # CommandProcessor#rmdir(*path)
+ # path: String
+ # same as Dir.rmdir()
+ #
+ def rmdir(*path)
+ for dir in path
+ Dir.rmdir(expand_path(path))
+ end
+ end
+
+ #
+ # CommandProcessor#system(command, *opts)
+ # command: String
+ # opts: String
+ # retuen: SystemCommand
+ # Same as system() function
+ # example:
+ # print sh.system("ls", "-l")
+ # sh.system("ls", "-l") | sh.head > STDOUT
+ #
+ def system(command, *opts)
+ SystemCommand.new(@shell, find_system_command(command), *opts)
+ end
+
+ #
+ # ProcessCommand#rehash
+ # clear command hash table.
+ #
+ def rehash
+ @system_commands = {}
+ end
+
+ #
+ # ProcessCommand#transact
+ #
+ def check_point
+ @shell.process_controller.wait_all_jobs_execution
+ end
+ alias finish_all_jobs check_point
+
+ def transact(&block)
+ begin
+ @shell.instance_eval &block
+ ensure
+ check_point
+ end
+ end
+
+ #
+ # internal commands
+ #
+ def out(dev = STDOUT, &block)
+ dev.print transact &block
+ end
+
+ def echo(*strings)
+ Echo.new(@shell, *strings)
+ end
+
+ def cat(*filenames)
+ Cat.new(@shell, *filenames)
+ end
+
+ # def sort(*filenames)
+ # Sort.new(self, *filenames)
+ # end
+
+ def glob(pattern)
+ Glob.new(@shell, pattern)
+ end
+
+ def append(to, filter)
+ case to
+ when String
+ AppendFile.new(@shell, to, filter)
+ when IO
+ AppendIO.new(@shell, to, filter)
+ else
+ Shell.Fail CanNotMethodApply, "append", to.type
+ end
+ end
+
+ def tee(file)
+ Tee.new(@shell, file)
+ end
+
+ def concat(*jobs)
+ Concat.new(@shell, *jobs)
+ end
+
+ # %pwd, %cwd -> @pwd
+ def notify(*opts, &block)
+ Thread.exclusive do
+ Shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%pwd", "#{@cwd}")
+ mes.gsub!("%cwd", "#{@cwd}")
+ }
+ end
+ end
+
+ #
+ # private functions
+ #
+ def effect_umask
+ if @shell.umask
+ Thread.critical = true
+ save = File.umask
+ begin
+ yield
+ ensure
+ File.umask save
+ Thread.critical = false
+ end
+ else
+ yield
+ end
+ end
+ private :effect_umask
+
+ def find_system_command(command)
+ return command if /^\// =~ command
+ case path = @system_commands[command]
+ when String
+ if exists?(path)
+ return path
+ else
+ Shell.Fail CommandNotFound, command
+ end
+ when false
+ Shell.Fail CommandNotFound, command
+ end
+
+ for p in @shell.system_path
+ path = join(p, command)
+ if FileTest.exists?(path)
+ @system_commands[command] = path
+ return path
+ end
+ end
+ @system_commands[command] = false
+ Shell.Fail CommandNotFound, command
+ end
+
+ #
+ # CommandProcessor.def_system_command(command, path)
+ # command: String
+ # path: String
+ # define 'command()' method as method.
+ #
+ def self.def_system_command(command, path = command)
+ begin
+ eval ((d = %Q[def #{command}(*opts)
+ SystemCommand.new(@shell, '#{path}', *opts)
+ end]), nil, __FILE__, __LINE__ - 1)
+ rescue SyntaxError
+ Shell.notify "warn: Can't define #{command} path: #{path}."
+ end
+ Shell.notify "Define #{command} path: #{path}.", Shell.debug?
+ Shell.notify("Definition of #{command}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ end
+
+ def self.undef_system_command(command)
+ command = command.id2name if command.kind_of?(Symbol)
+ remove_method(command)
+ Shell.module_eval{remove_method(command)}
+ Filter.module_eval{remove_method(command)}
+ self
+ end
+
+ # define command alias
+ # ex)
+ # def_alias_command("ls_c", "ls", "-C", "-F")
+ # def_alias_command("ls_c", "ls"){|*opts| ["-C", "-F", *opts]}
+ #
+ @alias_map = {}
+ def self.alias_map
+ @alias_map
+ end
+ def self.alias_command(ali, command, *opts, &block)
+ ali = ali.id2name if ali.kind_of?(Symbol)
+ command = command.id2name if command.kind_of?(Symbol)
+ begin
+ if iterator?
+ @alias_map[ali.intern] = proc
+
+ eval ((d = %Q[def #{ali}(*opts)
+ @shell.__send__(:#{command},
+ *(CommandProcessor.alias_map[:#{ali}].call *opts))
+ end]), nil, __FILE__, __LINE__ - 1)
+
+ else
+ args = opts.collect{|opt| '"' + opt + '"'}.join ","
+ eval ((d = %Q[def #{ali}(*opts)
+ @shell.__send__(:#{command}, #{args}, *opts)
+ end]), nil, __FILE__, __LINE__ - 1)
+ end
+ rescue SyntaxError
+ Shell.notify "warn: Can't alias #{ali} command: #{command}."
+ Shell.notify("Definition of #{ali}: ", d)
+ raise
+ end
+ Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
+ Shell.notify("Definition of #{ali}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ self
+ end
+
+ def self.unalias_command(ali)
+ ali = ali.id2name if ali.kind_of?(Symbol)
+ @alias_map.delete ali.intern
+ undef_system_command(ali)
+ end
+
+ #
+ # CommandProcessor.def_builtin_commands(delegation_class, command_specs)
+ # delegation_class: Class or Module
+ # command_specs: [[command_name, [argument,...]],...]
+ # command_name: String
+ # arguments: String
+ # FILENAME?? -> expand_path(filename??)
+ # *FILENAME?? -> filename??.collect{|f|expand_path(f)}.join(", ")
+ # define command_name(argument,...) as
+ # delegation_class.command_name(argument,...)
+ #
+ def self.def_builtin_commands(delegation_class, command_specs)
+ for meth, args in command_specs
+ arg_str = args.collect{|arg| arg.downcase}.join(", ")
+ call_arg_str = args.collect{
+ |arg|
+ case arg
+ when /^(FILENAME.*)$/
+ format("expand_path(%s)", $1.downcase)
+ when /^(\*FILENAME.*)$/
+ # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
+ $1.downcase + '.collect{|fn| expand_path(fn)}'
+ else
+ arg
+ end
+ }.join(", ")
+ d = %Q[def #{meth}(#{arg_str})
+ #{delegation_class}.#{meth}(#{call_arg_str})
+ end]
+ Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
+ Shell.notify("Definition of #{meth}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ eval d
+ end
+ end
+
+ #
+ # CommandProcessor.install_system_commands(pre)
+ # pre: String - command name prefix
+ # defines every command which belongs in default_system_path via
+ # CommandProcessor.command(). It doesn't define already defined
+ # methods twice. By default, "pre_" is prefixes to each method
+ # name. Characters that may not be used in a method name are
+ # all converted to '_'. Definition errors are just ignored.
+ #
+ def self.install_system_commands(pre = "sys_")
+ defined_meth = {}
+ for m in Shell.methods
+ defined_meth[m] = true
+ end
+ sh = Shell.new
+ for path in Shell.default_system_path
+ next unless sh.directory? path
+ sh.cd path
+ sh.foreach do
+ |cn|
+ if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
+ command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
+ begin
+ def_system_command(command, sh.expand_path(cn))
+ rescue
+ Shell.notify "warn: Can't define #{command} path: #{cn}"
+ end
+ defined_meth[command] = command
+ end
+ end
+ end
+ end
+
+ #----------------------------------------------------------------------
+ #
+ # class initializing methods -
+ #
+ #----------------------------------------------------------------------
+ def self.add_delegate_command_to_shell(id)
+ id = id.intern if id.kind_of?(String)
+ name = id.id2name
+ if Shell.method_defined?(id)
+ Shell.notify "warn: override definnition of Shell##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
+ Shell.module_eval "alias #{name}_org #{name}"
+ end
+ Shell.notify "method added: Shell##{name}.", Shell.debug?
+ Shell.module_eval(%Q[def #{name}(*args, &block)
+ begin
+ @command_processor.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
+ end], __FILE__, __LINE__)
+
+ if Shell::Filter.method_defined?(id)
+ Shell.notify "warn: override definnition of Shell::Filter##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
+ Filter.module_eval "alias #{name}_org #{name}"
+ end
+ Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
+ Filter.module_eval(%Q[def #{name}(*args, &block)
+ begin
+ self | @shell.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
+ end], __FILE__, __LINE__)
+ end
+
+ #
+ # define default builtin commands
+ #
+ def self.install_builtin_commands
+ # method related File.
+ # (exclude open/foreach/unlink)
+ normal_delegation_file_methods = [
+ ["atime", ["FILENAME"]],
+ ["basename", ["fn", "*opts"]],
+ ["chmod", ["mode", "*FILENAMES"]],
+ ["chown", ["owner", "group", "*FILENAME"]],
+ ["ctime", ["FILENAMES"]],
+ ["delete", ["*FILENAMES"]],
+ ["dirname", ["FILENAME"]],
+ ["ftype", ["FILENAME"]],
+ ["join", ["*items"]],
+ ["link", ["FILENAME_O", "FILENAME_N"]],
+ ["lstat", ["FILENAME"]],
+ ["mtime", ["FILENAME"]],
+ ["readlink", ["FILENAME"]],
+ ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
+ # ["size", ["FILENAME"]],
+ ["split", ["pathname"]],
+ ["stat", ["FILENAME"]],
+ ["symlink", ["FILENAME_O", "FILENAME_N"]],
+ ["truncate", ["FILENAME", "length"]],
+ ["utime", ["atime", "mtime", "*FILENAMES"]]]
+
+ def_builtin_commands(File, normal_delegation_file_methods)
+ alias_method :rm, :delete
+
+ # method related FileTest
+ def_builtin_commands(FileTest,
+ FileTest.singleton_methods.collect{|m| [m, ["FILENAME"]]})
+
+ # method related ftools
+ normal_delegation_ftools_methods = [
+ ["syscopy", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["copy", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["move", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["compare", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["safe_unlink", ["*FILENAMES"]],
+ ["makedirs", ["*FILENAMES"]],
+ # ["chmod", ["mode", "*FILENAMES"]],
+ ["install", ["FILENAME_FROM", "FILENAME_TO", "mode"]],
+ ]
+ def_builtin_commands(File,
+ normal_delegation_ftools_methods)
+ alias_method :cmp, :compare
+ alias_method :mv, :move
+ alias_method :cp, :copy
+ alias_method :rm_f, :safe_unlink
+ alias_method :mkpath, :makedirs
+ end
+
+ end
+end
diff --git a/lib/shell/error.rb b/lib/shell/error.rb
new file mode 100644
index 0000000000..df5e669af6
--- /dev/null
+++ b/lib/shell/error.rb
@@ -0,0 +1,26 @@
+#
+# shell/error.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+
+class Shell
+ module Error
+ extend Exception2MessageMapper
+ def_e2message TypeError, "wrong argument type %s (expected %s)"
+
+ def_exception :DirStackEmpty, "Directory stack empty."
+ def_exception :CanNotDefine, "Can't define method(%s, %s)."
+ def_exception :CanNotMethodApply, "This method(%s) can't apply this type(%s)."
+ def_exception :CommandNotFound, "Command not found(%s)."
+ end
+end
+
diff --git a/lib/shell/filter.rb b/lib/shell/filter.rb
new file mode 100644
index 0000000000..441cded221
--- /dev/null
+++ b/lib/shell/filter.rb
@@ -0,0 +1,111 @@
+#
+# shell/filter.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+class Shell
+ #
+ # Filter
+ # A method to require
+ # each()
+ #
+ class Filter
+ include Enumerable
+ include Error
+
+ def initialize(sh)
+ @shell = sh # parent shell
+ @input = nil # input filter
+ end
+
+ attr_reader :input
+
+ def input=(filter)
+ @input = filter
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ if @input
+ @input.each(rs){|l| yield l}
+ end
+ end
+
+ def < (src)
+ case src
+ when String
+ cat = Cat.new(@shell, src)
+ cat | self
+ when IO
+ self.input = src
+ self
+ else
+ Filter.Fail CanNotMethodApply, "<", to.type
+ end
+ end
+
+ def > (to)
+ case to
+ when String
+ dst = @shell.open(to, "w")
+ begin
+ each(){|l| dst << l}
+ ensure
+ dst.close
+ end
+ when IO
+ each(){|l| to << l}
+ else
+ Filter.Fail CanNotMethodApply, ">", to.type
+ end
+ self
+ end
+
+ def >> (to)
+ begin
+ Shell.cd(@shell.pwd).append(to, self)
+ rescue CanNotMethodApply
+ Shell.Fail CanNotMethodApply, ">>", to.type
+ end
+ end
+
+ def | (filter)
+ filter.input = self
+ if active?
+ @shell.process_controller.start_job filter
+ end
+ filter
+ end
+
+ def + (filter)
+ Join.new(@shell, self, filter)
+ end
+
+ def to_a
+ ary = []
+ each(){|l| ary.push l}
+ ary
+ end
+
+ def to_s
+ str = ""
+ each(){|l| str.concat l}
+ str
+ end
+
+ def inspect
+ if @shell.debug.kind_of?(Integer) && @shell.debug > 2
+ super
+ else
+ to_s
+ end
+ end
+ end
+end
diff --git a/lib/shell/process-controller.rb b/lib/shell/process-controller.rb
new file mode 100644
index 0000000000..26fb1d9f08
--- /dev/null
+++ b/lib/shell/process-controller.rb
@@ -0,0 +1,258 @@
+#
+# shell/process-controller.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "mutex_m"
+require "monitor"
+require "sync"
+
+class Shell
+ class ProcessController
+
+ @ProcessControllers = {}
+ @ProcessControllers.extend Mutex_m
+
+ class<<self
+
+ def process_controllers_exclusive
+ begin
+ @ProcessControllers.lock unless Thread.critical
+ yield
+ ensure
+ @ProcessControllers.unlock unless Thread.critical
+ end
+ end
+
+ def activate(pc)
+ process_controllers_exclusive do
+ @ProcessControllers[pc] ||= 0
+ @ProcessControllers[pc] += 1
+ end
+ end
+
+ def inactivate(pc)
+ process_controllers_exclusive do
+ if @ProcessControllers[pc]
+ if (@ProcessControllers[pc] -= 1) == 0
+ @ProcessControllers.delete(pc)
+ end
+ end
+ end
+ end
+
+ def each_active_object
+ process_controllers_exclusive do
+ for ref in @ProcessControllers.keys
+ yield ref
+ end
+ end
+ end
+ end
+
+ def initialize(shell)
+ @shell = shell
+ @waiting_jobs = []
+ @active_jobs = []
+ @jobs_sync = Sync.new
+
+ @job_monitor = Mutex.new
+ @job_condition = ConditionVariable.new
+ end
+
+ def jobs
+ jobs = []
+ @jobs_sync.synchronize(:SH) do
+ jobs.concat @waiting_jobs
+ jobs.concat @active_jobs
+ end
+ jobs
+ end
+
+ def active_jobs
+ @active_jobs
+ end
+
+ def waiting_jobs
+ @waiting_jobs
+ end
+
+ def jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.empty? or @waiting_jobs.empty?
+ end
+ end
+
+ def active_jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.empty?
+ end
+ end
+
+ def waiting_jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @waiting_jobs.empty?
+ end
+ end
+
+ # schedule a command
+ def add_schedule(command)
+ @jobs_sync.synchronize(:EX) do
+ ProcessController.activate(self)
+ if @active_jobs.empty?
+ start_job command
+ else
+ @waiting_jobs.push(command)
+ end
+ end
+ end
+
+ # start a job
+ def start_job(command = nil)
+ @jobs_sync.synchronize(:EX) do
+ if command
+ return if command.active?
+ @waiting_jobs.delete command
+ else
+ command = @waiting_jobs.shift
+ return unless command
+ end
+ @active_jobs.push command
+ command.start
+
+ # start all jobs that input from the job
+ for job in @waiting_jobs
+ start_job(job) if job.input == command
+ end
+ end
+ end
+
+ def waiting_job?(job)
+ @jobs_sync.synchronize(:SH) do
+ @waiting_jobs.include?(job)
+ end
+ end
+
+ def active_job?(job)
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.include?(job)
+ end
+ end
+
+ # terminate a job
+ def terminate_job(command)
+ @jobs_sync.synchronize(:EX) do
+ @active_jobs.delete command
+ ProcessController.inactivate(self)
+ if @active_jobs.empty?
+ start_job
+ end
+ end
+ end
+
+ # kill a job
+ def kill_job(sig, command)
+ @jobs_sync.synchronize(:SH) do
+ if @waiting_jobs.delete command
+ ProcessController.inactivate(self)
+ return
+ elsif @active_jobs.include?(command)
+ begin
+ r = command.kill sig
+ ProcessController.inactivate(self)
+ rescue
+ print "Shell: Warn: $!\n" if @shell.verbose?
+ return nil
+ end
+ @active_jobs.delete command
+ r
+ end
+ end
+ end
+
+ # wait for all jobs to terminate
+ def wait_all_jobs_execution
+ @job_monitor.synchronize do
+ begin
+ while !jobs.empty?
+ @job_condition.wait(@job_monitor)
+ end
+ ensure
+ redo unless jobs.empty?
+ end
+ end
+ end
+
+ # simple fork
+ def sfork(command, &block)
+ pipe_me_in, pipe_peer_out = IO.pipe
+ pipe_peer_in, pipe_me_out = IO.pipe
+ Thread.critical = true
+
+ STDOUT.flush
+ ProcessController.each_active_object do |pc|
+ for jobs in pc.active_jobs
+ jobs.flush
+ end
+ end
+
+ pid = fork {
+ Thread.critical = true
+
+ Thread.list.each do |th|
+ th.kill unless [Thread.main, Thread.current].include?(th)
+ end
+
+ STDIN.reopen(pipe_peer_in)
+ STDOUT.reopen(pipe_peer_out)
+
+ ObjectSpace.each_object(IO) do |io|
+ if ![STDIN, STDOUT, STDERR].include?(io)
+ io.close unless io.closed?
+ end
+ end
+ yield
+ }
+
+ pipe_peer_in.close
+ pipe_peer_out.close
+ command.notify "job(%name:##{pid}) start", @shell.debug?
+ Thread.critical = false
+
+ th = Thread.start {
+ Thread.critical = true
+ begin
+ _pid = nil
+ command.notify("job(%id) start to waiting finish.", @shell.debug?)
+ Thread.critical = false
+ _pid = Process.waitpid(pid, nil)
+ rescue Errno::ECHILD
+ command.notify "warn: job(%id) was done already waitipd."
+ _pid = true
+ ensure
+ # when the process ends, wait until the command termintes
+ if _pid
+ else
+ command.notify("notice: Process finishing...",
+ "wait for Job[%id] to finish.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ redo
+ end
+ Thread.exclusive do
+ terminate_job(command)
+ @job_condition.signal
+ command.notify "job(%id) finish.", @shell.debug?
+ end
+ end
+ }
+ return pid, pipe_me_in, pipe_me_out
+ end
+ end
+end
diff --git a/lib/shell/system-command.rb b/lib/shell/system-command.rb
new file mode 100644
index 0000000000..c22b9ac0a4
--- /dev/null
+++ b/lib/shell/system-command.rb
@@ -0,0 +1,168 @@
+#
+# shell/system-command.rb -
+# $Release Version: 0.6.0 $
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "shell/filter"
+
+class Shell
+ class SystemCommand < Filter
+ def initialize(sh, command, *opts)
+ if t = opts.find{|opt| !opt.kind_of?(String) && opt.type}
+ Shell.Fail TypeError, t.type, "String"
+ end
+ super(sh)
+ @command = command
+ @opts = opts
+
+ @input_queue = Queue.new
+ @pid = nil
+
+ sh.process_controller.add_schedule(self)
+ end
+
+ attr_reader :command
+ alias name command
+
+ def wait?
+ @shell.process_controller.waiting_job?(self)
+ end
+
+ def active?
+ @shell.process_controller.active_job?(self)
+ end
+
+ def input=(inp)
+ super
+ if active?
+ start_export
+ end
+ end
+
+ def start
+ @pid, @pipe_in, @pipe_out = @shell.process_controller.sfork(self) {
+ Dir.chdir @shell.pwd
+ exec(@command, *@opts)
+ }
+ if @input
+ start_export
+ end
+ start_import
+ end
+
+ def flush
+ @pipe_out.flush if @pipe_out and !@pipe_out.closed?
+ end
+
+ def terminate
+ begin
+ @pipe_in.close
+ rescue IOError
+ end
+ begin
+ @pipe_out.close
+ rescue IOError
+ end
+ end
+
+ def kill(sig)
+ if @pid
+ Process.kill(sig, @pid)
+ end
+ end
+
+
+ def start_import
+# Thread.critical = true
+ notify "Job(%id) start imp-pipe.", @shell.debug?
+ rs = @shell.record_separator unless rs
+ _eop = true
+# Thread.critical = false
+ th = Thread.start {
+ Thread.critical = true
+ begin
+ Thread.critical = false
+ while l = @pipe_in.gets
+ @input_queue.push l
+ end
+ _eop = false
+ rescue Errno::EPIPE
+ _eop = false
+ ensure
+ if _eop
+ notify("warn: Process finishing...",
+ "wait for Job[%id] to finish pipe importing.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+# Tracer.on
+ Thread.current.run
+ redo
+ end
+ Thread.exclusive do
+ notify "job(%id}) close imp-pipe.", @shell.debug?
+ @input_queue.push :EOF
+ @pipe_in.close
+ end
+ end
+ }
+ end
+
+ def start_export
+ notify "job(%id) start exp-pipe.", @shell.debug?
+ _eop = true
+ th = Thread.start{
+ Thread.critical = true
+ begin
+ Thread.critical = false
+ @input.each{|l| @pipe_out.print l}
+ _eop = false
+ rescue Errno::EPIPE
+ _eop = false
+ ensure
+ if _eop
+ notify("shell: warn: Process finishing...",
+ "wait for Job(%id) to finish pipe exporting.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+# Tracer.on
+ redo
+ end
+ Thread.exclusive do
+ notify "job(%id) close exp-pipe.", @shell.debug?
+ @pipe_out.close
+ end
+ end
+ }
+ end
+
+ alias super_each each
+ def each(rs = nil)
+ while (l = @input_queue.pop) != :EOF
+ yield l
+ end
+ end
+
+ # ex)
+ # if you wish to output:
+ # "shell: job(#{@command}:#{@pid}) close pipe-out."
+ # then
+ # mes: "job(%id) close pipe-out."
+ # yorn: Boolean(@shell.debug? or @shell.verbose?)
+ def notify(*opts, &block)
+ Thread.exclusive do
+ @shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%id", "#{@command}:##{@pid}")
+ mes.gsub!("%name", "#{@command}")
+ mes.gsub!("%pid", "#{@pid}")
+ }
+ end
+ end
+ end
+end
diff --git a/lib/shell/version.rb b/lib/shell/version.rb
new file mode 100644
index 0000000000..6694c804d8
--- /dev/null
+++ b/lib/shell/version.rb
@@ -0,0 +1,16 @@
+#
+# version.rb - shell version definition file
+# $Release Version: 0.6.0$
+# $Revision$
+# $Date$
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+class Shell
+ @RELEASE_VERSION = "0.6.0"
+ @LAST_UPDATE_DATE = "01/03/19"
+end
diff --git a/lib/thread.rb b/lib/thread.rb
index d4b6ad6ec1..0537c78650 100644
--- a/lib/thread.rb
+++ b/lib/thread.rb
@@ -74,7 +74,10 @@ class Mutex
retry
end
Thread.critical = false
- t.run if t
+ begin
+ t.run if t
+ rescue ThreadError
+ end
self
end
@@ -160,7 +163,10 @@ class Queue
ensure
Thread.critical = false
end
- t.run if t
+ begin
+ t.run if t
+ rescue ThreadError
+ end
end
def enq(obj)
push(obj)
@@ -170,7 +176,7 @@ class Queue
Thread.critical = true
begin
loop do
- if @que.length == 0
+ if @que.empty?
if non_block
raise ThreadError, "queue empty"
end
@@ -184,13 +190,11 @@ class Queue
Thread.critical = false
end
end
- def shift(non_block=false)
- pop(non_block=false)
- end
- alias deq shift
+ alias shift pop
+ alias deq pop
def empty?
- @que.length == 0
+ @que.empty?
end
def clear
@@ -223,11 +227,11 @@ class SizedQueue<Queue
def max=(max)
Thread.critical = true
- if max >= @max
+ if max <= @max
@max = max
Thread.critical = false
else
- diff = max - @max
+ diff = @max - max
@max = max
Thread.critical = false
diff.times do
@@ -253,6 +257,7 @@ class SizedQueue<Queue
end
def pop(*args)
+ retval = super
Thread.critical = true
if @que.length < @max
begin
@@ -263,9 +268,12 @@ class SizedQueue<Queue
ensure
Thread.critical = false
end
- t.run if t
+ begin
+ t.run if t
+ rescue ThreadError
+ end
end
- super
+ retval
end
def num_waiting