1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Merge -r16241:16456 from ruby_1_8.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8_7@16458 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2008-05-18 15:02:36 +00:00
parent 32378c5abe
commit 8480bcc8d5
64 changed files with 5134 additions and 851 deletions

326
ChangeLog
View file

@ -1,3 +1,329 @@
Sun May 18 22:26:51 2008 GOTOU Yuuzou <gotoyuzo@notwork.org>
* lib/webrick/httpservlet/filehandler.rb: should normalize path
name in path_info to prevent script disclosure vulnerability on
DOSISH filesystems. (fix: CVE-2008-1891)
Note: NTFS/FAT filesystem should not be published by the platforms
other than Windows. Pathname interpretation (including short
filename) is less than perfect.
* lib/webrick/httpservlet/abstract.rb
(WEBrick::HTTPServlet::AbstracServlet#redirect_to_directory_uri):
should escape the value of Location: header.
* lib/webrick/httpservlet/cgi_runner.rb: accept interpreter
command line arguments.
Sat May 17 23:53:57 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* file.c (file_expand_path): fix for short file name on Cygwin.
Sat May 17 11:29:11 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* file.c (rb_file_s_extname): first dot is not an extension name.
Sat May 17 10:18:44 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* re.c (rb_reg_search): need to free allocated buffer in re_register.
Fri May 16 17:01:44 2008 NAKAMURA Usaku <usa@ruby-lang.org>
* win32/Makefile.sub (test-rubyspec): added.
Fri May 16 16:22:40 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/tcltklib.c: sometimes freeze when receive Interrupt signal.
Fri May 16 14:54:56 2008 Tanaka Akira <akr@fsij.org>
* Makefile.in (update-rubyspec): move rubyspec to srcdir.
(test-rubyspec): ditto.
Fri May 16 14:25:22 2008 Tanaka Akira <akr@fsij.org>
* Makefile.in (test-rubyspec): use RUNRUBY. suggested by nobu.
Fri May 16 13:01:43 2008 Tanaka Akira <akr@fsij.org>
* Makefile.in (update-rubyspec): new target to download rubyspec.
(test-rubyspec): new target to run rubyspec. this doesn't work
before install.
Fri May 16 08:15:52 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tk.rb: fix memory (object) leak bug.
* ext/tk/sample/demos-jp/aniwave.rb, ext/tk/sample/demos-en/aniwave.rb:
bug fix.
Thu May 15 17:00:22 2008 Akinori MUSHA <knu@iDaemons.org>
* string.c (Init_String): Define #bytesize as an alias for #size
for compatibility with 1.9.
Thu May 15 15:33:59 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* file.c (file_expand_path): support for alternative data stream
and ignored trailing garbages of NTFS.
* file.c (rb_file_s_basename): ditto.
* file.c (rb_file_s_extname): ditto.
Wed May 14 19:24:59 2008 Akinori MUSHA <knu@iDaemons.org>
* array.c (rb_ary_count): Override Enumerable#count for better
performance.
(rb_ary_nitems): Undo the backport. Use #count {} instead.
* enumerator.c (enumerator_iter_i): Remove an unused function.
(enumerator_with_index, enumerator_each): Remove unused
variables.
Wed May 14 17:15:11 2008 NAKAMURA Usaku <usa@ruby-lang.org>
* ext/tk/tkutil/extronf.rb: check stdndup() because it's not standard
function of C.
* ext/tk/tkutil/tkutil.c (cbsubst_table_setup): use malloc() and
strncpy() instead of strndup() if not available.
Wed May 14 09:52:02 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/tkutil/tkutil.c: improve handling callback-subst-keys.
Now, support longnam-keys (e.g. '%CTT' on tkdnd-2.0; however, still
not support tkdnd-2.0 on tkextlib), and symbols of parameters (e.g.
:widget=>'%W', :keycode=>'%k', '%x'=>:x, '%X'=>:root_x, and so on;
those are attributes of event object). It means that Ruby/Tk accepts
not only "widget.bind(ev, '%W', '%k', ...){|w, k, ...| ... }", but
also "widget.bind(ev, :widget, :keycode, ...){|w, k, ...| ... }".
It is potentially incompatible, when user passes symbols to the
arguments of the callback block (the block receives the symbols as
strings). I think that is very rare case (probably, used by Ruby/Tk
experts only). When causes such trouble, please give strings instead
of such symbol parameters (e.g. call Symbol#to_s method).
* ext/tk/lib/tk/event.rb, ext/tk/lib/tk/validation.rb,
ext/tk/lib/tkextlib/blt/treeview.rb,
ext/tk/lib/tkextlib/winico/winico.rb: ditto.
* ext/tk/tkutil/tkutil.c: strings are available on subst_tables on
TkUtil::CallbackSubst class (it is useful on Ruby 1.9).
* ext/tk/lib/tk/spinbox.rb, ext/tk/lib/tkextlib/iwidgets/hierarchy.rb,
ext/tk/lib/tkextlib/iwidgets/spinner.rb,
ext/tk/lib/tkextlib/iwidgets/entryfield.rb,
ext/tk/lib/tkextlib/iwidgets/calendar.rb,
ext/tk/lib/tkextlib/blt/dragdrop.rb,
ext/tk/lib/tkextlib/tkDND/tkdnd.rb,
ext/tk/lib/tkextlib/treectrl/tktreectrl.rb,
ext/tk/lib/tkextlib/tktable/tktable.rb: disable code piece became
unnecessary by reason of the changes of ext/tk/tkutil/tkutil.c.
Tue May 13 15:10:50 2008 Akinori MUSHA <knu@iDaemons.org>
* enumerator.c: Update rdoc.
(enumerator_initialize): Discourage the use.
(enum_each_slice, enum_each_cons, enumerator_each)
(enumerator_with_index): Add a note about a call without a block.
* NEWS: Intentionally omit enum_slice and enum_cons, which are
removed in 1.9.
Tue May 13 07:56:36 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* string.c (rb_str_cat): fixed buffer overrun reported by
Christopher Thompson <cthompson at nexopia.com> in [ruby-core:16746]
Mon May 12 13:57:19 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (is_defined): add NODE_OP_ASGN_{OR,AND}. "defined?(a||=1)"
should not operate assignment. [ruby-dev:34645]
Mon May 12 12:59:23 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tk/wm.rb: Wm#overrideredirect overwrites arguemnt to
an invalid value.
* ext/tk/sample/ttk_wrapper.rb: support "if __FILE__ == $0" idiom.
Mon May 12 12:36:55 2008 NAKAMURA Usaku <usa@ruby-lang.org>
* win32/win32.c (rb_w32_select): backport from trunk.
[ruby-talk:300743]
Mon May 12 12:33:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* common.mk (RUBYLIB, RUBYOPT): clear.
Mon May 12 10:41:10 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* lib/delegate.rb (SimpleDelegator::dup): removed needless argument.
[ruby-list:44910]
* lib/delegate.rb (clone, dup): keep relationship with the target
object.
Sun May 11 23:19:39 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* enum.c (all_iter_i, any_iter_i): reduced duplicated code.
Sun May 11 17:57:36 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* configure.in (MINIRUBY): should not include extension library path.
Sun May 11 10:36:10 2008 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
* eval.c (method_name, method_owner): New methods; backported
from 1.9. (UnboundMethod#name, UnboundMethod#owner)
Sun May 11 02:48:13 2008 <nagai@orca16.orcabay.ddo.jp>
* ext/tk/lib/tk/pack.rb, ext/tk/lib/tk/grid.rb: fail to do pack/grid
without options.
* ext/tk/lib/tk.rb: add TkWindow#grid_anchor, grid_column, grid_row.
Sat May 10 18:19:16 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* string.c (rb_str_each_line): RDoc updated. [ruby-dev:34586]
Sat May 10 13:17:56 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tk/pack.rb, ext/tk/lib/tk/grid.rb: increase supported
parameter patterns of configure method.
Sat May 10 09:16:13 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* util.c (ruby_strtod): backported from 1.9. a patch from Satoshi
Nakagawa <psychs at limechat.net> in [ruby-dev:34625].
fixed: [ruby-dev:34623]
Fri May 9 23:33:25 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tk/wm.rb: methods of Tk::Wm_for_General module cannot
pass the given block to methods of Tk::Wm module.
* ext/tk/lib/tk/grid.rb: lack of module-method definitions.
* ext/tk/lib/tkextlib/tile.rb: lack of autoload definitions.
* ext/tk/lib/tkextlib/tile/tnotebook.rb: cannot use kanji (not UTF-8)
characters for headings.
* ext/tk/tcltklib.c: maybe a little more stable about @encoding value
of TclTkIp object.
Wed May 7 08:46:44 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* struct.c (rb_struct_s_def): to_str should be called only once.
[ruby-core:16647]
Wed May 7 00:54:25 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* ext/zlib/zlib.c (gzreader_gets): may cause infinite loop.
a patch from Kouya <kouyataifu4 at gmail.com> in
[ruby-reference-manual:762].
Sun May 4 09:35:51 2008 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
* sample/erb/erb4html.rb (ERB4Html) : add example of ERB#set_eoutvar.
ERB4Html is an auto-quote ERB.
Sat May 3 22:52:48 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tkextlib/tile.rb, ext/tk/lib/tkextlib/tile/style.rb,
ext/tk/sample/ttk_wrapper.rb: improve treating and control themes.
add Tk::Tile.themes and Tk::Tile.set_theme(theme).
Fri May 2 14:52:33 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* misc/ruby-mode.el: move fontifying code from hook. a patch from
Phil Hagelberg <phil at hagelb.org> in [ruby-core:16636].
Fri May 2 13:47:51 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* re.c (match_select): restore previous behavior of MatchData#select.
RDoc updated as well, mentioning the plan to remove this method
in the future. [ruby-dev:34556]
Fri May 2 13:04:04 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* ext/dbm/dbm.c (Init_dbm): defines DBM::VERSION even when
DB_VERSION_STRING is not available. [ruby-dev:34569]
Thu May 1 23:57:06 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16257 from trunk.
* lib/net/telnet.rb: This patch from Brian Candler adds a FailEOF mode which
can be activated to have net/telnet raise EOFError exceptions when the
remote connection is closed. The default behavior remains unchanged though.
Thu May 1 23:43:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* range.c (range_step): check if step can be converted to an integer.
[ruby-dev:34558]
* range.c (range_step): allow float step bigger than zero but less
than one. [ruby-dev:34557]
Wed Apr 30 20:22:40 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16241 from trunk.
* lib/net/telnet.rb: Fixing a bug where line endings would not be properly
escaped when the two character ending was broken up into separate TCP
packets. Issue reported and patched by Brian Candler.
Thu May 1 23:57:06 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16257 from trunk.
* lib/net/telnet.rb: This patch from Brian Candler adds a FailEOF mode which
can be activated to have net/telnet raise EOFError exceptions when the
remote connection is closed. The default behavior remains unchanged though.
Thu May 1 23:43:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* range.c (range_step): check if step can be converted to an integer.
[ruby-dev:34558]
* range.c (range_step): allow float step bigger than zero but less
than one. [ruby-dev:34557]
Wed Apr 30 20:22:40 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16241 from trunk.
* lib/net/telnet.rb: Fixing a bug where line endings would not be properly
escaped when the two character ending was broken up into separate TCP
packets. Issue reported and patched by Brian Candler.
Thu May 1 23:57:06 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16257 from trunk.
* lib/net/telnet.rb: This patch from Brian Candler adds a FailEOF mode which
can be activated to have net/telnet raise EOFError exceptions when the
remote connection is closed. The default behavior remains unchanged though.
Thu May 1 23:43:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* range.c (range_step): check if step can be converted to an integer.
[ruby-dev:34558]
* range.c (range_step): allow float step bigger than zero but less
than one. [ruby-dev:34557]
Wed Apr 30 20:22:40 2008 James Edward Gray II <jeg2@ruby-lang.org>
Merged 16241 from trunk.
* lib/net/telnet.rb: Fixing a bug where line endings would not be properly
escaped when the two character ending was broken up into separate TCP
packets. Issue reported and patched by Brian Candler.
Wed Apr 30 17:47:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org> Wed Apr 30 17:47:21 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* re.c (rb_reg_search): use local variable. a patch from wanabe * re.c (rb_reg_search): use local variable. a patch from wanabe

View file

@ -181,3 +181,22 @@ distclean-local::
ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP) ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP)
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(OUTFLAG)$@ -c ext/extinit.c $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(OUTFLAG)$@ -c ext/extinit.c
MSPEC_GIT_URL=git://github.com/brixen/mspec.git
RUBYSPEC_GIT_URL=git://github.com/brixen/rubyspec.git
update-rubyspec:
if [ -d $(srcdir)/rubyspec ]; then \
cd $(srcdir)/rubyspec/mspec; \
git pull; \
cd ../spec/rubyspec; \
git pull; \
else \
git clone $(MSPEC_GIT_URL) $(srcdir)/rubyspec/mspec; \
git clone $(RUBYSPEC_GIT_URL) $(srcdir)/rubyspec/spec/rubyspec; \
fi
test-rubyspec:
@if [ ! -d $(srcdir)/rubyspec ]; then echo No rubyspec here. make update-rubyspec first.; exit 1; fi
$(RUNRUBY) $(srcdir)/rubyspec/mspec/bin/mspec -r$(srcdir)/ext/purelib.rb $(srcdir)/rubyspec/spec/rubyspec/$(MAJOR).$(MINOR)

12
NEWS
View file

@ -17,10 +17,6 @@ with all sufficient information, see the ChangeLog file.
* builtin classes * builtin classes
* Array#nitems now takes a block optionally, which is used to
determine if each element should be counted instead of checking if
the element is non-nil.
* Array#flatten * Array#flatten
* Array#flatten! * Array#flatten!
@ -87,9 +83,7 @@ with all sufficient information, see the ChangeLog file.
New class for various enumeration defined by the enumerator library. New class for various enumeration defined by the enumerator library.
* Enumerable#each_slice * Enumerable#each_slice
* Enumerable#enum_slice
* Enumerable#each_cons * Enumerable#each_cons
* Enumerable#enum_cons
* Object#to_enum * Object#to_enum
* Object#enum_for * Object#enum_for
@ -187,6 +181,8 @@ with all sufficient information, see the ChangeLog file.
* Method#receiver * Method#receiver
* Method#name * Method#name
* Method#owner * Method#owner
* UnboundMethod#name
* UnboundMethod#owner
New methods. New methods.
@ -209,6 +205,10 @@ with all sufficient information, see the ChangeLog file.
* Regexp.union accepts an array of patterns. * Regexp.union accepts an array of patterns.
* String#bytesize
New method, returning the size in bytes. (alias length and size)
* String#chars * String#chars
* String#each_char * String#each_char
* String#partition * String#partition

55
array.c
View file

@ -3032,16 +3032,12 @@ rb_ary_compact(ary)
/* /*
* call-seq: * call-seq:
* array.nitems -> int * array.nitems -> int
* array.nitems { |item| block } -> int
* *
* Returns the number of non-<code>nil</code> elements in _self_. * Returns the number of non-<code>nil</code> elements in _self_.
* If a block is given, the elements yielding a true value are
* counted.
* *
* May be zero. * May be zero.
* *
* [ 1, nil, 3, nil, 5 ].nitems #=> 3 * [ 1, nil, 3, nil, 5 ].nitems #=> 3
* [5,6,7,8,9].nitems { |x| x % 2 != 0 } #=> 3
*/ */
static VALUE static VALUE
@ -3049,24 +3045,54 @@ rb_ary_nitems(ary)
VALUE ary; VALUE ary;
{ {
long n = 0; long n = 0;
VALUE *p, *pend;
if (rb_block_given_p()) { for (p = RARRAY(ary)->ptr, pend = p + RARRAY(ary)->len; p < pend; p++) {
long i; if (!NIL_P(*p)) n++;
}
return LONG2NUM(n);
}
for (i=0; i<RARRAY(ary)->len; i++) { /*
VALUE v = RARRAY(ary)->ptr[i]; * call-seq:
if (RTEST(rb_yield(v))) n++; * array.count(obj) -> int
* array.count { |item| block } -> int
*
* Returns the number of elements which equals to <i>obj</i>.
* If a block is given, counts tthe number of elements yielding a true value.
*
* ary = [1, 2, 4, 2]
* ary.count(2) # => 2
* ary.count{|x|x%2==0} # => 3
*
*/
static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
long n = 0;
if (argc == 0) {
VALUE *p, *pend;
RETURN_ENUMERATOR(ary, 0, 0);
for (p = RARRAY_PTR(ary), pend = p + RARRAY_LEN(ary); p < pend; p++) {
if (RTEST(rb_yield(*p))) n++;
} }
} }
else { else {
VALUE *p = RARRAY(ary)->ptr; VALUE obj, *p, *pend;
VALUE *pend = p + RARRAY(ary)->len;
while (p < pend) { rb_scan_args(argc, argv, "1", &obj);
if (!NIL_P(*p)) n++; if (rb_block_given_p()) {
p++; rb_warn("given block not used");
}
for (p = RARRAY_PTR(ary), pend = p + RARRAY_LEN(ary); p < pend; p++) {
if (rb_equal(*p, obj)) n++;
} }
} }
return LONG2NUM(n); return LONG2NUM(n);
} }
@ -3789,6 +3815,7 @@ Init_Array()
rb_define_method(rb_cArray, "flatten", rb_ary_flatten, -1); rb_define_method(rb_cArray, "flatten", rb_ary_flatten, -1);
rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, -1); rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, -1);
rb_define_method(rb_cArray, "nitems", rb_ary_nitems, 0); rb_define_method(rb_cArray, "nitems", rb_ary_nitems, 0);
rb_define_method(rb_cArray, "count", rb_ary_count, -1);
rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, 0); rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, 0);
rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, 0); rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, 0);
rb_define_method(rb_cArray, "choice", rb_ary_choice, 0); rb_define_method(rb_cArray, "choice", rb_ary_choice, 0);

