ruby-changes:4965
From: ko1@a...
Date: Mon, 19 May 2008 00:03:26 +0900 (JST)
Subject: [ruby-changes:4965] knu - Ruby:r16458 (ruby_1_8_7): Merge -r16241:16456 from ruby_1_8.
knu 2008-05-19 00:02:36 +0900 (Mon, 19 May 2008) New Revision: 16458 Added directories: branches/ruby_1_8_7/sample/erb/ Added files: branches/ruby_1_8_7/test/webrick/.htaccess branches/ruby_1_8_7/test/webrick/webrick_long_filename.cgi Modified files: branches/ruby_1_8_7/ChangeLog branches/ruby_1_8_7/Makefile.in branches/ruby_1_8_7/NEWS branches/ruby_1_8_7/array.c branches/ruby_1_8_7/common.mk branches/ruby_1_8_7/configure.in branches/ruby_1_8_7/defines.h branches/ruby_1_8_7/enum.c branches/ruby_1_8_7/enumerator.c branches/ruby_1_8_7/eval.c branches/ruby_1_8_7/ext/dbm/dbm.c branches/ruby_1_8_7/ext/purelib.rb branches/ruby_1_8_7/ext/tk/ChangeLog.tkextlib branches/ruby_1_8_7/ext/tk/lib/tk/event.rb branches/ruby_1_8_7/ext/tk/lib/tk/grid.rb branches/ruby_1_8_7/ext/tk/lib/tk/pack.rb branches/ruby_1_8_7/ext/tk/lib/tk/spinbox.rb branches/ruby_1_8_7/ext/tk/lib/tk/validation.rb branches/ruby_1_8_7/ext/tk/lib/tk/wm.rb branches/ruby_1_8_7/ext/tk/lib/tk.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/blt/dragdrop.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/blt/treeview.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/calendar.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/entryfield.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/hierarchy.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/spinner.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile/style.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile/tnotebook.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/shape.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/tkdnd.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/tktable/tktable.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/treectrl/tktreectrl.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/version.rb branches/ruby_1_8_7/ext/tk/lib/tkextlib/winico/winico.rb branches/ruby_1_8_7/ext/tk/sample/demos-en/aniwave.rb branches/ruby_1_8_7/ext/tk/sample/demos-jp/aniwave.rb branches/ruby_1_8_7/ext/tk/sample/ttk_wrapper.rb branches/ruby_1_8_7/ext/tk/tcltklib.c branches/ruby_1_8_7/ext/tk/tkutil/extconf.rb branches/ruby_1_8_7/ext/tk/tkutil/tkutil.c branches/ruby_1_8_7/ext/zlib/zlib.c branches/ruby_1_8_7/file.c branches/ruby_1_8_7/lib/delegate.rb branches/ruby_1_8_7/lib/net/telnet.rb branches/ruby_1_8_7/lib/webrick/httpservlet/abstract.rb branches/ruby_1_8_7/lib/webrick/httpservlet/cgi_runner.rb branches/ruby_1_8_7/lib/webrick/httpservlet/filehandler.rb branches/ruby_1_8_7/misc/ruby-mode.el branches/ruby_1_8_7/range.c branches/ruby_1_8_7/re.c branches/ruby_1_8_7/string.c branches/ruby_1_8_7/struct.c branches/ruby_1_8_7/test/ruby/test_array.rb branches/ruby_1_8_7/test/ruby/test_method.rb branches/ruby_1_8_7/test/webrick/test_cgi.rb branches/ruby_1_8_7/test/webrick/test_filehandler.rb branches/ruby_1_8_7/test/webrick/utils.rb branches/ruby_1_8_7/util.c branches/ruby_1_8_7/win32/Makefile.sub branches/ruby_1_8_7/win32/win32.c Log: Merge -r16241:16456 from ruby_1_8. Added: branches/ruby_1_8_7/sample/erb/ http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/webrick/test_cgi.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/struct.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/sample/erb http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/tcltklib.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/purelib.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/Makefile.in?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/webrick/test_filehandler.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/webrick/.htaccess http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/grid.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/enumerator.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/win32/win32.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/winico/winico.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/hierarchy.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/enum.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/defines.h?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/win32/Makefile.sub?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/util.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/zlib/zlib.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/calendar.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/blt/dragdrop.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/validation.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/pack.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/dbm/dbm.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/common.mk?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/webrick/webrick_long_filename.cgi http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/misc/ruby-mode.el?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/sample/demos-en/aniwave.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/treectrl/tktreectrl.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/entryfield.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/eval.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/configure.in?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/re.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/lib/webrick/httpservlet/filehandler.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile/style.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/spinner.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/spinbox.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/ChangeLog.tkextlib?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ChangeLog?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/lib/webrick/httpservlet/abstract.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/lib/net/telnet.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/lib/delegate.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/tkutil/tkutil.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/tkutil/extconf.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/sample/ttk_wrapper.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/shape.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/wm.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/NEWS?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/ruby/test_array.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/string.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/sample/demos-jp/aniwave.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/version.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tktable/tktable.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tile/tnotebook.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/blt/treeview.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/webrick/utils.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/file.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/tkdnd.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/test/ruby/test_method.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/range.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/lib/webrick/httpservlet/cgi_runner.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk/event.rb?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/array.c?r1=16458&r2=16457&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/ext/tk/lib/tk.rb?r1=16458&r2=16457&diff_format=u Index: ruby_1_8_7/Makefile.in =================================================================== --- ruby_1_8_7/Makefile.in (revision 16457) +++ ruby_1_8_7/Makefile.in (revision 16458) @@ -181,3 +181,22 @@ ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP) $(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) + Index: ruby_1_8_7/util.c =================================================================== --- ruby_1_8_7/util.c (revision 16457) +++ ruby_1_8_7/util.c (revision 16458) @@ -664,288 +664,3193 @@ return buf; } -/* copyright notice for strtod implementation -- + +/**************************************************************** * - * Copyright (c) 1988-1993 The Regents of the University of California. - * Copyright (c) 1994 Sun Microsystems, Inc. + * The author of this software is David M. Gay. * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. * - */ + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ -#define MDMINEXPT DBL_MIN_10_EXP -#define MDMAXEXPT DBL_MAX_10_EXP +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ -static const -double powersOf10[] = { /* Table giving binary powers of 10. Entry */ - 10.0, /* is 10^2^i. Used to convert decimal */ - 100.0, /* exponents into floating-point numbers. */ - 1.0e4, - 1.0e8, - 1.0e16, - 1.0e32, - 1.0e64, - 1.0e128, - 1.0e256 -}; +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ -/* - *---------------------------------------------------------------------- +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. * - * strtod -- + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). * - * This procedure converts a floating-point number from an ASCII - * decimal representation to internal double-precision format. + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. * - * Results: - * The return value is the double-precision floating-point - * representation of the characters in string. If endPtr isn't - * NULL, then *endPtr is filled in with the address of the - * next character after the last one that was part of the - * floating-point number. + * Modifications: * - * Side effects: - * None. - * - *---------------------------------------------------------------------- + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). */ +/* + * #define IEEE_LITTLE_ENDIAN for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_BIG_ENDIAN for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define INFNAN_CHECK on IEEE systems to cause strtod to check for + * Infinity and NaN (case insensitively). On some systems (e.g., + * some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define YES_ALIAS to permit aliasing certain double values with + * arrays of ULongs. This leads to slightly better code with + * some compilers and was always used prior to 19990916, but it + * is not strictly legal and can cause trouble with aggressively + * optimizing compilers (e.g., gcc 2.95.1 under -O2). + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + */ + +#ifdef WORDS_BIGENDIAN +#define IEEE_BIG_ENDIAN +#else +#define IEEE_LITTLE_ENDIAN +#endif + +#ifdef __vax__ +#define VAX +#undef IEEE_BIG_ENDIAN +#undef IEEE_LITTLE_ENDIAN +#endif + +#if defined(__arm__) && !defined(__VFP_FP__) +#define IEEE_BIG_ENDIAN +#undef IEEE_LITTLE_ENDIAN +#endif + +#undef Long +#undef ULong + +#if SIZEOF_INT == 4 +#define Long int +#define ULong unsigned int +#elif SIZEOF_LONG == 4 +#define Long long int +#define ULong unsigned long int +#endif + +#if HAVE_LONG_LONG +#define Llong LONG_LONG +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef MALLOC +extern void *MALLOC(size_t); +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_BIG_ENDIAN +#define IEEE_Arith +#endif +#ifdef IEEE_LITTLE_ENDIAN +#define IEEE_Arith +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_LITTLE_ENDIAN, IEEE_BIG_ENDIAN, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#ifdef YES_ALIAS +#define dval(x) x +#ifdef IEEE_LITTLE_ENDIAN +#define word0(x) ((ULong *)&x)[1] +#define word1(x) ((ULong *)&x)[0] +#else +#define word0(x) ((ULong *)&x)[0] +#define word1(x) ((ULong *)&x)[1] +#endif +#else +#ifdef IEEE_LITTLE_ENDIAN +#define word0(x) ((U*)&x)->L[1] +#define word1(x) ((U*)&x)->L[0] +#else +#define word0(x) ((U*)&x)->L[0] +#define word1(x) ((U*)&x)->L[1] +#endif +#define dval(x) ((U*)&x)->d +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_LITTLE_ENDIAN) + defined(VAX) + defined(__arm__) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#define Rounding rounding +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +#define FFFFFFFF 0xffffffffUL + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 15 + +struct Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; +}; + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +static Bigint * +Balloc(int k) +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + if ((rv = freelist[k]) != 0) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; +} + +static void +Bfree(Bigint *v) +{ + if (v) { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + +static Bigint * +multadd(Bigint *b, int m, int a) /* multiply by m and add a */ +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } while (++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; +} + +static Bigint * +s2b(const char *s, int nd0, int nd, ULong y9) +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for (k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do { + b = multadd(b, 10, *s++ - '0'); + } while (++i < nd0); + s++; + } + else + s += 10; + for (; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; +} + +static int +hi0bits(register ULong x) +{ + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} + +static int +lo0bits(ULong *y) +{ + register int k; + register ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; +} + +static Bigint * +i2b(int i) +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; +} + +static Bigint * +mult(Bigint *a, Bigint *b) +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for (x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for (; xb < xbe; xc0++) { + if ((y = *xb++) != 0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } while (x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for (; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } while (x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } while (x < xae); + *xc = z2; + } + } +#else + for (; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } while (x < xae); + *xc = carry; + } + } +#endif +#endif + for (xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +static Bigint *p5s; + +static Bigint * +pow5mult(Bigint *b, int k) +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3) != 0) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for (;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; +} + +static Bigint * +lshift(Bigint *b, int k) +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for (i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for (i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } while (x < xe); + if ((*x1 = z) != 0) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } while (x < xe); + if (*x1 = z) + ++n1; + } +#endif + else + do { + *x1++ = *x++; + } while (x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + +static int +cmp(Bigint *a, Bigint *b) +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for (;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + +static Bigint * +diff(Bigint *a, Bigint *b) +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } while (xb < xbe); + while (xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } while (xb < xbe); + while (xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } while (xb < xbe); + while (xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while (!*--xc) + wa--; + c->wds = wa; + return c; +} + +static double +ulp(double x) +{ + register Long L; + double a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(a); +} + +static double +b2d(Bigint *a, int *e) +{ + ULong *xa, *xa0, w, y, z; + int k; + double d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif +ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(d); +} + +static Bigint * +d2b(double d, int *e, int *bits) +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift)) != 0) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1) != 0) { + if ((k = lo0bits(&y)) != 0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while (!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +} +#undef d0 +#undef d1 + +static double +ratio(Bigint *a, Bigint *b) +{ + double da, db; + int k, ka, kb; + + dval(da) = b2d(a, &ka); + dval(db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(da) *= 1 << k; + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(db) *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return dval(da) / dval(db); +} + +static const double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif +}; + +static const double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-53 */ +#else + 1e-256 +#endif +}; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static const double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#ifndef IEEE_Arith +#undef INFNAN_CHECK +#endif + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + +static int +match(const char **sp, char *t) +{ + int c, d; + const char *s = *sp; + + while (d = *t++) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; +} + +#ifndef No_Hex_NaN +static void +hexnan(double *rvp, const char **sp) +{ + ULong c, x[2]; + const char *s; + int havedig, udx0, xshift; + + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + while (c = *(const unsigned char*)++s) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c += 10 - 'a'; + else if (c >= 'A' && c <= 'F') + c += 10 - 'A'; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(*rvp) = Exp_mask | x[0]; + word1(*rvp) = x[1]; + } +} +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + double -ruby_strtod(string, endPtr) - const char *string; /* A decimal ASCII floating-point number, - * optionally preceded by white space. - * Must have form "-I.FE-X", where I is the - * integer part of the mantissa, F is the - * fractional part of the mantissa, and X - * is the exponent. Either of the signs - * may be "+", "-", or omitted. Either I - * or F may be omitted, but both cannot be - * ommitted at once. The decimal - * point isn't necessary unless F is present. - * The "E" may actually be an "e". E and X - * may both be omitted (but not just one). - */ - char **endPtr; /* If non-NULL, store terminating character's - * address here. */ +ruby_strtod(const char *s00, char **se) { - int sign, expSign = Qfalse; - double fraction = 0.0, dblExp; - const double *d; - register const char *p; - register int c; - int exp = 0; /* Exponent read from "EX" field. */ - int fracExp = 0; /* Exponent that derives from the fractional - * part. Under normal circumstatnces, it is - * the negative of the number of digits in F. - * However, if I is very long, the last digits - * of I get dropped (otherwise a long I with a - * large negative exponent could cause an - * unnecessary overflow on I alone). In this - * case, fracExp is incremented one for each - * dropped digit. */ - int mantSize = 0; /* Number of digits in mantissa. */ - int hasPoint = Qfalse; /* Decimal point exists. */ - int hasDigit = Qfalse; /* I or F exists. */ - const char *pMant; /* Temporarily holds location of mantissa - * in string. */ - const char *pExp; /* Temporarily holds location of exponent - * in string. */ +#ifdef Avoid_Underflow + int scale; +#endif + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + const char *s, *s0, *s1; + double aadj, aadj1, adj, rv, rv0; + Long L; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef USE_LOCALE + const char *s2; +#endif - /* - * Strip off leading blanks and check for a sign. - */ + sign = nz0 = nz = 0; + dval(rv) = 0.; + for (s = s00;;s++) + switch (*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } +break2: + if (*s == '0') { + nz0 = 1; + while (*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for (nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for (;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + if (!nd) { + for (; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for (; c >= '0' && c <= '9'; c = *++s) { +have_dig: + nz++; + if (c -= '0') { + nf += nz; + for (i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } +dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch (c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while (c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while ((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + switch (c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(rv) = 0x7ff00000; + word1(rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ +ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; - errno = 0; - p = string; - while (ISSPACE(*p)) p++; - if (*p == '-') { - sign = Qtrue; - p++; + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(rv) = tens[k - 9] * dval(rv) + z; } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + e -= i; + dval(rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ +vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(rv), tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15) != 0) + dval(rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { +ovfl: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch (rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(rv) = Big0; + word1(rv) = Big1; + break; + default: + word0(rv) = Exp_mask; + word1(rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(rv0) = 1e300; + dval(rv0) *= dval(rv0); +#endif +#else /*IEEE_Arith*/ + word0(rv) = Big0; + word1(rv) = Big1; +#endif /*IEEE_Arith*/ + if (bd0) + goto retfree; + goto ret; + } + e1 >>= 4; + for (j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + dval(rv) *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15) != 0) + dval(rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = 2*P; + for (j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + word1(rv) = 0; + if (j >= 53) + word0(rv) = (P+2)*Exp_msk1; + else + word0(rv) &= 0xffffffff << (j-32); + } + else + word1(rv) &= 0xffffffff << j; + } +#else + for (j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(rv0) = dval(rv); + dval(rv) *= tinytens[j]; + if (!dval(rv)) { + dval(rv) = 2.*dval(rv0); + dval(rv) *= tinytens[j]; +#endif + if (!dval(rv)) { +undfl: + dval(rv) = 0.; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + if (bd0) + goto retfree; + goto ret; + } +#ifndef Avoid_Underflow + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for (;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (rounding) { + if (dsign) { + adj = 1.; + goto apply_adj; + } + } + else if (!dsign) { + adj = -1.; + if (!word1(rv) + && !(word0(rv) & Frac_mask)) { + y = word0(rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj = -0.5; + } + } +apply_adj: +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= + P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + dval(rv) += adj*ulp(dval(rv)); + word0(rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(rv) += adj*ulp(dval(rv)); + } + break; + } + adj = ratio(delta, bs); + if (adj < 1.) + adj = 1.; + if (adj <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj; + if (y != adj) { + if (!((rounding>>1) ^ dsign)) + y++; + adj = y; + } + } +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + adj *= ulp(dval(rv)); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + word0(rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + adj *= ulp(dval(rv)); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == ( +#ifdef Avoid_Underflow + (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { +drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + goto undfl; + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (scale) { + L = word0(rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + dval(rv) += ulp(dval(rv)); +#ifndef ROUND_BIASED + else { + dval(rv) -= ulp(dval(rv)); +#ifndef Sudden_Underflow + if (!dval(rv)) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch (Rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(rv0) = dval(rv); + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else + word0(rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + aadj1 = dsign ? aadj : -aadj; + } + word0(aadj1) += (2*P+1)*Exp_msk1 - y; + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + dval(rv0) = dval(rv); + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else + word0(rv) -= P*Exp_msk1; + } + else { + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(rv) & Exp_mask; +#ifndef SET_INEXACT +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } +#endif +cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(rv0) = Exp_1 + (70 << Exp_shift); + word1(rv0) = 0; + dval(rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (scale) { + word0(rv0) = Exp_1 - 2*P*Exp_msk1; + word1(rv0) = 0; + dval(rv) *= dval(rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ + if (word0(rv) == 0 && word1(rv) == 0) + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (inexact && !(word0(rv) & Exp_mask)) { + /* set underflow bit */ + dval(rv0) = 1e-300; + dval(rv0) *= dval(rv0); + } +#endif +retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +ret: + if (se) + *se = (char *)s; + return sign ? -dval(rv) : dval(rv); +} + +static int +quorem(Bigint *b, Bigint *S) +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } while (sx <= sxe); + if (!*bxe) { + bx = b->x; + while (--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } while (sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while (--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; +} + +#ifndef MULTIPLE_THREADS +static char *dtoa_result; +#endif + +static char * +rv_alloc(int i) +{ + int j, k, *r; + + j = sizeof(ULong); + for (k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); +} + +static char * +nrv_alloc(char *s, char **rve, int n) +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while ((*t = *s++) != 0) t++; + if (rve) + *rve = t; + return rv; +} + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + +void +freedtoa(char *s) +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +char * +dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s, *s0; +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(d) += 0; /* normalize */ +#endif + if (!dval(d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (*sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif + + b = d2b(dval(d), &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { +#endif + dval(d2) = dval(d); + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + dval(d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } else { - if (*p == '+') p++; - sign = Qfalse; + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) + : word1(d) << (32 - i); + dval(d2) = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; } +#endif + ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; - fraction = 0.; - exp = 0; +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ - /* - * Count the number of digits in the mantissa - * and also locate the decimal point. - */ + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; + switch (mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); - for ( ; (c = *p) != '\0'; p++) { - if (!ISDIGIT(c)) { - if (c != '.' || hasPoint || !ISDIGIT(p[1])) { - break; - } - hasPoint = Qtrue; - } - else { - if (hasPoint) { /* already in fractional part */ - fracExp--; - } - if (mantSize) { /* already in mantissa */ - mantSize++; - } - else if (c != '0') { /* have entered mantissa */ - mantSize++; - pMant = p; - } - hasDigit = Qtrue; - } +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(d2) = dval(d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(d) /= bigtens[n_bigtens-1]; + ieps++; + } + for (; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(d) /= ds; + } + else if ((j1 = -k) != 0) { + dval(d) *= tens[j1 & 0xf]; + for (j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(d) *= bigtens[i]; + } + } + if (k_check && dval(d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(d) *= 10.; + ieps++; + } + dval(eps) = ieps*dval(d) + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(d) -= 5.; + if (dval(d) > dval(eps)) + goto one_digit; + if (dval(d) < -dval(eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(eps) = 0.5/tens[ilim-1] - dval(eps); + for (i = 0;;) { + L = dval(d); + dval(d) -= L; + *s++ = '0' + (int)L; + if (dval(d) < dval(eps)) + goto ret1; + if (1. - dval(d) < dval(eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(eps) *= 10.; + dval(d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(eps) *= tens[ilim-1]; + for (i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d)); + if (!(dval(d) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(d) > 0.5 + dval(eps)) + goto bump_up; + else if (dval(d) < 0.5 - dval(eps)) { + while (*--s == '0') ; + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif +fast_failed: + s = s0; + dval(d) = dval(d2); + k = k0; + ilim = ilim0; } - /* - * Now suck up the digits in the mantissa. Use two integers to - * collect 9 digits each (this is faster than using floating-point). - * If the mantissa has more than 18 digits, ignore the extras, since - * they can't affect the value anyway. + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for (i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d) / ds); + dval(d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(d) < 0) { + L--; + dval(d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch (rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(d) += dval(d); + if (dval(d) > ds || (dval(d) == ds && (L & 1))) { +bump_up: + while (*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5) != 0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && rounding == 1 +#endif + ) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. */ - - pExp = p; - if (mantSize) { - p = pMant; +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; +#else + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) != 0) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; } - if (mantSize > 18) { - fracExp += (mantSize - 18); - mantSize = 18; + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; } - if (!hasDigit) { - fraction = 0.0; - p = string; + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } } - else { - double frac1, frac2; - frac1 = 0; - for ( ; mantSize > 9; mantSize -= 1) { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac1 = 10*frac1 + (c - '0'); - } - frac2 = 0; - for (; mantSize > 0; mantSize -= 1) { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac2 = 10*frac2 + (c - '0'); - } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ +no_digits: + k = -1 - ndigits; + goto ret; + } +one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); - /* - * Skim off the exponent. - */ + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ - p = pExp; - if ((*p == 'E') || (*p == 'e')) { - p++; - if (*p == '-') { - expSign = Qtrue; - p++; - } - else { - if (*p == '+') { - p++; - } - expSign = Qfalse; - } - if (ISDIGIT(*p)) { - do { - exp = exp * 10 + (*p++ - '0'); - } - while (ISDIGIT(*p)); - } - else { - p = pExp; - } - } - if (expSign) { - exp = fracExp - exp; - } - else { - exp = fracExp + exp; - } + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } - /* - * Generate a floating-point number that represents the exponent. - * Do this by processing the exponent one bit at a time to combine - * many powers of 2 of 10. Then combine the exponent with the - * fraction. - */ - - if (exp >= MDMAXEXPT) { - errno = ERANGE; - fraction = HUGE_VAL; - goto ret; - } - else if (exp < MDMINEXPT) { - errno = ERANGE; - fraction = 0.0; - goto ret; - } - fracExp = exp; - exp += 9; - if (exp < 0) { - expSign = Qtrue; - exp = -exp; - } - else { - expSign = Qfalse; - } - dblExp = 1.0; - for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { - if (exp & 01) { - dblExp *= *d; - } - } - if (expSign) { - frac1 /= dblExp; - } - else { - frac1 *= dblExp; - } - exp = fracExp; - if (exp < 0) { - expSign = Qtrue; - exp = -exp; - } - else { - expSign = Qfalse; - } - dblExp = 1.0; - for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { - if (exp & 01) { - dblExp *= *d; - } - } - if (expSign) { - frac2 /= dblExp; - } - else { - frac2 *= dblExp; - } - fraction = frac1 + frac2; + for (i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(d) & 1) +#ifdef Honor_FLT_ROUNDS + && rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch (rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || (j1 == 0 && (dig & 1))) && dig++ == '9') + goto round_9_up; + } +accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ +round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS +keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } } + else + for (i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } - ret: - if (endPtr != NULL) { - *endPtr = (char *)p; + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch (rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; } - if (sign) { - return -fraction; +#endif + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || (j == 0 && (dig & 1))) { + roundoff: + while (*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; } - return fraction; + else { + while (*--s == '0') ; + s++; + } +ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } +ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(d) = Exp_1 + (70 << Exp_shift); + word1(d) = 0; + dval(d) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; } + +void +ruby_each_words(const char *str, void (*func)(const char*, int, void*), void *arg) +{ + const char *end; + int len; + + if (!str) return; + for (; *str; str = end) { + while (ISSPACE(*str) || *str == ',') str++; + if (!*str) break; + end = str; + while (*end && !ISSPACE(*end) && *end != ',') end++; + len = end - str; + (*func)(str, len, arg); + } +} + +#ifdef __cplusplus +} +#endif Index: ruby_1_8_7/array.c =================================================================== --- ruby_1_8_7/array.c (revision 16457) +++ ruby_1_8_7/array.c (revision 16458) @@ -3032,16 +3032,12 @@ /* * call-seq: * array.nitems -> int - * array.nitems { |item| block } -> int * * 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. * * [ 1, nil, 3, nil, 5 ].nitems #=> 3 - * [5,6,7,8,9].nitems { |x| x % 2 != 0 } #=> 3 */ static VALUE @@ -3049,24 +3045,54 @@ VALUE ary; { long n = 0; - - if (rb_block_given_p()) { - long i; + VALUE *p, *pend; - for (i=0; i<RARRAY(ary)->len; i++) { - VALUE v = RARRAY(ary)->ptr[i]; - if (RTEST(rb_yield(v))) n++; - } + for (p = RARRAY(ary)->ptr, pend = p + RARRAY(ary)->len; p < pend; p++) { + if (!NIL_P(*p)) n++; } + return LONG2NUM(n); +} + +/* + * call-seq: + * 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 { - VALUE *p = RARRAY(ary)->ptr; - VALUE *pend = p + RARRAY(ary)->len; + VALUE obj, *p, *pend; - while (p < pend) { - if (!NIL_P(*p)) n++; - p++; - } + rb_scan_args(argc, argv, "1", &obj); + if (rb_block_given_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); } @@ -3789,6 +3815,7 @@ 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, "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, 0); rb_define_method(rb_cArray, "choice", rb_ary_choice, 0); Index: ruby_1_8_7/ext/zlib/zlib.c =================================================================== --- ruby_1_8_7/ext/zlib/zlib.c (revision 16457) +++ ruby_1_8_7/ext/zlib/zlib.c (revision 16458) @@ -3111,6 +3111,8 @@ if (NIL_P(rs)) { dst = gzfile_read_all(gz); if (RSTRING(dst)->len != 0) gz->lineno++; + else + return Qnil; return dst; } Index: ruby_1_8_7/ext/tk/sample/ttk_wrapper.rb =================================================================== --- ruby_1_8_7/ext/tk/sample/ttk_wrapper.rb (revision 16457) +++ ruby_1_8_7/ext/tk/sample/ttk_wrapper.rb (revision 16458) @@ -4,7 +4,7 @@ # # by Hidetoshi NAGAI (nagai@a...) # -version = '0.1.1' +version = '0.1.3' # ########################################################################## # parse commandline arguments @@ -108,37 +108,36 @@ ########################################################################## -# define utility method +# set theme of widget style ########################################################################## -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) +if OPTS[:list] || OPTS[:verbose] + print "supported theme names: #{Tk::Tile.themes.inspect}\n" + exit if OPTS[:list] && ARGV.empty? end +print "use theme: \"#{OPTS[:theme]}\"\n" if OPTS[:theme] && OPTS[:verbose] +#setTheme(OPTS[:theme]) if OPTS[:theme] +Tk::Tile.set_theme(OPTS[:theme]) if OPTS[:theme] ########################################################################## -# make theme name list +# replace $0 and $RPAGRAM_NAME ########################################################################## -ThemesList = Tk::Tile::Style.theme_names -TkPackage.names.find_all{|n| n =~ /^(tile|ttk)::theme::/}.each{|pkg| - ThemesList << pkg.split('::')[-1] -} -ThemesList.uniq! +# 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 -########################################################################## -# set theme of widget style -########################################################################## -if OPTS[:list] || OPTS[:verbose] - print "supported theme names: #{ThemesList.inspect}\n" - exit if OPTS[:list] && ARGV.empty? -end -print "use theme: \"#{OPTS[:theme]}\"\n" if OPTS[:theme] && OPTS[:verbose] -setTheme(OPTS[:theme]) if OPTS[:theme] +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 @@ ########################################################################## if (path = ARGV.shift) && (script = File.expand_path(path)) print "load script \"#{script}\"\n" if OPTS[:verbose] + $0 = script load(script) else print "Error: no script is given.\n" Index: ruby_1_8_7/ext/tk/sample/demos-jp/aniwave.rb =================================================================== --- ruby_1_8_7/ext/tk/sample/demos-jp/aniwave.rb (revision 16457) +++ ruby_1_8_7/ext/tk/sample/demos-jp/aniwave.rb (revision 16458) @@ -60,6 +60,7 @@ @backupCoords = [] n = 0 (-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] } + n = 305 @waveCoords << [n, 0]; @backupCoords << [n, 0] @waveCoords << [n+5, 200]; @backupCoords << [n+5, 200] @coordsLen = @waveCoords.length Index: ruby_1_8_7/ext/tk/sample/demos-en/aniwave.rb =================================================================== --- ruby_1_8_7/ext/tk/sample/demos-en/aniwave.rb (revision 16457) +++ ruby_1_8_7/ext/tk/sample/demos-en/aniwave.rb (revision 16458) @@ -58,6 +58,7 @@ @backupCoords = [] n = 0 (-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] } + n = 305 @waveCoords << [n, 0]; @backupCoords << [n, 0] @waveCoords << [n+5, 200]; @backupCoords << [n+5, 200] @coordsLen = @waveCoords.length Index: ruby_1_8_7/ext/tk/tcltklib.c =================================================================== --- ruby_1_8_7/ext/tk/tcltklib.c (revision 16457) +++ ruby_1_8_7/ext/tk/tcltklib.c (revision 16458) @@ -4,7 +4,7 @@ * Oct. 24, 1997 Y. Matsumoto */ -#define TCLTKLIB_RELEASE_DATE "2008-04-02" +#define TCLTKLIB_RELEASE_DATE "2008-05-16" #include "ruby.h" @@ -3153,6 +3153,7 @@ str, "'", (char *)NULL); rbtk_pending_exception = rb_exc_new2(rb_eArgError, Tcl_GetStringResult(interp)); + if (old_gc == Qfalse) rb_gc_enable(); return TCL_ERROR; #endif } @@ -5155,6 +5156,8 @@ Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); #endif + rb_thread_critical = thr_crit_bup; + return; } /* delete root widget */ @@ -5162,7 +5165,7 @@ DUMP1("check `destroy'"); if (Tcl_GetCommandInfo(ip, "destroy", &info)) { DUMP1("call `destroy'"); - Tcl_GlobalEval(ip, "destroy ."); + Tcl_GlobalEval(ip, "catch {destroy .}"); } #endif #if 1 @@ -7106,7 +7109,8 @@ if (NIL_P(enc)) { encoding = (Tcl_Encoding)NULL; } else { - StringValue(enc); + /* StringValue(enc); */ + enc = rb_funcall(enc, ID_to_s, 0, 0); /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */ encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc)); @@ -7292,7 +7296,8 @@ if (NIL_P(enc)) { encoding = (Tcl_Encoding)NULL; } else { - StringValue(enc); + /* StringValue(enc); */ + enc = rb_funcall(enc, ID_to_s, 0, 0); /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */ encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc)); Index: ruby_1_8_7/ext/tk/lib/tk.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk.rb (revision 16458) @@ -775,7 +775,7 @@ private :_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 == '' begin ns = TkCore::INTERP._invoke_without_enc('namespace', 'current') @@ -794,6 +794,15 @@ @cmdtbl = [] unless defined? @cmdtbl @cmdtbl.taint unless @cmdtbl.tainted? @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); if ns 'rb_out' << TkCore::INTERP._ip_id_ << ' ' << ns << ' ' << id @@ -801,19 +810,29 @@ 'rb_out' << TkCore::INTERP._ip_id_ << ' ' << id 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 = $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) TkCore::INTERP.tk_cmd_tbl.delete(id) end # private :install_cmd, :uninstall_cmd # module_function :install_cmd, :uninstall_cmd def install_cmd(cmd) - TkComm.install_cmd(cmd) + TkComm.install_cmd(cmd, @cmdtbl) end def uninstall_cmd(id) - TkComm.uninstall_cmd(id) + TkComm.uninstall_cmd(id, @cmdtbl) end =begin @@ -1447,7 +1466,9 @@ def after(ms, cmd=Proc.new) 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 =begin def after(ms, cmd=Proc.new) @@ -1477,7 +1498,9 @@ def after_idle(cmd=Proc.new) 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 =begin def after_idle(cmd=Proc.new) @@ -1495,6 +1518,11 @@ def 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 def windowingsystem @@ -4947,6 +4975,15 @@ self end + def grid_anchor(anchor=None) + if anchor == None + TkGrid.anchor(self) + else + TkGrid.anchor(self, anchor) + self + end + end + def grid_forget #tk_call('grid', 'forget', epath) TkGrid.forget(self) @@ -4978,12 +5015,14 @@ TkGrid.columnconfigure(self, index, keys) end alias grid_columnconfigure grid_columnconfig + alias grid_column grid_columnconfig def grid_rowconfig(index, keys) #tk_call('grid', 'rowconfigure', epath, index, *hash_kv(keys)) TkGrid.rowconfigure(self, index, keys) end alias grid_rowconfigure grid_rowconfig + alias grid_row grid_rowconfig def grid_columnconfiginfo(index, slot=nil) #if slot @@ -5226,11 +5265,13 @@ end children.each{|path, obj| - if defined?(@cmdtbl) - for id in @cmdtbl - uninstall_cmd id + obj.instance_eval{ + if defined?(@cmdtbl) + for id in @cmdtbl + uninstall_cmd id + end end - end + } TkCore::INTERP.tk_windows.delete(path) } @@ -5348,7 +5389,7 @@ #Tk.freeze module Tk - RELEASE_DATE = '2008-04-18'.freeze + RELEASE_DATE = '2008-05-16'.freeze autoload :AUTO_PATH, 'tk/variable' autoload :TCL_PACKAGE_PATH, 'tk/variable' Index: ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/hierarchy.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/hierarchy.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/hierarchy.rb (revision 16458) @@ -31,6 +31,7 @@ KEY_TBL = [ [?n, ?s, :node], nil ] PROC_TBL = [ [?s, TkComm.method(:string) ], nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -46,6 +47,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -74,6 +76,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -89,6 +92,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -112,6 +116,7 @@ ] PROC_TBL = [ [ ?s, TkComm.method(:string) ], nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -127,6 +132,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); Index: ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/spinner.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/spinner.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/spinner.rb (revision 16458) @@ -38,6 +38,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -53,6 +54,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); end Index: ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/entryfield.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/entryfield.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/entryfield.rb (revision 16458) @@ -43,6 +43,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -58,6 +59,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); end Index: ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/calendar.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/calendar.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/iwidgets/calendar.rb (revision 16458) @@ -46,6 +46,7 @@ KEY_TBL = [ [?d, ?s, :date], nil ] PROC_TBL = [ [?s, TkComm.method(:string) ], nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -61,6 +62,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); Index: ruby_1_8_7/ext/tk/lib/tkextlib/blt/treeview.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/blt/treeview.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/blt/treeview.rb (revision 16458) @@ -239,6 +239,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -254,6 +255,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -492,6 +494,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -507,6 +510,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -523,7 +527,8 @@ def _find_exec_flag_value(val) if val.kind_of?(Array) cmd, *args = val - FindExecFlagValue.new(cmd, args.join(' ')) + #FindExecFlagValue.new(cmd, args.join(' ')) + FindExecFlagValue.new(cmd, *args) elsif TkComm._callback_entry?(val) FindExecFlagValue.new(val) else Index: ruby_1_8_7/ext/tk/lib/tkextlib/blt/dragdrop.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/blt/dragdrop.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/blt/dragdrop.rb (revision 16458) @@ -81,6 +81,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -96,6 +97,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL) @@ -123,6 +125,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -138,6 +141,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL) @@ -177,6 +181,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -192,6 +197,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL) end Index: ruby_1_8_7/ext/tk/lib/tkextlib/winico/winico.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/winico/winico.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/winico/winico.rb (revision 16458) @@ -150,6 +150,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -165,6 +166,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -185,7 +187,8 @@ Winico_callback._config_keys.each{|k| if keys[k].kind_of?(Array) 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 TkComm._callback_entry?(keys[k]) keys[k] = Winico_callback.new(keys[k]) @@ -201,7 +204,8 @@ Winico_callback._config_keys.each{|k| if keys[k].kind_of?(Array) 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 TkComm._callback_entry?(keys[k]) keys[k] = Winico_callback.new(keys[k]) Index: ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/shape.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/shape.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/shape.rb (revision 16458) @@ -11,15 +11,15 @@ # call setup script require 'tkextlib/tkDND/setup.rb' -# TkPackage.require('shape', '0.3') -TkPackage.require('shape') +# TkPackage.require('Shape', '0.3') +TkPackage.require('Shape') module Tk module TkDND module Shape extend TkCore - PACKAGE_NAME = 'shape'.freeze + PACKAGE_NAME = 'Shape'.freeze def self.package_name PACKAGE_NAME end @@ -27,26 +27,28 @@ =begin def self.package_version begin - TkPackage.require('shape') + TkPackage.require('Shape') rescue '' end end =end - def self.package_version - Tk.tk_call('set', 'shape_version') - end - alias shape_version package_version + class << self + def package_version + Tk.tk_call('set', 'shape_version') + end + alias shape_version package_version - def self.package_patchlevel - Tk.tk_call('set', 'shape_patchlevel') - end - alias shape_patchlevel package_patchlevel + def package_patchlevel + Tk.tk_call('set', 'shape_patchLevel') + end + alias shape_patchlevel package_patchlevel - def self.version - tk_call('shape', 'version') + def version + tk_call('shape', 'version') + end + alias xshape_version version end - alias xshape_version version ############################ Index: ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/tkdnd.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/tkdnd.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tkDND/tkdnd.rb (revision 16458) @@ -57,6 +57,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -72,6 +73,7 @@ end inf } +=end # setup tables _setup_subst_table(KEY_TBL, PROC_TBL); Index: ruby_1_8_7/ext/tk/lib/tkextlib/treectrl/tktreectrl.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/treectrl/tktreectrl.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/treectrl/tktreectrl.rb (revision 16458) @@ -137,6 +137,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -152,6 +153,7 @@ end inf } +=end # setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys # Index: ruby_1_8_7/ext/tk/lib/tkextlib/tktable/tktable.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tktable/tktable.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tktable/tktable.rb (revision 16458) @@ -291,6 +291,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -306,6 +307,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -340,6 +342,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -355,6 +358,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -387,6 +391,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -402,6 +407,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -437,6 +443,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -452,6 +459,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); end Index: ruby_1_8_7/ext/tk/lib/tkextlib/version.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/version.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/version.rb (revision 16458) @@ -2,5 +2,5 @@ # release date of tkextlib # module Tk - Tkextlib_RELEASE_DATE = '2008-04-18'.freeze + Tkextlib_RELEASE_DATE = '2008-05-14'.freeze end Index: ruby_1_8_7/ext/tk/lib/tkextlib/tile/style.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tile/style.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tile/style.rb (revision 16458) @@ -33,6 +33,8 @@ # conflict with some definitions on Tcl/Tk scripts. if Tk::Tile::TILE_SPEC_VERSION_ID < 7 def __define_wrapper_proc_for_compatibility__! + __define_themes_and_setTheme_proc__! + unless Tk.info(:commands, '::ttk::style').empty? # fail RuntimeError, # "can't define '::ttk::style' command (already exist)" @@ -59,6 +61,8 @@ end else ### TILE_SPEC_VERSION_ID == 7 def __define_wrapper_proc_for_compatibility__! + __define_themes_and_setTheme_proc__! + unless Tk.info(:commands, '::ttk::style').empty? # fail RuntimeError, # "can't define '::ttk::style' command (already exist)" @@ -91,6 +95,8 @@ TkCommandNames = ['::ttk::style'.freeze].freeze def __define_wrapper_proc_for_compatibility__! + __define_themes_and_setTheme_proc__! + unless Tk.info(:commands, '::style').empty? # fail RuntimeError, "can't define '::style' command (already exist)" @@ -120,6 +126,36 @@ 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) if style.kind_of?(Hash) keys = style Index: ruby_1_8_7/ext/tk/lib/tkextlib/tile/tnotebook.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tile/tnotebook.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tile/tnotebook.rb (revision 16458) @@ -77,9 +77,9 @@ def add(child, keys=nil) if keys && keys != None - tk_send_without_enc('add', _epath(child), *hash_kv(keys)) + tk_send('add', _epath(child), *hash_kv(keys)) else - tk_send_without_enc('add', _epath(child)) + tk_send('add', _epath(child)) end self end Index: ruby_1_8_7/ext/tk/lib/tkextlib/tile.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tkextlib/tile.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tkextlib/tile.rb (revision 16458) @@ -201,6 +201,38 @@ args.map!{|arg| TkComm._get_eval_string(arg)}.join('.') 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 if Tk::Tile::TILE_SPEC_VERSION_ID < 8 def self.enableMnemonics(w) @@ -332,12 +364,16 @@ autoload :TLabelframe, 'tkextlib/tile/tlabelframe' autoload :Labelframe, 'tkextlib/tile/tlabelframe' + autoload :TLabelFrame, 'tkextlib/tile/tlabelframe' + autoload :LabelFrame, 'tkextlib/tile/tlabelframe' autoload :TLabel, 'tkextlib/tile/tlabel' autoload :Label, 'tkextlib/tile/tlabel' autoload :TMenubutton, 'tkextlib/tile/tmenubutton' autoload :Menubutton, 'tkextlib/tile/tmenubutton' + autoload :TMenuButton, 'tkextlib/tile/tmenubutton' + autoload :MenuButton, 'tkextlib/tile/tmenubutton' autoload :TNotebook, 'tkextlib/tile/tnotebook' autoload :Notebook, 'tkextlib/tile/tnotebook' Index: ruby_1_8_7/ext/tk/lib/tk/wm.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/wm.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/wm.rb (revision 16458) @@ -362,7 +362,7 @@ end end def overrideredirect(mode=TkComm::None) - Wm.overrideredirect(self, mode=TkComm::None) + Wm.overrideredirect(self, mode) end alias wm_overrideredirect overrideredirect TOPLEVEL_METHODCALL_OPTKEYS['overrideredirect'] = 'overrideredirect' @@ -545,7 +545,7 @@ module Wm_for_General Wm.instance_methods.each{|m| 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 Index: ruby_1_8_7/ext/tk/lib/tk/pack.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/pack.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/pack.rb (revision 16458) @@ -9,6 +9,7 @@ TkCommandNames = ['pack'.freeze].freeze +=begin def configure(win, *args) if args[-1].kind_of?(Hash) opts = args.pop @@ -29,6 +30,22 @@ } tk_call_without_enc("pack", 'configure', *params) 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 def forget(*args) Index: ruby_1_8_7/ext/tk/lib/tk/validation.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/validation.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/validation.rb (revision 16458) @@ -50,7 +50,8 @@ key2class.each{|key, klass| if keys[key].kind_of?(Array) 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 TkComm._callback_entry?(keys[key]) keys[key] = klass.new(keys[key]) @@ -151,7 +152,8 @@ key2class.each{|key, klass| if keys[key].kind_of?(Array) 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 TkComm._callback_entry?(keys[key]) keys[key] = klass.new(keys[key]) @@ -249,6 +251,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -264,6 +267,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); @@ -293,6 +297,7 @@ extra_args_tbl = klass._get_extra_args_tbl if args.compact.size > 0 + args.map!{|arg| klass._sym2subst(arg)} args = args.join(' ') keys = klass._get_subst_key(args) if cmd.kind_of?(String) Index: ruby_1_8_7/ext/tk/lib/tk/spinbox.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/spinbox.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/spinbox.rb (revision 16458) @@ -37,6 +37,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -52,6 +53,7 @@ end inf } +=end _setup_subst_table(KEY_TBL, PROC_TBL); Index: ruby_1_8_7/ext/tk/lib/tk/event.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/event.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/event.rb (revision 16458) @@ -352,6 +352,14 @@ 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_TBL = [ [ ?n, TkComm.method(:num_or_str) ], @@ -371,6 +379,7 @@ nil ] +=begin # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) ) KEY_TBL.map!{|inf| if inf.kind_of?(Array) @@ -386,6 +395,7 @@ end inf } +=end # setup tables to be used by scan_args, _get_subst_key, _get_all_subst_keys # @@ -399,7 +409,8 @@ # ( which are Tcl strings ) to ruby objects based on the key string # 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 @@ -447,6 +458,7 @@ extra_args_tbl = klass._get_extra_args_tbl if args.compact.size > 0 + args.map!{|arg| klass._sym2subst(arg)} args = args.join(' ') keys = klass._get_subst_key(args) Index: ruby_1_8_7/ext/tk/lib/tk/grid.rb =================================================================== --- ruby_1_8_7/ext/tk/lib/tk/grid.rb (revision 16457) +++ ruby_1_8_7/ext/tk/lib/tk/grid.rb (revision 16458) @@ -22,6 +22,7 @@ list(tk_call_without_enc('grid', 'bbox', *args)) end +=begin def configure(win, *args) if args[-1].kind_of?(Hash) opts = args.pop @@ -53,6 +54,48 @@ tk_call_without_enc('grid', '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| + 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 def columnconfigure(master, index, args) @@ -61,12 +104,14 @@ tk_call_without_enc("grid", 'columnconfigure', master, index, *hash_kv(args)) end + alias column columnconfigure def rowconfigure(master, index, args) # master = master.epath if master.kind_of?(TkObject) master = _epath(master) tk_call_without_enc("grid", 'rowconfigure', master, index, *hash_kv(args)) end + alias row rowconfigure def columnconfiginfo(master, index, slot=nil) # master = master.epath if master.kind_of?(TkObject) @@ -189,10 +234,10 @@ list(tk_call_without_enc('grid', 'slaves', master, *hash_kv(args))) end - module_function :bbox, :forget, :propagate, :info + module_function :anchor, :bbox, :add, :forget, :propagate, :info module_function :remove, :size, :slaves, :location module_function :grid, :configure, :columnconfigure, :rowconfigure - module_function :columnconfiginfo, :rowconfiginfo + module_function :column, :row, :columnconfiginfo, :rowconfiginfo end =begin def TkGrid(win, *args) Index: ruby_1_8_7/ext/tk/ChangeLog.tkextlib =================================================================== --- ruby_1_8_7/ext/tk/ChangeLog.tkextlib (revision 16457) +++ ruby_1_8_7/ext/tk/ChangeLog.tkextlib (revision 16458) @@ -1,3 +1,9 @@ +2008-05-12 Hidetoshi NAGAI <nagai@a...> + + * ext/tk/lib/tkextlib/tkDND/shape.rb: wrong package name. + +--------------< ... some changes ... >------------------ + 2007-05-26 Hidetoshi NAGAI <nagai@a...> * ext/tk/lib/tkextlib/tcllib/tablelist.rb: fix typo. Index: ruby_1_8_7/ext/tk/tkutil/extconf.rb =================================================================== --- ruby_1_8_7/ext/tk/tkutil/extconf.rb (revision 16457) +++ ruby_1_8_7/ext/tk/tkutil/extconf.rb (revision 16458) @@ -8,5 +8,6 @@ if has_tk require 'mkmf' have_func("rb_obj_instance_exec", "ruby.h") + have_func("strndup", "string.h") create_makefile('tkutil') end Index: ruby_1_8_7/ext/tk/tkutil/tkutil.c =================================================================== --- ruby_1_8_7/ext/tk/tkutil/tkutil.c (revision 16457) +++ ruby_1_8_7/ext/tk/tkutil/tkutil.c (revision 16458) @@ -7,7 +7,7 @@ ************************************************/ -#define TKUTIL_RELEASE_DATE "2008-03-29" +#define TKUTIL_RELEASE_DATE "2008-05-14" #include "ruby.h" @@ -1073,11 +1073,13 @@ /*************************************/ +#define CBSUBST_TBL_MAX (256) struct cbsubst_info { - int size; - char *key; - char *type; - ID *ivar; + int full_subst_length; + int keylen[CBSUBST_TBL_MAX]; + unsigned char *key[CBSUBST_TBL_MAX]; + unsigned char type[CBSUBST_TBL_MAX]; + ID ivar[CBSUBST_TBL_MAX]; VALUE proc; VALUE aliases; }; @@ -1094,42 +1096,49 @@ subst_free(ptr) struct cbsubst_info *ptr; { + int i; + if (ptr) { - if (ptr->key != (char*)NULL) free(ptr->key); - if (ptr->type != (char*)NULL) free(ptr->type); - if (ptr->ivar != (ID*)NULL) free(ptr->ivar); - free(ptr); + for(i = 0; i < CBSUBST_TBL_MAX; i++) { + if (ptr->key[i] != (unsigned char *)NULL) free(ptr->key[i]); + } + free(ptr); } } -static void -cbsubst_init() +static struct cbsubst_info * +allocate_cbsubst_info() { - struct cbsubst_info *inf; - ID *ivar; - volatile VALUE proc, aliases; + struct cbsubst_info *inf; + 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); - inf->key[0] = '\0'; + for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) { + inf->keylen[idx] = 0; + inf->key[idx] = (unsigned char *) NULL; + inf->type[idx] = '\0'; + inf->ivar[idx] = (ID) 0; + } - inf->type = ALLOC_N(char, 1); - inf->type[0] = '\0'; + proc = rb_hash_new(); + inf->proc = proc; - ivar = ALLOC_N(ID, 1); - inf->ivar = ivar; + aliases = rb_hash_new(); + inf->aliases = aliases; - proc = rb_hash_new(); - inf->proc = proc; + return inf; +} - aliases = rb_hash_new(); - inf->aliases = aliases; - +static void +cbsubst_init() +{ 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 @@ -1139,24 +1148,29 @@ VALUE self; { struct cbsubst_info *inf; - int idx; + int idx, iv_idx; Data_Get_Struct(rb_const_get(rb_obj_class(self), ID_SUBST_INFO), struct cbsubst_info, inf); - for(idx = 0; idx < argc; idx++) { - rb_ivar_set(self, inf->ivar[idx], argv[idx]); + idx = 0; + 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; } - static VALUE cbsubst_ret_val(self, val) VALUE self; 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; } @@ -1217,6 +1231,59 @@ } 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 cbsubst_get_subst_arg(argc, argv, self) int argc; VALUE *argv; @@ -1224,17 +1291,15 @@ { struct cbsubst_info *inf; const char *str; - char *buf, *ptr; - int i, j, len; + unsigned char *buf, *ptr; + int i, idx, len; ID id; volatile VALUE arg_sym, ret; Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), struct cbsubst_info, inf); - buf = ALLOC_N(char, 3*argc + 1); - ptr = buf; - len = strlen(inf->key); + ptr = buf = ALLOC_N(char, inf->full_subst_length + 1); for(i = 0; i < argc; i++) { switch(TYPE(argv[i])) { @@ -1256,17 +1321,25 @@ id = rb_intern(RSTRING_PTR(rb_str_cat2(rb_str_new2("@"), str))); - for(j = 0; j < len; j++) { - if (inf->ivar[j] == id) break; - } - - if (j >= len) { + for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) { + if (inf->ivar[idx] == id) break; + } + if (idx >= CBSUBST_TBL_MAX) { rb_raise(rb_eArgError, "cannot find attribute :%s", str); } - *(ptr++) = '%'; - *(ptr++) = *(inf->key + j); - *(ptr++) = ' '; + *(ptr++) = '%'; + + if (len = inf->keylen[idx]) { + /* longname */ + strncpy(ptr, inf->key[idx], len); + ptr += len; + } else { + /* single char */ + *(ptr++) = idx; + } + + *(ptr++) = ' '; } *ptr = '\0'; @@ -1283,27 +1356,50 @@ VALUE self; VALUE str; { + struct cbsubst_info *inf; volatile VALUE list; volatile VALUE ret; - int i, len; - char *buf, *ptr; + VALUE keyval; + int i, len, keylen, idx; + unsigned char *buf, *ptr, *key; list = rb_funcall(cTclTkLib, ID_split_tklist, 1, str); - 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++) { - ptr = RSTRING_PTR(RARRAY_PTR(list)[i]); - if (*ptr == '%' && *(ptr + 2) == '\0') { - *(buf + i) = *(ptr + 1); - } else { - *(buf + i) = ' '; - } + keyval = RARRAY_PTR(list)[i]; + key = (unsigned char*)RSTRING_PTR(keyval); + if (*key == '%') { + if (*(key + 2) == '\0') { + /* single char */ + *(ptr++) = *(key + 1); + } else { + /* 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++) = ' '; + } + } + } else { + *(ptr++) = ' '; + } } - *(buf + len) = '\0'; + *ptr = '\0'; - ret = rb_str_new2(buf); + ret = rb_str_new2((const char*)buf); free(buf); return ret; } @@ -1313,100 +1409,165 @@ VALUE self; { struct cbsubst_info *inf; - char *buf, *ptr; - int i, len; + unsigned char *buf, *ptr; + unsigned char *keys_buf, *keys_ptr; + int idx, len; volatile VALUE ret; Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), struct cbsubst_info, inf); - len = strlen(inf->key); - buf = ALLOC_N(char, 3*len + 1); - ptr = buf; - for(i = 0; i < len; i++) { - *(ptr++) = '%'; - *(ptr++) = *(inf->key + i); - *(ptr++) = ' '; + ptr = buf = ALLOC_N(unsigned char, inf->full_subst_length + 1); + keys_ptr = keys_buf = ALLOC_N(unsigned char, CBSUBST_TBL_MAX + 1); + + for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) { + if (inf->ivar[idx] == (ID) 0) continue; + + *(keys_ptr++) = (unsigned char)idx; + + *(ptr++) = '%'; + + if (len = inf->keylen[idx]) { + /* longname */ + strncpy(ptr, inf->key[idx], len); + ptr += len; + } else { + /* single char */ + *(ptr++) = (unsigned char)idx; + } + + *(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); return ret; } static VALUE -cbsubst_table_setup(self, key_inf, proc_inf) - VALUE self; - VALUE key_inf; - VALUE proc_inf; +cbsubst_table_setup(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; { - struct cbsubst_info *subst_inf; - int idx; - int len = RARRAY_LEN(key_inf); - int real_len = 0; - char *key = ALLOC_N(char, len + 1); - char *type = ALLOC_N(char, len + 1); - ID *ivar = ALLOC_N(ID, len + 1); - volatile VALUE proc = rb_hash_new(); - volatile VALUE aliases = rb_hash_new(); - volatile VALUE inf; + volatile VALUE key_inf; + volatile VALUE longkey_inf; + volatile VALUE proc_inf; + VALUE inf; + ID id; + struct cbsubst_info *subst_inf; + int idx, len; + unsigned char chr; - /* init */ - subst_inf = ALLOC(struct 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; + /* accept (key_inf, proc_inf) or (key_inf, longkey_inf, procinf) */ + if (rb_scan_args(argc, argv, "21", &key_inf, &longkey_inf, &proc_inf) == 2) { + proc_inf = longkey_inf; + longkey_inf = rb_ary_new(); + } - /* - * keys : array of [subst, type, ivar] - * subst ==> char code - * type ==> char code - * ivar ==> symbol - */ - for(idx = 0; idx < len; idx++) { - inf = RARRAY_PTR(key_inf)[idx]; - if (TYPE(inf) != T_ARRAY) continue; - *(key + real_len) = NUM2CHR(RARRAY_PTR(inf)[0]); - *(type + real_len) = NUM2CHR(RARRAY_PTR(inf)[1]); + /* check the number of longkeys */ + if (RARRAY_LEN(longkey_inf) > 125 /* from 0x80 to 0xFD */) { + rb_raise(rb_eArgError, "too many longname-key definitions"); + } - *(ivar + real_len) - = rb_intern( - RSTRING_PTR( - rb_str_cat2(rb_str_new2("@"), - rb_id2name(SYM2ID(RARRAY_PTR(inf)[2]))) - ) - ); + /* init */ + subst_inf = allocate_cbsubst_info(); - rb_attr(self, SYM2ID(RARRAY_PTR(inf)[2]), 1, 0, Qtrue); - real_len++; + /* + * keys : array of [subst, type, ivar] + * subst ==> char code or string + * type ==> char code or string + * ivar ==> symbol + */ + len = RARRAY_LEN(key_inf); + for(idx = 0; idx < len; idx++) { + inf = RARRAY_PTR(key_inf)[idx]; + if (TYPE(inf) != T_ARRAY) continue; + + if (TYPE(RARRAY_PTR(inf)[0]) == T_STRING) { + chr = *(RSTRING_PTR(RARRAY_PTR(inf)[0])); + } else { + chr = NUM2CHR(RARRAY_PTR(inf)[0]); } - *(key + real_len) = '\0'; - *(type + real_len) = '\0'; - subst_inf->size = real_len; + 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]); + } - /* - * procs : array of [type, proc] - * type ==> char code - * proc ==> proc/method/obj (must respond to 'call') - */ - len = RARRAY_LEN(proc_inf); - for(idx = 0; idx < len; idx++) { - inf = RARRAY_PTR(proc_inf)[idx]; - if (TYPE(inf) != T_ARRAY) continue; - rb_hash_aset(proc, RARRAY_PTR(inf)[0], RARRAY_PTR(inf)[1]); + 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]); + } - rb_const_set(self, ID_SUBST_INFO, - Data_Wrap_Struct(cSUBST_INFO, subst_mark, - subst_free, subst_inf)); + subst_inf->full_subst_length += (subst_inf->keylen[chr] + 2); - return self; + 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); + } + + /* + * procs : array of [type, proc] + * type ==> char code or string + * proc ==> proc/method/obj (must respond to 'call') + */ + len = RARRAY_LEN(proc_inf); + for(idx = 0; idx < len; idx++) { + inf = RARRAY_PTR(proc_inf)[idx]; + if (TYPE(inf) != T_ARRAY) continue; + 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, + Data_Wrap_Struct(cSUBST_INFO, subst_mark, + subst_free, subst_inf)); + + return self; } static VALUE @@ -1424,10 +1585,11 @@ { struct cbsubst_info *inf; int idx; - int len = RARRAY_LEN(val_ary); - char c; - char *ptr; - volatile VALUE dst = rb_ary_new2(len); + unsigned char *keyptr = (unsigned char*)RSTRING_PTR(arg_key); + int keylen = RSTRING_LEN(arg_key); + int vallen = RARRAY_LEN(val_ary); + unsigned char type_chr; + volatile VALUE dst = rb_ary_new2(vallen); volatile VALUE proc; int thr_crit_bup; VALUE old_gc; @@ -1440,26 +1602,25 @@ Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO), struct cbsubst_info, inf); - for(idx = 0; idx < len; idx++) { - if (idx >= RSTRING_LEN(arg_key)) { - proc = Qnil; - } else if (*(RSTRING_PTR(arg_key) + idx) == ' ') { - proc = Qnil; - } else { - ptr = strchr(inf->key, *(RSTRING_PTR(arg_key) + idx)); - if (ptr == (char*)NULL) { - proc = Qnil; - } else { - c = *(inf->type + (ptr - inf->key)); - proc = rb_hash_aref(inf->proc, INT2FIX(c)); - } - } + for(idx = 0; idx < vallen; idx++) { + if (idx >= keylen) { + proc = Qnil; + } else if (*(keyptr + idx) == ' ') { + proc = Qnil; + } else { + if (type_chr = inf->type[*(keyptr + idx)]) { + proc = rb_hash_aref(inf->proc, INT2FIX((int)type_chr)); + } else { + proc = Qnil; + } + } - if (NIL_P(proc)) { - rb_ary_push(dst, RARRAY_PTR(val_ary)[idx]); - } else { - rb_ary_push(dst, rb_funcall(proc, ID_call, 1, RARRAY_PTR(val_ary)[idx])); - } + if (NIL_P(proc)) { + rb_ary_push(dst, RARRAY_PTR(val_ary)[idx]); + } else { + rb_ary_push(dst, rb_funcall(proc, ID_call, 1, + RARRAY_PTR(val_ary)[idx])); + } } if (old_gc == Qfalse) rb_gc_enable(); @@ -1543,6 +1704,8 @@ 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, "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", cbsubst_get_subst_arg, -1); rb_define_singleton_method(cCB_SUBST, "_get_subst_key", @@ -1550,7 +1713,7 @@ rb_define_singleton_method(cCB_SUBST, "_get_all_subst_keys", cbsubst_get_all_subst_keys, 0); 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", cbsubst_get_extra_args_tbl, 0); rb_define_singleton_method(cCB_SUBST, "_define_attribute_aliases", Index: ruby_1_8_7/ext/dbm/dbm.c =================================================================== --- ruby_1_8_7/ext/dbm/dbm.c (revision 16457) +++ ruby_1_8_7/ext/dbm/dbm.c (revision 16458) @@ -812,5 +812,7 @@ #ifdef 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 } Index: ruby_1_8_7/ext/purelib.rb =================================================================== --- ruby_1_8_7/ext/purelib.rb (revision 16457) +++ ruby_1_8_7/ext/purelib.rb (revision 16458) @@ -1,3 +1,3 @@ -if nul = $:.index("-") +if nul = $:.find_index {|path| /\A(?:\.\/)*-\z/ =~ path} $:[nul..-1] = ["."] end Index: ruby_1_8_7/NEWS =================================================================== --- ruby_1_8_7/NEWS (revision 16457) +++ ruby_1_8_7/NEWS (revision 16458) @@ -17,10 +17,6 @@ * 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! @@ -87,9 +83,7 @@ New class for various enumeration defined by the enumerator library. * Enumerable#each_slice - * Enumerable#enum_slice * Enumerable#each_cons - * Enumerable#enum_cons * Object#to_enum * Object#enum_for @@ -187,6 +181,8 @@ * Method#receiver * Method#name * Method#owner + * UnboundMethod#name + * UnboundMethod#owner New methods. @@ -209,6 +205,10 @@ * Regexp.union accepts an array of patterns. + * String#bytesize + + New method, returning the size in bytes. (alias length and size) + * String#chars * String#each_char * String#partition Index: ruby_1_8_7/configure.in =================================================================== --- ruby_1_8_7/configure.in (revision 16457) +++ ruby_1_8_7/configure.in (revision 16458) @@ -1402,6 +1402,7 @@ RUNRUBY='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`' else MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib' + MINIRUBY="$MINIRUBY"' -I$(EXTOUT)/common -I./- -r$(srcdir)/ext/purelib.rb' PREP='miniruby$(EXEEXT)' RUNRUBY='$(MINIRUBY) $(srcdir)/runruby.rb --extout=$(EXTOUT)' fi Index: ruby_1_8_7/ChangeLog =================================================================== --- ruby_1_8_7/ChangeLog (revision 16457) +++ ruby_1_8_7/ChangeLog (revision 16458) @@ -1,3 +1,329 @@ +Sun May 18 22:26:51 2008 GOTOU Yuuzou <gotoyuzo@n...> + + * 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@r...> + + * file.c (file_expand_path): fix for short file name on Cygwin. + +Sat May 17 11:29:11 2008 Nobuyoshi Nakada <nobu@r...> + + * file.c (rb_file_s_extname): first dot is not an extension name. + +Sat May 17 10:18:44 2008 Yukihiro Matsumoto <matz@r...> + + * re.c (rb_reg_search): need to free allocated buffer in re_register. + +Fri May 16 17:01:44 2008 NAKAMURA Usaku <usa@r...> + + * win32/Makefile.sub (test-rubyspec): added. + +Fri May 16 16:22:40 2008 Hidetoshi NAGAI <nagai@a...> + + * ext/tk/tcltklib.c: sometimes freeze when receive Interrupt signal. + +Fri May 16 14:54:56 2008 Tanaka Akira <akr@f...> + + * Makefile.in (update-rubyspec): move rubyspec to srcdir. + (test-rubyspec): ditto. + +Fri May 16 14:25:22 2008 Tanaka Akira <akr@f...> + + * Makefile.in (test-rubyspec): use RUNRUBY. suggested by nobu. + +Fri May 16 13:01:43 2008 Tanaka Akira <akr@f...> + + * 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@a...> + + * 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@i...> + + * 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@r...> + + * 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@i...> + + * 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@r...> + + * 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@a...> + + * 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@i...> + + * 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@r...> + + * 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@r...> + + * 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@a...> + + * 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@r...> + + * win32/win32.c (rb_w32_select): backport from trunk. + [ruby-talk:300743] + +Mon May 12 12:33:21 2008 Nobuyoshi Nakada <nobu@r...> + + * common.mk (RUBYLIB, RUBYOPT): clear. + +Mon May 12 10:41:10 2008 Nobuyoshi Nakada <nobu@r...> + + * 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@r...> + + * enum.c (all_iter_i, any_iter_i): reduced duplicated code. + +Sun May 11 17:57:36 2008 Nobuyoshi Nakada <nobu@r...> + + * configure.in (MINIRUBY): should not include extension library path. + +Sun May 11 10:36:10 2008 Kazuhiro NISHIYAMA <zn@m...> + + * eval.c (method_name, method_owner): New methods; backported + from 1.9. (UnboundMethod#name, UnboundMethod#owner) + +Sun May 11 02:48:13 2008 <nagai@o...> + + * 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@r...> + + * string.c (rb_str_each_line): RDoc updated. [ruby-dev:34586] + +Sat May 10 13:17:56 2008 Hidetoshi NAGAI <nagai@a...> + + * 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@r...> + + * 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@a...> + + * 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@r...> + + * 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@r...> + + * 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@m...> + + * 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@a...> + + * 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@r...> + + * 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@r...> + + * 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@r...> + + * 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@r...> + + 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@r...> + + * 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@r...> + + 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@r...> + + 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@r...> + + * 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@r...> + + 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@r...> + + 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@r...> + + * 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@r...> + + 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@r...> * re.c (rb_reg_search): use local variable. a patch from wanabe Index: ruby_1_8_7/re.c =================================================================== --- ruby_1_8_7/re.c (revision 16457) +++ ruby_1_8_7/re.c (revision 16458) @@ -927,6 +927,7 @@ } if (result < 0) { + re_free_registers(®s); rb_backref_set(Qnil); return result; } @@ -943,6 +944,7 @@ } re_copy_registers(RMATCH(match)->regs, ®s); + re_free_registers(®s); RMATCH(match)->str = rb_str_new4(str); rb_backref_set(match); @@ -1219,7 +1221,6 @@ /* * call-seq: * mtch.values_at([index]*) => array - * mtch.select([index]*) => array * * Uses each <i>index</i> to access the matching values, returning an array of * the corresponding matches. @@ -1239,7 +1240,46 @@ } +/* + * 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: * mtch.to_s => str @@ -2326,7 +2366,7 @@ rb_define_method(rb_cMatch, "[]", match_aref, -1); 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, "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, "post_match", rb_reg_match_post, 0); rb_define_method(rb_cMatch, "to_s", match_to_s, 0); Index: ruby_1_8_7/misc/ruby-mode.el =================================================================== --- ruby_1_8_7/misc/ruby-mode.el (revision 16457) +++ ruby_1_8_7/misc/ruby-mode.el (revision 16458) @@ -255,6 +255,11 @@ (make-local-variable 'add-log-current-defun-function) (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)) (defun ruby-current-indentation () @@ -1020,24 +1025,13 @@ ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)))) - (cond ((featurep 'xemacs) - (put 'ruby-mode 'font-lock-defaults - '((ruby-font-lock-keywords) - nil nil nil - beginning-of-line - (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))))) + (if (featurep 'xemacs) + (put 'ruby-mode 'font-lock-defaults + '((ruby-font-lock-keywords) + nil nil nil + beginning-of-line + (font-lock-syntactic-keywords + . ruby-font-lock-syntactic-keywords)))) (defun ruby-font-lock-docs (limit) (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t) Index: ruby_1_8_7/enumerator.c =================================================================== --- ruby_1_8_7/enumerator.c (revision 16457) +++ ruby_1_8_7/enumerator.c (revision 16458) @@ -72,16 +72,6 @@ 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: * obj.to_enum(method = :each, *args) @@ -138,8 +128,10 @@ /* * call-seq: * 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.: * (1..10).each_slice(3) {|a| p a} @@ -192,9 +184,10 @@ /* * call-seq: * each_cons(n) {...} + * each_cons(n) * * Iterates the given block for each array of consecutive <n> - * elements. + * elements. If no block is given, returns an enumerator.a * * e.g.: * (1..10).each_cons(3) {|a| p a} @@ -271,12 +264,8 @@ * used as an Enumerable object using the given object's given * method with the given arguments. * - * e.g.: - * str = "xyz" - * - * enum = Enumerable::Enumerator.new(str, :each_byte) - * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"] - * + * Use of this method is not discouraged. Use Kernel#enum_for() + * instead. */ static VALUE enumerator_initialize(argc, argv, obj) @@ -330,7 +319,7 @@ * enum.each {...} * * 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 @@ -340,7 +329,6 @@ struct enumerator *e; int argc = 0; VALUE *argv = 0; - VALUE method; if (!rb_block_given_p()) return obj; e = enumerator_ptr(obj); @@ -364,9 +352,10 @@ /* * call-seq: * e.with_index {|(*args), idx| ... } + * e.with_index * * 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 @@ -377,7 +366,6 @@ VALUE memo = 0; int argc = 0; VALUE *argv = 0; - VALUE method; RETURN_ENUMERATOR(obj, 0, 0); if (e->args) { Index: ruby_1_8_7/sample/erb/erb4html.rb =================================================================== --- ruby_1_8_7/sample/erb/erb4html.rb (revision 0) +++ ruby_1_8_7/sample/erb/erb4html.rb (revision 16458) @@ -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 = "<quoted>" + puts erb.result + + title = "<auto-quote>" + para = ERB4Html.quoted("<quoted>") + puts erb.result +end Property changes on: ruby_1_8_7/sample/erb/erb4html.rb ___________________________________________________________________ Name: svn:eol-style + LF Index: ruby_1_8_7/enum.c =================================================================== --- ruby_1_8_7/enum.c (revision 16457) +++ ruby_1_8_7/enum.c (revision 16458) @@ -84,18 +84,18 @@ * call-seq: * enum.grep(pattern) => array * enum.grep(pattern) {| obj | block } => array - * + * * Returns an array of every element in <i>enum</i> for which * <code>Pattern === element</code>. If the optional <em>block</em> is * supplied, each matching element is passed to it, and the block's * result is stored in the output array. - * + * * (1..100).grep 38..44 #=> [38, 39, 40, 41, 42, 43, 44] * c = IO.constants * c.grep(/SEEK/) #=> ["SEEK_END", "SEEK_SET", "SEEK_CUR"] * res = c.grep(/SEEK/) {|v| IO.const_get(v) } * res #=> [2, 0, 1] - * + * */ static VALUE @@ -109,7 +109,7 @@ arg[1] = ary; rb_iterate(rb_each, obj, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)arg); - + return ary; } @@ -139,14 +139,14 @@ * call-seq: * enum.count(item) => int * enum.count {| obj | block } => int - * + * * Returns the number of items in <i>enum</i> for which equals to <i>item</i>. * If a block is given, counts the 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 @@ -198,15 +198,15 @@ * call-seq: * enum.detect(ifnone = nil) {| obj | block } => obj or nil * enum.find(ifnone = nil) {| obj | block } => obj or nil - * + * * Passes each entry in <i>enum</i> to <em>block</em>. Returns the * first for which <em>block</em> is not <code>false</code>. If no * object matches, calls <i>ifnone</i> and returns its result when it * is specified, or returns <code>nil</code> - * + * * (1..10).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> nil * (1..100).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> 35 - * + * */ static VALUE @@ -266,16 +266,16 @@ * call-seq: * enum.find_index(value) => int or nil * enum.find_index {| obj | block } => int or nil - * + * * Compares each entry in <i>enum</i> with <em>value</em> or passes * to <em>block</em>. Returns the index for the first for which the * evaluated value is non-false. If no object matches, returns * <code>nil</code> - * + * * (1..10).find_index {|i| i % 5 == 0 and i % 7 == 0 } #=> nil * (1..100).find_index {|i| i % 5 == 0 and i % 7 == 0 } #=> 34 * (1..100).find_index(50) #=> 49 - * + * */ static VALUE @@ -319,13 +319,13 @@ * call-seq: * enum.find_all {| obj | block } => array * enum.select {| obj | block } => array - * + * * Returns an array containing all elements of <i>enum</i> for which * <em>block</em> is not <code>false</code> (see also * <code>Enumerable#reject</code>). - * + * * (1..10).find_all {|i| i % 3 == 0 } #=> [3, 6, 9] - * + * */ static VALUE @@ -353,12 +353,12 @@ /* * call-seq: * enum.reject {| obj | block } => array - * + * * Returns an array for all elements of <i>enum</i> for which * <em>block</em> is false (see also <code>Enumerable#find_all</code>). - * + * * (1..10).reject {|i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10] - * + * */ static VALUE @@ -366,7 +366,7 @@ VALUE obj; { VALUE ary = rb_ary_new(); - + RETURN_ENUMERATOR(obj, 0, 0); rb_iterate(rb_each, obj, reject_i, ary); @@ -395,13 +395,13 @@ * call-seq: * enum.collect {| obj | block } => array * enum.map {| obj | block } => array - * + * * Returns a new array with the results of running <em>block</em> once * for every element in <i>enum</i>. - * + * * (1..4).collect {|i| i*i } #=> [1, 4, 9, 16] * (1..4).collect { "cat" } #=> ["cat", "cat", "cat", "cat"] - * + * */ static VALUE @@ -419,9 +419,9 @@ * call-seq: * enum.to_a => array * enum.entries => array - * + * * Returns an array containing the items in <i>enum</i>. - * + * * (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7] * { 'a'=>1, 'b'=>2, 'c'=>3 }.to_a #=> [["a", 1], ["b", 2], ["c", 3]] */ @@ -480,7 +480,7 @@ * enum.reduce(sym) => obj * enum.reduce(initial) {| memo, obj | block } => obj * enum.reduce {| memo, obj | block } => obj - * + * * Combines all elements of <i>enum</i> by applying a binary * operation, specified by a block or a symbol that names a * method or operator. @@ -489,7 +489,7 @@ * the block is passed an accumulator value (<i>memo</i>) and the element. * If you specify a symbol instead, then each element in the collection * will be passed to the named method of <i>memo</i>. - * In either case, the result becomes the new value for <i>memo</i>. + * In either case, the result becomes the new value for <i>memo</i>. * At the end of the iteration, the final value of <i>memo</i> is the * return value fo the method. * @@ -497,7 +497,7 @@ * then uses the first element of collection is used as the initial value * of <i>memo</i>. * - * Examples: + * Examples: * * # Sum some numbers * (5..10).reduce(:+) #=> 45 @@ -512,7 +512,7 @@ * memo.length > word.length ? memo : word * end * longest #=> "sheep" - * + * */ static VALUE enum_inject(argc, argv, obj) @@ -564,13 +564,13 @@ /* * call-seq: * enum.partition {| obj | block } => [ true_array, false_array ] - * + * * Returns two arrays, the first containing the elements of * <i>enum</i> for which the block evaluates to true, the second * containing the rest. - * + * * (1..6).partition {|i| (i&1).zero?} #=> [[2, 4, 6], [1, 3, 5]] - * + * */ static VALUE @@ -610,13 +610,13 @@ /* * call-seq: * enum.group_by {| obj | block } => a_hash - * + * * Returns a hash, which keys are evaluated result from the * block, and values are arrays of elements in <i>enum</i> * corresponding to the key. - * + * * (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]} - * + * */ static VALUE @@ -659,11 +659,11 @@ * call-seq: * enum.first -> obj or nil * enum.first(n) -> an_array - * + * * Returns the first element, or the first +n+ elements, of the enumerable. * If the enumerable is empty, the first form returns <code>nil</code>, and the * second form returns an empty array. - * + * */ static VALUE @@ -673,7 +673,7 @@ VALUE obj; { VALUE n, ary[2]; - + if (argc == 0) { ary[0] = ary[1] = Qnil; } @@ -692,7 +692,7 @@ * call-seq: * enum.sort => array * enum.sort {| a, b | block } => array - * + * * Returns an array containing the items in <i>enum</i> sorted, * either according to their own <code><=></code> method, or by using * the results of the supplied block. The block should return -1, 0, or @@ -700,7 +700,7 @@ * Ruby 1.8, the method <code>Enumerable#sort_by</code> implements a * built-in Schwartzian Transform, useful when key computation or * comparison is expensive.. - * + * * %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"] * (1..10).sort {|a,b| b <=> a} #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] */ @@ -746,53 +746,53 @@ /* * call-seq: * enum.sort_by {| obj | block } => array - * + * * Sorts <i>enum</i> using a set of keys generated by mapping the * values in <i>enum</i> through the given block. - * + * * %w{ apple pear fig }.sort_by {|word| word.length} #=> ["fig", "pear", "apple"] - * + * * The current implementation of <code>sort_by</code> generates an * array of tuples containing the original collection element and the * mapped value. This makes <code>sort_by</code> fairly expensive when * the keysets are simple - * + * * require 'benchmark' * include Benchmark - * + * * a = (1..100000).map {rand(100000)} - * + * * bm(10) do |b| * b.report("Sort") { a.sort } * b.report("Sort by") { a.sort_by {|a| a} } * end - * + * * <em>produces:</em> - * + * * user system total real * Sort 0.180000 0.000000 0.180000 ( 0.175469) * Sort by 1.980000 0.040000 2.020000 ( 2.013586) - * + * * However, consider the case where comparing the keys is a non-trivial * operation. The following code sorts some files on modification time * using the basic <code>sort</code> method. - * + * * files = Dir["*"] * sorted = files.sort {|a,b| File.new(a).mtime <=> File.new(b).mtime} * sorted #=> ["mon", "tues", "wed", "thurs"] - * + * * This sort is inefficient: it generates two new <code>File</code> * objects during every comparison. A slightly better technique is to * use the <code>Kernel#test</code> method to generate the modification * times directly. - * + * * files = Dir["*"] * sorted = files.sort { |a,b| * test(?M, a) <=> test(?M, b) * } * sorted #=> ["mon", "tues", "wed", "thurs"] - * + * * This still generates many unnecessary <code>Time</code> objects. A * more efficient technique is to cache the sort keys (modification * times in this case) before the sort. Perl users often call this @@ -800,14 +800,14 @@ * construct a temporary array, where each element is an array * containing our sort key along with the filename. We sort this array, * and then extract the filename from the result. - * + * * sorted = Dir["*"].collect { |f| * [test(?M, f), f] * }.sort.collect { |f| f[1] } * sorted #=> ["mon", "tues", "wed", "thurs"] - * + * * This is exactly what <code>sort_by</code> does internally. - * + * * sorted = Dir["*"].sort_by {|f| test(?M, f)} * sorted #=> ["mon", "tues", "wed", "thurs"] */ @@ -844,11 +844,11 @@ } static VALUE -all_iter_i(i, memo) +all_i(i, memo) VALUE i; VALUE *memo; { - if (!RTEST(rb_yield(i))) { + if (!RTEST(i)) { *memo = Qfalse; rb_iter_break(); } @@ -856,32 +856,28 @@ } static VALUE -all_i(i, memo) +all_iter_i(i, memo) VALUE i; VALUE *memo; { - if (!RTEST(i)) { - *memo = Qfalse; - rb_iter_break(); - } - return Qnil; + return all_i(rb_yield(i), memo); } /* * call-seq: * enum.all? [{|obj| block } ] => true or false - * + * * Passes each element of the collection to the given block. The method * returns <code>true</code> if the block never returns * <code>false</code> or <code>nil</code>. If the block is not given, * Ruby adds an implicit block of <code>{|obj| obj}</code> (that is * <code>all?</code> will return <code>true</code> only if none of the * collection members are <code>false</code> or <code>nil</code>.) - * + * * %w{ ant bear cat}.all? {|word| word.length >= 3} #=> true * %w{ ant bear cat}.all? {|word| word.length >= 4} #=> false * [ nil, true, 99 ].all? #=> false - * + * */ static VALUE @@ -895,11 +891,11 @@ } static VALUE -any_iter_i(i, memo) +any_i(i, memo) VALUE i; VALUE *memo; { - if (RTEST(rb_yield(i))) { + if (RTEST(i)) { *memo = Qtrue; rb_iter_break(); } @@ -907,21 +903,17 @@ } static VALUE -any_i(i, memo) +any_iter_i(i, memo) VALUE i; VALUE *memo; { - if (RTEST(i)) { - *memo = Qtrue; - rb_iter_break(); - } - return Qnil; + return any_i(rb_yield(i), memo); } /* * call-seq: * enum.any? [{|obj| block } ] => true or false - * + * * Passes each element of the collection to the given block. The method * returns <code>true</code> if the block ever returns a value other * than <code>false</code> or <code>nil</code>. If the block is not @@ -929,11 +921,11 @@ * is <code>any?</code> will return <code>true</code> if at least one * of the collection members is not <code>false</code> or * <code>nil</code>. - * + * * %w{ ant bear cat}.any? {|word| word.length >= 3} #=> true * %w{ ant bear cat}.any? {|word| word.length >= 4} #=> true * [ nil, true, 99 ].any? #=> true - * + * */ static VALUE @@ -974,19 +966,19 @@ /* * call-seq: * enum.one? [{|obj| block }] => true or false - * + * * Passes each element of the collection to the given block. The method * returns <code>true</code> if the block returns <code>true</code> * exactly once. If the block is not given, <code>one?</code> will return * <code>true</code> only if exactly one of the collection members is * true. - * + * * %w{ant bear cat}.one? {|word| word.length == 4} #=> true * %w{ant bear cat}.one? {|word| word.length > 4} #=> false * %w{ant bear cat}.one? {|word| word.length < 4} #=> false * [ nil, true, 99 ].one? #=> false * [ nil, true, false ].one? #=> true - * + * */ static VALUE @@ -1023,12 +1015,12 @@ /* * call-seq: * enum.none? [{|obj| block }] => true or false - * + * * Passes each element of the collection to the given block. The method * returns <code>true</code> if the block never returns <code>true</code> * for all elements. If the block is not given, <code>none?</code> will return * <code>true</code> only if none of the collection members is true. - * + * * %w{ant bear cat}.none? {|word| word.length == 5} #=> true * %w{ant bear cat}.none? {|word| word.length >= 4} #=> false * [].none? #=> true @@ -1088,11 +1080,11 @@ * call-seq: * enum.min => obj * enum.min {| a,b | block } => obj - * + * * Returns the object in <i>enum</i> with the minimum value. The * first form assumes all objects implement <code>Comparable</code>; * the second uses the block to return <em>a <=> b</em>. - * + * * a = %w(albatross dog horse) * a.min #=> "albatross" * a.min {|a,b| a.length <=> b.length } #=> "dog" @@ -1113,11 +1105,11 @@ * call-seq: * enum.max => obj * enum.max {| a,b | block } => obj - * + * * Returns the object in <i>enum</i> with the maximum value. The * first form assumes all objects implement <code>Comparable</code>; * the second uses the block to return <em>a <=> b</em>. - * + * * a = %w(albatross dog horse) * a.max #=> "horse" * a.max {|a,b| a.length <=> b.length } #=> "albatross" @@ -1165,15 +1157,15 @@ * call-seq: * enum.max => obj * enum.max {|a,b| block } => obj - * + * * Returns the object in _enum_ with the maximum value. The * first form assumes all objects implement <code>Comparable</code>; * the second uses the block to return <em>a <=> b</em>. - * + * * a = %w(albatross dog horse) * a.max #=> "horse" * a.max {|a,b| a.length <=> b.length } #=> "albatross" - */ + */ static VALUE enum_max(obj) @@ -1244,16 +1236,16 @@ * call-seq: * enum.minmax => [min,max] * enum.minmax {|a,b| block } => [min,max] - * + * * Returns two elements array which contains the minimum and the * maximum value in the enumerable. The first form assumes all * objects implement <code>Comparable</code>; the second uses the * block to return <em>a <=> b</em>. - * + * * a = %w(albatross dog horse) * a.minmax #=> ["albatross", "horse"] * a.minmax {|a,b| a.length <=> b.length } #=> ["dog", "albatross"] - */ + */ static VALUE enum_minmax(obj) @@ -1299,10 +1291,10 @@ /* * call-seq: * enum.min_by {| obj| block } => obj - * + * * Returns the object in <i>enum</i> that gives the minimum * value from the given block. - * + * * a = %w(albatross dog horse) * a.min_by {|x| x.length } #=> "dog" */ @@ -1343,10 +1335,10 @@ /* * call-seq: * enum.max_by {| obj| block } => obj - * + * * Returns the object in <i>enum</i> that gives the maximum * value from the given block. - * + * * a = %w(albatross dog horse) * a.max_by {|x| x.length } #=> "albatross" */ @@ -1364,7 +1356,7 @@ rb_block_call(obj, id_each, 0, 0, max_by_i, (VALUE)memo); return memo[1]; } - + static VALUE minmax_by_i(i, memo) VALUE i; @@ -1395,11 +1387,11 @@ /* * call-seq: * enum.minmax_by {| obj| block } => [min, max] - * + * * Returns two elements array array containing the objects in * <i>enum</i> that gives the minimum and maximum values respectively * from the given block. - * + * * a = %w(albatross dog horse) * a.minmax_by {|x| x.length } #=> ["dog", "albatross"] */ @@ -1436,13 +1428,13 @@ * call-seq: * enum.include?(obj) => true or false * enum.member?(obj) => true or false - * + * * Returns <code>true</code> if any member of <i>enum</i> equals * <i>obj</i>. Equality is tested using <code>==</code>. - * + * * IO.constants.include? "SEEK_SET" #=> true * IO.constants.include? "SEEK_NO_FURTHER" #=> false - * + * */ static VALUE @@ -1470,16 +1462,16 @@ /* * call-seq: * enum.each_with_index {|obj, i| block } -> enum - * + * * Calls <em>block</em> with two arguments, the item and its index, for * each item in <i>enum</i>. - * + * * hash = Hash.new * %w(cat dog wombat).each_with_index {|item, index| * hash[item] = index * } * hash #=> {"cat"=>0, "wombat"=>2, "dog"=>1} - * + * */ static VALUE @@ -1524,7 +1516,7 @@ * call-seq: * enum.zip(arg, ...) => array * enum.zip(arg, ...) {|arr| block } => nil - * + * * Converts any arguments to arrays, then merges elements of * <i>enum</i> with corresponding elements from each argument. This * generates a sequence of <code>enum#size</code> <em>n</em>-element @@ -1533,14 +1525,14 @@ * <code>nil</code> values are supplied. If a block given, it is * invoked for each output array, otherwise an array of arrays is * returned. - * + * * a = [ 4, 5, 6 ] * b = [ 7, 8, 9 ] - * + * * (1..3).zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] * "cat\ndog".zip([1]) #=> [["cat\n", 1], ["dog", nil]] * (1..3).zip #=> [[1], [2], [3]] - * + * */ static VALUE @@ -1578,12 +1570,12 @@ /* * call-seq: * enum.take(n) => array - * + * * Returns first n elements from <i>enum</i>. - * + * * a = [1, 2, 3, 4, 5, 0] * a.take(3) # => [1, 2, 3] - * + * */ static VALUE @@ -1618,13 +1610,13 @@ /* * call-seq: * enum.take_while {|arr| block } => array - * + * * Passes elements to the block until the block returns nil or false, * then stops iterating and returns an array of all prior elements. - * + * * a = [1, 2, 3, 4, 5, 0] * a.take_while {|i| i < 3 } # => [1, 2] - * + * */ static VALUE @@ -1656,13 +1648,13 @@ /* * call-seq: * enum.drop(n) => array - * + * * Drops first n elements from <i>enum</i>, and returns rest elements * in an array. - * + * * a = [1, 2, 3, 4, 5, 0] * a.drop(3) # => [4, 5, 0] - * + * */ static VALUE @@ -1701,14 +1693,14 @@ /* * call-seq: * enum.drop_while {|arr| block } => array - * + * * Drops elements up to, but not including, the first element for * which the block returns nil or false and returns an array * containing the remaining elements. - * + * * a = [1, 2, 3, 4, 5, 0] * a.drop_while {|i| i < 3 } # => [3, 4, 5, 0] - * + * */ static VALUE @@ -1738,7 +1730,7 @@ * call-seq: * enum.cycle {|obj| block } * enum.cycle(n) {|obj| block } - * + * * Calls <i>block</i> for each element of <i>enum</i> repeatedly _n_ * times or forever if none or nil is given. If a non-positive * number is given or the collection is empty, does nothing. Returns @@ -1746,11 +1738,11 @@ * * Enumerable#cycle saves elements in an internal array so changes * to <i>enum</i> after the first pass have no effect. - * + * * a = ["a", "b", "c"] * a.cycle {|x| puts x } # print, a, b, c, a, b, c,.. forever. * a.cycle(2) {|x| puts x } # print, a, b, c, a, b, c. - * + * */ static VALUE @@ -1829,8 +1821,8 @@ rb_define_method(rb_mEnumerable,"min", enum_min, 0); rb_define_method(rb_mEnumerable,"max", enum_max, 0); rb_define_method(rb_mEnumerable,"minmax", enum_minmax, 0); - rb_define_method(rb_mEnumerable,"min_by", enum_min_by, 0); - rb_define_method(rb_mEnumerable,"max_by", enum_max_by, 0); + rb_define_method(rb_mEnumerable,"min_by", enum_min_by, 0); + rb_define_method(rb_mEnumerable,"max_by", enum_max_by, 0); rb_define_method(rb_mEnumerable,"minmax_by", enum_minmax_by, 0); rb_define_method(rb_mEnumerable,"member?", enum_member, 1); rb_define_method(rb_mEnumerable,"include?", enum_member, 1); Index: ruby_1_8_7/string.c =================================================================== --- ruby_1_8_7/string.c (revision 16457) +++ ruby_1_8_7/string.c (revision 16458) @@ -761,7 +761,7 @@ } if (FL_TEST(str, STR_ASSOC)) { 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); RSTRING(str)->len += len; RSTRING(str)->ptr[RSTRING(str)->len] = '\0'; /* sentinel */ @@ -3698,9 +3698,8 @@ * * Splits <i>str</i> using the supplied parameter as the record separator * (<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 - * <code>\n</code> characters, except that multiple successive newlines are - * appended together. + * block. If a zero-length record separator is supplied, the string is split + * into paragraphs delimited by multiple successive newlines. * * print "Example one\n" * "hello\nworld".each {|s| p s} @@ -4922,6 +4921,7 @@ 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, "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, "=~", rb_str_match, 1); rb_define_method(rb_cString, "match", rb_str_match_m, 1); Index: ruby_1_8_7/defines.h =================================================================== --- ruby_1_8_7/defines.h (revision 16457) +++ ruby_1_8_7/defines.h (revision 16458) @@ -254,6 +254,14 @@ #define ENV_IGNORECASE #endif +#ifndef CASEFOLD_FILESYSTEM +# if defined DOSISH || defined __VMS +# define CASEFOLD_FILESYSTEM 1 +# else +# define CASEFOLD_FILESYSTEM 0 +# endif +#endif + #ifndef DLEXT_MAXLEN #define DLEXT_MAXLEN 4 #endif Index: ruby_1_8_7/lib/webrick/httpservlet/filehandler.rb =================================================================== --- ruby_1_8_7/lib/webrick/httpservlet/filehandler.rb (revision 16457) +++ ruby_1_8_7/lib/webrick/httpservlet/filehandler.rb (revision 16458) @@ -199,26 +199,38 @@ 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) - # Preventing directory traversal on DOSISH platforms; + # Preventing directory traversal on Windows platforms; # Backslashes (0x5c) in path_info are not interpreted as special # character in URI notation. So the value of path_info should be # normalize before accessing to the filesystem. - if File::ALT_SEPARATOR + + if trailing_pathsep?(req.path_info) # File.expand_path removes the trailing path separator. # Adding a character is a workaround to save it. # File.expand_path("/aaa/") #=> "/aaa" # File.expand_path("/aaa/" + "x") #=> "/aaa/x" expanded = File.expand_path(req.path_info + "x") - expanded[-1, 1] = "" # remove trailing "x" - req.path_info = expanded + expanded.chop! # remove trailing "x" + else + expanded = File.expand_path(req.path_info) end + req.path_info = expanded end def exec_handler(req, res) raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root if set_filename(req, res) - handler = get_handler(req) + handler = get_handler(req, res) call_callback(:HandlerCallback, req, res) h = handler.get_instance(@config, res.filename) h.service(req, res) @@ -228,9 +240,13 @@ return false end - def get_handler(req) - suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase - suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase + def get_handler(req, res) + suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase + if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename + if @options[:AcceptableLanguages].include?($2.downcase) + suffix2 = $1.downcase + end + end handler_table = @options[:HandlerTable] return handler_table[suffix1] || handler_table[suffix2] || HandlerTable[suffix1] || HandlerTable[suffix2] || @@ -243,15 +259,13 @@ path_info.unshift("") # dummy for checking @root dir while base = path_info.first - check_filename(req, res, 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) call_callback(:DirectoryCallback, req, res) end if base = path_info.first - check_filename(req, res, base) if base == "/" if file = search_index_file(req, res) shift_path_info(req, res, path_info, file) @@ -272,12 +286,10 @@ end def check_filename(req, res, name) - @options[:NondisclosureName].each{|pattern| - if File.fnmatch("/#{pattern}", name, File::FNM_CASEFOLD) - @logger.warn("the request refers nondisclosure name `#{name}'.") - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - } + if nondisclosure_name?(name) || windows_ambiguous_name?(name) + @logger.warn("the request refers nondisclosure name `#{name}'.") + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end end def shift_path_info(req, res, path_info, base=nil) @@ -285,7 +297,8 @@ base = base || tmp req.path_info = path_info.join req.script_name << base - res.filename << base + res.filename = File.expand_path(res.filename + base) + check_filename(req, res, File.basename(res.filename)) end def search_index_file(req, res) @@ -325,6 +338,12 @@ 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) @options[:NondisclosureName].each{|pattern| if File.fnmatch(pattern, name, File::FNM_CASEFOLD) @@ -343,7 +362,8 @@ list = Dir::entries(local_path).collect{|name| next if 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? [ name, nil, -1 ] elsif st.directory? @@ -383,7 +403,7 @@ res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n" res.body << "<HR>\n" - list.unshift [ "..", File::mtime(local_path+".."), -1 ] + list.unshift [ "..", File::mtime(local_path+"/.."), -1 ] list.each{ |name, time, size| if name == ".." dname = "Parent Directory" Index: ruby_1_8_7/lib/webrick/httpservlet/abstract.rb =================================================================== --- ruby_1_8_7/lib/webrick/httpservlet/abstract.rb (revision 16457) +++ ruby_1_8_7/lib/webrick/httpservlet/abstract.rb (revision 16458) @@ -58,7 +58,7 @@ def redirect_to_directory_uri(req, res) if req.path[-1] != ?/ - location = req.path + "/" + location = WEBrick::HTTPUtils.escape_path(req.path + "/") if req.query_string && req.query_string.size > 0 location << "?" << req.query_string end Index: ruby_1_8_7/lib/webrick/httpservlet/cgi_runner.rb =================================================================== --- ruby_1_8_7/lib/webrick/httpservlet/cgi_runner.rb (revision 16457) +++ ruby_1_8_7/lib/webrick/httpservlet/cgi_runner.rb (revision 16458) @@ -39,7 +39,9 @@ Dir::chdir dir if interpreter = ARGV[0] - exec(interpreter, ENV["SCRIPT_FILENAME"]) + argv = ARGV.dup + argv << ENV["SCRIPT_FILENAME"] + exec(*argv) # NOTREACHED end exec ENV["SCRIPT_FILENAME"] Index: ruby_1_8_7/lib/delegate.rb =================================================================== --- ruby_1_8_7/lib/delegate.rb (revision 16457) +++ ruby_1_8_7/lib/delegate.rb (revision 16458) @@ -227,13 +227,15 @@ # Clone support for the object returned by \_\_getobj\_\_. def clone - super - __setobj__(__getobj__.clone) + new = super + new.__setobj__(__getobj__.clone) + new end # Duplication support for the object returned by \_\_getobj\_\_. - def dup(obj) - super - __setobj__(__getobj__.dup) + def dup + new = super + new.__setobj__(__getobj__.clone) + new end end @@ -280,12 +282,14 @@ @_dc_obj = obj end def clone # :nodoc: - super - __setobj__(__getobj__.clone) + new = super + new.__setobj__(__getobj__.clone) + new end def dup # :nodoc: - super - __setobj__(__getobj__.dup) + new = super + new.__setobj__(__getobj__.clone) + new end } for method in methods Index: ruby_1_8_7/lib/net/telnet.rb =================================================================== --- ruby_1_8_7/lib/net/telnet.rb (revision 16457) +++ ruby_1_8_7/lib/net/telnet.rb (revision 16458) @@ -520,10 +520,15 @@ # value specified when this instance was created will be # used, or, failing that, the default value of 0 seconds, # 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 time_out = @options["Timeout"] waittime = @options["Waittime"] + fail_eof = @options["FailEOF"] if options.kind_of?(Hash) prompt = if options.has_key?("Match") @@ -535,6 +540,7 @@ end time_out = options["Timeout"] if options.has_key?("Timeout") waittime = options["Waittime"] if options.has_key?("Waittime") + fail_eof = options["FailEOF"] if options.has_key?("FailEOF") else prompt = options end @@ -559,7 +565,8 @@ Integer(c.rindex(/#{IAC}#{SB}/no)) buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) 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]) rest = c[pt .. -1] else @@ -571,14 +578,21 @@ # # We cannot use preprocess() on this data, because that # method makes some Telnetmode-specific assumptions. - buf = c - buf.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"] + buf = rest + c 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 @log.print(buf) if @options.has_key?("Output_log") line += buf yield buf if block_given? rescue EOFError # End of file reached + raise if fail_eof if line == '' line = nil yield nil if block_given? Index: ruby_1_8_7/range.c =================================================================== --- ruby_1_8_7/range.c (revision 16457) +++ ruby_1_8_7/range.c (revision 16458) @@ -307,23 +307,28 @@ VALUE *argv; VALUE range; { - VALUE b, e, step; + VALUE b, e, step, tmp; long unit; RETURN_ENUMERATOR(range, argc, argv); b = rb_ivar_get(range, id_beg); e = rb_ivar_get(range, id_end); - if (rb_scan_args(argc, argv, "01", &step) == 0) { + if (argc == 0) { step = INT2FIX(1); unit = 1; } - else if (FIXNUM_P(step)) { - unit = NUM2LONG(step); - } else { - VALUE tmp = rb_to_int(step); - unit = rb_cmpint(tmp, step, INT2FIX(0)); + rb_scan_args(argc, argv, "01", &step); + tmp = rb_check_to_integer(step, "to_int"); + if (!NIL_P(tmp)) { + step = tmp; + unit = NUM2LONG(step); + } + else { + tmp = rb_funcall(rb_funcall(b, '+', 1, step), '-', 1, b); + unit = rb_cmpint(tmp, step, INT2FIX(0)); + } } if (unit < 0) { rb_raise(rb_eArgError, "step can't be negative"); Index: ruby_1_8_7/test/ruby/test_method.rb =================================================================== --- ruby_1_8_7/test/ruby/test_method.rb (revision 16457) +++ ruby_1_8_7/test/ruby/test_method.rb (revision 16458) @@ -47,5 +47,7 @@ assert_equal(o, m.receiver) assert_equal("foo", m.name) 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 Index: ruby_1_8_7/test/ruby/test_array.rb =================================================================== --- ruby_1_8_7/test/ruby/test_array.rb (revision 16457) +++ ruby_1_8_7/test/ruby/test_array.rb (revision 16458) @@ -528,6 +528,14 @@ assert_equal([1, 2, 3, 1, 2, 3], a) 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 a = @cls[*('cab'..'cat').to_a] assert_equal('cap', a.delete('cap')) Index: ruby_1_8_7/test/webrick/utils.rb =================================================================== --- ruby_1_8_7/test/webrick/utils.rb (revision 16457) +++ ruby_1_8_7/test/webrick/utils.rb (revision 16458) @@ -1,3 +1,10 @@ +begin + loadpath = $:.dup + $:.replace($: | [File.expand_path("../ruby", File.dirname(__FILE__))]) + require 'envutil' +ensure + $:.replace(loadpath) +end require "webrick" begin require "webrick/https" @@ -12,6 +19,11 @@ return self 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 def start_server(klass, config={}, &block) Index: ruby_1_8_7/test/webrick/test_cgi.rb =================================================================== --- ruby_1_8_7/test/webrick/test_cgi.rb (revision 16457) +++ ruby_1_8_7/test/webrick/test_cgi.rb (revision 16458) @@ -1,20 +1,13 @@ require "webrick" require File.join(File.dirname(__FILE__), "utils.rb") 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 def test_cgi accepted = started = stopped = 0 requested0 = requested1 = 0 config = { - :CGIInterpreter => EnvUtil.rubybin, + :CGIInterpreter => TestWEBrick::RubyBin, :DocumentRoot => File.dirname(__FILE__), :DirectoryIndex => ["webrick.cgi"], } Index: ruby_1_8_7/test/webrick/webrick_long_filename.cgi =================================================================== --- ruby_1_8_7/test/webrick/webrick_long_filename.cgi (revision 0) +++ ruby_1_8_7/test/webrick/webrick_long_filename.cgi (revision 16458) @@ -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 Index: ruby_1_8_7/test/webrick/.htaccess =================================================================== --- ruby_1_8_7/test/webrick/.htaccess (revision 0) +++ ruby_1_8_7/test/webrick/.htaccess (revision 16458) @@ -0,0 +1 @@ +this file should not be published. Index: ruby_1_8_7/test/webrick/test_filehandler.rb =================================================================== --- ruby_1_8_7/test/webrick/test_filehandler.rb (revision 16457) +++ ruby_1_8_7/test/webrick/test_filehandler.rb (revision 16458) @@ -9,6 +9,10 @@ klass.new(WEBrick::Config::HTTP, filename) end + def windows? + File.directory?("\\") + end + def get_res_body(res) return res.body.read rescue res.body end @@ -115,10 +119,82 @@ http = Net::HTTP.new(addr, port) req = Net::HTTP::Get.new("/../../") http.request(req){|res| assert_equal("400", res.code) } - req = Net::HTTP::Get.new( - "/..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5cboot.ini" - ) + req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}") + 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) } 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 Index: ruby_1_8_7/common.mk =================================================================== --- ruby_1_8_7/common.mk (revision 16457) +++ ruby_1_8_7/common.mk (revision 16458) @@ -2,7 +2,8 @@ lib: $(LIBRUBY) dll: $(LIBRUBY_SO) -RUBYOPT = +RUBYLIB = - +RUBYOPT = - STATIC_RUBY = static-ruby Index: ruby_1_8_7/file.c =================================================================== --- ruby_1_8_7/file.c (revision 16457) +++ ruby_1_8_7/file.c (revision 16458) @@ -15,6 +15,10 @@ #ifdef _WIN32 #include "missing/file.h" #endif +#ifdef __CYGWIN__ +#include <windows.h> +#include <sys/cygwin.h> +#endif #include "ruby.h" #include "rubyio.h" @@ -2310,6 +2314,18 @@ #define isdirsep(x) ((x) == '/') #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. */ # if defined(DJGPP) # define CharNext(p) ((p) + mblen(p, MB_CUR_MAX)) @@ -2454,6 +2470,30 @@ 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 {\ long bdiff = p - buf;\ while (cond) {\ @@ -2480,7 +2520,8 @@ file_expand_path(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; int tainted; @@ -2621,15 +2662,21 @@ case '.': if (*(s+1) == '\0' || isdirsep(*(s+1))) { /* We must go back to the parent */ + char *n; *p = '\0'; - if (!(b = strrdirsep(root))) { + if (!(n = strrdirsep(root))) { *p = '/'; } else { - p = b; + p = n; } b = ++s; } +#if USE_NTFS + else { + do *++s; while (istrailinggabage(*s)); + } +#endif break; case '/': #if defined DOSISH || defined __CYGWIN__ @@ -2642,6 +2689,19 @@ break; } } +#if USE_NTFS + else { + --s; + case ' ': { + const char *e = s; + while (istrailinggabage(*s)) s++; + if (!*s) { + s = e; + goto endpath; + } + } + } +#endif break; case '/': #if defined DOSISH || defined __CYGWIN__ @@ -2664,15 +2724,79 @@ } 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); memcpy(++p, b, s-b); p += s-b; } 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); - RSTRING(result)->len = p - buf; - *p = '\0'; + rb_str_set_len(result, buflen); return result; } @@ -2716,23 +2840,31 @@ } static int -rmext(p, e) +rmext(p, l1, e) const char *p, *e; + int l1; { - int l1, l2; + int l2; if (!e) return 0; - l1 = chompdirsep(p) - p; l2 = strlen(e); if (l2 == 2 && e[1] == '*') { - e = strrchr(p, *e); - if (!e) return 0; + unsigned char c = *e; + e = p + l1; + do { + if (e <= p) return 0; + } while (*--e != c); return e - p; } 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 0; @@ -2762,7 +2894,7 @@ #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC char *root; #endif - int f; + int f, n; if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) { StringValue(fext); @@ -2796,18 +2928,22 @@ #endif #endif } - else if (!(p = strrdirsep(name))) { - if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) { - f = chompdirsep(name) - name; - if (f == RSTRING(fname)->len) return fname; - } - p = name; - } else { - while (isdirsep(*p)) p++; /* skip last / */ - if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) { - f = chompdirsep(p) - p; + if (!(p = strrdirsep(name))) { + p = name; } + else { + while (isdirsep(*p)) p++; /* skip last / */ + } +#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); OBJ_INFECT(basename, fname); @@ -2883,22 +3019,49 @@ rb_file_s_extname(klass, fname) VALUE klass, fname; { - char *name, *p, *e; + const char *name, *p, *e; VALUE extname; name = StringValueCStr(fname); p = strrdirsep(name); /* get the last path component */ if (!p) - p = name; + p = name; else - p++; - - e = strrchr(p, '.'); /* get the last dot of the last component */ - if (!e || e == p || !e[1]) /* no dot, or the only dot is first or end? */ - return rb_str_new2(""); - extname = rb_str_new(e, chompdirsep(e) - e); /* keep the dot, too! */ - OBJ_INFECT(extname, fname); - return extname; + name = ++p; + + e = 0; + while (*p) { + if (*p == '.' || istrailinggabage(*p)) { +#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); + return extname; } /* Index: ruby_1_8_7/win32/win32.c =================================================================== --- ruby_1_8_7/win32/win32.c (revision 16457) +++ ruby_1_8_7/win32/win32.c (revision 16458) @@ -371,6 +371,7 @@ } #endif +static CRITICAL_SECTION select_mutex; static BOOL fWinsock; static char *envarea; static void @@ -384,6 +385,7 @@ FreeEnvironmentStrings(envarea); envarea = NULL; } + DeleteCriticalSection(&select_mutex); } static void @@ -472,6 +474,8 @@ init_stdhandle(); + InitializeCriticalSection(&select_mutex); + atexit(exit_handler); // Initialize Winsock @@ -2057,87 +2061,250 @@ static int NtSocketsInitialized = 0; 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; - if (!set) - return 0; - for (idx = 0; idx < set->fd_count; idx++) { - SOCKET fd = set->fd_array[idx]; + while (s < src->fd_count) { + SOCKET fd = src->fd_array[s]; - if (!is_socket(fd)) { - int i; + if (!func || (*func)(fd)) { /* move it to dst */ + int d; - for (i = 0; i < fileset->fd_count; i++) { - if (fileset->fd_array[i] == fd) { - break; - } + for (d = 0; d < dst->fd_count; d++) { + if (dst->fd_array[d] == fd) break; } - if (i == fileset->fd_count) { - if (fileset->fd_count < FD_SETSIZE) { - fileset->fd_array[i] = fd; - fileset->fd_count++; - } + 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 fileset->fd_count; + + 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; + } + rest->tv_sec -= 1; + rest->tv_usec += 1000 * 1000; + } + rest->tv_sec -= wait->tv_sec; + rest->tv_usec -= wait->tv_usec; + return 1; +} + +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 rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *timeout) { long r; - fd_set file_rd; - fd_set file_wr; -#ifdef USE_INTERRUPT_WINSOCK - fd_set trap; -#endif /* USE_INTERRUPT_WINSOCK */ - int file_nfds; + fd_set pipe_rd; + fd_set cons_rd; + fd_set else_rd; + fd_set else_wr; + int nonsock = 0; + if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) { + errno = EINVAL; + return -1; + } if (!NtSocketsInitialized) { 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; if (rd && rd->fd_count > r) r = rd->fd_count; if (wr && wr->fd_count > r) r = wr->fd_count; if (ex && ex->fd_count > r) r = ex->fd_count; 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 - // fake read/write fd_set and return value - if (rd) *rd = file_rd; - if (wr) *wr = file_wr; - return file_nfds; + struct timeval rest; + struct timeval wait; + struct timeval zero; + if (timeout) rest = *timeout; + 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 (else_rd.fd_count || else_wr.fd_count) { + r = do_select(nfds, rd, wr, ex, &zero); // polling + if (r < 0) break; // XXX: should I ignore error and return signaled handles? + r += extract_fd(rd, &else_rd, NULL); // move all + r += extract_fd(wr, &else_wr, NULL); // move all + break; + } + 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)); } -#if USE_INTERRUPT_WINSOCK - if (ex) - trap = *ex; - else - trap.fd_count = 0; - if (trap.fd_count < FD_SETSIZE) - 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()); - } - }); return r; } @@ -3272,7 +3439,6 @@ return 0; } -#undef Sleep #define yield_once() Sleep(0) #define yield_until(condition) do yield_once(); while (!(condition)) Index: ruby_1_8_7/win32/Makefile.sub =================================================================== --- ruby_1_8_7/win32/Makefile.sub (revision 16457) +++ ruby_1_8_7/win32/Makefile.sub (revision 16458) @@ -639,6 +639,11 @@ end <<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: $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -c -Tc$(<:\=/) {$(srcdir)/win32}.c.obj: Index: ruby_1_8_7/struct.c =================================================================== --- ruby_1_8_7/struct.c (revision 16457) +++ ruby_1_8_7/struct.c (revision 16458) @@ -310,19 +310,14 @@ ID id; 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++) { id = rb_to_id(RARRAY(rest)->ptr[i]); 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); if (rb_block_given_p()) { rb_mod_module_eval(0, 0, st); Index: ruby_1_8_7/eval.c =================================================================== --- ruby_1_8_7/eval.c (revision 16457) +++ ruby_1_8_7/eval.c (revision 16458) @@ -2440,6 +2440,8 @@ case NODE_ATTRSET: case NODE_OP_ASGN1: case NODE_OP_ASGN2: + case NODE_OP_ASGN_OR: + case NODE_OP_ASGN_AND: case NODE_MASGN: case NODE_LASGN: case NODE_DASGN: @@ -9917,6 +9919,8 @@ rb_define_method(rb_cUnboundMethod, "arity", method_arity, 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, "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_cModule, "instance_method", rb_mod_method, 1); } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/