View file

@ -2,7 +2,8 @@ bin: $(PROGRAM) $(WPROGRAM)
lib: $(LIBRUBY) lib: $(LIBRUBY)
dll: $(LIBRUBY_SO) dll: $(LIBRUBY_SO)
RUBYOPT = RUBYLIB = -
RUBYOPT = -
STATIC_RUBY = static-ruby STATIC_RUBY = static-ruby

View file

@ -1402,6 +1402,7 @@ if test x"$cross_compiling" = xyes; then
RUNRUBY='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`' RUNRUBY='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`'
else else
MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib' MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib'
MINIRUBY="$MINIRUBY"' -I$(EXTOUT)/common -I./- -r$(srcdir)/ext/purelib.rb'
PREP='miniruby$(EXEEXT)' PREP='miniruby$(EXEEXT)'
RUNRUBY='$(MINIRUBY) $(srcdir)/runruby.rb --extout=$(EXTOUT)' RUNRUBY='$(MINIRUBY) $(srcdir)/runruby.rb --extout=$(EXTOUT)'
fi fi

View file

@ -254,6 +254,14 @@ void rb_ia64_flushrs(void);
#define ENV_IGNORECASE #define ENV_IGNORECASE
#endif #endif
#ifndef CASEFOLD_FILESYSTEM
# if defined DOSISH || defined __VMS
# define CASEFOLD_FILESYSTEM 1
# else
# define CASEFOLD_FILESYSTEM 0
# endif
#endif
#ifndef DLEXT_MAXLEN #ifndef DLEXT_MAXLEN
#define DLEXT_MAXLEN 4 #define DLEXT_MAXLEN 4
#endif #endif

40
enum.c
View file

@ -843,18 +843,6 @@ enum_sort_by(obj)
return ary; return ary;
} }
static VALUE
all_iter_i(i, memo)
VALUE i;
VALUE *memo;
{
if (!RTEST(rb_yield(i))) {
*memo = Qfalse;
rb_iter_break();
}
return Qnil;
}
static VALUE static VALUE
all_i(i, memo) all_i(i, memo)
VALUE i; VALUE i;
@ -867,6 +855,14 @@ all_i(i, memo)
return Qnil; return Qnil;
} }
static VALUE
all_iter_i(i, memo)
VALUE i;
VALUE *memo;
{
return all_i(rb_yield(i), memo);
}
/* /*
* call-seq: * call-seq:
* enum.all? [{|obj| block } ] => true or false * enum.all? [{|obj| block } ] => true or false
@ -894,18 +890,6 @@ enum_all(obj)
return result; return result;
} }
static VALUE
any_iter_i(i, memo)
VALUE i;
VALUE *memo;
{
if (RTEST(rb_yield(i))) {
*memo = Qtrue;
rb_iter_break();
}
return Qnil;
}
static VALUE static VALUE
any_i(i, memo) any_i(i, memo)
VALUE i; VALUE i;
@ -918,6 +902,14 @@ any_i(i, memo)
return Qnil; return Qnil;
} }
static VALUE
any_iter_i(i, memo)
VALUE i;
VALUE *memo;
{
return any_i(rb_yield(i), memo);
}
/* /*
* call-seq: * call-seq:
* enum.any? [{|obj| block } ] => true or false * enum.any? [{|obj| block } ] => true or false

View file

@ -72,16 +72,6 @@ enumerator_ptr(obj)
return ptr; return ptr;
} }
static VALUE enumerator_iter_i _((VALUE, VALUE));
static VALUE
enumerator_iter_i(i, enum_obj)
VALUE i;
VALUE enum_obj;
{
struct enumerator *e = (struct enumerator *)enum_obj;
return rb_yield(proc_call(e->proc, i));
}
/* /*
* call-seq: * call-seq:
* obj.to_enum(method = :each, *args) * obj.to_enum(method = :each, *args)
@ -138,8 +128,10 @@ each_slice_i(val, memo)
/* /*
* call-seq: * call-seq:
* e.each_slice(n) {...} * e.each_slice(n) {...}
* e.each_slice(n)
* *
* Iterates the given block for each slice of <n> elements. * Iterates the given block for each slice of <n> elements. If no
* block is given, returns an enumerator.
* *
* e.g.: * e.g.:
* (1..10).each_slice(3) {|a| p a} * (1..10).each_slice(3) {|a| p a}
@ -192,9 +184,10 @@ each_cons_i(val, memo)
/* /*
* call-seq: * call-seq:
* each_cons(n) {...} * each_cons(n) {...}
* each_cons(n)
* *
* Iterates the given block for each array of consecutive <n> * Iterates the given block for each array of consecutive <n>
* elements. * elements. If no block is given, returns an enumerator.a
* *
* e.g.: * e.g.:
* (1..10).each_cons(3) {|a| p a} * (1..10).each_cons(3) {|a| p a}
@ -271,12 +264,8 @@ enumerator_init(enum_obj, obj, meth, argc, argv)
* used as an Enumerable object using the given object's given * used as an Enumerable object using the given object's given
* method with the given arguments. * method with the given arguments.
* *
* e.g.: * Use of this method is not discouraged. Use Kernel#enum_for()
* str = "xyz" * instead.
*
* enum = Enumerable::Enumerator.new(str, :each_byte)
* a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
*
*/ */
static VALUE static VALUE
enumerator_initialize(argc, argv, obj) enumerator_initialize(argc, argv, obj)
@ -330,7 +319,7 @@ rb_enumeratorize(obj, meth, argc, argv)
* enum.each {...} * enum.each {...}
* *
* Iterates the given block using the object and the method specified * Iterates the given block using the object and the method specified
* in the first place. * in the first place. If no block is given, returns self.
* *
*/ */
static VALUE static VALUE
@ -340,7 +329,6 @@ enumerator_each(obj)
struct enumerator *e; struct enumerator *e;
int argc = 0; int argc = 0;
VALUE *argv = 0; VALUE *argv = 0;
VALUE method;
if (!rb_block_given_p()) return obj; if (!rb_block_given_p()) return obj;
e = enumerator_ptr(obj); e = enumerator_ptr(obj);
@ -364,9 +352,10 @@ enumerator_with_index_i(val, memo)
/* /*
* call-seq: * call-seq:
* e.with_index {|(*args), idx| ... } * e.with_index {|(*args), idx| ... }
* e.with_index
* *
* Iterates the given block for each elements with an index, which * Iterates the given block for each elements with an index, which
* start from 0. * start from 0. If no block is given, returns an enumerator.
* *
*/ */
static VALUE static VALUE
@ -377,7 +366,6 @@ enumerator_with_index(obj)
VALUE memo = 0; VALUE memo = 0;
int argc = 0; int argc = 0;
VALUE *argv = 0; VALUE *argv = 0;
VALUE method;
RETURN_ENUMERATOR(obj, 0, 0); RETURN_ENUMERATOR(obj, 0, 0);
if (e->args) { if (e->args) {

4
eval.c
View file

@ -2440,6 +2440,8 @@ is_defined(self, node, buf)
case NODE_ATTRSET: case NODE_ATTRSET:
case NODE_OP_ASGN1: case NODE_OP_ASGN1:
case NODE_OP_ASGN2: case NODE_OP_ASGN2:
case NODE_OP_ASGN_OR:
case NODE_OP_ASGN_AND:
case NODE_MASGN: case NODE_MASGN:
case NODE_LASGN: case NODE_LASGN:
case NODE_DASGN: case NODE_DASGN:
@ -9917,6 +9919,8 @@ Init_Proc()
rb_define_method(rb_cUnboundMethod, "arity", method_arity, 0); rb_define_method(rb_cUnboundMethod, "arity", method_arity, 0);
rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0); rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0); rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "name", method_name, 0);
rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1); rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1);
} }

View file

@ -812,5 +812,7 @@ Init_dbm()
#ifdef DB_VERSION_STRING #ifdef DB_VERSION_STRING
rb_define_const(rb_cDBM, "VERSION", rb_str_new2(DB_VERSION_STRING)); rb_define_const(rb_cDBM, "VERSION", rb_str_new2(DB_VERSION_STRING));
#else
rb_define_const(rb_cDBM, "VERSION", rb_str_new2("unknown"));
#endif #endif
} }

View file

@ -1,3 +1,3 @@
if nul = $:.index("-") if nul = $:.find_index {|path| /\A(?:\.\/)*-\z/ =~ path}
$:[nul..-1] = ["."] $:[nul..-1] = ["."]
end end

View file

@ -1,3 +1,9 @@
2008-05-12 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tkextlib/tkDND/shape.rb: wrong package name.
--------------< ... some changes ... >------------------
2007-05-26 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> 2007-05-26 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
* ext/tk/lib/tkextlib/tcllib/tablelist.rb: fix typo. * ext/tk/lib/tkextlib/tcllib/tablelist.rb: fix typo.

View file

@ -775,7 +775,7 @@ end
private :_curr_cmd_id, :_next_cmd_id private :_curr_cmd_id, :_next_cmd_id
module_function :_curr_cmd_id, :_next_cmd_id module_function :_curr_cmd_id, :_next_cmd_id
def TkComm.install_cmd(cmd) def TkComm.install_cmd(cmd, local_cmdtbl=nil)
return '' if cmd == '' return '' if cmd == ''
begin begin
ns = TkCore::INTERP._invoke_without_enc('namespace', 'current') ns = TkCore::INTERP._invoke_without_enc('namespace', 'current')
@ -794,6 +794,15 @@ end
@cmdtbl = [] unless defined? @cmdtbl @cmdtbl = [] unless defined? @cmdtbl
@cmdtbl.taint unless @cmdtbl.tainted? @cmdtbl.taint unless @cmdtbl.tainted?
@cmdtbl.push id @cmdtbl.push id
if local_cmdtbl && local_cmdtbl.kind_of?(Array)
begin
local_cmdtbl << id
rescue Exception
# ignore
end
end
#return Kernel.format("rb_out %s", id); #return Kernel.format("rb_out %s", id);
if ns if ns
'rb_out' << TkCore::INTERP._ip_id_ << ' ' << ns << ' ' << id 'rb_out' << TkCore::INTERP._ip_id_ << ' ' << ns << ' ' << id
@ -801,19 +810,29 @@ end
'rb_out' << TkCore::INTERP._ip_id_ << ' ' << id 'rb_out' << TkCore::INTERP._ip_id_ << ' ' << id
end end
end end
def TkComm.uninstall_cmd(id) def TkComm.uninstall_cmd(id, local_cmdtbl=nil)
#id = $1 if /rb_out\S* (c(_\d+_)?\d+)/ =~ id #id = $1 if /rb_out\S* (c(_\d+_)?\d+)/ =~ id
id = $4 if id =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/ id = $4 if id =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/
if local_cmdtbl && local_cmdtbl.kind_of?(Array)
begin
local_cmdtbl.delete(id)
rescue Exception
# ignore
end
end
@cmdtbl.delete(id)
#Tk_CMDTBL.delete(id) #Tk_CMDTBL.delete(id)
TkCore::INTERP.tk_cmd_tbl.delete(id) TkCore::INTERP.tk_cmd_tbl.delete(id)
end end
# private :install_cmd, :uninstall_cmd # private :install_cmd, :uninstall_cmd
# module_function :install_cmd, :uninstall_cmd # module_function :install_cmd, :uninstall_cmd
def install_cmd(cmd) def install_cmd(cmd)
TkComm.install_cmd(cmd) TkComm.install_cmd(cmd, @cmdtbl)
end end
def uninstall_cmd(id) def uninstall_cmd(id)
TkComm.uninstall_cmd(id) TkComm.uninstall_cmd(id, @cmdtbl)
end end
=begin =begin
@ -1447,7 +1466,9 @@ module TkCore
def after(ms, cmd=Proc.new) def after(ms, cmd=Proc.new)
cmdid = install_cmd(proc{ret = cmd.call;uninstall_cmd(cmdid); ret}) cmdid = install_cmd(proc{ret = cmd.call;uninstall_cmd(cmdid); ret})
tk_call_without_enc("after",ms,cmdid) # return id after_id = tk_call_without_enc("after",ms,cmdid)
after_id.instance_variable_set('@cmdid', cmdid)
after_id
end end
=begin =begin
def after(ms, cmd=Proc.new) def after(ms, cmd=Proc.new)
@ -1477,7 +1498,9 @@ module TkCore
def after_idle(cmd=Proc.new) def after_idle(cmd=Proc.new)
cmdid = install_cmd(proc{ret = cmd.call;uninstall_cmd(cmdid); ret}) cmdid = install_cmd(proc{ret = cmd.call;uninstall_cmd(cmdid); ret})
tk_call_without_enc('after','idle',cmdid) after_id = tk_call_without_enc('after','idle',cmdid)
after_id.instance_variable_set('@cmdid', cmdid)
after_id
end end
=begin =begin
def after_idle(cmd=Proc.new) def after_idle(cmd=Proc.new)
@ -1495,6 +1518,11 @@ module TkCore
def after_cancel(afterId) def after_cancel(afterId)
tk_call_without_enc('after','cancel',afterId) tk_call_without_enc('after','cancel',afterId)
if (cmdid = afterId.instance_variable_get('@cmdid'))
afterId.instance_variable_set('@cmdid', nil)
uninstall_cmd(cmdid)
end
afterId
end end
def windowingsystem def windowingsystem
@ -4947,6 +4975,15 @@ class TkWindow<TkObject
self self
end end
def grid_anchor(anchor=None)
if anchor == None
TkGrid.anchor(self)
else
TkGrid.anchor(self, anchor)
self
end
end
def grid_forget def grid_forget
#tk_call('grid', 'forget', epath) #tk_call('grid', 'forget', epath)
TkGrid.forget(self) TkGrid.forget(self)
@ -4978,12 +5015,14 @@ class TkWindow<TkObject
TkGrid.columnconfigure(self, index, keys) TkGrid.columnconfigure(self, index, keys)
end end
alias grid_columnconfigure grid_columnconfig alias grid_columnconfigure grid_columnconfig
alias grid_column grid_columnconfig
def grid_rowconfig(index, keys) def grid_rowconfig(index, keys)
#tk_call('grid', 'rowconfigure', epath, index, *hash_kv(keys)) #tk_call('grid', 'rowconfigure', epath, index, *hash_kv(keys))
TkGrid.rowconfigure(self, index, keys) TkGrid.rowconfigure(self, index, keys)
end end
alias grid_rowconfigure grid_rowconfig alias grid_rowconfigure grid_rowconfig
alias grid_row grid_rowconfig
def grid_columnconfiginfo(index, slot=nil) def grid_columnconfiginfo(index, slot=nil)
#if slot #if slot
@ -5226,11 +5265,13 @@ class TkWindow<TkObject
end end
children.each{|path, obj| children.each{|path, obj|
obj.instance_eval{
if defined?(@cmdtbl) if defined?(@cmdtbl)
for id in @cmdtbl for id in @cmdtbl
uninstall_cmd id uninstall_cmd id
end end
end end
}
TkCore::INTERP.tk_windows.delete(path) TkCore::INTERP.tk_windows.delete(path)
} }
@ -5348,7 +5389,7 @@ TkWidget = TkWindow
#Tk.freeze #Tk.freeze
module Tk module Tk
RELEASE_DATE = '2008-04-18'.freeze RELEASE_DATE = '2008-05-16'.freeze
autoload :AUTO_PATH, 'tk/variable' autoload :AUTO_PATH, 'tk/variable'
autoload :TCL_PACKAGE_PATH, 'tk/variable' autoload :TCL_PACKAGE_PATH, 'tk/variable'

View file

@ -352,6 +352,14 @@ module TkEvent
nil nil
] ]
# [ <'%' subst-key str>, <proc type char>, <instance var (accessor) name>]
# the subst-key string will be converted to a bytecode (128+idx).
LONGKEY_TBL = [
# for example, for %CTT and %CST subst-key on tkdnd-2.0
# ['CTT', ?l, :drop_target_type],
# ['CST', ?l, :drop_source_type],
]
# [ <proc type char>, <proc/method to convert tcl-str to ruby-obj>] # [ <proc type char>, <proc/method to convert tcl-str to ruby-obj>]
PROC_TBL = [ PROC_TBL = [
[ ?n, TkComm.method(:num_or_str) ], [ ?n, TkComm.method(:num_or_str) ],
@ -371,6 +379,7 @@ module TkEvent
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -386,6 +395,7 @@ module TkEvent
end end
inf inf
} }
=end
# setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys # setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys
# #
@ -399,7 +409,8 @@ module TkEvent
# ( which are Tcl strings ) to ruby objects based on the key string # ( which are Tcl strings ) to ruby objects based on the key string
# that is generated by _get_subst_key() or _get_all_subst_keys(). # that is generated by _get_subst_key() or _get_all_subst_keys().
# #
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL)
# _setup_subst_table(KEY_TBL, LONGKEY_TBL, PROC_TBL) # if use longname-keys
# #
# NOTE: The order of parameters which passed to callback procedure is # NOTE: The order of parameters which passed to callback procedure is
@ -447,6 +458,7 @@ module TkEvent
extra_args_tbl = klass._get_extra_args_tbl extra_args_tbl = klass._get_extra_args_tbl
if args.compact.size > 0 if args.compact.size > 0
args.map!{|arg| klass._sym2subst(arg)}
args = args.join(' ') args = args.join(' ')
keys = klass._get_subst_key(args) keys = klass._get_subst_key(args)

View file

@ -22,6 +22,7 @@ module TkGrid
list(tk_call_without_enc('grid', 'bbox', *args)) list(tk_call_without_enc('grid', 'bbox', *args))
end end
=begin
def configure(win, *args) def configure(win, *args)
if args[-1].kind_of?(Hash) if args[-1].kind_of?(Hash)
opts = args.pop opts = args.pop
@ -53,6 +54,48 @@ module TkGrid
tk_call_without_enc('grid', 'configure', *params) tk_call_without_enc('grid', 'configure', *params)
end end
end end
=end
def configure(*args)
if args[-1].kind_of?(Hash)
opts = args.pop
else
opts = {}
end
fail ArgumentError, 'no widget is given' if args.empty?
params = []
args.flatten(1).each{|win|
case win
when '-', ?- # RELATIVE PLACEMENT (increase columnspan)
params.push('-')
when /^-+$/ # RELATIVE PLACEMENT (increase columnspan)
params.concat(win.to_s.split(//))
when '^', ?^ # RELATIVE PLACEMENT (increase rowspan)
params.push('^')
when /^\^+$/ # RELATIVE PLACEMENT (increase rowspan)
params.concat(win.to_s.split(//))
when 'x', :x, ?x, nil, '' # RELATIVE PLACEMENT (empty column)
params.push('x')
when /^x+$/ # RELATIVE PLACEMENT (empty column)
params.concat(win.to_s.split(//))
else
params.push(_epath(win))
end
}
opts.each{|k, v|
params.push("-#{k}")
params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
}
if Tk::TCL_MAJOR_VERSION < 8 ||
(Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION <= 3)
if params[0] == '-' || params[0] == 'x' || params[0] == '^'
tk_call_without_enc('grid', *params)
else
tk_call_without_enc('grid', 'configure', *params)
end
else
tk_call_without_enc('grid', 'configure', *params)
end
end
alias grid configure alias grid configure
def columnconfigure(master, index, args) def columnconfigure(master, index, args)
@ -61,12 +104,14 @@ module TkGrid
tk_call_without_enc("grid", 'columnconfigure', tk_call_without_enc("grid", 'columnconfigure',
master, index, *hash_kv(args)) master, index, *hash_kv(args))
end end
alias column columnconfigure
def rowconfigure(master, index, args) def rowconfigure(master, index, args)
# master = master.epath if master.kind_of?(TkObject) # master = master.epath if master.kind_of?(TkObject)
master = _epath(master) master = _epath(master)
tk_call_without_enc("grid", 'rowconfigure', master, index, *hash_kv(args)) tk_call_without_enc("grid", 'rowconfigure', master, index, *hash_kv(args))
end end
alias row rowconfigure
def columnconfiginfo(master, index, slot=nil) def columnconfiginfo(master, index, slot=nil)
# master = master.epath if master.kind_of?(TkObject) # master = master.epath if master.kind_of?(TkObject)
@ -189,10 +234,10 @@ module TkGrid
list(tk_call_without_enc('grid', 'slaves', master, *hash_kv(args))) list(tk_call_without_enc('grid', 'slaves', master, *hash_kv(args)))
end end
module_function :bbox, :forget, :propagate, :info module_function :anchor, :bbox, :add, :forget, :propagate, :info
module_function :remove, :size, :slaves, :location module_function :remove, :size, :slaves, :location
module_function :grid, :configure, :columnconfigure, :rowconfigure module_function :grid, :configure, :columnconfigure, :rowconfigure
module_function :columnconfiginfo, :rowconfiginfo module_function :column, :row, :columnconfiginfo, :rowconfiginfo
end end
=begin =begin
def TkGrid(win, *args) def TkGrid(win, *args)

View file

@ -9,6 +9,7 @@ module TkPack
TkCommandNames = ['pack'.freeze].freeze TkCommandNames = ['pack'.freeze].freeze
=begin
def configure(win, *args) def configure(win, *args)
if args[-1].kind_of?(Hash) if args[-1].kind_of?(Hash)
opts = args.pop opts = args.pop
@ -29,6 +30,22 @@ module TkPack
} }
tk_call_without_enc("pack", 'configure', *params) tk_call_without_enc("pack", 'configure', *params)
end end
=end
def configure(*args)
if args[-1].kind_of?(Hash)
opts = args.pop
else
opts = {}
end
fail ArgumentError, 'no widget is given' if args.empty?
params = []
args.flatten(1).each{|win| params.push(_epath(win))}
opts.each{|k, v|
params.push("-#{k}")
params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
}
tk_call_without_enc("pack", 'configure', *params)
end
alias pack configure alias pack configure
def forget(*args) def forget(*args)

View file

@ -37,6 +37,7 @@ class Tk::Spinbox<Tk::Entry
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -52,6 +53,7 @@ class Tk::Spinbox<Tk::Entry
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);

View file

@ -50,7 +50,8 @@ module Tk
key2class.each{|key, klass| key2class.each{|key, klass|
if keys[key].kind_of?(Array) if keys[key].kind_of?(Array)
cmd, *args = keys[key] cmd, *args = keys[key]
keys[key] = klass.new(cmd, args.join(' ')) #keys[key] = klass.new(cmd, args.join(' '))
keys[key] = klass.new(cmd, *args)
# elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method) # elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method)
elsif TkComm._callback_entry?(keys[key]) elsif TkComm._callback_entry?(keys[key])
keys[key] = klass.new(keys[key]) keys[key] = klass.new(keys[key])
@ -151,7 +152,8 @@ module Tk
key2class.each{|key, klass| key2class.each{|key, klass|
if keys[key].kind_of?(Array) if keys[key].kind_of?(Array)
cmd, *args = keys[key] cmd, *args = keys[key]
keys[key] = klass.new(cmd, args.join(' ')) #keys[key] = klass.new(cmd, args.join(' '))
keys[key] = klass.new(cmd, *args)
# elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method) # elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method)
elsif TkComm._callback_entry?(keys[key]) elsif TkComm._callback_entry?(keys[key])
keys[key] = klass.new(keys[key]) keys[key] = klass.new(keys[key])
@ -249,6 +251,7 @@ class TkValidateCommand
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -264,6 +267,7 @@ class TkValidateCommand
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -293,6 +297,7 @@ class TkValidateCommand
extra_args_tbl = klass._get_extra_args_tbl extra_args_tbl = klass._get_extra_args_tbl
if args.compact.size > 0 if args.compact.size > 0
args.map!{|arg| klass._sym2subst(arg)}
args = args.join(' ') args = args.join(' ')
keys = klass._get_subst_key(args) keys = klass._get_subst_key(args)
if cmd.kind_of?(String) if cmd.kind_of?(String)

View file

@ -362,7 +362,7 @@ module Tk
end end
end end
def overrideredirect(mode=TkComm::None) def overrideredirect(mode=TkComm::None)
Wm.overrideredirect(self, mode=TkComm::None) Wm.overrideredirect(self, mode)
end end
alias wm_overrideredirect overrideredirect alias wm_overrideredirect overrideredirect
TOPLEVEL_METHODCALL_OPTKEYS['overrideredirect'] = 'overrideredirect' TOPLEVEL_METHODCALL_OPTKEYS['overrideredirect'] = 'overrideredirect'
@ -545,7 +545,7 @@ module Tk
module Wm_for_General module Wm_for_General
Wm.instance_methods.each{|m| Wm.instance_methods.each{|m|
if (m = m.to_s) =~ /^wm_(.*)$/ if (m = m.to_s) =~ /^wm_(.*)$/
eval "def #{m}(*args); Tk::Wm.#{$1}(self, *args); end" eval "def #{m}(*args, &b); Tk::Wm.#{$1}(self, *args, &b); end"
end end
} }
end end

View file

@ -81,6 +81,7 @@ module Tk::BLT
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -96,6 +97,7 @@ module Tk::BLT
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL) _setup_subst_table(KEY_TBL, PROC_TBL)
@ -123,6 +125,7 @@ module Tk::BLT
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -138,6 +141,7 @@ module Tk::BLT
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL) _setup_subst_table(KEY_TBL, PROC_TBL)
@ -177,6 +181,7 @@ module Tk::BLT
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -192,6 +197,7 @@ module Tk::BLT
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL) _setup_subst_table(KEY_TBL, PROC_TBL)
end end

View file

@ -239,6 +239,7 @@ class Tk::BLT::Treeview
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -254,6 +255,7 @@ class Tk::BLT::Treeview
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -492,6 +494,7 @@ class Tk::BLT::Treeview
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -507,6 +510,7 @@ class Tk::BLT::Treeview
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -523,7 +527,8 @@ class Tk::BLT::Treeview
def _find_exec_flag_value(val) def _find_exec_flag_value(val)
if val.kind_of?(Array) if val.kind_of?(Array)
cmd, *args = val cmd, *args = val
FindExecFlagValue.new(cmd, args.join(' ')) #FindExecFlagValue.new(cmd, args.join(' '))
FindExecFlagValue.new(cmd, *args)
elsif TkComm._callback_entry?(val) elsif TkComm._callback_entry?(val)
FindExecFlagValue.new(val) FindExecFlagValue.new(val)
else else

View file

@ -46,6 +46,7 @@ class Tk::Iwidgets::Calendar
KEY_TBL = [ [?d, ?s, :date], nil ] KEY_TBL = [ [?d, ?s, :date], nil ]
PROC_TBL = [ [?s, TkComm.method(:string) ], nil ] PROC_TBL = [ [?s, TkComm.method(:string) ], nil ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -61,6 +62,7 @@ class Tk::Iwidgets::Calendar
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);

View file

@ -43,6 +43,7 @@ class Tk::Iwidgets::Entryfield
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -58,6 +59,7 @@ class Tk::Iwidgets::Entryfield
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
end end

View file

@ -31,6 +31,7 @@ class Tk::Iwidgets::Hierarchy
KEY_TBL = [ [?n, ?s, :node], nil ] KEY_TBL = [ [?n, ?s, :node], nil ]
PROC_TBL = [ [?s, TkComm.method(:string) ], nil ] PROC_TBL = [ [?s, TkComm.method(:string) ], nil ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -46,6 +47,7 @@ class Tk::Iwidgets::Hierarchy
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -74,6 +76,7 @@ class Tk::Iwidgets::Hierarchy
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -89,6 +92,7 @@ class Tk::Iwidgets::Hierarchy
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -112,6 +116,7 @@ class Tk::Iwidgets::Hierarchy
] ]
PROC_TBL = [ [ ?s, TkComm.method(:string) ], nil ] PROC_TBL = [ [ ?s, TkComm.method(:string) ], nil ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -127,6 +132,7 @@ class Tk::Iwidgets::Hierarchy
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);

View file

@ -38,6 +38,7 @@ class Tk::Iwidgets::Spinner
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -53,6 +54,7 @@ class Tk::Iwidgets::Spinner
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
end end

View file

@ -201,6 +201,38 @@ module Tk
args.map!{|arg| TkComm._get_eval_string(arg)}.join('.') args.map!{|arg| TkComm._get_eval_string(arg)}.join('.')
end end
def self.themes(glob_ptn = nil)
if TILE_SPEC_VERSION_ID < 8 && Tk.info(:commands, '::ttk::themes').empty?
fail RuntimeError, 'not support glob option' if glob_ptn
cmd = ['::tile::availableThemes']
else
glob_ptn = '*' unless glob_ptn
cmd = ['::ttk::themes', glob_ptn]
end
begin
TkComm.simplelist(Tk.tk_call_without_enc(*cmd))
rescue
TkComm.simplelist(Tk.tk_call('lsearch', '-all', '-inline',
Tk::Tile::Style.theme_names,
glob_ptn))
end
end
def self.set_theme(theme)
if TILE_SPEC_VERSION_ID < 8 && Tk.info(:commands, '::ttk::setTheme').empty?
cmd = '::tile::setTheme'
else
cmd = '::ttk::setTheme'
end
begin
Tk.tk_call_without_enc(cmd, theme)
rescue
Tk::Tile::Style.theme_use(theme)
end
end
module KeyNav module KeyNav
if Tk::Tile::TILE_SPEC_VERSION_ID < 8 if Tk::Tile::TILE_SPEC_VERSION_ID < 8
def self.enableMnemonics(w) def self.enableMnemonics(w)
@ -332,12 +364,16 @@ module Tk
autoload :TLabelframe, 'tkextlib/tile/tlabelframe' autoload :TLabelframe, 'tkextlib/tile/tlabelframe'
autoload :Labelframe, 'tkextlib/tile/tlabelframe' autoload :Labelframe, 'tkextlib/tile/tlabelframe'
autoload :TLabelFrame, 'tkextlib/tile/tlabelframe'
autoload :LabelFrame, 'tkextlib/tile/tlabelframe'
autoload :TLabel, 'tkextlib/tile/tlabel' autoload :TLabel, 'tkextlib/tile/tlabel'
autoload :Label, 'tkextlib/tile/tlabel' autoload :Label, 'tkextlib/tile/tlabel'
autoload :TMenubutton, 'tkextlib/tile/tmenubutton' autoload :TMenubutton, 'tkextlib/tile/tmenubutton'
autoload :Menubutton, 'tkextlib/tile/tmenubutton' autoload :Menubutton, 'tkextlib/tile/tmenubutton'
autoload :TMenuButton, 'tkextlib/tile/tmenubutton'
autoload :MenuButton, 'tkextlib/tile/tmenubutton'
autoload :TNotebook, 'tkextlib/tile/tnotebook' autoload :TNotebook, 'tkextlib/tile/tnotebook'
autoload :Notebook, 'tkextlib/tile/tnotebook' autoload :Notebook, 'tkextlib/tile/tnotebook'

View file

@ -33,6 +33,8 @@ class << Tk::Tile::Style
# conflict with some definitions on Tcl/Tk scripts. # conflict with some definitions on Tcl/Tk scripts.
if Tk::Tile::TILE_SPEC_VERSION_ID < 7 if Tk::Tile::TILE_SPEC_VERSION_ID < 7
def __define_wrapper_proc_for_compatibility__! def __define_wrapper_proc_for_compatibility__!
__define_themes_and_setTheme_proc__!
unless Tk.info(:commands, '::ttk::style').empty? unless Tk.info(:commands, '::ttk::style').empty?
# fail RuntimeError, # fail RuntimeError,
# "can't define '::ttk::style' command (already exist)" # "can't define '::ttk::style' command (already exist)"
@ -59,6 +61,8 @@ class << Tk::Tile::Style
end end
else ### TILE_SPEC_VERSION_ID == 7 else ### TILE_SPEC_VERSION_ID == 7
def __define_wrapper_proc_for_compatibility__! def __define_wrapper_proc_for_compatibility__!
__define_themes_and_setTheme_proc__!
unless Tk.info(:commands, '::ttk::style').empty? unless Tk.info(:commands, '::ttk::style').empty?
# fail RuntimeError, # fail RuntimeError,
# "can't define '::ttk::style' command (already exist)" # "can't define '::ttk::style' command (already exist)"
@ -91,6 +95,8 @@ class << Tk::Tile::Style
TkCommandNames = ['::ttk::style'.freeze].freeze TkCommandNames = ['::ttk::style'.freeze].freeze
def __define_wrapper_proc_for_compatibility__! def __define_wrapper_proc_for_compatibility__!
__define_themes_and_setTheme_proc__!
unless Tk.info(:commands, '::style').empty? unless Tk.info(:commands, '::style').empty?
# fail RuntimeError, "can't define '::style' command (already exist)" # fail RuntimeError, "can't define '::style' command (already exist)"
@ -120,6 +126,36 @@ class << Tk::Tile::Style
end end
end end
def __define_themes_and_setTheme_proc__!
TkCore::INTERP.add_tk_procs('::ttk::themes', '{ptn *}', <<-'EOS')
#set themes [list]
set themes [::ttk::style theme names]
foreach pkg [lsearch -inline -all -glob [package names] ttk::theme::$ptn] {
set theme [namespace tail $pkg]
if {[lsearch -exact $themes $theme] < 0} {
lappend themes $theme
}
}
foreach pkg [lsearch -inline -all -glob [package names] tile::theme::$ptn] {
set theme [namespace tail $pkg]
if {[lsearch -exact $themes $theme] < 0} {
lappend themes $theme
}
}
return $themes
EOS
#########################
TkCore::INTERP.add_tk_procs('::ttk::setTheme', 'theme', <<-'EOS')
variable currentTheme
if {[lsearch -exact [::ttk::style theme names] $theme] < 0} {
package require [lsearch -inline -regexp [package names] (ttk|tile)::theme::$theme]
}
::ttk::style theme use $theme
set currentTheme $theme
EOS
end
private :__define_themes_and_setTheme_proc__!
def configure(style=nil, keys=nil) def configure(style=nil, keys=nil)
if style.kind_of?(Hash) if style.kind_of?(Hash)
keys = style keys = style

View file

@ -77,9 +77,9 @@ class Tk::Tile::TNotebook < TkWindow
def add(child, keys=nil) def add(child, keys=nil)
if keys && keys != None if keys && keys != None
tk_send_without_enc('add', _epath(child), *hash_kv(keys)) tk_send('add', _epath(child), *hash_kv(keys))
else else
tk_send_without_enc('add', _epath(child)) tk_send('add', _epath(child))
end end
self self
end end

View file

@ -11,15 +11,15 @@ require 'tkextlib/setup.rb'
# call setup script # call setup script
require 'tkextlib/tkDND/setup.rb' require 'tkextlib/tkDND/setup.rb'
# TkPackage.require('shape', '0.3') # TkPackage.require('Shape', '0.3')
TkPackage.require('shape') TkPackage.require('Shape')
module Tk module Tk
module TkDND module TkDND
module Shape module Shape
extend TkCore extend TkCore
PACKAGE_NAME = 'shape'.freeze PACKAGE_NAME = 'Shape'.freeze
def self.package_name def self.package_name
PACKAGE_NAME PACKAGE_NAME
end end
@ -27,26 +27,28 @@ module Tk
=begin =begin
def self.package_version def self.package_version
begin begin
TkPackage.require('shape') TkPackage.require('Shape')
rescue rescue
'' ''
end end
end end
=end =end
def self.package_version class << self
def package_version
Tk.tk_call('set', 'shape_version') Tk.tk_call('set', 'shape_version')
end end
alias shape_version package_version alias shape_version package_version
def self.package_patchlevel def package_patchlevel
Tk.tk_call('set', 'shape_patchlevel') Tk.tk_call('set', 'shape_patchLevel')
end end
alias shape_patchlevel package_patchlevel alias shape_patchlevel package_patchlevel
def self.version def version
tk_call('shape', 'version') tk_call('shape', 'version')
end end
alias xshape_version version alias xshape_version version
end
############################ ############################

View file

@ -57,6 +57,7 @@ module Tk
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -72,6 +73,7 @@ module Tk
end end
inf inf
} }
=end
# setup tables # setup tables
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);

View file

@ -291,6 +291,7 @@ class Tk::TkTable
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -306,6 +307,7 @@ class Tk::TkTable
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -340,6 +342,7 @@ class Tk::TkTable
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -355,6 +358,7 @@ class Tk::TkTable
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -387,6 +391,7 @@ class Tk::TkTable
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -402,6 +407,7 @@ class Tk::TkTable
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -437,6 +443,7 @@ class Tk::TkTable
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -452,6 +459,7 @@ class Tk::TkTable
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
end end

View file

@ -137,6 +137,7 @@ class Tk::TreeCtrl::NotifyEvent
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -152,6 +153,7 @@ class Tk::TreeCtrl::NotifyEvent
end end
inf inf
} }
=end
# setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys # setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys
# #

View file

@ -2,5 +2,5 @@
# release date of tkextlib # release date of tkextlib
# #
module Tk module Tk
Tkextlib_RELEASE_DATE = '2008-04-18'.freeze Tkextlib_RELEASE_DATE = '2008-05-14'.freeze
end end

View file

@ -150,6 +150,7 @@ class Tk::Winico
nil nil
] ]
=begin
# for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
KEY_TBL.map!{|inf| KEY_TBL.map!{|inf|
if inf.kind_of?(Array) if inf.kind_of?(Array)
@ -165,6 +166,7 @@ class Tk::Winico
end end
inf inf
} }
=end
_setup_subst_table(KEY_TBL, PROC_TBL); _setup_subst_table(KEY_TBL, PROC_TBL);
@ -185,7 +187,8 @@ class Tk::Winico
Winico_callback._config_keys.each{|k| Winico_callback._config_keys.each{|k|
if keys[k].kind_of?(Array) if keys[k].kind_of?(Array)
cmd, *args = keys[k] cmd, *args = keys[k]
keys[k] = Winico_callback.new(cmd, args.join(' ')) #keys[k] = Winico_callback.new(cmd, args.join(' '))
keys[k] = Winico_callback.new(cmd, *args)
# elsif keys[k].kind_of?(Proc) # elsif keys[k].kind_of?(Proc)
elsif TkComm._callback_entry?(keys[k]) elsif TkComm._callback_entry?(keys[k])
keys[k] = Winico_callback.new(keys[k]) keys[k] = Winico_callback.new(keys[k])
@ -201,7 +204,8 @@ class Tk::Winico
Winico_callback._config_keys.each{|k| Winico_callback._config_keys.each{|k|
if keys[k].kind_of?(Array) if keys[k].kind_of?(Array)
cmd, *args = keys[k] cmd, *args = keys[k]
keys[k] = Winico_callback.new(cmd, args.join(' ')) #keys[k] = Winico_callback.new(cmd, args.join(' '))
keys[k] = Winico_callback.new(cmd, *args)
# elsif keys[k].kind_of?(Proc) # elsif keys[k].kind_of?(Proc)
elsif TkComm._callback_entry?(keys[k]) elsif TkComm._callback_entry?(keys[k])
keys[k] = Winico_callback.new(keys[k]) keys[k] = Winico_callback.new(keys[k])

View file

@ -58,6 +58,7 @@ class AnimatedWaveDemo
@backupCoords = [] @backupCoords = []
n = 0 n = 0
(-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] } (-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] }
n = 305
@waveCoords << [n, 0]; @backupCoords << [n, 0] @waveCoords << [n, 0]; @backupCoords << [n, 0]
@waveCoords << [n+5, 200]; @backupCoords << [n+5, 200] @waveCoords << [n+5, 200]; @backupCoords << [n+5, 200]
@coordsLen = @waveCoords.length @coordsLen = @waveCoords.length

View file

@ -60,6 +60,7 @@ class AnimatedWaveDemo
@backupCoords = [] @backupCoords = []
n = 0 n = 0
(-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] } (-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] }
n = 305
@waveCoords << [n, 0]; @backupCoords << [n, 0] @waveCoords << [n, 0]; @backupCoords << [n, 0]
@waveCoords << [n+5, 200]; @backupCoords << [n+5, 200] @waveCoords << [n+5, 200]; @backupCoords << [n+5, 200]
@coordsLen = @waveCoords.length @coordsLen = @waveCoords.length

View file

@ -4,7 +4,7 @@
# #
# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) # by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
# #
version = '0.1.1' version = '0.1.3'
# #
########################################################################## ##########################################################################
# parse commandline arguments # parse commandline arguments
@ -107,38 +107,37 @@ TkConfigMethod.__set_IGNORE_UNKNOWN_CONFIGURE_OPTION__! true
TkItemConfigMethod.__set_IGNORE_UNKNOWN_CONFIGURE_OPTION__! true TkItemConfigMethod.__set_IGNORE_UNKNOWN_CONFIGURE_OPTION__! true
##########################################################################
# define utility method
##########################################################################
def setTheme(theme)
unless Tk::Tile::Style.theme_names.find{|n| n == theme}
if (pkg = TkPackage.names.find{|n| n =~ /(tile|ttk)::theme::#{theme}/})
TkPackage.require(pkg)
end
end
Tk::Tile::Style.theme_use(theme)
end
##########################################################################
# make theme name list
##########################################################################
ThemesList = Tk::Tile::Style.theme_names
TkPackage.names.find_all{|n| n =~ /^(tile|ttk)::theme::/}.each{|pkg|
ThemesList << pkg.split('::')[-1]
}
ThemesList.uniq!
########################################################################## ##########################################################################
# set theme of widget style # set theme of widget style
########################################################################## ##########################################################################
if OPTS[:list] || OPTS[:verbose] if OPTS[:list] || OPTS[:verbose]
print "supported theme names: #{ThemesList.inspect}\n" print "supported theme names: #{Tk::Tile.themes.inspect}\n"
exit if OPTS[:list] && ARGV.empty? exit if OPTS[:list] && ARGV.empty?
end end
print "use theme: \"#{OPTS[:theme]}\"\n" if OPTS[:theme] && OPTS[:verbose] print "use theme: \"#{OPTS[:theme]}\"\n" if OPTS[:theme] && OPTS[:verbose]
setTheme(OPTS[:theme]) if OPTS[:theme] #setTheme(OPTS[:theme]) if OPTS[:theme]
Tk::Tile.set_theme(OPTS[:theme]) if OPTS[:theme]
##########################################################################
# replace $0 and $RPAGRAM_NAME
##########################################################################
# When the expand_path of the target script is long, ruby sometimes
# fails to set the path to $0 (the path string is trimmed).
# The following replaces $0 and $PROGNAME to avoid such trouble.
progname_obj = $0.dup
$program_name = progname_obj
alias $REAL_PROGRAM_NAME $0
alias $PROGRAM_NAME $program_name
alias $0 $program_name
trace_var(:$program_name){|val|
unless progname_obj.object_id == val.object_id
progname_obj.replace(val.to_s)
$program_name = progname_obj
end
}
########################################################################## ##########################################################################
@ -146,6 +145,7 @@ setTheme(OPTS[:theme]) if OPTS[:theme]
########################################################################## ##########################################################################
if (path = ARGV.shift) && (script = File.expand_path(path)) if (path = ARGV.shift) && (script = File.expand_path(path))
print "load script \"#{script}\"\n" if OPTS[:verbose] print "load script \"#{script}\"\n" if OPTS[:verbose]
$0 = script
load(script) load(script)
else else
print "Error: no script is given.\n" print "Error: no script is given.\n"

View file

@ -4,7 +4,7 @@
* Oct. 24, 1997 Y. Matsumoto * Oct. 24, 1997 Y. Matsumoto
*/ */
#define TCLTKLIB_RELEASE_DATE "2008-04-02" #define TCLTKLIB_RELEASE_DATE "2008-05-16"
#include "ruby.h" #include "ruby.h"
@ -3153,6 +3153,7 @@ ip_ruby_cmd(clientData, interp, argc, argv)
str, "'", (char *)NULL); str, "'", (char *)NULL);
rbtk_pending_exception = rb_exc_new2(rb_eArgError, rbtk_pending_exception = rb_exc_new2(rb_eArgError,
Tcl_GetStringResult(interp)); Tcl_GetStringResult(interp));
if (old_gc == Qfalse) rb_gc_enable();
return TCL_ERROR; return TCL_ERROR;
#endif #endif
} }
@ -5155,6 +5156,8 @@ ip_finalize(ip)
Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc, Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc,
(ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
#endif #endif
rb_thread_critical = thr_crit_bup;
return;
} }
/* delete root widget */ /* delete root widget */
@ -5162,7 +5165,7 @@ ip_finalize(ip)
DUMP1("check `destroy'"); DUMP1("check `destroy'");
if (Tcl_GetCommandInfo(ip, "destroy", &info)) { if (Tcl_GetCommandInfo(ip, "destroy", &info)) {
DUMP1("call `destroy'"); DUMP1("call `destroy'");
Tcl_GlobalEval(ip, "destroy ."); Tcl_GlobalEval(ip, "catch {destroy .}");
} }
#endif #endif
#if 1 #if 1
@ -7106,7 +7109,8 @@ lib_toUTF8_core(ip_obj, src, encodename)
if (NIL_P(enc)) { if (NIL_P(enc)) {
encoding = (Tcl_Encoding)NULL; encoding = (Tcl_Encoding)NULL;
} else { } else {
StringValue(enc); /* StringValue(enc); */
enc = rb_funcall(enc, ID_to_s, 0, 0);
/* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */ /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
RSTRING_PTR(enc)); RSTRING_PTR(enc));
@ -7292,7 +7296,8 @@ lib_fromUTF8_core(ip_obj, src, encodename)
if (NIL_P(enc)) { if (NIL_P(enc)) {
encoding = (Tcl_Encoding)NULL; encoding = (Tcl_Encoding)NULL;
} else { } else {
StringValue(enc); /* StringValue(enc); */
enc = rb_funcall(enc, ID_to_s, 0, 0);
/* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */ /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
RSTRING_PTR(enc)); RSTRING_PTR(enc));

View file

@ -8,5 +8,6 @@ end
if has_tk if has_tk
require 'mkmf' require 'mkmf'
have_func("rb_obj_instance_exec", "ruby.h") have_func("rb_obj_instance_exec", "ruby.h")
have_func("strndup", "string.h")
create_makefile('tkutil') create_makefile('tkutil')
end end

View file

@ -7,7 +7,7 @@
************************************************/ ************************************************/
#define TKUTIL_RELEASE_DATE "2008-03-29" #define TKUTIL_RELEASE_DATE "2008-05-14"
#include "ruby.h" #include "ruby.h"
@ -1073,11 +1073,13 @@ tcl2rb_num_or_str(self, value)
/*************************************/ /*************************************/
#define CBSUBST_TBL_MAX (256)
struct cbsubst_info { struct cbsubst_info {
int size; int full_subst_length;
char *key; int keylen[CBSUBST_TBL_MAX];
char *type; unsigned char *key[CBSUBST_TBL_MAX];
ID *ivar; unsigned char type[CBSUBST_TBL_MAX];
ID ivar[CBSUBST_TBL_MAX];
VALUE proc; VALUE proc;
VALUE aliases; VALUE aliases;
}; };
@ -1094,33 +1096,33 @@ static void
subst_free(ptr) subst_free(ptr)
struct cbsubst_info *ptr; struct cbsubst_info *ptr;
{ {
int i;
if (ptr) { if (ptr) {
if (ptr->key != (char*)NULL) free(ptr->key); for(i = 0; i < CBSUBST_TBL_MAX; i++) {
if (ptr->type != (char*)NULL) free(ptr->type); if (ptr->key[i] != (unsigned char *)NULL) free(ptr->key[i]);
if (ptr->ivar != (ID*)NULL) free(ptr->ivar); }
free(ptr); free(ptr);
} }
} }
static void static struct cbsubst_info *
cbsubst_init() allocate_cbsubst_info()
{ {
struct cbsubst_info *inf; struct cbsubst_info *inf;
ID *ivar;
volatile VALUE proc, aliases; volatile VALUE proc, aliases;
int idx;
inf = ALLOC(struct cbsubst_info); inf = ALLOC(struct cbsubst_info);
inf->size = 0; inf->full_subst_length = 0;
inf->key = ALLOC_N(char, 1); for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
inf->key[0] = '\0'; inf->keylen[idx] = 0;
inf->key[idx] = (unsigned char *) NULL;
inf->type = ALLOC_N(char, 1); inf->type[idx] = '\0';
inf->type[0] = '\0'; inf->ivar[idx] = (ID) 0;
}
ivar = ALLOC_N(ID, 1);
inf->ivar = ivar;
proc = rb_hash_new(); proc = rb_hash_new();
inf->proc = proc; inf->proc = proc;
@ -1128,8 +1130,15 @@ cbsubst_init()
aliases = rb_hash_new(); aliases = rb_hash_new();
inf->aliases = aliases; inf->aliases = aliases;
return inf;
}
static void
cbsubst_init()
{
rb_const_set(cCB_SUBST, ID_SUBST_INFO, rb_const_set(cCB_SUBST, ID_SUBST_INFO,
Data_Wrap_Struct(cSUBST_INFO, subst_mark, subst_free, inf)); Data_Wrap_Struct(cSUBST_INFO, subst_mark, subst_free,
allocate_cbsubst_info()));
} }
static VALUE static VALUE
@ -1139,24 +1148,29 @@ cbsubst_initialize(argc, argv, self)
VALUE self; VALUE self;
{ {
struct cbsubst_info *inf; struct cbsubst_info *inf;
int idx; int idx, iv_idx;
Data_Get_Struct(rb_const_get(rb_obj_class(self), ID_SUBST_INFO), Data_Get_Struct(rb_const_get(rb_obj_class(self), ID_SUBST_INFO),
struct cbsubst_info, inf); struct cbsubst_info, inf);
for(idx = 0; idx < argc; idx++) { idx = 0;
rb_ivar_set(self, inf->ivar[idx], argv[idx]); for(iv_idx = 0; iv_idx < CBSUBST_TBL_MAX; iv_idx++) {
if ( inf->ivar[iv_idx] == (ID) 0 ) continue;
rb_ivar_set(self, inf->ivar[iv_idx], argv[idx++]);
if (idx >= argc) break;
} }
return self; return self;
} }
static VALUE static VALUE
cbsubst_ret_val(self, val) cbsubst_ret_val(self, val)
VALUE self; VALUE self;
VALUE val; VALUE val;
{ {
/* This method may be overwritten on some sub-classes. */
/* This method is used for converting from ruby's callback-return-value */
/* to tcl's value (e.g. validation procedure of entry widget). */
return val; return val;
} }
@ -1216,6 +1230,59 @@ cbsubst_def_attr_aliases(self, tbl)
return rb_funcall(inf->aliases, rb_intern("update"), 1, tbl); return rb_funcall(inf->aliases, rb_intern("update"), 1, tbl);
} }
static VALUE
cbsubst_sym_to_subst(self, sym)
VALUE self;
VALUE sym;
{
struct cbsubst_info *inf;
const char *str;
unsigned char *buf, *ptr;
int idx, len;
ID id;
volatile VALUE ret;
if (TYPE(sym) != T_SYMBOL) return sym;
Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
struct cbsubst_info, inf);
if (!NIL_P(ret = rb_hash_aref(inf->aliases, sym))) {
str = rb_id2name(SYM2ID(ret));
} else {
str = rb_id2name(SYM2ID(sym));
}
id = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), str)));
for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
if (inf->ivar[idx] == id) break;
}
if (idx >= CBSUBST_TBL_MAX) return sym;
ptr = buf = ALLOC_N(char, inf->full_subst_length + 1);
*(ptr++) = '%';
if (len = inf->keylen[idx]) {
/* longname */
strncpy(ptr, inf->key[idx], len);
ptr += len;
} else {
/* single char */
*(ptr++) = idx;
}
*(ptr++) = ' ';
*(ptr++) = '\0';
ret = rb_str_new2(buf);
free(buf);
return ret;
}
static VALUE static VALUE
cbsubst_get_subst_arg(argc, argv, self) cbsubst_get_subst_arg(argc, argv, self)
int argc; int argc;
@ -1224,17 +1291,15 @@ cbsubst_get_subst_arg(argc, argv, self)
{ {
struct cbsubst_info *inf; struct cbsubst_info *inf;
const char *str; const char *str;
char *buf, *ptr; unsigned char *buf, *ptr;
int i, j, len; int i, idx, len;
ID id; ID id;
volatile VALUE arg_sym, ret; volatile VALUE arg_sym, ret;
Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
struct cbsubst_info, inf); struct cbsubst_info, inf);
buf = ALLOC_N(char, 3*argc + 1); ptr = buf = ALLOC_N(char, inf->full_subst_length + 1);
ptr = buf;
len = strlen(inf->key);
for(i = 0; i < argc; i++) { for(i = 0; i < argc; i++) {
switch(TYPE(argv[i])) { switch(TYPE(argv[i])) {
@ -1256,16 +1321,24 @@ cbsubst_get_subst_arg(argc, argv, self)
id = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), str))); id = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), str)));
for(j = 0; j < len; j++) { for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
if (inf->ivar[j] == id) break; if (inf->ivar[idx] == id) break;
} }
if (idx >= CBSUBST_TBL_MAX) {
if (j >= len) {
rb_raise(rb_eArgError, "cannot find attribute :%s", str); rb_raise(rb_eArgError, "cannot find attribute :%s", str);
} }
*(ptr++) = '%'; *(ptr++) = '%';
*(ptr++) = *(inf->key + j);
if (len = inf->keylen[idx]) {
/* longname */
strncpy(ptr, inf->key[idx], len);
ptr += len;
} else {
/* single char */
*(ptr++) = idx;
}
*(ptr++) = ' '; *(ptr++) = ' ';
} }
@ -1283,27 +1356,50 @@ cbsubst_get_subst_key(self, str)
VALUE self; VALUE self;
VALUE str; VALUE str;
{ {
struct cbsubst_info *inf;
volatile VALUE list; volatile VALUE list;
volatile VALUE ret; volatile VALUE ret;
int i, len; VALUE keyval;
char *buf, *ptr; int i, len, keylen, idx;
unsigned char *buf, *ptr, *key;
list = rb_funcall(cTclTkLib, ID_split_tklist, 1, str); list = rb_funcall(cTclTkLib, ID_split_tklist, 1, str);
len = RARRAY_LEN(list); len = RARRAY_LEN(list);
buf = ALLOC_N(char, len + 1);
Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
struct cbsubst_info, inf);
ptr = buf = ALLOC_N(unsigned char, inf->full_subst_length + len + 1);
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
ptr = RSTRING_PTR(RARRAY_PTR(list)[i]); keyval = RARRAY_PTR(list)[i];
if (*ptr == '%' && *(ptr + 2) == '\0') { key = (unsigned char*)RSTRING_PTR(keyval);
*(buf + i) = *(ptr + 1); if (*key == '%') {
if (*(key + 2) == '\0') {
/* single char */
*(ptr++) = *(key + 1);
} else { } else {
*(buf + i) = ' '; /* search longname-key */
keylen = RSTRING_LEN(keyval) - 1;
for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
if (inf->keylen[idx] != keylen) continue;
if (inf->key[idx][0] != *(key + 1)) continue;
if (strncmp(inf->key[idx], key + 1, keylen)) continue;
break;
}
if (idx < CBSUBST_TBL_MAX) {
*(ptr++) = (unsigned char)idx;
} else {
*(ptr++) = ' ';
} }
} }
*(buf + len) = '\0'; } else {
*(ptr++) = ' ';
}
}
*ptr = '\0';
ret = rb_str_new2(buf); ret = rb_str_new2((const char*)buf);
free(buf); free(buf);
return ret; return ret;
} }
@ -1313,24 +1409,40 @@ cbsubst_get_all_subst_keys(self)
VALUE self; VALUE self;
{ {
struct cbsubst_info *inf; struct cbsubst_info *inf;
char *buf, *ptr; unsigned char *buf, *ptr;
int i, len; unsigned char *keys_buf, *keys_ptr;
int idx, len;
volatile VALUE ret; volatile VALUE ret;
Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
struct cbsubst_info, inf); struct cbsubst_info, inf);
len = strlen(inf->key); ptr = buf = ALLOC_N(unsigned char, inf->full_subst_length + 1);
buf = ALLOC_N(char, 3*len + 1); keys_ptr = keys_buf = ALLOC_N(unsigned char, CBSUBST_TBL_MAX + 1);
ptr = buf;
for(i = 0; i < len; i++) { for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
if (inf->ivar[idx] == (ID) 0) continue;
*(keys_ptr++) = (unsigned char)idx;
*(ptr++) = '%'; *(ptr++) = '%';
*(ptr++) = *(inf->key + i);
if (len = inf->keylen[idx]) {
/* longname */
strncpy(ptr, inf->key[idx], len);
ptr += len;
} else {
/* single char */
*(ptr++) = (unsigned char)idx;
}
*(ptr++) = ' '; *(ptr++) = ' ';
} }
*(buf + 3*len) = '\0';
ret = rb_ary_new3(2, rb_str_new2(inf->key), rb_str_new2(buf)); *ptr = '\0';
*keys_ptr = '\0';
ret = rb_ary_new3(2, rb_str_new2(keys_buf), rb_str_new2((const char*)buf));
free(buf); free(buf);
@ -1338,68 +1450,117 @@ cbsubst_get_all_subst_keys(self)
} }
static VALUE static VALUE
cbsubst_table_setup(self, key_inf, proc_inf) cbsubst_table_setup(argc, argv, self)
int argc;
VALUE *argv;
VALUE self; VALUE self;
VALUE key_inf;
VALUE proc_inf;
{ {
volatile VALUE key_inf;
volatile VALUE longkey_inf;
volatile VALUE proc_inf;
VALUE inf;
ID id;
struct cbsubst_info *subst_inf; struct cbsubst_info *subst_inf;
int idx; int idx, len;
int len = RARRAY_LEN(key_inf); unsigned char chr;
int real_len = 0;
char *key = ALLOC_N(char, len + 1); /* accept (key_inf, proc_inf) or (key_inf, longkey_inf, procinf) */
char *type = ALLOC_N(char, len + 1); if (rb_scan_args(argc, argv, "21", &key_inf, &longkey_inf, &proc_inf) == 2) {
ID *ivar = ALLOC_N(ID, len + 1); proc_inf = longkey_inf;
volatile VALUE proc = rb_hash_new(); longkey_inf = rb_ary_new();
volatile VALUE aliases = rb_hash_new(); }
volatile VALUE inf;
/* check the number of longkeys */
if (RARRAY_LEN(longkey_inf) > 125 /* from 0x80 to 0xFD */) {
rb_raise(rb_eArgError, "too many longname-key definitions");
}
/* init */ /* init */
subst_inf = ALLOC(struct cbsubst_info); subst_inf = allocate_cbsubst_info();
/* subst_inf->size = len; */
subst_inf->key = key;
subst_inf->type = type;
subst_inf->ivar = ivar;
subst_inf->proc = proc;
subst_inf->aliases = aliases;
/* /*
* keys : array of [subst, type, ivar] * keys : array of [subst, type, ivar]
* subst ==> char code * subst ==> char code or string
* type ==> char code * type ==> char code or string
* ivar ==> symbol * ivar ==> symbol
*/ */
len = RARRAY_LEN(key_inf);
for(idx = 0; idx < len; idx++) { for(idx = 0; idx < len; idx++) {
inf = RARRAY_PTR(key_inf)[idx]; inf = RARRAY_PTR(key_inf)[idx];
if (TYPE(inf) != T_ARRAY) continue; if (TYPE(inf) != T_ARRAY) continue;
*(key + real_len) = NUM2CHR(RARRAY_PTR(inf)[0]);
*(type + real_len) = NUM2CHR(RARRAY_PTR(inf)[1]);
*(ivar + real_len) if (TYPE(RARRAY_PTR(inf)[0]) == T_STRING) {
= rb_intern( chr = *(RSTRING_PTR(RARRAY_PTR(inf)[0]));
RSTRING_PTR( } else {
rb_str_cat2(rb_str_new2("@"), chr = NUM2CHR(RARRAY_PTR(inf)[0]);
rb_id2name(SYM2ID(RARRAY_PTR(inf)[2]))) }
) if (TYPE(RARRAY_PTR(inf)[1]) == T_STRING) {
); subst_inf->type[chr] = *(RSTRING_PTR(RARRAY_PTR(inf)[1]));
} else {
rb_attr(self, SYM2ID(RARRAY_PTR(inf)[2]), 1, 0, Qtrue); subst_inf->type[chr] = NUM2CHR(RARRAY_PTR(inf)[1]);
real_len++; }
subst_inf->full_subst_length += 3;
id = SYM2ID(RARRAY_PTR(inf)[2]);
subst_inf->ivar[chr] = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), rb_id2name(id))));
rb_attr(self, id, 1, 0, Qtrue);
}
/*
* longkeys : array of [name, type, ivar]
* name ==> longname key string
* type ==> char code or string
* ivar ==> symbol
*/
len = RARRAY_LEN(longkey_inf);
for(idx = 0; idx < len; idx++) {
inf = RARRAY_PTR(longkey_inf)[idx];
if (TYPE(inf) != T_ARRAY) continue;
chr = (unsigned char)(0x80 + idx);
subst_inf->keylen[chr] = RSTRING_LEN(RARRAY_PTR(inf)[0]);
#if HAVE_STRNDUP
subst_inf->key[chr] = strndup(RSTRING_PTR(RARRAY_PTR(inf)[0]),
RSTRING_LEN(RARRAY_PTR(inf)[0]));
#else
subst_inf->key[chr] = malloc(RSTRING_LEN(RARRAY_PTR(inf)[0]) + 1);
if (subst_inf->key[chr]) {
strncpy(subst_inf->key[chr], RSTRING_PTR(RARRAY_PTR(inf)[0]),
RSTRING_LEN(RARRAY_PTR(inf)[0]) + 1);
subst_inf->key[chr][RSTRING_LEN(RARRAY_PTR(inf)[0])] = '\0';
}
#endif
if (TYPE(RARRAY_PTR(inf)[1]) == T_STRING) {
subst_inf->type[chr] = *(RSTRING_PTR(RARRAY_PTR(inf)[1]));
} else {
subst_inf->type[chr] = NUM2CHR(RARRAY_PTR(inf)[1]);
}
subst_inf->full_subst_length += (subst_inf->keylen[chr] + 2);
id = SYM2ID(RARRAY_PTR(inf)[2]);
subst_inf->ivar[chr] = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), rb_id2name(id))));
rb_attr(self, id, 1, 0, Qtrue);
} }
*(key + real_len) = '\0';
*(type + real_len) = '\0';
subst_inf->size = real_len;
/* /*
* procs : array of [type, proc] * procs : array of [type, proc]
* type ==> char code * type ==> char code or string
* proc ==> proc/method/obj (must respond to 'call') * proc ==> proc/method/obj (must respond to 'call')
*/ */
len = RARRAY_LEN(proc_inf); len = RARRAY_LEN(proc_inf);
for(idx = 0; idx < len; idx++) { for(idx = 0; idx < len; idx++) {
inf = RARRAY_PTR(proc_inf)[idx]; inf = RARRAY_PTR(proc_inf)[idx];
if (TYPE(inf) != T_ARRAY) continue; if (TYPE(inf) != T_ARRAY) continue;
rb_hash_aset(proc, RARRAY_PTR(inf)[0], RARRAY_PTR(inf)[1]); rb_hash_aset(subst_inf->proc,
((TYPE(RARRAY_PTR(inf)[0]) == T_STRING)?
INT2FIX(*(RSTRING_PTR(RARRAY_PTR(inf)[0]))) :
RARRAY_PTR(inf)[0]),
RARRAY_PTR(inf)[1]);
} }
rb_const_set(self, ID_SUBST_INFO, rb_const_set(self, ID_SUBST_INFO,
@ -1424,10 +1585,11 @@ cbsubst_scan_args(self, arg_key, val_ary)
{ {
struct cbsubst_info *inf; struct cbsubst_info *inf;
int idx; int idx;
int len = RARRAY_LEN(val_ary); unsigned char *keyptr = (unsigned char*)RSTRING_PTR(arg_key);
char c; int keylen = RSTRING_LEN(arg_key);
char *ptr; int vallen = RARRAY_LEN(val_ary);
volatile VALUE dst = rb_ary_new2(len); unsigned char type_chr;
volatile VALUE dst = rb_ary_new2(vallen);
volatile VALUE proc; volatile VALUE proc;
int thr_crit_bup; int thr_crit_bup;
VALUE old_gc; VALUE old_gc;
@ -1440,25 +1602,24 @@ cbsubst_scan_args(self, arg_key, val_ary)
Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
struct cbsubst_info, inf); struct cbsubst_info, inf);
for(idx = 0; idx < len; idx++) { for(idx = 0; idx < vallen; idx++) {
if (idx >= RSTRING_LEN(arg_key)) { if (idx >= keylen) {
proc = Qnil; proc = Qnil;
} else if (*(RSTRING_PTR(arg_key) + idx) == ' ') { } else if (*(keyptr + idx) == ' ') {
proc = Qnil; proc = Qnil;
} else { } else {
ptr = strchr(inf->key, *(RSTRING_PTR(arg_key) + idx)); if (type_chr = inf->type[*(keyptr + idx)]) {
if (ptr == (char*)NULL) { proc = rb_hash_aref(inf->proc, INT2FIX((int)type_chr));
proc = Qnil;
} else { } else {
c = *(inf->type + (ptr - inf->key)); proc = Qnil;
proc = rb_hash_aref(inf->proc, INT2FIX(c));
} }
} }
if (NIL_P(proc)) { if (NIL_P(proc)) {
rb_ary_push(dst, RARRAY_PTR(val_ary)[idx]); rb_ary_push(dst, RARRAY_PTR(val_ary)[idx]);
} else { } else {
rb_ary_push(dst, rb_funcall(proc, ID_call, 1, RARRAY_PTR(val_ary)[idx])); rb_ary_push(dst, rb_funcall(proc, ID_call, 1,
RARRAY_PTR(val_ary)[idx]));
} }
} }
@ -1543,6 +1704,8 @@ Init_tkutil()
ID_SUBST_INFO = rb_intern("SUBST_INFO"); ID_SUBST_INFO = rb_intern("SUBST_INFO");
rb_define_singleton_method(cCB_SUBST, "ret_val", cbsubst_ret_val, 1); rb_define_singleton_method(cCB_SUBST, "ret_val", cbsubst_ret_val, 1);
rb_define_singleton_method(cCB_SUBST, "scan_args", cbsubst_scan_args, 2); rb_define_singleton_method(cCB_SUBST, "scan_args", cbsubst_scan_args, 2);
rb_define_singleton_method(cCB_SUBST, "_sym2subst",
cbsubst_sym_to_subst, 1);
rb_define_singleton_method(cCB_SUBST, "subst_arg", rb_define_singleton_method(cCB_SUBST, "subst_arg",
cbsubst_get_subst_arg, -1); cbsubst_get_subst_arg, -1);
rb_define_singleton_method(cCB_SUBST, "_get_subst_key", rb_define_singleton_method(cCB_SUBST, "_get_subst_key",
@ -1550,7 +1713,7 @@ Init_tkutil()
rb_define_singleton_method(cCB_SUBST, "_get_all_subst_keys", rb_define_singleton_method(cCB_SUBST, "_get_all_subst_keys",
cbsubst_get_all_subst_keys, 0); cbsubst_get_all_subst_keys, 0);
rb_define_singleton_method(cCB_SUBST, "_setup_subst_table", rb_define_singleton_method(cCB_SUBST, "_setup_subst_table",
cbsubst_table_setup, 2); cbsubst_table_setup, -1);
rb_define_singleton_method(cCB_SUBST, "_get_extra_args_tbl", rb_define_singleton_method(cCB_SUBST, "_get_extra_args_tbl",
cbsubst_get_extra_args_tbl, 0); cbsubst_get_extra_args_tbl, 0);
rb_define_singleton_method(cCB_SUBST, "_define_attribute_aliases", rb_define_singleton_method(cCB_SUBST, "_define_attribute_aliases",

View file

@ -3111,6 +3111,8 @@ gzreader_gets(argc, argv, obj)
if (NIL_P(rs)) { if (NIL_P(rs)) {
dst = gzfile_read_all(gz); dst = gzfile_read_all(gz);
if (RSTRING(dst)->len != 0) gz->lineno++; if (RSTRING(dst)->len != 0) gz->lineno++;
else
return Qnil;
return dst; return dst;
} }

213
file.c
View file

@ -15,6 +15,10 @@
#ifdef _WIN32 #ifdef _WIN32
#include "missing/file.h" #include "missing/file.h"
#endif #endif
#ifdef __CYGWIN__
#include <windows.h>
#include <sys/cygwin.h>
#endif
#include "ruby.h" #include "ruby.h"
#include "rubyio.h" #include "rubyio.h"
@ -2310,6 +2314,18 @@ rb_file_s_umask(argc, argv)
#define isdirsep(x) ((x) == '/') #define isdirsep(x) ((x) == '/')
#endif #endif
#if defined _WIN32 || defined __CYGWIN__
#define USE_NTFS 1
#else
#define USE_NTFS 0
#endif
#if USE_NTFS
#define istrailinggabage(x) ((x) == '.' || (x) == ' ')
#else
#define istrailinggabage(x) 0
#endif
#ifndef CharNext /* defined as CharNext[AW] on Windows. */ #ifndef CharNext /* defined as CharNext[AW] on Windows. */
# if defined(DJGPP) # if defined(DJGPP)
# define CharNext(p) ((p) + mblen(p, MB_CUR_MAX)) # define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
@ -2454,6 +2470,30 @@ rb_path_end(path)
return chompdirsep(path); return chompdirsep(path);
} }
#if USE_NTFS
static char *
ntfs_tail(const char *path)
{
while (*path && *path != ':') {
if (istrailinggabage(*path)) {
const char *last = path++;
while (istrailinggabage(*path)) path++;
if (!*path || *path == ':') return (char *)last;
}
else if (isdirsep(*path)) {
const char *last = path++;
while (isdirsep(*path)) path++;
if (!*path) return (char *)last;
if (*path == ':') path++;
}
else {
path = CharNext(path);
}
}
return (char *)path;
}
#endif
#define BUFCHECK(cond) do {\ #define BUFCHECK(cond) do {\
long bdiff = p - buf;\ long bdiff = p - buf;\
while (cond) {\ while (cond) {\
@ -2480,7 +2520,8 @@ static VALUE
file_expand_path(fname, dname, result) file_expand_path(fname, dname, result)
VALUE fname, dname, result; VALUE fname, dname, result;
{ {
char *s, *buf, *b, *p, *pend, *root; const char *s, *b;
char *buf, *p, *pend, *root;
long buflen, dirlen; long buflen, dirlen;
int tainted; int tainted;
@ -2621,15 +2662,21 @@ file_expand_path(fname, dname, result)
case '.': case '.':
if (*(s+1) == '\0' || isdirsep(*(s+1))) { if (*(s+1) == '\0' || isdirsep(*(s+1))) {
/* We must go back to the parent */ /* We must go back to the parent */
char *n;
*p = '\0'; *p = '\0';
if (!(b = strrdirsep(root))) { if (!(n = strrdirsep(root))) {
*p = '/'; *p = '/';
} }
else { else {
p = b; p = n;
} }
b = ++s; b = ++s;
} }
#if USE_NTFS
else {
do *++s; while (istrailinggabage(*s));
}
#endif
break; break;
case '/': case '/':
#if defined DOSISH || defined __CYGWIN__ #if defined DOSISH || defined __CYGWIN__
@ -2642,6 +2689,19 @@ file_expand_path(fname, dname, result)
break; break;
} }
} }
#if USE_NTFS
else {
--s;
case ' ': {
const char *e = s;
while (istrailinggabage(*s)) s++;
if (!*s) {
s = e;
goto endpath;
}
}
}
#endif
break; break;
case '/': case '/':
#if defined DOSISH || defined __CYGWIN__ #if defined DOSISH || defined __CYGWIN__
@ -2664,15 +2724,79 @@ file_expand_path(fname, dname, result)
} }
if (s > b) { if (s > b) {
#if USE_NTFS
endpath:
if (s > b + 6 && strncasecmp(s - 6, ":$DATA", 6) == 0) {
/* alias of stream */
/* get rid of a bug of x64 VC++ */
if (*(s-7) == ':') s -= 7; /* prime */
else if (memchr(b, ':', s - 6 - b)) s -= 6; /* alternative */
}
#endif
BUFCHECK(bdiff + (s-b) >= buflen); BUFCHECK(bdiff + (s-b) >= buflen);
memcpy(++p, b, s-b); memcpy(++p, b, s-b);
p += s-b; p += s-b;
} }
if (p == skiproot(buf) - 1) p++; if (p == skiproot(buf) - 1) p++;
buflen = p - buf;
#if USE_NTFS
*p = '\0';
if (1 &&
#ifdef __CYGWIN__
!(buf[0] == '/' && !buf[1]) &&
#endif
!strpbrk(b = buf, "*?")) {
size_t len;
WIN32_FIND_DATA wfd;
#ifdef __CYGWIN__
int lnk_added = 0, is_symlink = 0;
struct stat st;
char w32buf[MAXPATHLEN], sep = 0;
p = 0;
if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
is_symlink = 1;
p = strrdirsep(buf);
if (!p) p = skipprefix(buf);
if (p) {
sep = *p;
*p = '\0';
}
}
if (cygwin_conv_to_win32_path(buf, w32buf) == 0) {
b = w32buf;
}
if (p) *p = sep;
else p = buf;
if (is_symlink && b == w32buf) {
len = strlen(p);
if (len > 4 && strcasecmp(p + len - 4, ".lnk") != 0) {
lnk_added = 1;
strlcat(w32buf, ".lnk", sizeof(w32buf));
}
}
#endif
HANDLE h = FindFirstFile(b, &wfd);
if (h != INVALID_HANDLE_VALUE) {
FindClose(h);
p = strrdirsep(buf);
len = strlen(wfd.cFileName);
#ifdef __CYGWIN__
if (lnk_added && len > 4 &&
strcasecmp(wfd.cFileName + len - 4, ".lnk") == 0) {
len -= 4;
}
#endif
if (!p) p = buf;
buflen = ++p - buf + len;
rb_str_resize(result, buflen);
memcpy(p, wfd.cFileName, len + 1);
}
}
#endif
if (tainted) OBJ_TAINT(result); if (tainted) OBJ_TAINT(result);
RSTRING(result)->len = p - buf; rb_str_set_len(result, buflen);
*p = '\0';
return result; return result;
} }
@ -2716,23 +2840,31 @@ rb_file_s_expand_path(argc, argv)
} }
static int static int
rmext(p, e) rmext(p, l1, e)
const char *p, *e; const char *p, *e;
int l1;
{ {
int l1, l2; int l2;
if (!e) return 0; if (!e) return 0;
l1 = chompdirsep(p) - p;
l2 = strlen(e); l2 = strlen(e);
if (l2 == 2 && e[1] == '*') { if (l2 == 2 && e[1] == '*') {
e = strrchr(p, *e); unsigned char c = *e;
if (!e) return 0; e = p + l1;
do {
if (e <= p) return 0;
} while (*--e != c);
return e - p; return e - p;
} }
if (l1 < l2) return l1; if (l1 < l2) return l1;
if (strncmp(p+l1-l2, e, l2) == 0) { #if CASEFOLD_FILESYSTEM
#define fncomp strncasecmp
#else
#define fncomp strncmp
#endif
if (fncomp(p+l1-l2, e, l2) == 0) {
return l1-l2; return l1-l2;
} }
return 0; return 0;
@ -2762,7 +2894,7 @@ rb_file_s_basename(argc, argv)
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
char *root; char *root;
#endif #endif
int f; int f, n;
if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) { if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
StringValue(fext); StringValue(fext);
@ -2796,18 +2928,22 @@ rb_file_s_basename(argc, argv)
#endif #endif
#endif #endif
} }
else if (!(p = strrdirsep(name))) { else {
if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) { if (!(p = strrdirsep(name))) {
f = chompdirsep(name) - name;
if (f == RSTRING(fname)->len) return fname;
}
p = name; p = name;
} }
else { else {
while (isdirsep(*p)) p++; /* skip last / */ while (isdirsep(*p)) p++; /* skip last / */
if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
f = chompdirsep(p) - p;
} }
#if USE_NTFS
n = ntfs_tail(p) - p;
#else
n = chompdirsep(p) - p;
#endif
if (NIL_P(fext) || !(f = rmext(p, n, StringValueCStr(fext)))) {
f = n;
}
if (f == RSTRING_LEN(fname)) return fname;
} }
basename = rb_str_new(p, f); basename = rb_str_new(p, f);
OBJ_INFECT(basename, fname); OBJ_INFECT(basename, fname);
@ -2883,7 +3019,7 @@ static VALUE
rb_file_s_extname(klass, fname) rb_file_s_extname(klass, fname)
VALUE klass, fname; VALUE klass, fname;
{ {
char *name, *p, *e; const char *name, *p, *e;
VALUE extname; VALUE extname;
name = StringValueCStr(fname); name = StringValueCStr(fname);
@ -2891,12 +3027,39 @@ rb_file_s_extname(klass, fname)
if (!p) if (!p)
p = name; p = name;
else else
p++; name = ++p;
e = strrchr(p, '.'); /* get the last dot of the last component */ e = 0;
if (!e || e == p || !e[1]) /* no dot, or the only dot is first or end? */ while (*p) {
return rb_str_new2(""); if (*p == '.' || istrailinggabage(*p)) {
extname = rb_str_new(e, chompdirsep(e) - e); /* keep the dot, too! */ #if USE_NTFS
const char *last = p++, *dot = last;
while (istrailinggabage(*p)) {
if (*p == '.') dot = p;
p++;
}
if (!*p || *p == ':') {
p = last;
break;
}
e = dot;
continue;
#else
e = p; /* get the last dot of the last component */
#endif
}
#if USE_NTFS
else if (*p == ':') {
break;
}
#endif
else if (isdirsep(*p))
break;
p = CharNext(p);
}
if (!e || e == name || e+1 == p) /* no dot, or the only dot is first or end? */
return rb_str_new(0, 0);
extname = rb_str_new(e, p - e); /* keep the dot, too! */
OBJ_INFECT(extname, fname); OBJ_INFECT(extname, fname);
return extname; return extname;
} }

View file

@ -227,13 +227,15 @@ class SimpleDelegator<Delegator
# Clone support for the object returned by \_\_getobj\_\_. # Clone support for the object returned by \_\_getobj\_\_.
def clone def clone
super new = super
__setobj__(__getobj__.clone) new.__setobj__(__getobj__.clone)
new
end end
# Duplication support for the object returned by \_\_getobj\_\_. # Duplication support for the object returned by \_\_getobj\_\_.
def dup(obj) def dup
super new = super
__setobj__(__getobj__.dup) new.__setobj__(__getobj__.clone)
new
end end
end end
@ -280,12 +282,14 @@ def DelegateClass(superclass)
@_dc_obj = obj @_dc_obj = obj
end end
def clone # :nodoc: def clone # :nodoc:
super new = super
__setobj__(__getobj__.clone) new.__setobj__(__getobj__.clone)
new
end end
def dup # :nodoc: def dup # :nodoc:
super new = super
__setobj__(__getobj__.dup) new.__setobj__(__getobj__.clone)
new
end end
} }
for method in methods for method in methods

View file

@ -520,10 +520,15 @@ module Net
# value specified when this instance was created will be # value specified when this instance was created will be
# used, or, failing that, the default value of 0 seconds, # used, or, failing that, the default value of 0 seconds,
# which means not to wait for more input. # which means not to wait for more input.
# FailEOF:: if true, when the remote end closes the connection then an
# EOFError will be raised. Otherwise, defaults to the old
# behaviour that the function will return whatever data
# has been received already, or nil if nothing was received.
# #
def waitfor(options) # :yield: recvdata def waitfor(options) # :yield: recvdata
time_out = @options["Timeout"] time_out = @options["Timeout"]
waittime = @options["Waittime"] waittime = @options["Waittime"]
fail_eof = @options["FailEOF"]
if options.kind_of?(Hash) if options.kind_of?(Hash)
prompt = if options.has_key?("Match") prompt = if options.has_key?("Match")
@ -535,6 +540,7 @@ module Net
end end
time_out = options["Timeout"] if options.has_key?("Timeout") time_out = options["Timeout"] if options.has_key?("Timeout")
waittime = options["Waittime"] if options.has_key?("Waittime") waittime = options["Waittime"] if options.has_key?("Waittime")
fail_eof = options["FailEOF"] if options.has_key?("FailEOF")
else else
prompt = options prompt = options
end end
@ -559,7 +565,8 @@ module Net
Integer(c.rindex(/#{IAC}#{SB}/no)) Integer(c.rindex(/#{IAC}#{SB}/no))
buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)])
rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1] rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1]
elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
c.rindex(/\r\z/no)
buf = preprocess(c[0 ... pt]) buf = preprocess(c[0 ... pt])
rest = c[pt .. -1] rest = c[pt .. -1]
else else
@ -571,14 +578,21 @@ module Net
# #
# We cannot use preprocess() on this data, because that # We cannot use preprocess() on this data, because that
# method makes some Telnetmode-specific assumptions. # method makes some Telnetmode-specific assumptions.
buf = c buf = rest + c
buf.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"]
rest = '' rest = ''
unless @options["Binmode"]
if pt = buf.rindex(/\r\z/no)
buf = buf[0 ... pt]
rest = buf[pt .. -1]
end
buf.gsub!(/#{EOL}/no, "\n")
end
end end
@log.print(buf) if @options.has_key?("Output_log") @log.print(buf) if @options.has_key?("Output_log")
line += buf line += buf
yield buf if block_given? yield buf if block_given?
rescue EOFError # End of file reached rescue EOFError # End of file reached
raise if fail_eof
if line == '' if line == ''
line = nil line = nil
yield nil if block_given? yield nil if block_given?

View file

@ -58,7 +58,7 @@ module WEBrick
def redirect_to_directory_uri(req, res) def redirect_to_directory_uri(req, res)
if req.path[-1] != ?/ if req.path[-1] != ?/
location = req.path + "/" location = WEBrick::HTTPUtils.escape_path(req.path + "/")
if req.query_string && req.query_string.size > 0 if req.query_string && req.query_string.size > 0
location << "?" << req.query_string location << "?" << req.query_string
end end

View file

@ -39,7 +39,9 @@ dir = File::dirname(ENV["SCRIPT_FILENAME"])
Dir::chdir dir Dir::chdir dir
if interpreter = ARGV[0] if interpreter = ARGV[0]
exec(interpreter, ENV["SCRIPT_FILENAME"]) argv = ARGV.dup
argv << ENV["SCRIPT_FILENAME"]
exec(*argv)
# NOTREACHED # NOTREACHED
end end
exec ENV["SCRIPT_FILENAME"] exec ENV["SCRIPT_FILENAME"]

View file

@ -199,26 +199,38 @@ module WEBrick
private private
def trailing_pathsep?(path)
# check for trailing path separator:
# File.dirname("/aaaa/bbbb/") #=> "/aaaa")
# File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb")
# File.dirname("/aaaa/bbbb") #=> "/aaaa")
# File.dirname("/aaaa/bbbbx") #=> "/aaaa")
return File.dirname(path) != File.dirname(path+"x")
end
def prevent_directory_traversal(req, res) def prevent_directory_traversal(req, res)
# Preventing directory traversal on DOSISH platforms; # Preventing directory traversal on Windows platforms;
# Backslashes (0x5c) in path_info are not interpreted as special # Backslashes (0x5c) in path_info are not interpreted as special
# character in URI notation. So the value of path_info should be # character in URI notation. So the value of path_info should be
# normalize before accessing to the filesystem. # normalize before accessing to the filesystem.
if File::ALT_SEPARATOR
if trailing_pathsep?(req.path_info)
# File.expand_path removes the trailing path separator. # File.expand_path removes the trailing path separator.
# Adding a character is a workaround to save it. # Adding a character is a workaround to save it.
# File.expand_path("/aaa/") #=> "/aaa" # File.expand_path("/aaa/") #=> "/aaa"
# File.expand_path("/aaa/" + "x") #=> "/aaa/x" # File.expand_path("/aaa/" + "x") #=> "/aaa/x"
expanded = File.expand_path(req.path_info + "x") expanded = File.expand_path(req.path_info + "x")
expanded[-1, 1] = "" # remove trailing "x" expanded.chop! # remove trailing "x"
req.path_info = expanded else
expanded = File.expand_path(req.path_info)
end end
req.path_info = expanded
end end
def exec_handler(req, res) def exec_handler(req, res)
raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
if set_filename(req, res) if set_filename(req, res)
handler = get_handler(req) handler = get_handler(req, res)
call_callback(:HandlerCallback, req, res) call_callback(:HandlerCallback, req, res)
h = handler.get_instance(@config, res.filename) h = handler.get_instance(@config, res.filename)
h.service(req, res) h.service(req, res)
@ -228,9 +240,13 @@ module WEBrick
return false return false
end end
def get_handler(req) def get_handler(req, res)
suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
if @options[:AcceptableLanguages].include?($2.downcase)
suffix2 = $1.downcase
end
end
handler_table = @options[:HandlerTable] handler_table = @options[:HandlerTable]
return handler_table[suffix1] || handler_table[suffix2] || return handler_table[suffix1] || handler_table[suffix2] ||
HandlerTable[suffix1] || HandlerTable[suffix2] || HandlerTable[suffix1] || HandlerTable[suffix2] ||
@ -243,15 +259,13 @@ module WEBrick
path_info.unshift("") # dummy for checking @root dir path_info.unshift("") # dummy for checking @root dir
while base = path_info.first while base = path_info.first
check_filename(req, res, base)
break if base == "/" break if base == "/"
break unless File.directory?(res.filename + base) break unless File.directory?(File.expand_path(res.filename + base))
shift_path_info(req, res, path_info) shift_path_info(req, res, path_info)
call_callback(:DirectoryCallback, req, res) call_callback(:DirectoryCallback, req, res)
end end
if base = path_info.first if base = path_info.first
check_filename(req, res, base)
if base == "/" if base == "/"
if file = search_index_file(req, res) if file = search_index_file(req, res)
shift_path_info(req, res, path_info, file) shift_path_info(req, res, path_info, file)
@ -272,12 +286,10 @@ module WEBrick
end end
def check_filename(req, res, name) def check_filename(req, res, name)
@options[:NondisclosureName].each{|pattern| if nondisclosure_name?(name) || windows_ambiguous_name?(name)
if File.fnmatch("/#{pattern}", name, File::FNM_CASEFOLD)
@logger.warn("the request refers nondisclosure name `#{name}'.") @logger.warn("the request refers nondisclosure name `#{name}'.")
raise HTTPStatus::NotFound, "`#{req.path}' not found." raise HTTPStatus::NotFound, "`#{req.path}' not found."
end end
}
end end
def shift_path_info(req, res, path_info, base=nil) def shift_path_info(req, res, path_info, base=nil)
@ -285,7 +297,8 @@ module WEBrick
base = base || tmp base = base || tmp
req.path_info = path_info.join req.path_info = path_info.join
req.script_name << base req.script_name << base
res.filename << base res.filename = File.expand_path(res.filename + base)
check_filename(req, res, File.basename(res.filename))
end end
def search_index_file(req, res) def search_index_file(req, res)
@ -325,6 +338,12 @@ module WEBrick
end end
end end
def windows_ambiguous_name?(name)
return true if /[. ]+\z/ =~ name
return true if /::\$DATA\z/ =~ name
return false
end
def nondisclosure_name?(name) def nondisclosure_name?(name)
@options[:NondisclosureName].each{|pattern| @options[:NondisclosureName].each{|pattern|
if File.fnmatch(pattern, name, File::FNM_CASEFOLD) if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
@ -343,7 +362,8 @@ module WEBrick
list = Dir::entries(local_path).collect{|name| list = Dir::entries(local_path).collect{|name|
next if name == "." || name == ".." next if name == "." || name == ".."
next if nondisclosure_name?(name) next if nondisclosure_name?(name)
st = (File::stat(local_path + name) rescue nil) next if windows_ambiguous_name?(name)
st = (File::stat(File.join(local_path, name)) rescue nil)
if st.nil? if st.nil?
[ name, nil, -1 ] [ name, nil, -1 ]
elsif st.directory? elsif st.directory?
@ -383,7 +403,7 @@ module WEBrick
res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n" res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
res.body << "<HR>\n" res.body << "<HR>\n"
list.unshift [ "..", File::mtime(local_path+".."), -1 ] list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
list.each{ |name, time, size| list.each{ |name, time, size|
if name == ".." if name == ".."
dname = "Parent Directory" dname = "Parent Directory"

View file

@ -255,6 +255,11 @@ The variable ruby-indent-level controls the amount of indentation.
(make-local-variable 'add-log-current-defun-function) (make-local-variable 'add-log-current-defun-function)
(setq add-log-current-defun-function 'ruby-add-log-current-method) (setq add-log-current-defun-function 'ruby-add-log-current-method)
(set (make-local-variable 'font-lock-defaults) '((ruby-font-lock-keywords) nil nil))
(set (make-local-variable 'font-lock-keywords) ruby-font-lock-keywords)
(set (make-local-variable 'font-lock-syntax-table) ruby-font-lock-syntax-table)
(set (make-local-variable 'font-lock-syntactic-keywords) ruby-font-lock-syntactic-keywords)
(run-mode-hooks 'ruby-mode-hook)) (run-mode-hooks 'ruby-mode-hook))
(defun ruby-current-indentation () (defun ruby-current-indentation ()
@ -1020,24 +1025,13 @@ balanced expression is found."
("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)))) ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil))))
(cond ((featurep 'xemacs) (if (featurep 'xemacs)
(put 'ruby-mode 'font-lock-defaults (put 'ruby-mode 'font-lock-defaults
'((ruby-font-lock-keywords) '((ruby-font-lock-keywords)
nil nil nil nil nil nil
beginning-of-line beginning-of-line
(font-lock-syntactic-keywords (font-lock-syntactic-keywords
. ruby-font-lock-syntactic-keywords)))) . ruby-font-lock-syntactic-keywords))))
(t
(add-hook 'ruby-mode-hook
'(lambda ()
(make-local-variable 'font-lock-defaults)
(make-local-variable 'font-lock-keywords)
(make-local-variable 'font-lock-syntax-table)
(make-local-variable 'font-lock-syntactic-keywords)
(setq font-lock-defaults '((ruby-font-lock-keywords) nil nil))
(setq font-lock-keywords ruby-font-lock-keywords)
(setq font-lock-syntax-table ruby-font-lock-syntax-table)
(setq font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords)))))
(defun ruby-font-lock-docs (limit) (defun ruby-font-lock-docs (limit)
(if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t) (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t)

13
range.c
View file

@ -307,24 +307,29 @@ range_step(argc, argv, range)
VALUE *argv; VALUE *argv;
VALUE range; VALUE range;
{ {
VALUE b, e, step; VALUE b, e, step, tmp;
long unit; long unit;
RETURN_ENUMERATOR(range, argc, argv); RETURN_ENUMERATOR(range, argc, argv);
b = rb_ivar_get(range, id_beg); b = rb_ivar_get(range, id_beg);
e = rb_ivar_get(range, id_end); e = rb_ivar_get(range, id_end);
if (rb_scan_args(argc, argv, "01", &step) == 0) { if (argc == 0) {
step = INT2FIX(1); step = INT2FIX(1);
unit = 1; unit = 1;
} }
else if (FIXNUM_P(step)) { else {
rb_scan_args(argc, argv, "01", &step);
tmp = rb_check_to_integer(step, "to_int");
if (!NIL_P(tmp)) {
step = tmp;
unit = NUM2LONG(step); unit = NUM2LONG(step);
} }
else { else {
VALUE tmp = rb_to_int(step); tmp = rb_funcall(rb_funcall(b, '+', 1, step), '-', 1, b);
unit = rb_cmpint(tmp, step, INT2FIX(0)); unit = rb_cmpint(tmp, step, INT2FIX(0));
} }
}
if (unit < 0) { if (unit < 0) {
rb_raise(rb_eArgError, "step can't be negative"); rb_raise(rb_eArgError, "step can't be negative");
} }

44
re.c
View file

@ -927,6 +927,7 @@ rb_reg_search(re, str, pos, reverse)
} }
if (result < 0) { if (result < 0) {
re_free_registers(&regs);
rb_backref_set(Qnil); rb_backref_set(Qnil);
return result; return result;
} }
@ -943,6 +944,7 @@ rb_reg_search(re, str, pos, reverse)
} }
re_copy_registers(RMATCH(match)->regs, &regs); re_copy_registers(RMATCH(match)->regs, &regs);
re_free_registers(&regs);
RMATCH(match)->str = rb_str_new4(str); RMATCH(match)->str = rb_str_new4(str);
rb_backref_set(match); rb_backref_set(match);
@ -1219,7 +1221,6 @@ match_entry(match, n)
/* /*
* call-seq: * call-seq:
* mtch.values_at([index]*) => array * mtch.values_at([index]*) => array
* mtch.select([index]*) => array
* *
* Uses each <i>index</i> to access the matching values, returning an array of * Uses each <i>index</i> to access the matching values, returning an array of
* the corresponding matches. * the corresponding matches.
@ -1239,6 +1240,45 @@ match_values_at(argc, argv, match)
} }
/*
* call-seq:
* mtch.select{|obj| block} => array
*
* Returns an array containing match strings for which <em>block</em>
* gives <code>true</code>. MatchData#select will be removed from Ruby 1.9.
*
* m = /(.)(.)(\d+)(\d)/.match("THX1138: The Movie")
* p m.select{|x| /X/ =~ x} #=> ["HX1138", "X"]
*/
static VALUE
match_select(argc, argv, match)
int argc;
VALUE *argv;
VALUE match;
{
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
else {
struct re_registers *regs = RMATCH(match)->regs;
VALUE target = RMATCH(match)->str;
VALUE result = rb_ary_new();
int i;
int taint = OBJ_TAINTED(match);
for (i=0; i<regs->num_regs; i++) {
VALUE str = rb_str_substr(target, regs->beg[i], regs->end[i]-regs->beg[i]);
if (taint) OBJ_TAINT(str);
if (RTEST(rb_yield(str))) {
rb_ary_push(result, str);
}
}
return result;
}
}
/* /*
* call-seq: * call-seq:
@ -2326,7 +2366,7 @@ Init_Regexp()
rb_define_method(rb_cMatch, "[]", match_aref, -1); rb_define_method(rb_cMatch, "[]", match_aref, -1);
rb_define_method(rb_cMatch, "captures", match_captures, 0); rb_define_method(rb_cMatch, "captures", match_captures, 0);
rb_define_method(rb_cMatch, "values_at", match_values_at, -1); rb_define_method(rb_cMatch, "values_at", match_values_at, -1);
rb_define_method(rb_cMatch, "select", match_values_at, -1); rb_define_method(rb_cMatch, "select", match_select, -1);
rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0); rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0);
rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0); rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0);
rb_define_method(rb_cMatch, "to_s", match_to_s, 0); rb_define_method(rb_cMatch, "to_s", match_to_s, 0);

60
sample/erb/erb4html.rb Normal file
View file

@ -0,0 +1,60 @@
require 'erb'
class ERB
class ERBString < String
def to_s; self; end
def erb_concat(s)
if self.class === s
concat(s)
else
concat(erb_quote(s))
end
end
def erb_quote(s); s; end
end
end
class ERB4Html < ERB
def self.quoted(s)
HtmlString.new(s)
end
class HtmlString < ERB::ERBString
def erb_quote(s)
ERB::Util::html_escape(s)
end
end
def set_eoutvar(compiler, eoutvar = '_erbout')
compiler.put_cmd = "#{eoutvar}.concat"
compiler.insert_cmd = "#{eoutvar}.erb_concat"
cmd = []
cmd.push "#{eoutvar} = ERB4Html.quoted('')"
compiler.pre_cmd = cmd
cmd = []
cmd.push(eoutvar)
compiler.post_cmd = cmd
end
end
if __FILE__ == $0
page = <<EOP
<title><%=title%></title>
<p><%=para%></p>
EOP
erb = ERB4Html.new(page)
title = "<auto-quote>"
para = "&lt;quoted&gt;"
puts erb.result
title = "<auto-quote>"
para = ERB4Html.quoted("&lt;quoted&gt;")
puts erb.result
end

View file

@ -761,7 +761,7 @@ rb_str_cat(str, ptr, len)
} }
if (FL_TEST(str, STR_ASSOC)) { if (FL_TEST(str, STR_ASSOC)) {
rb_str_modify(str); rb_str_modify(str);
REALLOC_N(RSTRING(str)->ptr, char, RSTRING(str)->len+len); REALLOC_N(RSTRING(str)->ptr, char, RSTRING(str)->len+len+1);
memcpy(RSTRING(str)->ptr + RSTRING(str)->len, ptr, len); memcpy(RSTRING(str)->ptr + RSTRING(str)->len, ptr, len);
RSTRING(str)->len += len; RSTRING(str)->len += len;
RSTRING(str)->ptr[RSTRING(str)->len] = '\0'; /* sentinel */ RSTRING(str)->ptr[RSTRING(str)->len] = '\0'; /* sentinel */
@ -3698,9 +3698,8 @@ rb_f_split(argc, argv)
* *
* Splits <i>str</i> using the supplied parameter as the record separator * Splits <i>str</i> using the supplied parameter as the record separator
* (<code>$/</code> by default), passing each substring in turn to the supplied * (<code>$/</code> by default), passing each substring in turn to the supplied
* block. If a zero-length record separator is supplied, the string is split on * block. If a zero-length record separator is supplied, the string is split
* <code>\n</code> characters, except that multiple successive newlines are * into paragraphs delimited by multiple successive newlines.
* appended together.
* *
* print "Example one\n" * print "Example one\n"
* "hello\nworld".each {|s| p s} * "hello\nworld".each {|s| p s}
@ -4922,6 +4921,7 @@ Init_String()
rb_define_method(rb_cString, "insert", rb_str_insert, 2); rb_define_method(rb_cString, "insert", rb_str_insert, 2);
rb_define_method(rb_cString, "length", rb_str_length, 0); rb_define_method(rb_cString, "length", rb_str_length, 0);
rb_define_method(rb_cString, "size", rb_str_length, 0); rb_define_method(rb_cString, "size", rb_str_length, 0);
rb_define_method(rb_cString, "bytesize", rb_str_length, 0);
rb_define_method(rb_cString, "empty?", rb_str_empty, 0); rb_define_method(rb_cString, "empty?", rb_str_empty, 0);
rb_define_method(rb_cString, "=~", rb_str_match, 1); rb_define_method(rb_cString, "=~", rb_str_match, 1);
rb_define_method(rb_cString, "match", rb_str_match_m, 1); rb_define_method(rb_cString, "match", rb_str_match_m, 1);

View file

@ -310,19 +310,14 @@ rb_struct_s_def(argc, argv, klass)
ID id; ID id;
rb_scan_args(argc, argv, "1*", &name, &rest); rb_scan_args(argc, argv, "1*", &name, &rest);
if (!NIL_P(name) && SYMBOL_P(name)) {
rb_ary_unshift(rest, name);
name = Qnil;
}
for (i=0; i<RARRAY(rest)->len; i++) { for (i=0; i<RARRAY(rest)->len; i++) {
id = rb_to_id(RARRAY(rest)->ptr[i]); id = rb_to_id(RARRAY(rest)->ptr[i]);
RARRAY(rest)->ptr[i] = ID2SYM(id); RARRAY(rest)->ptr[i] = ID2SYM(id);
} }
if (!NIL_P(name)) {
VALUE tmp = rb_check_string_type(name);
if (NIL_P(tmp)) {
id = rb_to_id(name);
rb_ary_unshift(rest, ID2SYM(id));
name = Qnil;
}
}
st = make_struct(name, rest, klass); st = make_struct(name, rest, klass);
if (rb_block_given_p()) { if (rb_block_given_p()) {
rb_mod_module_eval(0, 0, st); rb_mod_module_eval(0, 0, st);

View file

@ -528,6 +528,14 @@ class TestArray < Test::Unit::TestCase
assert_equal([1, 2, 3, 1, 2, 3], a) assert_equal([1, 2, 3, 1, 2, 3], a)
end end
def test_count
a = @cls[1, 2, 3, 1, 2]
assert_equal(2, a.count(1))
assert_equal(3, a.count {|x| x % 2 == 1 })
assert_equal(2, a.count(1) {|x| x % 2 == 1 })
assert_raise(ArgumentError) { a.count(0, 1) }
end
def test_delete def test_delete
a = @cls[*('cab'..'cat').to_a] a = @cls[*('cab'..'cat').to_a]
assert_equal('cap', a.delete('cap')) assert_equal('cap', a.delete('cap'))

View file

@ -47,5 +47,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal(o, m.receiver) assert_equal(o, m.receiver)
assert_equal("foo", m.name) assert_equal("foo", m.name)
assert_equal(class << o; self; end, m.owner) assert_equal(class << o; self; end, m.owner)
assert_equal("foo", m.unbind.name)
assert_equal(class << o; self; end, m.unbind.owner)
end end
end end

1
test/webrick/.htaccess Normal file
View file

@ -0,0 +1 @@
this file should not be published.

View file

@ -1,20 +1,13 @@
require "webrick" require "webrick"
require File.join(File.dirname(__FILE__), "utils.rb") require File.join(File.dirname(__FILE__), "utils.rb")
require "test/unit" require "test/unit"
begin
loadpath = $:.dup
$:.replace($: | [File.expand_path("../ruby", File.dirname(__FILE__))])
require 'envutil'
ensure
$:.replace(loadpath)
end
class TestWEBrickCGI < Test::Unit::TestCase class TestWEBrickCGI < Test::Unit::TestCase
def test_cgi def test_cgi
accepted = started = stopped = 0 accepted = started = stopped = 0
requested0 = requested1 = 0 requested0 = requested1 = 0
config = { config = {
:CGIInterpreter => EnvUtil.rubybin, :CGIInterpreter => TestWEBrick::RubyBin,
:DocumentRoot => File.dirname(__FILE__), :DocumentRoot => File.dirname(__FILE__),
:DirectoryIndex => ["webrick.cgi"], :DirectoryIndex => ["webrick.cgi"],
} }

View file

@ -9,6 +9,10 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
klass.new(WEBrick::Config::HTTP, filename) klass.new(WEBrick::Config::HTTP, filename)
end end
def windows?
File.directory?("\\")
end
def get_res_body(res) def get_res_body(res)
return res.body.read rescue res.body return res.body.read rescue res.body
end end
@ -115,10 +119,82 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
http = Net::HTTP.new(addr, port) http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/../../") req = Net::HTTP::Get.new("/../../")
http.request(req){|res| assert_equal("400", res.code) } http.request(req){|res| assert_equal("400", res.code) }
req = Net::HTTP::Get.new( req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}")
"/..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5cboot.ini" http.request(req){|res| assert_equal(windows? ? "200" : "404", res.code) }
) req = Net::HTTP::Get.new("/..%5c..%5cruby.c")
http.request(req){|res| assert_equal("404", res.code) } http.request(req){|res| assert_equal("404", res.code) }
end end
end end
def test_unwise_in_path
if windows?
config = { :DocumentRoot => File.dirname(__FILE__), }
this_file = File.basename(__FILE__)
TestWEBrick.start_httpserver(config) do |server, addr, port|
http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/..%5c..")
http.request(req){|res| assert_equal("301", res.code) }
end
end
end
def test_short_filename
config = {
:CGIInterpreter => TestWEBrick::RubyBin,
:DocumentRoot => File.dirname(__FILE__),
:CGIPathEnv => ENV['PATH'],
}
TestWEBrick.start_httpserver(config) do |server, addr, port|
http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/webric~1.cgi/test")
http.request(req) do |res|
if windows?
assert_equal("200", res.code)
assert_equal("/test", res.body)
else
assert_equal("404", res.code)
end
end
req = Net::HTTP::Get.new("/.htaccess")
http.request(req) {|res| assert_equal("404", res.code) }
req = Net::HTTP::Get.new("/htacce~1")
http.request(req) {|res| assert_equal("404", res.code) }
req = Net::HTTP::Get.new("/HTACCE~1")
http.request(req) {|res| assert_equal("404", res.code) }
end
end
def test_script_disclosure
config = {
:CGIInterpreter => TestWEBrick::RubyBin,
:DocumentRoot => File.dirname(__FILE__),
:CGIPathEnv => ENV['PATH'],
}
TestWEBrick.start_httpserver(config) do |server, addr, port|
http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/webrick.cgi/test")
http.request(req) do |res|
assert_equal("200", res.code)
assert_equal("/test", res.body)
end
response_assertion = Proc.new do |res|
if windows?
assert_equal("200", res.code)
assert_equal("/test", res.body)
else
assert_equal("404", res.code)
end
end
req = Net::HTTP::Get.new("/webrick.cgi%20/test")
http.request(req, &response_assertion)
req = Net::HTTP::Get.new("/webrick.cgi./test")
http.request(req, &response_assertion)
req = Net::HTTP::Get.new("/webrick.cgi::$DATA/test")
http.request(req, &response_assertion)
end
end
end end

View file

@ -1,3 +1,10 @@
begin
loadpath = $:.dup
$:.replace($: | [File.expand_path("../ruby", File.dirname(__FILE__))])
require 'envutil'
ensure
$:.replace(loadpath)
end
require "webrick" require "webrick"
begin begin
require "webrick/https" require "webrick/https"
@ -12,6 +19,11 @@ module TestWEBrick
return self return self
end end
RubyBin = "\"#{EnvUtil.rubybin}\""
RubyBin << " \"-I#{File.expand_path("../..", File.dirname(__FILE__))}/lib\""
RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/common\""
RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/#{RUBY_PLATFORM}\""
module_function module_function
def start_server(klass, config={}, &block) def start_server(klass, config={}, &block)

View file

@ -0,0 +1,36 @@
#!ruby -d
require "webrick/cgi"
class TestApp < WEBrick::CGI
def do_GET(req, res)
res["content-type"] = "text/plain"
if (p = req.path_info) && p.length > 0
res.body = p
elsif (q = req.query).size > 0
res.body = q.keys.sort.collect{|key|
q[key].list.sort.collect{|v|
"#{key}=#{v}"
}.join(", ")
}.join(", ")
elsif %r{/$} =~ req.request_uri.to_s
res.body = ""
res.body << req.request_uri.to_s << "\n"
res.body << req.script_name
elsif !req.cookies.empty?
res.body = req.cookies.inject(""){|result, cookie|
result << "%s=%s\n" % [cookie.name, cookie.value]
}
res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE")
res.cookies << WEBrick::Cookie.new("Shipping", "FedEx")
else
res.body = req.script_name
end
end
def do_POST(req, res)
do_GET(req, res)
end
end
cgi = TestApp.new
cgi.start

3399
util.c

File diff suppressed because it is too large Load diff

View file

@ -639,6 +639,11 @@ class File
end end
<<KEEP <<KEEP
test-rubyspec:
@if not exist $(srcdir:/=\)\rubyspec\nul echo No rubyspec here. put rubyspec to srcdir first. && exit 1
$(RUNRUBY) $(srcdir)/rubyspec/mspec/bin/mspec -r$(srcdir)/ext/purelib.rb $(srcdir)/rubyspec/spec/rubyspec/$(MAJOR).$(MINOR)
{$(srcdir)/missing}.c.obj: {$(srcdir)/missing}.c.obj:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -c -Tc$(<:\=/) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -c -Tc$(<:\=/)
{$(srcdir)/win32}.c.obj: {$(srcdir)/win32}.c.obj:

View file

@ -371,6 +371,7 @@ static void invalid_parameter(const wchar_t *expr, const wchar_t *func, const wc
} }
#endif #endif
static CRITICAL_SECTION select_mutex;
static BOOL fWinsock; static BOOL fWinsock;
static char *envarea; static char *envarea;
static void static void
@ -384,6 +385,7 @@ exit_handler(void)
FreeEnvironmentStrings(envarea); FreeEnvironmentStrings(envarea);
envarea = NULL; envarea = NULL;
} }
DeleteCriticalSection(&select_mutex);
} }
static void static void
@ -472,6 +474,8 @@ NtInitialize(int *argc, char ***argv)
init_stdhandle(); init_stdhandle();
InitializeCriticalSection(&select_mutex);
atexit(exit_handler); atexit(exit_handler);
// Initialize Winsock // Initialize Winsock
@ -2057,87 +2061,250 @@ rb_w32_fdisset(int fd, fd_set *set)
static int NtSocketsInitialized = 0; static int NtSocketsInitialized = 0;
static int static int
extract_file_fd(fd_set *set, fd_set *fileset) extract_fd(fd_set *dst, fd_set *src, int (*func)(SOCKET))
{ {
int idx; int s = 0;
if (!src || !dst) return 0;
fileset->fd_count = 0; while (s < src->fd_count) {
if (!set) SOCKET fd = src->fd_array[s];
if (!func || (*func)(fd)) { /* move it to dst */
int d;
for (d = 0; d < dst->fd_count; d++) {
if (dst->fd_array[d] == fd) break;
}
if (d == dst->fd_count && dst->fd_count < FD_SETSIZE) {
dst->fd_array[dst->fd_count++] = fd;
}
memmove(
&src->fd_array[s],
&src->fd_array[s+1],
sizeof(src->fd_array[0]) * (--src->fd_count - s));
}
else s++;
}
return dst->fd_count;
}
static int
is_not_socket(SOCKET sock)
{
return !is_socket(sock);
}
static int
is_pipe(SOCKET sock) /* DONT call this for SOCKET! it clains it is PIPE. */
{
int ret;
RUBY_CRITICAL(
ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE)
);
return ret;
}
static int
is_readable_pipe(SOCKET sock) /* call this for pipe only */
{
int ret;
DWORD n = 0;
RUBY_CRITICAL(
if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
ret = (n > 0);
}
else {
ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
}
);
return ret;
}
static int
is_console(SOCKET sock) /* DONT call this for SOCKET! */
{
int ret;
DWORD n = 0;
INPUT_RECORD ir;
RUBY_CRITICAL(
ret = (PeekConsoleInput((HANDLE)sock, &ir, 1, &n))
);
return ret;
}
static int
is_readable_console(SOCKET sock) /* call this for console only */
{
int ret = 0;
DWORD n = 0;
INPUT_RECORD ir;
RUBY_CRITICAL(
if (PeekConsoleInput((HANDLE)sock, &ir, 1, &n) && n > 0) {
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
ir.Event.KeyEvent.uChar.AsciiChar) {
ret = 1;
}
else {
ReadConsoleInput((HANDLE)sock, &ir, 1, &n);
}
}
);
return ret;
}
static int
do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
struct timeval *timeout)
{
int r = 0;
if (nfds == 0) {
if (timeout)
rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
else
rb_w32_sleep(INFINITE);
}
else {
RUBY_CRITICAL(
EnterCriticalSection(&select_mutex);
r = select(nfds, rd, wr, ex, timeout);
LeaveCriticalSection(&select_mutex);
if (r == SOCKET_ERROR) {
errno = map_errno(WSAGetLastError());
r = -1;
}
);
}
return r;
}
static inline int
subst(struct timeval *rest, const struct timeval *wait)
{
while (rest->tv_usec < wait->tv_usec) {
if (rest->tv_sec <= wait->tv_sec) {
return 0; return 0;
for (idx = 0; idx < set->fd_count; idx++) {
SOCKET fd = set->fd_array[idx];
if (!is_socket(fd)) {
int i;
for (i = 0; i < fileset->fd_count; i++) {
if (fileset->fd_array[i] == fd) {
break;
} }
rest->tv_sec -= 1;
rest->tv_usec += 1000 * 1000;
} }
if (i == fileset->fd_count) { rest->tv_sec -= wait->tv_sec;
if (fileset->fd_count < FD_SETSIZE) { rest->tv_usec -= wait->tv_usec;
fileset->fd_array[i] = fd; return 1;
fileset->fd_count++;
}
}
}
}
return fileset->fd_count;
} }
static inline int
compare(const struct timeval *t1, const struct timeval *t2)
{
if (t1->tv_sec < t2->tv_sec)
return -1;
if (t1->tv_sec > t2->tv_sec)
return 1;
if (t1->tv_usec < t2->tv_usec)
return -1;
if (t1->tv_usec > t2->tv_usec)
return 1;
return 0;
}
#undef Sleep
long long
rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
struct timeval *timeout) struct timeval *timeout)
{ {
long r; long r;
fd_set file_rd; fd_set pipe_rd;
fd_set file_wr; fd_set cons_rd;
#ifdef USE_INTERRUPT_WINSOCK fd_set else_rd;
fd_set trap; fd_set else_wr;
#endif /* USE_INTERRUPT_WINSOCK */ int nonsock = 0;
int file_nfds;
if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
errno = EINVAL;
return -1;
}
if (!NtSocketsInitialized) { if (!NtSocketsInitialized) {
StartSockets(); StartSockets();
} }
// assume else_{rd,wr} (other than socket, pipe reader, console reader)
// are always readable/writable. but this implementation still has
// problem. if pipe's buffer is full, writing to pipe will block
// until some data is read from pipe. but ruby is single threaded system,
// so whole system will be blocked forever.
else_rd.fd_count = 0;
nonsock += extract_fd(&else_rd, rd, is_not_socket);
pipe_rd.fd_count = 0;
extract_fd(&pipe_rd, &else_rd, is_pipe); // should not call is_pipe for socket
cons_rd.fd_count = 0;
extract_fd(&cons_rd, &else_rd, is_console); // ditto
else_wr.fd_count = 0;
nonsock += extract_fd(&else_wr, wr, is_not_socket);
r = 0; r = 0;
if (rd && rd->fd_count > r) r = rd->fd_count; if (rd && rd->fd_count > r) r = rd->fd_count;
if (wr && wr->fd_count > r) r = wr->fd_count; if (wr && wr->fd_count > r) r = wr->fd_count;
if (ex && ex->fd_count > r) r = ex->fd_count; if (ex && ex->fd_count > r) r = ex->fd_count;
if (nfds > r) nfds = r; if (nfds > r) nfds = r;
if (nfds == 0 && timeout) {
Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
return 0;
}
file_nfds = extract_file_fd(rd, &file_rd);
file_nfds += extract_file_fd(wr, &file_wr);
if (file_nfds)
{ {
// assume normal files are always readable/writable struct timeval rest;
// fake read/write fd_set and return value struct timeval wait;
if (rd) *rd = file_rd; struct timeval zero;
if (wr) *wr = file_wr; if (timeout) rest = *timeout;
return file_nfds; wait.tv_sec = 0; wait.tv_usec = 10 * 1000; // 10ms
zero.tv_sec = 0; zero.tv_usec = 0; // 0ms
do {
if (nonsock) {
// modifying {else,pipe,cons}_rd is safe because
// if they are modified, function returns immediately.
extract_fd(&else_rd, &pipe_rd, is_readable_pipe);
extract_fd(&else_rd, &cons_rd, is_readable_console);
} }
#if USE_INTERRUPT_WINSOCK if (else_rd.fd_count || else_wr.fd_count) {
if (ex) r = do_select(nfds, rd, wr, ex, &zero); // polling
trap = *ex; if (r < 0) break; // XXX: should I ignore error and return signaled handles?
else r += extract_fd(rd, &else_rd, NULL); // move all
trap.fd_count = 0; r += extract_fd(wr, &else_wr, NULL); // move all
if (trap.fd_count < FD_SETSIZE) break;
trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event;
// else unable to catch interrupt.
ex = &trap;
#endif /* USE_INTERRUPT_WINSOCK */
RUBY_CRITICAL({
r = select(nfds, rd, wr, ex, timeout);
if (r == SOCKET_ERROR) {
errno = map_errno(WSAGetLastError());
} }
}); else {
struct timeval *dowait =
compare(&rest, &wait) < 0 ? &rest : &wait;
fd_set orig_rd;
fd_set orig_wr;
fd_set orig_ex;
if (rd) orig_rd = *rd;
if (wr) orig_wr = *wr;
if (ex) orig_ex = *ex;
r = do_select(nfds, rd, wr, ex, &zero); // polling
if (r != 0) break; // signaled or error
if (rd) *rd = orig_rd;
if (wr) *wr = orig_wr;
if (ex) *ex = orig_ex;
// XXX: should check the time select spent
Sleep(dowait->tv_sec * 1000 + dowait->tv_usec / 1000);
}
} while (!timeout || subst(&rest, &wait));
}
return r; return r;
} }
@ -3272,7 +3439,6 @@ rb_w32_times(struct tms *tmbuf)
return 0; return 0;
} }
#undef Sleep
#define yield_once() Sleep(0) #define yield_once() Sleep(0)
#define yield_until(condition) do yield_once(); while (!(condition)) #define yield_until(condition) do yield_once(); while (!(condition))