summaryrefslogtreecommitdiff
path: root/test/scanners/c/ruby.in.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/scanners/c/ruby.in.c')
-rw-r--r--test/scanners/c/ruby.in.c96308
1 files changed, 96308 insertions, 0 deletions
diff --git a/test/scanners/c/ruby.in.c b/test/scanners/c/ruby.in.c
new file mode 100644
index 0000000..868ea59
--- /dev/null
+++ b/test/scanners/c/ruby.in.c
@@ -0,0 +1,96308 @@
+/**********************************************************************
+
+ array.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-05 00:55:01 +0100 (Thu, 05 Feb 2009) $
+ created at: Fri Aug 6 09:46:12 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "util.h"
+#include "st.h"
+
+VALUE rb_cArray;
+static ID id_cmp;
+
+#define ARY_DEFAULT_SIZE 16
+#define ARY_MAX_SIZE (LONG_MAX / sizeof(VALUE))
+
+void
+rb_mem_clear(mem, size)
+ register VALUE *mem;
+ register long size;
+{
+ while (size--) {
+ *mem++ = Qnil;
+ }
+}
+
+static inline void
+memfill(mem, size, val)
+ register VALUE *mem;
+ register long size;
+ register VALUE val;
+{
+ while (size--) {
+ *mem++ = val;
+ }
+}
+
+#define ARY_TMPLOCK FL_USER1
+
+static inline void
+rb_ary_modify_check(ary)
+ VALUE ary;
+{
+ if (OBJ_FROZEN(ary)) rb_error_frozen("array");
+ if (FL_TEST(ary, ARY_TMPLOCK))
+ rb_raise(rb_eRuntimeError, "can't modify array during iteration");
+ if (!OBJ_TAINTED(ary) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify array");
+}
+
+static void
+rb_ary_modify(ary)
+ VALUE ary;
+{
+ VALUE *ptr;
+
+ rb_ary_modify_check(ary);
+ if (FL_TEST(ary, ELTS_SHARED)) {
+ ptr = ALLOC_N(VALUE, RARRAY(ary)->len);
+ FL_UNSET(ary, ELTS_SHARED);
+ RARRAY(ary)->aux.capa = RARRAY(ary)->len;
+ MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ RARRAY(ary)->ptr = ptr;
+ }
+}
+
+VALUE
+rb_ary_freeze(ary)
+ VALUE ary;
+{
+ return rb_obj_freeze(ary);
+}
+
+/*
+ * call-seq:
+ * array.frozen? -> true or false
+ *
+ * Return <code>true</code> if this array is frozen (or temporarily frozen
+ * while being sorted).
+ */
+
+static VALUE
+rb_ary_frozen_p(ary)
+ VALUE ary;
+{
+ if (OBJ_FROZEN(ary)) return Qtrue;
+ if (FL_TEST(ary, ARY_TMPLOCK)) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE ary_alloc _((VALUE));
+static VALUE
+ary_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(ary, struct RArray);
+ OBJSETUP(ary, klass, T_ARRAY);
+
+ ary->len = 0;
+ ary->ptr = 0;
+ ary->aux.capa = 0;
+
+ return (VALUE)ary;
+}
+
+static VALUE
+ary_new(klass, len)
+ VALUE klass;
+ long len;
+{
+ VALUE ary = ary_alloc(klass);
+
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
+ }
+ if (len > ARY_MAX_SIZE) {
+ rb_raise(rb_eArgError, "array size too big");
+ }
+ if (len == 0) len++;
+ RARRAY(ary)->ptr = ALLOC_N(VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+
+ return ary;
+}
+
+VALUE
+rb_ary_new2(len)
+ long len;
+{
+ return ary_new(rb_cArray, len);
+}
+
+
+VALUE
+rb_ary_new()
+{
+ return rb_ary_new2(ARY_DEFAULT_SIZE);
+}
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_ary_new3(long n, ...)
+#else
+rb_ary_new3(n, va_alist)
+ long n;
+ va_dcl
+#endif
+{
+ va_list ar;
+ VALUE ary;
+ long i;
+
+ ary = rb_ary_new2(n);
+
+ va_init_list(ar, n);
+ for (i=0; i<n; i++) {
+ RARRAY(ary)->ptr[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+
+ RARRAY(ary)->len = n;
+ return ary;
+}
+
+VALUE
+rb_ary_new4(n, elts)
+ long n;
+ const VALUE *elts;
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(n);
+ if (n > 0 && elts) {
+ MEMCPY(RARRAY(ary)->ptr, elts, VALUE, n);
+ }
+
+ /* This assignment to len will be moved to the above "if" block in Ruby 1.9 */
+ RARRAY(ary)->len = n;
+
+ return ary;
+}
+
+VALUE
+rb_assoc_new(car, cdr)
+ VALUE car, cdr;
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(2);
+ RARRAY(ary)->ptr[0] = car;
+ RARRAY(ary)->ptr[1] = cdr;
+ RARRAY(ary)->len = 2;
+
+ return ary;
+}
+
+static VALUE
+to_ary(ary)
+ VALUE ary;
+{
+ return rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
+}
+
+VALUE
+rb_check_array_type(ary)
+ VALUE ary;
+{
+ return rb_check_convert_type(ary, T_ARRAY, "Array", "to_ary");
+}
+
+static VALUE rb_ary_replace _((VALUE, VALUE));
+
+/*
+ * call-seq:
+ * Array.new(size=0, obj=nil)
+ * Array.new(array)
+ * Array.new(size) {|index| block }
+ *
+ * Returns a new array. In the first form, the new array is
+ * empty. In the second it is created with _size_ copies of _obj_
+ * (that is, _size_ references to the same
+ * _obj_). The third form creates a copy of the array
+ * passed as a parameter (the array is generated by calling
+ * to_ary on the parameter). In the last form, an array
+ * of the given size is created. Each element in this array is
+ * calculated by passing the element's index to the given block and
+ * storing the return value.
+ *
+ * Array.new
+ * Array.new(2)
+ * Array.new(5, "A")
+ *
+ * # only one copy of the object is created
+ * a = Array.new(2, Hash.new)
+ * a[0]['cat'] = 'feline'
+ * a
+ * a[1]['cat'] = 'Felix'
+ * a
+ *
+ * # here multiple copies are created
+ * a = Array.new(2) { Hash.new }
+ * a[0]['cat'] = 'feline'
+ * a
+ *
+ * squares = Array.new(5) {|i| i*i}
+ * squares
+ *
+ * copy = Array.new(squares)
+ */
+
+static VALUE
+rb_ary_initialize(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long len;
+ VALUE size, val;
+
+ rb_ary_modify(ary);
+ if (rb_scan_args(argc, argv, "02", &size, &val) == 0) {
+ RARRAY(ary)->len = 0;
+ if (rb_block_given_p()) {
+ rb_warning("given block not used");
+ }
+ return ary;
+ }
+
+ if (argc == 1 && !FIXNUM_P(size)) {
+ val = rb_check_array_type(size);
+ if (!NIL_P(val)) {
+ rb_ary_replace(ary, val);
+ return ary;
+ }
+ }
+
+ len = NUM2LONG(size);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative array size");
+ }
+ if (len > ARY_MAX_SIZE) {
+ rb_raise(rb_eArgError, "array size too big");
+ }
+ if (len > RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+ }
+ if (rb_block_given_p()) {
+ long i;
+
+ if (argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ for (i=0; i<len; i++) {
+ rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
+ RARRAY(ary)->len = i + 1;
+ }
+ }
+ else {
+ memfill(RARRAY(ary)->ptr, len, val);
+ RARRAY(ary)->len = len;
+ }
+
+ return ary;
+}
+
+
+/*
+* Returns a new array populated with the given objects.
+*
+* Array.[]( 1, 'a', /^A/ )
+* Array[ 1, 'a', /^A/ ]
+* [ 1, 'a', /^A/ ]
+*/
+
+static VALUE
+rb_ary_s_create(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE ary = ary_alloc(klass);
+
+ if (argc > 0) {
+ RARRAY(ary)->ptr = ALLOC_N(VALUE, argc);
+ MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+ }
+ RARRAY(ary)->len = RARRAY(ary)->aux.capa = argc;
+
+ return ary;
+}
+
+void
+rb_ary_store(ary, idx, val)
+ VALUE ary;
+ long idx;
+ VALUE val;
+{
+ if (idx < 0) {
+ idx += RARRAY(ary)->len;
+ if (idx < 0) {
+ rb_raise(rb_eIndexError, "index %ld out of array",
+ idx - RARRAY(ary)->len);
+ }
+ }
+ else if (idx >= ARY_MAX_SIZE) {
+ rb_raise(rb_eIndexError, "index %ld too big", idx);
+ }
+
+ rb_ary_modify(ary);
+ if (idx >= RARRAY(ary)->aux.capa) {
+ long new_capa = RARRAY(ary)->aux.capa / 2;
+
+ if (new_capa < ARY_DEFAULT_SIZE) {
+ new_capa = ARY_DEFAULT_SIZE;
+ }
+ if (new_capa >= ARY_MAX_SIZE - idx) {
+ new_capa = (ARY_MAX_SIZE - idx) / 2;
+ }
+ new_capa += idx;
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, new_capa);
+ RARRAY(ary)->aux.capa = new_capa;
+ }
+ if (idx > RARRAY(ary)->len) {
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len,
+ idx-RARRAY(ary)->len + 1);
+ }
+
+ if (idx >= RARRAY(ary)->len) {
+ RARRAY(ary)->len = idx + 1;
+ }
+ RARRAY(ary)->ptr[idx] = val;
+}
+
+/*
+ * call-seq:
+ * array << obj -> array
+ *
+ * Append---Pushes the given object on to the end of this array. This
+ * expression returns the array itself, so several appends
+ * may be chained together.
+ *
+ * [ 1, 2 ] << "c" << "d" << [ 3, 4 ]
+ * #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
+ *
+ */
+
+VALUE
+rb_ary_push(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ rb_ary_store(ary, RARRAY(ary)->len, item);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.push(obj, ... ) -> array
+ *
+ * Append---Pushes the given object(s) on to the end of this array. This
+ * expression returns the array itself, so several appends
+ * may be chained together.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.push("d", "e", "f")
+ * #=> ["a", "b", "c", "d", "e", "f"]
+ */
+
+static VALUE
+rb_ary_push_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ while (argc--) {
+ rb_ary_push(ary, *argv++);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.pop -> obj or nil
+ *
+ * Removes the last element from <i>self</i> and returns it, or
+ * <code>nil</code> if the array is empty.
+ *
+ * a = [ "a", "m", "z" ]
+ * a.pop #=> "z"
+ * a #=> ["a", "m"]
+ */
+
+VALUE
+rb_ary_pop(ary)
+ VALUE ary;
+{
+ rb_ary_modify_check(ary);
+ if (RARRAY(ary)->len == 0) return Qnil;
+ if (!FL_TEST(ary, ELTS_SHARED) &&
+ RARRAY(ary)->len * 2 < RARRAY(ary)->aux.capa &&
+ RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
+ RARRAY(ary)->aux.capa = RARRAY(ary)->len * 2;
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+ }
+ return RARRAY(ary)->ptr[--RARRAY(ary)->len];
+}
+
+static VALUE
+ary_make_shared(ary)
+ VALUE ary;
+{
+ if (!FL_TEST(ary, ELTS_SHARED)) {
+ NEWOBJ(shared, struct RArray);
+ OBJSETUP(shared, rb_cArray, T_ARRAY);
+
+ shared->len = RARRAY(ary)->len;
+ shared->ptr = RARRAY(ary)->ptr;
+ shared->aux.capa = RARRAY(ary)->aux.capa;
+ RARRAY(ary)->aux.shared = (VALUE)shared;
+ FL_SET(ary, ELTS_SHARED);
+ OBJ_FREEZE(shared);
+ return (VALUE)shared;
+ }
+ else {
+ return RARRAY(ary)->aux.shared;
+ }
+}
+
+/*
+ * call-seq:
+ * array.shift -> obj or nil
+ *
+ * Returns the first element of <i>self</i> and removes it (shifting all
+ * other elements down by one). Returns <code>nil</code> if the array
+ * is empty.
+ *
+ * args = [ "-m", "-q", "filename" ]
+ * args.shift #=> "-m"
+ * args #=> ["-q", "filename"]
+ */
+
+VALUE
+rb_ary_shift(ary)
+ VALUE ary;
+{
+ VALUE top;
+
+ rb_ary_modify_check(ary);
+ if (RARRAY(ary)->len == 0) return Qnil;
+ top = RARRAY(ary)->ptr[0];
+ if (RARRAY_LEN(ary) < ARY_DEFAULT_SIZE && !FL_TEST(ary, ELTS_SHARED)) {
+ MEMMOVE(RARRAY_PTR(ary), RARRAY_PTR(ary)+1, VALUE, RARRAY_LEN(ary)-1);
+ }
+ else {
+ if (!FL_TEST(ary, ELTS_SHARED)) {
+ RARRAY(ary)->ptr[0] = Qnil;
+ }
+ ary_make_shared(ary);
+ RARRAY(ary)->ptr++; /* shift ptr */
+ }
+ RARRAY(ary)->len--;
+
+ return top;
+}
+
+VALUE
+rb_ary_unshift(ary, item)
+ VALUE ary, item;
+{
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len == RARRAY(ary)->aux.capa) {
+ long capa_inc = RARRAY(ary)->aux.capa / 2;
+ if (capa_inc < ARY_DEFAULT_SIZE) {
+ capa_inc = ARY_DEFAULT_SIZE;
+ }
+ RARRAY(ary)->aux.capa += capa_inc;
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+ }
+
+ /* sliding items */
+ MEMMOVE(RARRAY(ary)->ptr + 1, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+
+ RARRAY(ary)->len++;
+ RARRAY(ary)->ptr[0] = item;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.unshift(obj, ...) -> array
+ *
+ * Prepends objects to the front of <i>array</i>.
+ * other elements up one.
+ *
+ * a = [ "b", "c", "d" ]
+ * a.unshift("a") #=> ["a", "b", "c", "d"]
+ * a.unshift(1, 2) #=> [ 1, 2, "a", "b", "c", "d"]
+ */
+
+static VALUE
+rb_ary_unshift_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long len = RARRAY(ary)->len;
+
+ if (argc == 0) return ary;
+
+ /* make rooms by setting the last item */
+ rb_ary_store(ary, len + argc - 1, Qnil);
+
+ /* sliding items */
+ MEMMOVE(RARRAY(ary)->ptr + argc, RARRAY(ary)->ptr, VALUE, len);
+ MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+
+ return ary;
+}
+
+/* faster version - use this if you don't need to treat negative offset */
+static inline VALUE
+rb_ary_elt(ary, offset)
+ VALUE ary;
+ long offset;
+{
+ if (RARRAY(ary)->len == 0) return Qnil;
+ if (offset < 0 || RARRAY(ary)->len <= offset) {
+ return Qnil;
+ }
+ return RARRAY(ary)->ptr[offset];
+}
+
+VALUE
+rb_ary_entry(ary, offset)
+ VALUE ary;
+ long offset;
+{
+ if (offset < 0) {
+ offset += RARRAY(ary)->len;
+ }
+ return rb_ary_elt(ary, offset);
+}
+
+static VALUE
+rb_ary_subseq(ary, beg, len)
+ VALUE ary;
+ long beg, len;
+{
+ VALUE klass, ary2, shared;
+ VALUE *ptr;
+
+ if (beg > RARRAY(ary)->len) return Qnil;
+ if (beg < 0 || len < 0) return Qnil;
+
+ if (RARRAY(ary)->len < len || RARRAY(ary)->len < beg + len) {
+ len = RARRAY(ary)->len - beg;
+ if (len < 0)
+ len = 0;
+ }
+ klass = rb_obj_class(ary);
+ if (len == 0) return ary_new(klass, 0);
+
+ shared = ary_make_shared(ary);
+ ptr = RARRAY(ary)->ptr;
+ ary2 = ary_alloc(klass);
+ RARRAY(ary2)->ptr = ptr + beg;
+ RARRAY(ary2)->len = len;
+ RARRAY(ary2)->aux.shared = shared;
+ FL_SET(ary2, ELTS_SHARED);
+
+ return ary2;
+}
+
+/*
+ * call-seq:
+ * array[index] -> obj or nil
+ * array[start, length] -> an_array or nil
+ * array[range] -> an_array or nil
+ * array.slice(index) -> obj or nil
+ * array.slice(start, length) -> an_array or nil
+ * array.slice(range) -> an_array or nil
+ *
+ * Element Reference---Returns the element at _index_,
+ * or returns a subarray starting at _start_ and
+ * continuing for _length_ elements, or returns a subarray
+ * specified by _range_.
+ * Negative indices count backward from the end of the
+ * array (-1 is the last element). Returns nil if the index
+ * (or starting index) are out of range.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a[2] + a[0] + a[1] #=> "cab"
+ * a[6] #=> nil
+ * a[1, 2] #=> [ "b", "c" ]
+ * a[1..3] #=> [ "b", "c", "d" ]
+ * a[4..7] #=> [ "e" ]
+ * a[6..10] #=> nil
+ * a[-3, 3] #=> [ "c", "d", "e" ]
+ * # special cases
+ * a[5] #=> nil
+ * a[5, 1] #=> []
+ * a[5..10] #=> []
+ *
+ */
+
+VALUE
+rb_ary_aref(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE arg;
+ long beg, len;
+
+ if (argc == 2) {
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
+ beg = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
+ if (beg < 0) {
+ beg += RARRAY(ary)->len;
+ }
+ return rb_ary_subseq(ary, beg, len);
+ }
+ if (argc != 1) {
+ rb_scan_args(argc, argv, "11", 0, 0);
+ }
+ arg = argv[0];
+ /* special case - speeding up */
+ if (FIXNUM_P(arg)) {
+ return rb_ary_entry(ary, FIX2LONG(arg));
+ }
+ if (SYMBOL_P(arg)) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
+ /* check if idx is Range */
+ switch (rb_range_beg_len(arg, &beg, &len, RARRAY(ary)->len, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ return Qnil;
+ default:
+ return rb_ary_subseq(ary, beg, len);
+ }
+ return rb_ary_entry(ary, NUM2LONG(arg));
+}
+
+/*
+ * call-seq:
+ * array.at(index) -> obj or nil
+ *
+ * Returns the element at _index_. A
+ * negative index counts from the end of _self_. Returns +nil+
+ * if the index is out of range. See also <code>Array#[]</code>.
+ * (<code>Array#at</code> is slightly faster than <code>Array#[]</code>,
+ * as it does not accept ranges and so on.)
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.at(0) #=> "a"
+ * a.at(-1) #=> "e"
+ */
+
+static VALUE
+rb_ary_at(ary, pos)
+ VALUE ary, pos;
+{
+ return rb_ary_entry(ary, NUM2LONG(pos));
+}
+
+/*
+ * call-seq:
+ * array.first -> obj or nil
+ * array.first(n) -> an_array
+ *
+ * Returns the first element, or the first +n+ elements, of the array.
+ * If the array is empty, the first form returns <code>nil</code>, and the
+ * second form returns an empty array.
+ *
+ * a = [ "q", "r", "s", "t" ]
+ * a.first #=> "q"
+ * a.first(1) #=> ["q"]
+ * a.first(3) #=> ["q", "r", "s"]
+ */
+
+static VALUE
+rb_ary_first(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[0];
+ }
+ else {
+ VALUE nv, result;
+ long n, i;
+
+ rb_scan_args(argc, argv, "01", &nv);
+ n = NUM2LONG(nv);
+ if (n > RARRAY(ary)->len) n = RARRAY(ary)->len;
+ result = rb_ary_new2(n);
+ for (i=0; i<n; i++) {
+ rb_ary_push(result, RARRAY(ary)->ptr[i]);
+ }
+ return result;
+ }
+}
+
+/*
+ * call-seq:
+ * array.last -> obj or nil
+ * array.last(n) -> an_array
+ *
+ * Returns the last element(s) of <i>self</i>. If the array is empty,
+ * the first form returns <code>nil</code>.
+ *
+ * [ "w", "x", "y", "z" ].last #=> "z"
+ */
+
+static VALUE
+rb_ary_last(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[RARRAY(ary)->len-1];
+ }
+ else {
+ VALUE nv, result;
+ long n, i;
+
+ rb_scan_args(argc, argv, "01", &nv);
+ n = NUM2LONG(nv);
+ if (n > RARRAY(ary)->len) n = RARRAY(ary)->len;
+ result = rb_ary_new2(n);
+ for (i=RARRAY(ary)->len-n; n--; i++) {
+ rb_ary_push(result, RARRAY(ary)->ptr[i]);
+ }
+ return result;
+ }
+}
+
+/*
+ * call-seq:
+ * array.fetch(index) -> obj
+ * array.fetch(index, default ) -> obj
+ * array.fetch(index) {|index| block } -> obj
+ *
+ * Tries to return the element at position <i>index</i>. If the index
+ * lies outside the array, the first form throws an
+ * <code>IndexError</code> exception, the second form returns
+ * <i>default</i>, and the third form returns the value of invoking
+ * the block, passing in the index. Negative values of <i>index</i>
+ * count from the end of the array.
+ *
+ * a = [ 11, 22, 33, 44 ]
+ * a.fetch(1) #=> 22
+ * a.fetch(-1) #=> 44
+ * a.fetch(4, 'cat') #=> "cat"
+ * a.fetch(4) { |i| i*i } #=> 16
+ */
+
+static VALUE
+rb_ary_fetch(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE pos, ifnone;
+ long block_given;
+ long idx;
+
+ rb_scan_args(argc, argv, "11", &pos, &ifnone);
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ idx = NUM2LONG(pos);
+
+ if (idx < 0) {
+ idx += RARRAY(ary)->len;
+ }
+ if (idx < 0 || RARRAY(ary)->len <= idx) {
+ if (block_given) return rb_yield(pos);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "index %ld out of array", idx);
+ }
+ return ifnone;
+ }
+ return RARRAY(ary)->ptr[idx];
+}
+
+/*
+ * call-seq:
+ * array.index(obj) -> int or nil
+ *
+ * Returns the index of the first object in <i>self</i> such that is
+ * <code>==</code> to <i>obj</i>. Returns <code>nil</code> if
+ * no match is found.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.index("b") #=> 1
+ * a.index("z") #=> nil
+ */
+
+static VALUE
+rb_ary_index(ary, val)
+ VALUE ary;
+ VALUE val;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (rb_equal(RARRAY(ary)->ptr[i], val))
+ return LONG2NUM(i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array.rindex(obj) -> int or nil
+ *
+ * Returns the index of the last object in <i>array</i>
+ * <code>==</code> to <i>obj</i>. Returns <code>nil</code> if
+ * no match is found.
+ *
+ * a = [ "a", "b", "b", "b", "c" ]
+ * a.rindex("b") #=> 3
+ * a.rindex("z") #=> nil
+ */
+
+static VALUE
+rb_ary_rindex(ary, val)
+ VALUE ary;
+ VALUE val;
+{
+ long i = RARRAY(ary)->len;
+
+ while (i--) {
+ if (i > RARRAY(ary)->len) {
+ i = RARRAY(ary)->len;
+ continue;
+ }
+ if (rb_equal(RARRAY(ary)->ptr[i], val))
+ return LONG2NUM(i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array.indexes( i1, i2, ... iN ) -> an_array
+ * array.indices( i1, i2, ... iN ) -> an_array
+ *
+ * Deprecated; use <code>Array#values_at</code>.
+ */
+
+static VALUE
+rb_ary_indexes(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE new_ary;
+ long i;
+
+ rb_warn("Array#%s is deprecated; use Array#values_at", rb_id2name(rb_frame_last_func()));
+ new_ary = rb_ary_new2(argc);
+ for (i=0; i<argc; i++) {
+ rb_ary_push(new_ary, rb_ary_aref(1, argv+i, ary));
+ }
+
+ return new_ary;
+}
+
+VALUE
+rb_ary_to_ary(obj)
+ VALUE obj;
+{
+ if (TYPE(obj) == T_ARRAY) {
+ return obj;
+ }
+ if (rb_respond_to(obj, rb_intern("to_ary"))) {
+ return rb_convert_type(obj, T_ARRAY, "Array", "to_ary");
+ }
+ return rb_ary_new3(1, obj);
+}
+
+static void
+rb_ary_splice(ary, beg, len, rpl)
+ VALUE ary;
+ long beg, len;
+ VALUE rpl;
+{
+ long rlen;
+
+ if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
+ if (beg < 0) {
+ beg += RARRAY(ary)->len;
+ if (beg < 0) {
+ beg -= RARRAY(ary)->len;
+ rb_raise(rb_eIndexError, "index %ld out of array", beg);
+ }
+ }
+ if (RARRAY(ary)->len < len || RARRAY(ary)->len < beg + len) {
+ len = RARRAY(ary)->len - beg;
+ }
+
+ if (NIL_P(rpl)) {
+ rlen = 0;
+ }
+ else {
+ rpl = rb_ary_to_ary(rpl);
+ rlen = RARRAY(rpl)->len;
+ }
+ rb_ary_modify(ary);
+
+ if (beg >= RARRAY(ary)->len) {
+ if (beg > ARY_MAX_SIZE - rlen) {
+ rb_raise(rb_eIndexError, "index %ld too big", beg);
+ }
+ len = beg + rlen;
+ if (len >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+ }
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, beg - RARRAY(ary)->len);
+ if (rlen > 0) {
+ MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
+ RARRAY(ary)->len = len;
+ }
+ else {
+ long alen;
+
+ if (beg + len > RARRAY(ary)->len) {
+ len = RARRAY(ary)->len - beg;
+ }
+
+ alen = RARRAY(ary)->len + rlen - len;
+ if (alen >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, alen);
+ RARRAY(ary)->aux.capa = alen;
+ }
+
+ if (len != rlen) {
+ MEMMOVE(RARRAY(ary)->ptr + beg + rlen, RARRAY(ary)->ptr + beg + len,
+ VALUE, RARRAY(ary)->len - (beg + len));
+ RARRAY(ary)->len = alen;
+ }
+ if (rlen > 0) {
+ MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * array[index] = obj -> obj
+ * array[start, length] = obj or an_array or nil -> obj or an_array or nil
+ * array[range] = obj or an_array or nil -> obj or an_array or nil
+ *
+ * Element Assignment---Sets the element at _index_,
+ * or replaces a subarray starting at _start_ and
+ * continuing for _length_ elements, or replaces a subarray
+ * specified by _range_. If indices are greater than
+ * the current capacity of the array, the array grows
+ * automatically. A negative indices will count backward
+ * from the end of the array. Inserts elements if _length_ is
+ * zero. If +nil+ is used in the second and third form,
+ * deletes elements from _self_. An +IndexError+ is raised if a
+ * negative index points past the beginning of the array. See also
+ * <code>Array#push</code>, and <code>Array#unshift</code>.
+ *
+ * a = Array.new
+ * a[4] = "4"; #=> [nil, nil, nil, nil, "4"]
+ * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"]
+ * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"]
+ * a[0, 2] = "?" #=> ["?", 2, nil, "4"]
+ * a[0..2] = "A" #=> ["A", "4"]
+ * a[-1] = "Z" #=> ["A", "Z"]
+ * a[1..-1] = nil #=> ["A"]
+ */
+
+static VALUE
+rb_ary_aset(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long offset, beg, len;
+
+ if (argc == 3) {
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
+ if (SYMBOL_P(argv[1])) {
+ rb_raise(rb_eTypeError, "Symbol as subarray length");
+ }
+ rb_ary_splice(ary, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
+ return argv[2];
+ }
+ if (argc != 2) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+ }
+ if (FIXNUM_P(argv[0])) {
+ offset = FIX2LONG(argv[0]);
+ goto fixnum;
+ }
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
+ if (rb_range_beg_len(argv[0], &beg, &len, RARRAY(ary)->len, 1)) {
+ /* check if idx is Range */
+ rb_ary_splice(ary, beg, len, argv[1]);
+ return argv[1];
+ }
+
+ offset = NUM2LONG(argv[0]);
+fixnum:
+ rb_ary_store(ary, offset, argv[1]);
+ return argv[1];
+}
+
+/*
+ * call-seq:
+ * array.insert(index, obj...) -> array
+ *
+ * Inserts the given values before the element with the given index
+ * (which may be negative).
+ *
+ * a = %w{ a b c d }
+ * a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
+ * a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
+ */
+
+static VALUE
+rb_ary_insert(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long pos;
+
+ if (argc == 1) return ary;
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments (at least 1)");
+ }
+ pos = NUM2LONG(argv[0]);
+ if (pos == -1) {
+ pos = RARRAY(ary)->len;
+ }
+ if (pos < 0) {
+ pos++;
+ }
+ rb_ary_splice(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1));
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.each {|item| block } -> array
+ *
+ * Calls <i>block</i> once for each element in <i>self</i>, passing that
+ * element as a parameter.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.each {|x| print x, " -- " }
+ *
+ * produces:
+ *
+ * a -- b -- c --
+ */
+
+VALUE
+rb_ary_each(ary)
+ VALUE ary;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ rb_yield(RARRAY(ary)->ptr[i]);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.each_index {|index| block } -> array
+ *
+ * Same as <code>Array#each</code>, but passes the index of the element
+ * instead of the element itself.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.each_index {|x| print x, " -- " }
+ *
+ * produces:
+ *
+ * 0 -- 1 -- 2 --
+ */
+
+static VALUE
+rb_ary_each_index(ary)
+ VALUE ary;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ rb_yield(LONG2NUM(i));
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reverse_each {|item| block }
+ *
+ * Same as <code>Array#each</code>, but traverses <i>self</i> in reverse
+ * order.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.reverse_each {|x| print x, " " }
+ *
+ * produces:
+ *
+ * c b a
+ */
+
+static VALUE
+rb_ary_reverse_each(ary)
+ VALUE ary;
+{
+ long len = RARRAY(ary)->len;
+
+ while (len--) {
+ rb_yield(RARRAY(ary)->ptr[len]);
+ if (RARRAY(ary)->len < len) {
+ len = RARRAY(ary)->len;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.length -> int
+ *
+ * Returns the number of elements in <i>self</i>. May be zero.
+ *
+ * [ 1, 2, 3, 4, 5 ].length #=> 5
+ */
+
+static VALUE
+rb_ary_length(ary)
+ VALUE ary;
+{
+ return LONG2NUM(RARRAY(ary)->len);
+}
+
+/*
+ * call-seq:
+ * array.empty? -> true or false
+ *
+ * Returns <code>true</code> if <i>self</i> array contains no elements.
+ *
+ * [].empty? #=> true
+ */
+
+static VALUE
+rb_ary_empty_p(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0)
+ return Qtrue;
+ return Qfalse;
+}
+
+VALUE
+rb_ary_dup(ary)
+ VALUE ary;
+{
+ VALUE dup = rb_ary_new2(RARRAY(ary)->len);
+
+ DUPSETUP(dup, ary);
+ MEMCPY(RARRAY(dup)->ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ RARRAY(dup)->len = RARRAY(ary)->len;
+ return dup;
+}
+
+extern VALUE rb_output_fs;
+
+static VALUE
+inspect_join(ary, arg)
+ VALUE ary;
+ VALUE *arg;
+{
+ return rb_ary_join(arg[0], arg[1]);
+}
+
+VALUE
+rb_ary_join(ary, sep)
+ VALUE ary, sep;
+{
+ long len = 1, i;
+ int taint = Qfalse;
+ VALUE result, tmp;
+
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+ if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = rb_check_string_type(RARRAY(ary)->ptr[i]);
+ len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len;
+ }
+ if (!NIL_P(sep)) {
+ StringValue(sep);
+ len += RSTRING(sep)->len * (RARRAY(ary)->len - 1);
+ }
+ result = rb_str_buf_new(len);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ break;
+ case T_ARRAY:
+ if (tmp == ary || rb_inspecting_p(tmp)) {
+ tmp = rb_str_new2("[...]");
+ }
+ else {
+ VALUE args[2];
+
+ args[0] = tmp;
+ args[1] = sep;
+ tmp = rb_protect_inspect(inspect_join, ary, (VALUE)args);
+ }
+ break;
+ default:
+ tmp = rb_obj_as_string(tmp);
+ }
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, tmp);
+ if (OBJ_TAINTED(tmp)) taint = Qtrue;
+ }
+
+ if (taint) OBJ_TAINT(result);
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.join(sep=$,) -> str
+ *
+ * Returns a string created by converting each element of the array to
+ * a string, separated by <i>sep</i>.
+ *
+ * [ "a", "b", "c" ].join #=> "abc"
+ * [ "a", "b", "c" ].join("-") #=> "a-b-c"
+ */
+
+static VALUE
+rb_ary_join_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE sep;
+
+ rb_scan_args(argc, argv, "01", &sep);
+ if (NIL_P(sep)) sep = rb_output_fs;
+
+ return rb_ary_join(ary, sep);
+}
+
+/*
+ * call-seq:
+ * array.to_s -> string
+ *
+ * Returns _self_<code>.join</code>.
+ *
+ * [ "a", "e", "i", "o" ].to_s #=> "aeio"
+ *
+ */
+
+VALUE
+rb_ary_to_s(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+
+ return rb_ary_join(ary, rb_output_fs);
+}
+
+static ID inspect_key;
+
+struct inspect_arg {
+ VALUE (*func)();
+ VALUE arg1, arg2;
+};
+
+static VALUE
+inspect_call(arg)
+ struct inspect_arg *arg;
+{
+ return (*arg->func)(arg->arg1, arg->arg2);
+}
+
+static VALUE
+get_inspect_tbl(create)
+ int create;
+{
+ VALUE inspect_tbl = rb_thread_local_aref(rb_thread_current(), inspect_key);
+
+ if (NIL_P(inspect_tbl)) {
+ if (create) {
+ tbl_init:
+ inspect_tbl = rb_ary_new();
+ rb_thread_local_aset(rb_thread_current(), inspect_key, inspect_tbl);
+ }
+ }
+ else if (TYPE(inspect_tbl) != T_ARRAY) {
+ rb_warn("invalid inspect_tbl value");
+ if (create) goto tbl_init;
+ rb_thread_local_aset(rb_thread_current(), inspect_key, Qnil);
+ return Qnil;
+ }
+ return inspect_tbl;
+}
+
+static VALUE
+inspect_ensure(obj)
+ VALUE obj;
+{
+ VALUE inspect_tbl;
+
+ inspect_tbl = get_inspect_tbl(Qfalse);
+ if (!NIL_P(inspect_tbl)) {
+ rb_ary_pop(inspect_tbl);
+ }
+ return 0;
+}
+
+VALUE
+rb_protect_inspect(func, obj, arg)
+ VALUE (*func)(ANYARGS);
+ VALUE obj, arg;
+{
+ struct inspect_arg iarg;
+ VALUE inspect_tbl;
+ VALUE id;
+
+ inspect_tbl = get_inspect_tbl(Qtrue);
+ id = rb_obj_id(obj);
+ if (rb_ary_includes(inspect_tbl, id)) {
+ return (*func)(obj, arg);
+ }
+ rb_ary_push(inspect_tbl, id);
+ iarg.func = func;
+ iarg.arg1 = obj;
+ iarg.arg2 = arg;
+
+ return rb_ensure(inspect_call, (VALUE)&iarg, inspect_ensure, obj);
+}
+
+VALUE
+rb_inspecting_p(obj)
+ VALUE obj;
+{
+ VALUE inspect_tbl;
+
+ inspect_tbl = get_inspect_tbl(Qfalse);
+ if (NIL_P(inspect_tbl)) return Qfalse;
+ return rb_ary_includes(inspect_tbl, rb_obj_id(obj));
+}
+
+static VALUE
+inspect_ary(ary)
+ VALUE ary;
+{
+ int tainted = OBJ_TAINTED(ary);
+ long i;
+ VALUE s, str;
+
+ str = rb_str_buf_new2("[");
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ s = rb_inspect(RARRAY(ary)->ptr[i]);
+ if (OBJ_TAINTED(s)) tainted = Qtrue;
+ if (i > 0) rb_str_buf_cat2(str, ", ");
+ rb_str_buf_append(str, s);
+ }
+ rb_str_buf_cat2(str, "]");
+ if (tainted) OBJ_TAINT(str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * array.inspect -> string
+ *
+ * Create a printable version of <i>array</i>.
+ */
+
+static VALUE
+rb_ary_inspect(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0) return rb_str_new2("[]");
+ if (rb_inspecting_p(ary)) return rb_str_new2("[...]");
+ return rb_protect_inspect(inspect_ary, ary, 0);
+}
+
+/*
+ * call-seq:
+ * array.to_a -> array
+ *
+ * Returns _self_. If called on a subclass of Array, converts
+ * the receiver to an Array object.
+ */
+
+static VALUE
+rb_ary_to_a(ary)
+ VALUE ary;
+{
+ if (rb_obj_class(ary) != rb_cArray) {
+ VALUE dup = rb_ary_new2(RARRAY(ary)->len);
+ rb_ary_replace(dup, ary);
+ return dup;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.to_ary -> array
+ *
+ * Returns _self_.
+ */
+
+static VALUE
+rb_ary_to_ary_m(ary)
+ VALUE ary;
+{
+ return ary;
+}
+
+VALUE
+rb_ary_reverse(ary)
+ VALUE ary;
+{
+ VALUE *p1, *p2;
+ VALUE tmp;
+
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > 1) {
+ p1 = RARRAY(ary)->ptr;
+ p2 = p1 + RARRAY(ary)->len - 1; /* points last item */
+
+ while (p1 < p2) {
+ tmp = *p1;
+ *p1++ = *p2;
+ *p2-- = tmp;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reverse! -> array
+ *
+ * Reverses _self_ in place.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.reverse! #=> ["c", "b", "a"]
+ * a #=> ["c", "b", "a"]
+ */
+
+static VALUE
+rb_ary_reverse_bang(ary)
+ VALUE ary;
+{
+ return rb_ary_reverse(ary);
+}
+
+/*
+ * call-seq:
+ * array.reverse -> an_array
+ *
+ * Returns a new array containing <i>self</i>'s elements in reverse order.
+ *
+ * [ "a", "b", "c" ].reverse #=> ["c", "b", "a"]
+ * [ 1 ].reverse #=> [1]
+ */
+
+static VALUE
+rb_ary_reverse_m(ary)
+ VALUE ary;
+{
+ return rb_ary_reverse(rb_ary_dup(ary));
+}
+
+struct ary_sort_data {
+ VALUE ary;
+ VALUE *ptr;
+ long len;
+};
+
+static void
+ary_sort_check(data)
+ struct ary_sort_data *data;
+{
+ if (RARRAY(data->ary)->ptr != data->ptr || RARRAY(data->ary)->len != data->len) {
+ rb_raise(rb_eArgError, "array modified during sort");
+ }
+}
+
+static int
+sort_1(a, b, data)
+ VALUE *a, *b;
+ struct ary_sort_data *data;
+{
+ VALUE retval = rb_yield_values(2, *a, *b);
+ int n;
+
+ n = rb_cmpint(retval, *a, *b);
+ ary_sort_check(data);
+ return n;
+}
+
+static int
+sort_2(ap, bp, data)
+ VALUE *ap, *bp;
+ struct ary_sort_data *data;
+{
+ VALUE retval;
+ VALUE a = *ap, b = *bp;
+ int n;
+
+ if (FIXNUM_P(a) && FIXNUM_P(b)) {
+ if ((long)a > (long)b) return 1;
+ if ((long)a < (long)b) return -1;
+ return 0;
+ }
+ if (TYPE(a) == T_STRING) {
+ if (TYPE(b) == T_STRING) return rb_str_cmp(a, b);
+ }
+
+ retval = rb_funcall(a, id_cmp, 1, b);
+ n = rb_cmpint(retval, a, b);
+ ary_sort_check(data);
+
+ return n;
+}
+
+static VALUE
+sort_internal(ary)
+ VALUE ary;
+{
+ struct ary_sort_data data;
+
+ data.ary = ary;
+ data.ptr = RARRAY(ary)->ptr; data.len = RARRAY(ary)->len;
+ qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE),
+ rb_block_given_p()?sort_1:sort_2, &data);
+ return ary;
+}
+
+static VALUE
+sort_unlock(ary)
+ VALUE ary;
+{
+ FL_UNSET(ary, ARY_TMPLOCK);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.sort! -> array
+ * array.sort! {| a,b | block } -> array
+ *
+ * Sorts _self_. Comparisons for
+ * the sort will be done using the <code><=></code> operator or using
+ * an optional code block. The block implements a comparison between
+ * <i>a</i> and <i>b</i>, returning -1, 0, or +1. See also
+ * <code>Enumerable#sort_by</code>.
+ *
+ * a = [ "d", "a", "e", "c", "b" ]
+ * a.sort #=> ["a", "b", "c", "d", "e"]
+ * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ */
+
+VALUE
+rb_ary_sort_bang(ary)
+ VALUE ary;
+{
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > 1) {
+ FL_SET(ary, ARY_TMPLOCK); /* prohibit modification during sort */
+ rb_ensure(sort_internal, ary, sort_unlock, ary);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.sort -> an_array
+ * array.sort {| a,b | block } -> an_array
+ *
+ * Returns a new array created by sorting <i>self</i>. Comparisons for
+ * the sort will be done using the <code><=></code> operator or using
+ * an optional code block. The block implements a comparison between
+ * <i>a</i> and <i>b</i>, returning -1, 0, or +1. See also
+ * <code>Enumerable#sort_by</code>.
+ *
+ * a = [ "d", "a", "e", "c", "b" ]
+ * a.sort #=> ["a", "b", "c", "d", "e"]
+ * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ */
+
+VALUE
+rb_ary_sort(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_sort_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.collect {|item| block } -> an_array
+ * array.map {|item| block } -> an_array
+ *
+ * Invokes <i>block</i> once for each element of <i>self</i>. Creates a
+ * new array containing the values returned by the block.
+ * See also <code>Enumerable#collect</code>.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.collect {|x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
+ * a #=> ["a", "b", "c", "d"]
+ */
+
+static VALUE
+rb_ary_collect(ary)
+ VALUE ary;
+{
+ long i;
+ VALUE collect;
+
+ if (!rb_block_given_p()) {
+ return rb_ary_new4(RARRAY(ary)->len, RARRAY(ary)->ptr);
+ }
+
+ collect = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ rb_ary_push(collect, rb_yield(RARRAY(ary)->ptr[i]));
+ }
+ return collect;
+}
+
+/*
+ * call-seq:
+ * array.collect! {|item| block } -> array
+ * array.map! {|item| block } -> array
+ *
+ * Invokes the block once for each element of _self_, replacing the
+ * element with the value returned by _block_.
+ * See also <code>Enumerable#collect</code>.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.collect! {|x| x + "!" }
+ * a #=> [ "a!", "b!", "c!", "d!" ]
+ */
+
+static VALUE
+rb_ary_collect_bang(ary)
+ VALUE ary;
+{
+ long i;
+
+ rb_ary_modify(ary);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ rb_ary_store(ary, i, rb_yield(RARRAY(ary)->ptr[i]));
+ }
+ return ary;
+}
+
+VALUE
+rb_values_at(obj, olen, argc, argv, func)
+ VALUE obj;
+ long olen;
+ int argc;
+ VALUE *argv;
+ VALUE (*func) _((VALUE,long));
+{
+ VALUE result = rb_ary_new2(argc);
+ long beg, len, i, j;
+
+ for (i=0; i<argc; i++) {
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
+ continue;
+ }
+ /* check if idx is Range */
+ switch (rb_range_beg_len(argv[i], &beg, &len, olen, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ continue;
+ default:
+ for (j=0; j<len; j++) {
+ rb_ary_push(result, (*func)(obj, j+beg));
+ }
+ continue;
+ }
+ rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.values_at(selector,... ) -> an_array
+ *
+ * Returns an array containing the elements in
+ * _self_ corresponding to the given selector(s). The selectors
+ * may be either integer indices or ranges.
+ * See also <code>Array#select</code>.
+ *
+ * a = %w{ a b c d e f }
+ * a.values_at(1, 3, 5)
+ * a.values_at(1, 3, 5, 7)
+ * a.values_at(-1, -3, -5, -7)
+ * a.values_at(1..3, 2...5)
+ */
+
+static VALUE
+rb_ary_values_at(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ return rb_values_at(ary, RARRAY(ary)->len, argc, argv, rb_ary_entry);
+}
+
+/*
+ * call-seq:
+ * array.select {|item| block } -> an_array
+ *
+ * Invokes the block passing in successive elements from <i>array</i>,
+ * returning an array containing those elements for which the block
+ * returns a true value (equivalent to <code>Enumerable#select</code>).
+ *
+ * a = %w{ a b c d e f }
+ * a.select {|v| v =~ /[aeiou]/} #=> ["a", "e"]
+ */
+
+static VALUE
+rb_ary_select(ary)
+ VALUE ary;
+{
+ VALUE result;
+ long i;
+
+ result = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
+ rb_ary_push(result, rb_ary_elt(ary, i));
+ }
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.delete(obj) -> obj or nil
+ * array.delete(obj) { block } -> obj or nil
+ *
+ * Deletes items from <i>self</i> that are equal to <i>obj</i>. If
+ * the item is not found, returns <code>nil</code>. If the optional
+ * code block is given, returns the result of <i>block</i> if the item
+ * is not found.
+ *
+ * a = [ "a", "b", "b", "b", "c" ]
+ * a.delete("b") #=> "b"
+ * a #=> ["a", "c"]
+ * a.delete("z") #=> nil
+ * a.delete("z") { "not found" } #=> "not found"
+ */
+
+VALUE
+rb_ary_delete(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ long i1, i2;
+
+ for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
+ VALUE e = RARRAY(ary)->ptr[i1];
+
+ if (rb_equal(e, item)) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
+ }
+ if (RARRAY(ary)->len == i2) {
+ if (rb_block_given_p()) {
+ return rb_yield(item);
+ }
+ return Qnil;
+ }
+
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > i2) {
+ RARRAY(ary)->len = i2;
+ if (i2 * 2 < RARRAY(ary)->aux.capa &&
+ RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, i2 * 2);
+ RARRAY(ary)->aux.capa = i2 * 2;
+ }
+ }
+
+ return item;
+}
+
+VALUE
+rb_ary_delete_at(ary, pos)
+ VALUE ary;
+ long pos;
+{
+ long i, len = RARRAY(ary)->len;
+ VALUE del;
+
+ if (pos >= len) return Qnil;
+ if (pos < 0) {
+ pos += len;
+ if (pos < 0) return Qnil;
+ }
+
+ rb_ary_modify(ary);
+ del = RARRAY(ary)->ptr[pos];
+ for (i = pos + 1; i < len; i++, pos++) {
+ RARRAY(ary)->ptr[pos] = RARRAY(ary)->ptr[i];
+ }
+ RARRAY(ary)->len = pos;
+
+ return del;
+}
+
+/*
+ * call-seq:
+ * array.delete_at(index) -> obj or nil
+ *
+ * Deletes the element at the specified index, returning that element,
+ * or <code>nil</code> if the index is out of range. See also
+ * <code>Array#slice!</code>.
+ *
+ * a = %w( ant bat cat dog )
+ * a.delete_at(2) #=> "cat"
+ * a #=> ["ant", "bat", "dog"]
+ * a.delete_at(99) #=> nil
+ */
+
+static VALUE
+rb_ary_delete_at_m(ary, pos)
+ VALUE ary, pos;
+{
+ return rb_ary_delete_at(ary, NUM2LONG(pos));
+}
+
+/*
+ * call-seq:
+ * array.slice!(index) -> obj or nil
+ * array.slice!(start, length) -> sub_array or nil
+ * array.slice!(range) -> sub_array or nil
+ *
+ * Deletes the element(s) given by an index (optionally with a length)
+ * or by a range. Returns the deleted object, subarray, or
+ * <code>nil</code> if the index is out of range. Equivalent to:
+ *
+ * def slice!(*args)
+ * result = self[*args]
+ * self[*args] = nil
+ * result
+ * end
+ *
+ * a = [ "a", "b", "c" ]
+ * a.slice!(1) #=> "b"
+ * a #=> ["a", "c"]
+ * a.slice!(-1) #=> "c"
+ * a #=> ["a"]
+ * a.slice!(100) #=> nil
+ * a #=> ["a"]
+ */
+
+static VALUE
+rb_ary_slice_bang(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE arg1, arg2;
+ long pos, len;
+
+ if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) {
+ pos = NUM2LONG(arg1);
+ len = NUM2LONG(arg2);
+ delete_pos_len:
+ if (pos < 0) {
+ pos = RARRAY(ary)->len + pos;
+ }
+ arg2 = rb_ary_subseq(ary, pos, len);
+ rb_ary_splice(ary, pos, len, Qnil); /* Qnil/rb_ary_new2(0) */
+ return arg2;
+ }
+
+ if (!FIXNUM_P(arg1) && rb_range_beg_len(arg1, &pos, &len, RARRAY(ary)->len, 1)) {
+ goto delete_pos_len;
+ }
+
+ return rb_ary_delete_at(ary, NUM2LONG(arg1));
+}
+
+/*
+ * call-seq:
+ * array.reject! {|item| block } -> array or nil
+ *
+ * Equivalent to <code>Array#delete_if</code>, deleting elements from
+ * _self_ for which the block evaluates to true, but returns
+ * <code>nil</code> if no changes were made. Also see
+ * <code>Enumerable#reject</code>.
+ */
+
+static VALUE
+rb_ary_reject_bang(ary)
+ VALUE ary;
+{
+ long i1, i2;
+
+ rb_ary_modify(ary);
+ for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
+ VALUE v = RARRAY(ary)->ptr[i1];
+ if (RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ i2++;
+ }
+ if (RARRAY(ary)->len == i2) return Qnil;
+ if (i2 < RARRAY(ary)->len)
+ RARRAY(ary)->len = i2;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reject {|item| block } -> an_array
+ *
+ * Returns a new array containing the items in _self_
+ * for which the block is not true.
+ */
+
+static VALUE
+rb_ary_reject(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_reject_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.delete_if {|item| block } -> array
+ *
+ * Deletes every element of <i>self</i> for which <i>block</i> evaluates
+ * to <code>true</code>.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.delete_if {|x| x >= "b" } #=> ["a"]
+ */
+
+static VALUE
+rb_ary_delete_if(ary)
+ VALUE ary;
+{
+ rb_ary_reject_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.zip(arg, ...) -> an_array
+ * array.zip(arg, ...) {| arr | block } -> nil
+ *
+ * Converts any arguments to arrays, then merges elements of
+ * <i>self</i> with corresponding elements from each argument. This
+ * generates a sequence of <code>self.size</code> <em>n</em>-element
+ * arrays, where <em>n</em> is one more that the count of arguments. If
+ * the size of any argument is less than <code>enumObj.size</code>,
+ * <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,2,3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
+ * [1,2].zip(a,b) #=> [[1, 4, 7], [2, 5, 8]]
+ * a.zip([1,2],[8]) #=> [[4,1,8], [5,2,nil], [6,nil,nil]]
+ */
+
+static VALUE
+rb_ary_zip(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ int i, j;
+ long len;
+ VALUE result;
+
+ for (i=0; i<argc; i++) {
+ argv[i] = to_ary(argv[i]);
+ }
+ if (rb_block_given_p()) {
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, rb_ary_elt(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_yield(tmp);
+ }
+ return Qnil;
+ }
+ len = RARRAY(ary)->len;
+ result = rb_ary_new2(len);
+ for (i=0; i<len; i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, rb_ary_elt(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_ary_push(result, tmp);
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.transpose -> an_array
+ *
+ * Assumes that <i>self</i> is an array of arrays and transposes the
+ * rows and columns.
+ *
+ * a = [[1,2], [3,4], [5,6]]
+ * a.transpose #=> [[1, 3, 5], [2, 4, 6]]
+ */
+
+static VALUE
+rb_ary_transpose(ary)
+ VALUE ary;
+{
+ long elen = -1, alen, i, j;
+ VALUE tmp, result = 0;
+
+ alen = RARRAY(ary)->len;
+ if (alen == 0) return rb_ary_dup(ary);
+ for (i=0; i<alen; i++) {
+ tmp = to_ary(rb_ary_elt(ary, i));
+ if (elen < 0) { /* first element */
+ elen = RARRAY(tmp)->len;
+ result = rb_ary_new2(elen);
+ for (j=0; j<elen; j++) {
+ rb_ary_store(result, j, rb_ary_new2(alen));
+ }
+ }
+ else if (elen != RARRAY(tmp)->len) {
+ rb_raise(rb_eIndexError, "element size differs (%d should be %d)",
+ RARRAY(tmp)->len, elen);
+ }
+ for (j=0; j<elen; j++) {
+ rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
+ }
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.replace(other_array) -> array
+ *
+ * Replaces the contents of <i>self</i> with the contents of
+ * <i>other_array</i>, truncating or expanding if necessary.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.replace([ "x", "y", "z" ]) #=> ["x", "y", "z"]
+ * a #=> ["x", "y", "z"]
+ */
+
+static VALUE
+rb_ary_replace(copy, orig)
+ VALUE copy, orig;
+{
+ VALUE shared;
+
+ rb_ary_modify(copy);
+ orig = to_ary(orig);
+ if (copy == orig) return copy;
+ shared = ary_make_shared(orig);
+ if (RARRAY(copy)->ptr && !FL_TEST(copy, ELTS_SHARED))
+ free(RARRAY(copy)->ptr);
+ RARRAY(copy)->ptr = RARRAY(orig)->ptr;
+ RARRAY(copy)->len = RARRAY(orig)->len;
+ RARRAY(copy)->aux.shared = shared;
+ FL_SET(copy, ELTS_SHARED);
+
+ return copy;
+}
+
+/*
+ * call-seq:
+ * array.clear -> array
+ *
+ * Removes all elements from _self_.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.clear #=> [ ]
+ */
+
+VALUE
+rb_ary_clear(ary)
+ VALUE ary;
+{
+ rb_ary_modify(ary);
+ RARRAY(ary)->len = 0;
+ if (ARY_DEFAULT_SIZE * 2 < RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, ARY_DEFAULT_SIZE * 2);
+ RARRAY(ary)->aux.capa = ARY_DEFAULT_SIZE * 2;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.fill(obj) -> array
+ * array.fill(obj, start [, length]) -> array
+ * array.fill(obj, range ) -> array
+ * array.fill {|index| block } -> array
+ * array.fill(start [, length] ) {|index| block } -> array
+ * array.fill(range) {|index| block } -> array
+ *
+ * The first three forms set the selected elements of <i>self</i> (which
+ * may be the entire array) to <i>obj</i>. A <i>start</i> of
+ * <code>nil</code> is equivalent to zero. A <i>length</i> of
+ * <code>nil</code> is equivalent to <i>self.length</i>. The last three
+ * forms fill the array with the value of the block. The block is
+ * passed the absolute index of each element to be filled.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.fill("x") #=> ["x", "x", "x", "x"]
+ * a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
+ * a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
+ * a.fill {|i| i*i} #=> [0, 1, 4, 9]
+ * a.fill(-2) {|i| i*i*i} #=> [0, 1, 8, 27]
+ */
+
+static VALUE
+rb_ary_fill(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE item, arg1, arg2;
+ long beg = 0, end = 0, len = 0;
+ VALUE *p, *pend;
+ int block_p = Qfalse;
+
+ if (rb_block_given_p()) {
+ block_p = Qtrue;
+ rb_scan_args(argc, argv, "02", &arg1, &arg2);
+ argc += 1; /* hackish */
+ }
+ else {
+ rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
+ }
+ switch (argc) {
+ case 1:
+ beg = 0;
+ len = RARRAY(ary)->len;
+ break;
+ case 2:
+ if (rb_range_beg_len(arg1, &beg, &len, RARRAY(ary)->len, 1)) {
+ break;
+ }
+ /* fall through */
+ case 3:
+ beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
+ if (beg < 0) {
+ beg = RARRAY(ary)->len + beg;
+ if (beg < 0) beg = 0;
+ }
+ len = NIL_P(arg2) ? RARRAY(ary)->len - beg : NUM2LONG(arg2);
+ break;
+ }
+ rb_ary_modify(ary);
+ if (len < 0) {
+ return ary;
+ }
+ if (beg >= ARY_MAX_SIZE || len > ARY_MAX_SIZE - beg) {
+ rb_raise(rb_eArgError, "argument too big");
+ }
+ end = beg + len;
+ if (end > RARRAY(ary)->len) {
+ if (end >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, end);
+ RARRAY(ary)->aux.capa = end;
+ }
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, end - RARRAY(ary)->len);
+ RARRAY(ary)->len = end;
+ }
+
+ if (block_p) {
+ VALUE v;
+ long i;
+
+ for (i=beg; i<end; i++) {
+ v = rb_yield(LONG2NUM(i));
+ if (i>=RARRAY(ary)->len) break;
+ RARRAY(ary)->ptr[i] = v;
+ }
+ }
+ else {
+ p = RARRAY(ary)->ptr + beg;
+ pend = p + len;
+ while (p < pend) {
+ *p++ = item;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array + other_array -> an_array
+ *
+ * Concatenation---Returns a new array built by concatenating the
+ * two arrays together to produce a third array.
+ *
+ * [ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ]
+ */
+
+VALUE
+rb_ary_plus(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+ long len;
+
+ y = to_ary(y);
+ len = RARRAY(x)->len + RARRAY(y)->len;
+ z = rb_ary_new2(len);
+ MEMCPY(RARRAY(z)->ptr, RARRAY(x)->ptr, VALUE, RARRAY(x)->len);
+ MEMCPY(RARRAY(z)->ptr + RARRAY(x)->len, RARRAY(y)->ptr, VALUE, RARRAY(y)->len);
+ RARRAY(z)->len = len;
+ return z;
+}
+
+/*
+ * call-seq:
+ * array.concat(other_array) -> array
+ *
+ * Appends the elements in other_array to _self_.
+ *
+ * [ "a", "b" ].concat( ["c", "d"] ) #=> [ "a", "b", "c", "d" ]
+ */
+
+
+VALUE
+rb_ary_concat(x, y)
+ VALUE x, y;
+{
+ y = to_ary(y);
+ if (RARRAY(y)->len > 0) {
+ rb_ary_splice(x, RARRAY(x)->len, 0, y);
+ }
+ return x;
+}
+
+
+/*
+ * call-seq:
+ * array * int -> an_array
+ * array * str -> a_string
+ *
+ * Repetition---With a String argument, equivalent to
+ * self.join(str). Otherwise, returns a new array
+ * built by concatenating the _int_ copies of _self_.
+ *
+ *
+ * [ 1, 2, 3 ] * 3 #=> [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ]
+ * [ 1, 2, 3 ] * "," #=> "1,2,3"
+ *
+ */
+
+static VALUE
+rb_ary_times(ary, times)
+ VALUE ary, times;
+{
+ VALUE ary2, tmp;
+ long i, len;
+
+ tmp = rb_check_string_type(times);
+ if (!NIL_P(tmp)) {
+ return rb_ary_join(ary, tmp);
+ }
+
+ len = NUM2LONG(times);
+ if (len == 0) return ary_new(rb_obj_class(ary), 0);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative argument");
+ }
+ if (ARY_MAX_SIZE/len < RARRAY(ary)->len) {
+ rb_raise(rb_eArgError, "argument too big");
+ }
+ len *= RARRAY(ary)->len;
+
+ ary2 = ary_new(rb_obj_class(ary), len);
+ RARRAY(ary2)->len = len;
+
+ for (i=0; i<len; i+=RARRAY(ary)->len) {
+ MEMCPY(RARRAY(ary2)->ptr+i, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ }
+ OBJ_INFECT(ary2, ary);
+
+ return ary2;
+}
+
+/*
+ * call-seq:
+ * array.assoc(obj) -> an_array or nil
+ *
+ * Searches through an array whose elements are also arrays
+ * comparing _obj_ with the first element of each contained array
+ * using obj.==.
+ * Returns the first contained array that matches (that
+ * is, the first associated array),
+ * or +nil+ if no match is found.
+ * See also <code>Array#rassoc</code>.
+ *
+ * s1 = [ "colors", "red", "blue", "green" ]
+ * s2 = [ "letters", "a", "b", "c" ]
+ * s3 = "foo"
+ * a = [ s1, s2, s3 ]
+ * a.assoc("letters") #=> [ "letters", "a", "b", "c" ]
+ * a.assoc("foo") #=> nil
+ */
+
+VALUE
+rb_ary_assoc(ary, key)
+ VALUE ary, key;
+{
+ long i;
+ VALUE v;
+
+ for (i = 0; i < RARRAY(ary)->len; ++i) {
+ v = RARRAY(ary)->ptr[i];
+ if (TYPE(v) == T_ARRAY &&
+ RARRAY(v)->len > 0 &&
+ rb_equal(RARRAY(v)->ptr[0], key))
+ return v;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array.rassoc(key) -> an_array or nil
+ *
+ * Searches through the array whose elements are also arrays. Compares
+ * <em>key</em> with the second element of each contained array using
+ * <code>==</code>. Returns the first contained array that matches. See
+ * also <code>Array#assoc</code>.
+ *
+ * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
+ * a.rassoc("two") #=> [2, "two"]
+ * a.rassoc("four") #=> nil
+ */
+
+VALUE
+rb_ary_rassoc(ary, value)
+ VALUE ary, value;
+{
+ long i;
+ VALUE v;
+
+ for (i = 0; i < RARRAY(ary)->len; ++i) {
+ v = RARRAY(ary)->ptr[i];
+ if (TYPE(v) == T_ARRAY &&
+ RARRAY(v)->len > 1 &&
+ rb_equal(RARRAY(v)->ptr[1], value))
+ return v;
+ }
+ return Qnil;
+}
+
+static VALUE
+recursive_equal(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (!rb_equal(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * array == other_array -> bool
+ *
+ * Equality---Two arrays are equal if they contain the same number
+ * of elements and if each element is equal to (according to
+ * Object.==) the corresponding element in the other array.
+ *
+ * [ "a", "c" ] == [ "a", "c", 7 ] #=> false
+ * [ "a", "c", 7 ] == [ "a", "c", 7 ] #=> true
+ * [ "a", "c", 7 ] == [ "a", "d", "f" ] #=> false
+ *
+ */
+
+static VALUE
+rb_ary_equal(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ if (ary1 == ary2) return Qtrue;
+ if (TYPE(ary2) != T_ARRAY) {
+ if (!rb_respond_to(ary2, rb_intern("to_ary"))) {
+ return Qfalse;
+ }
+ return rb_equal(ary2, ary1);
+ }
+ if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
+ if (rb_inspecting_p(ary1)) return Qfalse;
+ return rb_protect_inspect(recursive_equal, ary1, ary2);
+}
+
+static VALUE
+recursive_eql(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * array.eql?(other) -> true or false
+ *
+ * Returns <code>true</code> if _array_ and _other_ are the same object,
+ * or are both arrays with the same content.
+ */
+
+static VALUE
+rb_ary_eql(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ if (ary1 == ary2) return Qtrue;
+ if (TYPE(ary2) != T_ARRAY) return Qfalse;
+ if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
+ if (rb_inspecting_p(ary1)) return Qfalse;
+ return rb_protect_inspect(recursive_eql, ary1, ary2);
+}
+
+static VALUE recursive_hash _((VALUE ary));
+
+static VALUE
+recursive_hash(ary)
+ VALUE ary;
+{
+ long i, h;
+ VALUE n;
+
+ h = RARRAY(ary)->len;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ h = (h << 1) | (h<0 ? 1 : 0);
+ n = rb_hash(RARRAY(ary)->ptr[i]);
+ h ^= NUM2LONG(n);
+ }
+ return LONG2FIX(h);
+}
+
+/*
+ * call-seq:
+ * array.hash -> fixnum
+ *
+ * Compute a hash-code for this array. Two arrays with the same content
+ * will have the same hash code (and will compare using <code>eql?</code>).
+ */
+
+static VALUE
+rb_ary_hash(ary)
+ VALUE ary;
+{
+ if (rb_inspecting_p(ary)) {
+ return LONG2FIX(0);
+ }
+ return rb_protect_inspect(recursive_hash, ary, 0);
+}
+
+/*
+ * call-seq:
+ * array.include?(obj) -> true or false
+ *
+ * Returns <code>true</code> if the given object is present in
+ * <i>self</i> (that is, if any object <code>==</code> <i>anObject</i>),
+ * <code>false</code> otherwise.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.include?("b") #=> true
+ * a.include?("z") #=> false
+ */
+
+VALUE
+rb_ary_includes(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (rb_equal(RARRAY(ary)->ptr[i], item)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+VALUE
+recursive_cmp(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i, len;
+
+ len = RARRAY(ary1)->len;
+ if (len > RARRAY(ary2)->len) {
+ len = RARRAY(ary2)->len;
+ }
+ for (i=0; i<len; i++) {
+ VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
+ if (v != INT2FIX(0)) {
+ return v;
+ }
+ }
+ return Qundef;
+}
+
+/*
+ * call-seq:
+ * array <=> other_array -> -1, 0, +1
+ *
+ * Comparison---Returns an integer (-1, 0,
+ * or +1) if this array is less than, equal to, or greater than
+ * other_array. Each object in each array is compared
+ * (using <=>). If any value isn't
+ * equal, then that inequality is the return value. If all the
+ * values found are equal, then the return is based on a
+ * comparison of the array lengths. Thus, two arrays are
+ * ``equal'' according to <code>Array#<=></code> if and only if they have
+ * the same length and the value of each element is equal to the
+ * value of the corresponding element in the other array.
+ *
+ * [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1
+ * [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1
+ *
+ */
+
+VALUE
+rb_ary_cmp(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long len;
+ VALUE v;
+
+ ary2 = to_ary(ary2);
+ if (ary1 == ary2) return INT2FIX(0);
+ if (rb_inspecting_p(ary1)) return INT2FIX(0);
+ v = rb_protect_inspect(recursive_cmp, ary1, ary2);
+ if (v != Qundef) return v;
+ len = RARRAY(ary1)->len - RARRAY(ary2)->len;
+ if (len == 0) return INT2FIX(0);
+ if (len > 0) return INT2FIX(1);
+ return INT2FIX(-1);
+}
+
+static VALUE
+ary_make_hash(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash = rb_hash_new();
+ long i;
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ rb_hash_aset(hash, RARRAY(ary1)->ptr[i], Qtrue);
+ }
+ if (ary2) {
+ for (i=0; i<RARRAY(ary2)->len; i++) {
+ rb_hash_aset(hash, RARRAY(ary2)->ptr[i], Qtrue);
+ }
+ }
+ return hash;
+}
+
+/*
+ * call-seq:
+ * array - other_array -> an_array
+ *
+ * Array Difference---Returns a new array that is a copy of
+ * the original array, removing any items that also appear in
+ * other_array. (If you need set-like behavior, see the
+ * library class Set.)
+ *
+ * [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
+ */
+
+static VALUE
+rb_ary_diff(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE ary3;
+ volatile VALUE hash;
+ long i;
+
+ hash = ary_make_hash(to_ary(ary2), 0);
+ ary3 = rb_ary_new();
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (st_lookup(RHASH(hash)->tbl, RARRAY(ary1)->ptr[i], 0)) continue;
+ rb_ary_push(ary3, rb_ary_elt(ary1, i));
+ }
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array & other_array
+ *
+ * Set Intersection---Returns a new array
+ * containing elements common to the two arrays, with no duplicates.
+ *
+ * [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
+ */
+
+
+static VALUE
+rb_ary_and(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash, ary3, v, vv;
+ long i;
+
+ ary2 = to_ary(ary2);
+ ary3 = rb_ary_new2(RARRAY(ary1)->len < RARRAY(ary2)->len ?
+ RARRAY(ary1)->len : RARRAY(ary2)->len);
+ hash = ary_make_hash(ary2, 0);
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ v = vv = rb_ary_elt(ary1, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array | other_array -> an_array
+ *
+ * Set Union---Returns a new array by joining this array with
+ * other_array, removing duplicates.
+ *
+ * [ "a", "b", "c" ] | [ "c", "d", "a" ]
+ * #=> [ "a", "b", "c", "d" ]
+ */
+
+static VALUE
+rb_ary_or(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash, ary3;
+ VALUE v, vv;
+ long i;
+
+ ary2 = to_ary(ary2);
+ ary3 = rb_ary_new2(RARRAY(ary1)->len+RARRAY(ary2)->len);
+ hash = ary_make_hash(ary1, ary2);
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ v = vv = rb_ary_elt(ary1, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+ for (i=0; i<RARRAY(ary2)->len; i++) {
+ v = vv = rb_ary_elt(ary2, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array.uniq! -> array or nil
+ *
+ * Removes duplicate elements from _self_.
+ * Returns <code>nil</code> if no changes are made (that is, no
+ * duplicates are found).
+ *
+ * a = [ "a", "a", "b", "b", "c" ]
+ * a.uniq! #=> ["a", "b", "c"]
+ * b = [ "a", "b", "c" ]
+ * b.uniq! #=> nil
+ */
+
+static VALUE
+rb_ary_uniq_bang(ary)
+ VALUE ary;
+{
+ VALUE hash, v, vv;
+ long i, j;
+
+ hash = ary_make_hash(ary, 0);
+
+ if (RARRAY(ary)->len == RHASH(hash)->tbl->num_entries) {
+ return Qnil;
+ }
+ for (i=j=0; i<RARRAY(ary)->len; i++) {
+ v = vv = rb_ary_elt(ary, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_store(ary, j++, v);
+ }
+ }
+ RARRAY(ary)->len = j;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.uniq -> an_array
+ *
+ * Returns a new array by removing duplicate values in <i>self</i>.
+ *
+ * a = [ "a", "a", "b", "b", "c" ]
+ * a.uniq #=> ["a", "b", "c"]
+ */
+
+static VALUE
+rb_ary_uniq(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_uniq_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.compact! -> array or nil
+ *
+ * Removes +nil+ elements from array.
+ * Returns +nil+ if no changes were made.
+ *
+ * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
+ * [ "a", "b", "c" ].compact! #=> nil
+ */
+
+static VALUE
+rb_ary_compact_bang(ary)
+ VALUE ary;
+{
+ VALUE *p, *t, *end;
+
+ rb_ary_modify(ary);
+ p = t = RARRAY(ary)->ptr;
+ end = p + RARRAY(ary)->len;
+
+ while (t < end) {
+ if (NIL_P(*t)) t++;
+ else *p++ = *t++;
+ }
+ if (RARRAY(ary)->len == (p - RARRAY(ary)->ptr)) {
+ return Qnil;
+ }
+ RARRAY(ary)->len = RARRAY(ary)->aux.capa = (p - RARRAY(ary)->ptr);
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.compact -> an_array
+ *
+ * Returns a copy of _self_ with all +nil+ elements removed.
+ *
+ * [ "a", nil, "b", nil, "c", nil ].compact
+ * #=> [ "a", "b", "c" ]
+ */
+
+static VALUE
+rb_ary_compact(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_compact_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.nitems -> int
+ *
+ * Returns the number of non-<code>nil</code> elements in _self_.
+ * May be zero.
+ *
+ * [ 1, nil, 3, nil, 5 ].nitems #=> 3
+ */
+
+static VALUE
+rb_ary_nitems(ary)
+ VALUE ary;
+{
+ long n = 0;
+ VALUE *p, *pend;
+
+ p = RARRAY(ary)->ptr;
+ pend = p + RARRAY(ary)->len;
+
+ while (p < pend) {
+ if (!NIL_P(*p)) n++;
+ p++;
+ }
+ return LONG2NUM(n);
+}
+
+static long
+flatten(ary, idx, ary2, memo)
+ VALUE ary;
+ long idx;
+ VALUE ary2, memo;
+{
+ VALUE id;
+ long i = idx;
+ long n, lim = idx + RARRAY(ary2)->len;
+
+ id = rb_obj_id(ary2);
+ if (rb_ary_includes(memo, id)) {
+ rb_raise(rb_eArgError, "tried to flatten recursive array");
+ }
+ rb_ary_push(memo, id);
+ rb_ary_splice(ary, idx, 1, ary2);
+ while (i < lim) {
+ VALUE tmp;
+
+ tmp = rb_check_array_type(rb_ary_elt(ary, i));
+ if (!NIL_P(tmp)) {
+ n = flatten(ary, i, tmp, memo);
+ i += n; lim += n;
+ }
+ i++;
+ }
+ rb_ary_pop(memo);
+
+ return lim - idx - 1; /* returns number of increased items */
+}
+
+/*
+ * call-seq:
+ * array.flatten! -> array or nil
+ *
+ * Flattens _self_ in place.
+ * Returns <code>nil</code> if no modifications were made (i.e.,
+ * <i>array</i> contains no subarrays.)
+ *
+ * a = [ 1, 2, [3, [4, 5] ] ]
+ * a.flatten! #=> [1, 2, 3, 4, 5]
+ * a.flatten! #=> nil
+ * a #=> [1, 2, 3, 4, 5]
+ */
+
+static VALUE
+rb_ary_flatten_bang(ary)
+ VALUE ary;
+{
+ long i = 0;
+ int mod = 0;
+ VALUE memo = Qnil;
+
+ while (i<RARRAY(ary)->len) {
+ VALUE ary2 = RARRAY(ary)->ptr[i];
+ VALUE tmp;
+
+ tmp = rb_check_array_type(ary2);
+ if (!NIL_P(tmp)) {
+ if (NIL_P(memo)) {
+ memo = rb_ary_new();
+ }
+ i += flatten(ary, i, tmp, memo);
+ mod = 1;
+ }
+ i++;
+ }
+ if (mod == 0) return Qnil;
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.flatten -> an_array
+ *
+ * Returns a new array that is a one-dimensional flattening of this
+ * array (recursively). That is, for every element that is an array,
+ * extract its elements into the new array.
+ *
+ * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+ * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+ * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+ * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+ */
+
+static VALUE
+rb_ary_flatten(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_flatten_bang(ary);
+ return ary;
+}
+
+
+/* Arrays are ordered, integer-indexed collections of any object.
+ * Array indexing starts at 0, as in C or Java. A negative index is
+ * assumed to be relative to the end of the array---that is, an index of -1
+ * indicates the last element of the array, -2 is the next to last
+ * element in the array, and so on.
+ */
+
+void
+Init_Array()
+{
+ rb_cArray = rb_define_class("Array", rb_cObject);
+ rb_include_module(rb_cArray, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cArray, ary_alloc);
+ rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
+ rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
+ rb_define_method(rb_cArray, "initialize_copy", rb_ary_replace, 1);
+
+ rb_define_method(rb_cArray, "to_s", rb_ary_to_s, 0);
+ rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);
+ rb_define_method(rb_cArray, "to_a", rb_ary_to_a, 0);
+ rb_define_method(rb_cArray, "to_ary", rb_ary_to_ary_m, 0);
+ rb_define_method(rb_cArray, "frozen?", rb_ary_frozen_p, 0);
+
+ rb_define_method(rb_cArray, "==", rb_ary_equal, 1);
+ rb_define_method(rb_cArray, "eql?", rb_ary_eql, 1);
+ rb_define_method(rb_cArray, "hash", rb_ary_hash, 0);
+
+ rb_define_method(rb_cArray, "[]", rb_ary_aref, -1);
+ rb_define_method(rb_cArray, "[]=", rb_ary_aset, -1);
+ rb_define_method(rb_cArray, "at", rb_ary_at, 1);
+ rb_define_method(rb_cArray, "fetch", rb_ary_fetch, -1);
+ rb_define_method(rb_cArray, "first", rb_ary_first, -1);
+ rb_define_method(rb_cArray, "last", rb_ary_last, -1);
+ rb_define_method(rb_cArray, "concat", rb_ary_concat, 1);
+ rb_define_method(rb_cArray, "<<", rb_ary_push, 1);
+ rb_define_method(rb_cArray, "push", rb_ary_push_m, -1);
+ rb_define_method(rb_cArray, "pop", rb_ary_pop, 0);
+ rb_define_method(rb_cArray, "shift", rb_ary_shift, 0);
+ rb_define_method(rb_cArray, "unshift", rb_ary_unshift_m, -1);
+ rb_define_method(rb_cArray, "insert", rb_ary_insert, -1);
+ rb_define_method(rb_cArray, "each", rb_ary_each, 0);
+ rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
+ rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0);
+ rb_define_method(rb_cArray, "length", rb_ary_length, 0);
+ rb_define_alias(rb_cArray, "size", "length");
+ rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
+ rb_define_method(rb_cArray, "index", rb_ary_index, 1);
+ rb_define_method(rb_cArray, "rindex", rb_ary_rindex, 1);
+ rb_define_method(rb_cArray, "indexes", rb_ary_indexes, -1);
+ rb_define_method(rb_cArray, "indices", rb_ary_indexes, -1);
+ rb_define_method(rb_cArray, "join", rb_ary_join_m, -1);
+ rb_define_method(rb_cArray, "reverse", rb_ary_reverse_m, 0);
+ rb_define_method(rb_cArray, "reverse!", rb_ary_reverse_bang, 0);
+ rb_define_method(rb_cArray, "sort", rb_ary_sort, 0);
+ rb_define_method(rb_cArray, "sort!", rb_ary_sort_bang, 0);
+ rb_define_method(rb_cArray, "collect", rb_ary_collect, 0);
+ rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0);
+ rb_define_method(rb_cArray, "map", rb_ary_collect, 0);
+ rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0);
+ rb_define_method(rb_cArray, "select", rb_ary_select, 0);
+ rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1);
+ rb_define_method(rb_cArray, "delete", rb_ary_delete, 1);
+ rb_define_method(rb_cArray, "delete_at", rb_ary_delete_at_m, 1);
+ rb_define_method(rb_cArray, "delete_if", rb_ary_delete_if, 0);
+ rb_define_method(rb_cArray, "reject", rb_ary_reject, 0);
+ rb_define_method(rb_cArray, "reject!", rb_ary_reject_bang, 0);
+ rb_define_method(rb_cArray, "zip", rb_ary_zip, -1);
+ rb_define_method(rb_cArray, "transpose", rb_ary_transpose, 0);
+ rb_define_method(rb_cArray, "replace", rb_ary_replace, 1);
+ rb_define_method(rb_cArray, "clear", rb_ary_clear, 0);
+ rb_define_method(rb_cArray, "fill", rb_ary_fill, -1);
+ rb_define_method(rb_cArray, "include?", rb_ary_includes, 1);
+ rb_define_method(rb_cArray, "<=>", rb_ary_cmp, 1);
+
+ rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);
+ rb_define_method(rb_cArray, "slice!", rb_ary_slice_bang, -1);
+
+ rb_define_method(rb_cArray, "assoc", rb_ary_assoc, 1);
+ rb_define_method(rb_cArray, "rassoc", rb_ary_rassoc, 1);
+
+ rb_define_method(rb_cArray, "+", rb_ary_plus, 1);
+ rb_define_method(rb_cArray, "*", rb_ary_times, 1);
+
+ rb_define_method(rb_cArray, "-", rb_ary_diff, 1);
+ rb_define_method(rb_cArray, "&", rb_ary_and, 1);
+ rb_define_method(rb_cArray, "|", rb_ary_or, 1);
+
+ rb_define_method(rb_cArray, "uniq", rb_ary_uniq, 0);
+ rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0);
+ rb_define_method(rb_cArray, "compact", rb_ary_compact, 0);
+ rb_define_method(rb_cArray, "compact!", rb_ary_compact_bang, 0);
+ rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+ rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, 0);
+ rb_define_method(rb_cArray, "nitems", rb_ary_nitems, 0);
+
+ id_cmp = rb_intern("<=>");
+ inspect_key = rb_intern("__inspect_key__");
+}
+/**********************************************************************
+
+ bignum.c -
+
+ $Author: wyhaines $
+ $Date: 2009-07-14 17:05:27 +0200 (Tue, 14 Jul 2009) $
+ created at: Fri Jun 10 00:48:55 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+
+#include <math.h>
+#include <ctype.h>
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+VALUE rb_cBignum;
+
+#if defined __MINGW32__
+#define USHORT _USHORT
+#endif
+
+#define BDIGITS(x) ((BDIGIT*)RBIGNUM(x)->digits)
+#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
+#define BIGRAD ((BDIGIT_DBL)1 << BITSPERDIG)
+#define DIGSPERLONG ((unsigned int)(SIZEOF_LONG/SIZEOF_BDIGITS))
+#if HAVE_LONG_LONG
+# define DIGSPERLL ((unsigned int)(SIZEOF_LONG_LONG/SIZEOF_BDIGITS))
+#endif
+#define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG)
+#define BIGDN(x) RSHIFT(x,BITSPERDIG)
+#define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1)))
+#define BDIGMAX ((BDIGIT)-1)
+
+#define BIGZEROP(x) (RBIGNUM(x)->len == 0 || \
+ (BDIGITS(x)[0] == 0 && \
+ (RBIGNUM(x)->len == 1 || bigzero_p(x))))
+
+static int bigzero_p(VALUE);
+static int
+bigzero_p(x)
+ VALUE x;
+{
+ long i;
+ for (i = 0; i < RBIGNUM(x)->len; ++i) {
+ if (BDIGITS(x)[i]) return 0;
+ }
+ return 1;
+}
+
+static VALUE
+bignew_1(klass, len, sign)
+ VALUE klass;
+ long len;
+ int sign;
+{
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, klass, T_BIGNUM);
+ big->sign = sign?1:0;
+ big->len = len;
+ big->digits = ALLOC_N(BDIGIT, len);
+
+ return (VALUE)big;
+}
+
+#define bignew(len,sign) bignew_1(rb_cBignum,len,sign)
+
+VALUE
+rb_big_clone(x)
+ VALUE x;
+{
+ VALUE z = bignew_1(CLASS_OF(x), RBIGNUM(x)->len, RBIGNUM(x)->sign);
+
+ MEMCPY(BDIGITS(z), BDIGITS(x), BDIGIT, RBIGNUM(x)->len);
+ return z;
+}
+
+/* modify a bignum by 2's complement */
+static void
+get2comp(x)
+ VALUE x;
+{
+ long i = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+ BDIGIT_DBL num;
+
+ if (!i) return;
+ while (i--) ds[i] = ~ds[i];
+ i = 0; num = 1;
+ do {
+ num += ds[i];
+ ds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ } while (i < RBIGNUM(x)->len);
+ if (num != 0) {
+ REALLOC_N(RBIGNUM(x)->digits, BDIGIT, ++RBIGNUM(x)->len);
+ ds = BDIGITS(x);
+ ds[RBIGNUM(x)->len-1] = RBIGNUM(x)->sign ? ~0 : 1;
+ }
+}
+
+void
+rb_big_2comp(x) /* get 2's complement */
+ VALUE x;
+{
+ get2comp(x);
+}
+
+static VALUE
+bigtrunc(x)
+ VALUE x;
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+
+ if (len == 0) return x;
+ while (--len && !ds[len]);
+ RBIGNUM(x)->len = ++len;
+ return x;
+}
+
+static VALUE
+bigfixize(VALUE x)
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+
+ if (len*SIZEOF_BDIGITS <= sizeof(VALUE)) {
+ long num = 0;
+ while (len--) {
+ num = BIGUP(num) + ds[len];
+ }
+ if (num >= 0) {
+ if (RBIGNUM(x)->sign) {
+ if (POSFIXABLE(num)) return LONG2FIX(num);
+ }
+ else {
+ if (NEGFIXABLE(-(long)num)) return LONG2FIX(-(long)num);
+ }
+ }
+ }
+ return x;
+}
+
+static VALUE
+bignorm(VALUE x)
+{
+ if (!FIXNUM_P(x) && TYPE(x) == T_BIGNUM) {
+ x = bigfixize(bigtrunc(x));
+ }
+ return x;
+}
+
+VALUE
+rb_big_norm(x)
+ VALUE x;
+{
+ return bignorm(x);
+}
+
+VALUE
+rb_uint2big(n)
+ unsigned long n;
+{
+ BDIGIT_DBL num = n;
+ long i = 0;
+ BDIGIT *digits;
+ VALUE big;
+
+ big = bignew(DIGSPERLONG, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLONG) {
+ digits[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+
+ i = DIGSPERLONG;
+ while (--i && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+ return big;
+}
+
+VALUE
+rb_int2big(n)
+ long n;
+{
+ long neg = 0;
+ VALUE big;
+
+ if (n < 0) {
+ n = -n;
+ neg = 1;
+ }
+ big = rb_uint2big(n);
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return big;
+}
+
+VALUE
+rb_uint2inum(n)
+ unsigned long n;
+{
+ if (POSFIXABLE(n)) return LONG2FIX(n);
+ return rb_uint2big(n);
+}
+
+VALUE
+rb_int2inum(n)
+ long n;
+{
+ if (FIXABLE(n)) return LONG2FIX(n);
+ return rb_int2big(n);
+}
+
+#ifdef HAVE_LONG_LONG
+
+void
+rb_quad_pack(buf, val)
+ char *buf;
+ VALUE val;
+{
+ LONG_LONG q;
+
+ val = rb_to_int(val);
+ if (FIXNUM_P(val)) {
+ q = FIX2LONG(val);
+ }
+ else {
+ long len = RBIGNUM(val)->len;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `quad int'");
+ ds = BDIGITS(val);
+ q = 0;
+ while (len--) {
+ q = BIGUP(q);
+ q += ds[len];
+ }
+ if (!RBIGNUM(val)->sign) q = -q;
+ }
+ memcpy(buf, (char*)&q, SIZEOF_LONG_LONG);
+}
+
+VALUE
+rb_quad_unpack(buf, sign)
+ const char *buf;
+ int sign;
+{
+ unsigned LONG_LONG q;
+ long neg = 0;
+ long i;
+ BDIGIT *digits;
+ VALUE big;
+
+ memcpy(&q, buf, SIZEOF_LONG_LONG);
+ if (sign) {
+ if (FIXABLE((LONG_LONG)q)) return LONG2FIX((LONG_LONG)q);
+ if ((LONG_LONG)q < 0) {
+ q = -(LONG_LONG)q;
+ neg = 1;
+ }
+ }
+ else {
+ if (POSFIXABLE(q)) return LONG2FIX(q);
+ }
+
+ i = 0;
+ big = bignew(DIGSPERLL, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLL) {
+ digits[i++] = BIGLO(q);
+ q = BIGDN(q);
+ }
+
+ i = DIGSPERLL;
+ while (i-- && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return bignorm(big);
+}
+
+#else
+
+#define QUAD_SIZE 8
+
+void
+rb_quad_pack(buf, val)
+ char *buf;
+ VALUE val;
+{
+ long len;
+
+ memset(buf, 0, QUAD_SIZE);
+ val = rb_to_int(val);
+ if (FIXNUM_P(val)) {
+ val = rb_int2big(FIX2LONG(val));
+ }
+ len = RBIGNUM(val)->len * SIZEOF_BDIGITS;
+ if (len > QUAD_SIZE) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `quad int'");
+ }
+ memcpy(buf, (char*)BDIGITS(val), len);
+ if (!RBIGNUM(val)->sign) {
+ len = QUAD_SIZE;
+ while (len--) {
+ *buf = ~*buf;
+ buf++;
+ }
+ }
+}
+
+#define BNEG(b) (RSHIFT(((BDIGIT*)b)[QUAD_SIZE/SIZEOF_BDIGITS-1],BITSPERDIG-1) != 0)
+
+VALUE
+rb_quad_unpack(buf, sign)
+ const char *buf;
+ int sign;
+{
+ VALUE big = bignew(QUAD_SIZE/SIZEOF_BDIGITS, 1);
+
+ memcpy((char*)BDIGITS(big), buf, QUAD_SIZE);
+ if (sign && BNEG(buf)) {
+ long len = QUAD_SIZE;
+ char *tmp = (char*)BDIGITS(big);
+
+ RBIGNUM(big)->sign = 0;
+ while (len--) {
+ *tmp = ~*tmp;
+ tmp++;
+ }
+ }
+
+ return bignorm(big);
+}
+
+#endif
+
+VALUE
+rb_cstr_to_inum(str, base, badcheck)
+ const char *str;
+ int base;
+ int badcheck;
+{
+ const char *s = str;
+ char *end;
+ char sign = 1, nondigit = 0;
+ int c;
+ BDIGIT_DBL num;
+ long len, blen = 1;
+ long i;
+ VALUE z;
+ BDIGIT *zds;
+
+#define conv_digit(c) \
+ (!ISASCII(c) ? -1 : \
+ isdigit(c) ? ((c) - '0') : \
+ islower(c) ? ((c) - 'a' + 10) : \
+ isupper(c) ? ((c) - 'A' + 10) : \
+ -1)
+
+ if (!str) {
+ if (badcheck) goto bad;
+ return INT2FIX(0);
+ }
+ if (badcheck) {
+ while (ISSPACE(*str)) str++;
+ }
+ else {
+ while (ISSPACE(*str) || *str == '_') str++;
+ }
+
+ if (str[0] == '+') {
+ str++;
+ }
+ else if (str[0] == '-') {
+ str++;
+ sign = 0;
+ }
+ if (str[0] == '+' || str[0] == '-') {
+ if (badcheck) goto bad;
+ return INT2FIX(0);
+ }
+ if (base <= 0) {
+ if (str[0] == '0') {
+ switch (str[1]) {
+ case 'x': case 'X':
+ base = 16;
+ break;
+ case 'b': case 'B':
+ base = 2;
+ break;
+ case 'o': case 'O':
+ base = 8;
+ break;
+ case 'd': case 'D':
+ base = 10;
+ break;
+ default:
+ base = 8;
+ }
+ }
+ else if (base < -1) {
+ base = -base;
+ }
+ else {
+ base = 10;
+ }
+ }
+ switch (base) {
+ case 2:
+ len = 1;
+ if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
+ str += 2;
+ }
+ break;
+ case 3:
+ len = 2;
+ break;
+ case 8:
+ if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
+ str += 2;
+ }
+ case 4: case 5: case 6: case 7:
+ len = 3;
+ break;
+ case 10:
+ if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
+ str += 2;
+ }
+ case 9: case 11: case 12: case 13: case 14: case 15:
+ len = 4;
+ break;
+ case 16:
+ len = 4;
+ if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
+ str += 2;
+ }
+ break;
+ default:
+ if (base < 2 || 36 < base) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ if (base <= 32) {
+ len = 5;
+ }
+ else {
+ len = 6;
+ }
+ break;
+ }
+ if (*str == '0') { /* squeeze preceeding 0s */
+ while (*++str == '0');
+ if (!(c = *str) || ISSPACE(c)) --str;
+ }
+ c = *str;
+ c = conv_digit(c);
+ if (c < 0 || c >= base) {
+ if (badcheck) goto bad;
+ return INT2FIX(0);
+ }
+ len *= strlen(str)*sizeof(char);
+
+ if (len <= (sizeof(VALUE)*CHAR_BIT)) {
+ unsigned long val = strtoul((char*)str, &end, base);
+
+ if (*end == '_') goto bigparse;
+ if (badcheck) {
+ if (end == str) goto bad; /* no number */
+ while (*end && ISSPACE(*end)) end++;
+ if (*end) goto bad; /* trailing garbage */
+ }
+
+ if (POSFIXABLE(val)) {
+ if (sign) return LONG2FIX(val);
+ else {
+ long result = -(long)val;
+ return LONG2FIX(result);
+ }
+ }
+ else {
+ VALUE big = rb_uint2big(val);
+ RBIGNUM(big)->sign = sign;
+ return bignorm(big);
+ }
+ }
+ bigparse:
+ len = (len/BITSPERDIG)+1;
+ if (badcheck && *str == '_') goto bad;
+
+ z = bignew(len, sign);
+ zds = BDIGITS(z);
+ for (i=len;i--;) zds[i]=0;
+ while ((c = *str++) != 0) {
+ if (c == '_') {
+ if (badcheck) {
+ if (nondigit) goto bad;
+ nondigit = c;
+ }
+ continue;
+ }
+ else if ((c = conv_digit(c)) < 0) {
+ break;
+ }
+ if (c >= base) break;
+ nondigit = 0;
+ i = 0;
+ num = c;
+ for (;;) {
+ while (i<blen) {
+ num += (BDIGIT_DBL)zds[i]*base;
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ if (num) {
+ blen++;
+ continue;
+ }
+ break;
+ }
+ }
+ if (badcheck) {
+ str--;
+ if (s+1 < str && str[-1] == '_') goto bad;
+ while (*str && ISSPACE(*str)) str++;
+ if (*str) {
+ bad:
+ rb_invalid_str(s, "Integer");
+ }
+ }
+
+ return bignorm(z);
+}
+
+VALUE
+rb_str_to_inum(str, base, badcheck)
+ VALUE str;
+ int base;
+ int badcheck;
+{
+ char *s;
+ long len;
+
+ StringValue(str);
+ if (badcheck) {
+ s = StringValueCStr(str);
+ }
+ else {
+ s = RSTRING(str)->ptr;
+ }
+ if (s) {
+ len = RSTRING(str)->len;
+ if (s[len]) { /* no sentinel somehow */
+ char *p = ALLOCA_N(char, len+1);
+
+ MEMCPY(p, s, char, len);
+ p[len] = '\0';
+ s = p;
+ }
+ }
+ return rb_cstr_to_inum(s, base, badcheck);
+}
+
+#if HAVE_LONG_LONG
+
+VALUE
+rb_ull2big(n)
+ unsigned LONG_LONG n;
+{
+ BDIGIT_DBL num = n;
+ long i = 0;
+ BDIGIT *digits;
+ VALUE big;
+
+ big = bignew(DIGSPERLL, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLL) {
+ digits[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+
+ i = DIGSPERLL;
+ while (i-- && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+ return big;
+}
+
+VALUE
+rb_ll2big(n)
+ LONG_LONG n;
+{
+ long neg = 0;
+ VALUE big;
+
+ if (n < 0) {
+ n = -n;
+ neg = 1;
+ }
+ big = rb_ull2big(n);
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return big;
+}
+
+VALUE
+rb_ull2inum(n)
+ unsigned LONG_LONG n;
+{
+ if (POSFIXABLE(n)) return LONG2FIX(n);
+ return rb_ull2big(n);
+}
+
+VALUE
+rb_ll2inum(n)
+ LONG_LONG n;
+{
+ if (FIXABLE(n)) return LONG2FIX(n);
+ return rb_ll2big(n);
+}
+
+#endif /* HAVE_LONG_LONG */
+
+VALUE
+rb_cstr2inum(str, base)
+ const char *str;
+ int base;
+{
+ return rb_cstr_to_inum(str, base, base==0);
+}
+
+VALUE
+rb_str2inum(str, base)
+ VALUE str;
+ int base;
+{
+ return rb_str_to_inum(str, base, base==0);
+}
+
+const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+VALUE
+rb_big2str0(x, base, trim)
+ VALUE x;
+ int base;
+ int trim;
+{
+ volatile VALUE t;
+ BDIGIT *ds;
+ long i, j, hbase;
+ VALUE ss;
+ char *s;
+
+ if (FIXNUM_P(x)) {
+ return rb_fix2str(x, base);
+ }
+ i = RBIGNUM(x)->len;
+ if (BIGZEROP(x)) {
+ return rb_str_new2("0");
+ }
+ if (i >= LONG_MAX/SIZEOF_BDIGITS/CHAR_BIT) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
+ }
+ j = SIZEOF_BDIGITS*CHAR_BIT*i;
+ switch (base) {
+ case 2: break;
+ case 3:
+ j = j * 53L / 84 + 1;
+ break;
+ case 4: case 5: case 6: case 7:
+ j = (j + 1) / 2;
+ break;
+ case 8: case 9:
+ j = (j + 2) / 3;
+ break;
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ j = j * 28L / 93 + 1;
+ break;
+ case 16: case 17: case 18: case 19: case 20: case 21:
+ case 22: case 23: case 24: case 25: case 26: case 27:
+ case 28: case 29: case 30: case 31:
+ j = (j + 3) / 4;
+ break;
+ case 32: case 33: case 34: case 35: case 36:
+ j = (j + 4) / 5;
+ break;
+ default:
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ break;
+ }
+ j++; /* space for sign */
+
+ hbase = base * base;
+#if SIZEOF_BDIGITS > 2
+ hbase *= hbase;
+#endif
+
+ t = rb_big_clone(x);
+ ds = BDIGITS(t);
+ ss = rb_str_new(0, j+1);
+ s = RSTRING(ss)->ptr;
+
+ s[0] = RBIGNUM(x)->sign ? '+' : '-';
+ TRAP_BEG;
+ while (i && j > 1) {
+ long k = i;
+ BDIGIT_DBL num = 0;
+
+ while (k--) {
+ num = BIGUP(num) + ds[k];
+ ds[k] = (BDIGIT)(num / hbase);
+ num %= hbase;
+ }
+ if (trim && ds[i-1] == 0) i--;
+ k = SIZEOF_BDIGITS;
+ while (k--) {
+ s[--j] = ruby_digitmap[num % base];
+ num /= base;
+ if (!trim && j <= 1) break;
+ if (trim && i == 0 && num == 0) break;
+ }
+ }
+ if (trim) {while (s[j] == '0') j++;}
+ i = RSTRING(ss)->len - j;
+ if (RBIGNUM(x)->sign) {
+ memmove(s, s+j, i);
+ RSTRING(ss)->len = i-1;
+ }
+ else {
+ memmove(s+1, s+j, i);
+ RSTRING(ss)->len = i;
+ }
+ s[RSTRING(ss)->len] = '\0';
+ TRAP_END;
+
+ return ss;
+}
+
+VALUE
+rb_big2str(VALUE x, int base)
+{
+ return rb_big2str0(x, base, Qtrue);
+}
+
+/*
+ * call-seq:
+ * big.to_s(base=10) => string
+ *
+ * Returns a string containing the representation of <i>big</i> radix
+ * <i>base</i> (2 through 36).
+ *
+ * 12345654321.to_s #=> "12345654321"
+ * 12345654321.to_s(2) #=> "1011011111110110111011110000110001"
+ * 12345654321.to_s(8) #=> "133766736061"
+ * 12345654321.to_s(16) #=> "2dfdbbc31"
+ * 78546939656932.to_s(36) #=> "rubyrules"
+ */
+
+static VALUE
+rb_big_to_s(argc, argv, x)
+ int argc;
+ VALUE *argv;
+ VALUE x;
+{
+ VALUE b;
+ int base;
+
+ rb_scan_args(argc, argv, "01", &b);
+ if (argc == 0) base = 10;
+ else base = NUM2INT(b);
+ return rb_big2str(x, base);
+}
+
+static unsigned long
+big2ulong(x, type)
+ VALUE x;
+ char *type;
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT_DBL num;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG/SIZEOF_BDIGITS)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ ds = BDIGITS(x);
+ num = 0;
+ while (len--) {
+ num = BIGUP(num);
+ num += ds[len];
+ }
+ return num;
+}
+
+unsigned long
+rb_big2ulong_pack(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "unsigned long");
+ if (!RBIGNUM(x)->sign) {
+ return -num;
+ }
+ return num;
+}
+
+unsigned long
+rb_big2ulong(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "unsigned long");
+
+ if (!RBIGNUM(x)->sign) {
+ if ((long)num < 0) {
+ rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
+ }
+ return -num;
+ }
+ return num;
+}
+
+long
+rb_big2long(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "long");
+
+ if ((long)num < 0 && (RBIGNUM(x)->sign || (long)num != LONG_MIN)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
+ }
+ if (!RBIGNUM(x)->sign) return -(long)num;
+ return num;
+}
+
+#if HAVE_LONG_LONG
+
+static unsigned LONG_LONG
+big2ull(x, type)
+ VALUE x;
+ char *type;
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT_DBL num;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ ds = BDIGITS(x);
+ num = 0;
+ while (len--) {
+ num = BIGUP(num);
+ num += ds[len];
+ }
+ return num;
+}
+
+unsigned LONG_LONG
+rb_big2ull(x)
+ VALUE x;
+{
+ unsigned LONG_LONG num = big2ull(x, "unsigned long long");
+
+ if (!RBIGNUM(x)->sign) return -num;
+ return num;
+}
+
+LONG_LONG
+rb_big2ll(x)
+ VALUE x;
+{
+ unsigned LONG_LONG num = big2ull(x, "long long");
+
+ if ((LONG_LONG)num < 0 && (RBIGNUM(x)->sign
+ || (LONG_LONG)num != LLONG_MIN)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
+ }
+ if (!RBIGNUM(x)->sign) return -(LONG_LONG)num;
+ return num;
+}
+
+#endif /* HAVE_LONG_LONG */
+
+static VALUE
+dbl2big(d)
+ double d;
+{
+ long i = 0;
+ BDIGIT c;
+ BDIGIT *digits;
+ VALUE z;
+ double u = (d < 0)?-d:d;
+
+ if (isinf(d)) {
+ rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
+ }
+ if (isnan(d)) {
+ rb_raise(rb_eFloatDomainError, "NaN");
+ }
+
+ while (!POSFIXABLE(u) || 0 != (long)u) {
+ u /= (double)(BIGRAD);
+ i++;
+ }
+ z = bignew(i, d>=0);
+ digits = BDIGITS(z);
+ while (i--) {
+ u *= BIGRAD;
+ c = (BDIGIT)u;
+ u -= c;
+ digits[i] = c;
+ }
+
+ return z;
+}
+
+VALUE
+rb_dbl2big(d)
+ double d;
+{
+ return bignorm(dbl2big(d));
+}
+
+double
+rb_big2dbl(x)
+ VALUE x;
+{
+ double d = 0.0;
+ long i = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+
+ while (i--) {
+ d = ds[i] + BIGRAD*d;
+ }
+ if (isinf(d)) {
+ rb_warn("Bignum out of Float range");
+ d = HUGE_VAL;
+ }
+ if (!RBIGNUM(x)->sign) d = -d;
+ return d;
+}
+
+/*
+ * call-seq:
+ * big.to_f -> float
+ *
+ * Converts <i>big</i> to a <code>Float</code>. If <i>big</i> doesn't
+ * fit in a <code>Float</code>, the result is infinity.
+ *
+ */
+
+static VALUE
+rb_big_to_f(x)
+ VALUE x;
+{
+ return rb_float_new(rb_big2dbl(x));
+}
+
+/*
+ * call-seq:
+ * big <=> numeric => -1, 0, +1
+ *
+ * Comparison---Returns -1, 0, or +1 depending on whether <i>big</i> is
+ * less than, equal to, or greater than <i>numeric</i>. This is the
+ * basis for the tests in <code>Comparable</code>.
+ *
+ */
+
+static VALUE
+rb_big_cmp(x, y)
+ VALUE x, y;
+{
+ long xlen = RBIGNUM(x)->len;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ {
+ double a = RFLOAT(y)->value;
+
+ if (isinf(a)) {
+ if (a > 0.0) return INT2FIX(-1);
+ else return INT2FIX(1);
+ }
+ return rb_dbl_cmp(rb_big2dbl(x), a);
+ }
+
+ default:
+ return rb_num_coerce_cmp(x, y);
+ }
+
+ if (RBIGNUM(x)->sign > RBIGNUM(y)->sign) return INT2FIX(1);
+ if (RBIGNUM(x)->sign < RBIGNUM(y)->sign) return INT2FIX(-1);
+ if (xlen < RBIGNUM(y)->len)
+ return (RBIGNUM(x)->sign) ? INT2FIX(-1) : INT2FIX(1);
+ if (xlen > RBIGNUM(y)->len)
+ return (RBIGNUM(x)->sign) ? INT2FIX(1) : INT2FIX(-1);
+
+ while(xlen-- && (BDIGITS(x)[xlen]==BDIGITS(y)[xlen]));
+ if (-1 == xlen) return INT2FIX(0);
+ return (BDIGITS(x)[xlen] > BDIGITS(y)[xlen]) ?
+ (RBIGNUM(x)->sign ? INT2FIX(1) : INT2FIX(-1)) :
+ (RBIGNUM(x)->sign ? INT2FIX(-1) : INT2FIX(1));
+}
+
+/*
+ * call-seq:
+ * big == obj => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> has the same value
+ * as <i>big</i>. Contrast this with <code>Bignum#eql?</code>, which
+ * requires <i>obj</i> to be a <code>Bignum</code>.
+ *
+ * 68719476736 == 68719476736.0 #=> true
+ */
+
+static VALUE
+rb_big_eq(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+ case T_BIGNUM:
+ break;
+ case T_FLOAT:
+ {
+ volatile double a, b;
+
+ a = RFLOAT(y)->value;
+ if (isnan(a)) return Qfalse;
+ b = rb_big2dbl(x);
+ return (a == b)?Qtrue:Qfalse;
+ }
+ default:
+ return rb_equal(y, x);
+ }
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign) return Qfalse;
+ if (RBIGNUM(x)->len != RBIGNUM(y)->len) return Qfalse;
+ if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM(y)->len) != 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * big.eql?(obj) => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a
+ * <code>Bignum</code> with the same value as <i>big</i>. Contrast this
+ * with <code>Bignum#==</code>, which performs type conversions.
+ *
+ * 68719476736.eql?(68719476736.0) #=> false
+ */
+
+static VALUE
+rb_big_eql(x, y)
+ VALUE x, y;
+{
+ if (TYPE(y) != T_BIGNUM) return Qfalse;
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign) return Qfalse;
+ if (RBIGNUM(x)->len != RBIGNUM(y)->len) return Qfalse;
+ if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM(y)->len) != 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * -big => other_big
+ *
+ * Unary minus (returns a new Bignum whose value is 0-big)
+ */
+
+static VALUE
+rb_big_uminus(x)
+ VALUE x;
+{
+ VALUE z = rb_big_clone(x);
+
+ RBIGNUM(z)->sign = !RBIGNUM(x)->sign;
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * ~big => integer
+ *
+ * Inverts the bits in big. As Bignums are conceptually infinite
+ * length, the result acts as if it had an infinite number of one
+ * bits to the left. In hex representations, this is displayed
+ * as two periods to the left of the digits.
+ *
+ * sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
+ */
+
+static VALUE
+rb_big_neg(x)
+ VALUE x;
+{
+ VALUE z = rb_big_clone(x);
+ long i;
+ BDIGIT *ds;
+
+ if (!RBIGNUM(x)->sign) get2comp(z);
+ ds = BDIGITS(z);
+ i = RBIGNUM(x)->len;
+ if (!i) return INT2FIX(~0);
+ while (i--) ds[i] = ~ds[i];
+ RBIGNUM(z)->sign = !RBIGNUM(z)->sign;
+ if (RBIGNUM(x)->sign) get2comp(z);
+
+ return bignorm(z);
+}
+
+static VALUE
+bigsub(x, y)
+ VALUE x, y;
+{
+ VALUE z = 0;
+ BDIGIT *zds;
+ BDIGIT_DBL_SIGNED num;
+ long i = RBIGNUM(x)->len;
+
+ /* if x is larger than y, swap */
+ if (RBIGNUM(x)->len < RBIGNUM(y)->len) {
+ z = x; x = y; y = z; /* swap x y */
+ }
+ else if (RBIGNUM(x)->len == RBIGNUM(y)->len) {
+ while (i > 0) {
+ i--;
+ if (BDIGITS(x)[i] > BDIGITS(y)[i]) {
+ break;
+ }
+ if (BDIGITS(x)[i] < BDIGITS(y)[i]) {
+ z = x; x = y; y = z; /* swap x y */
+ break;
+ }
+ }
+ }
+
+ z = bignew(RBIGNUM(x)->len, z==0);
+ zds = BDIGITS(z);
+
+ for (i = 0, num = 0; i < RBIGNUM(y)->len; i++) {
+ num += (BDIGIT_DBL_SIGNED)BDIGITS(x)[i] - BDIGITS(y)[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (num && i < RBIGNUM(x)->len) {
+ num += BDIGITS(x)[i];
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (i < RBIGNUM(x)->len) {
+ zds[i] = BDIGITS(x)[i];
+ i++;
+ }
+
+ return z;
+}
+
+static VALUE
+bigadd(x, y, sign)
+ VALUE x, y;
+ int sign;
+{
+ VALUE z;
+ BDIGIT_DBL num;
+ long i, len;
+
+ sign = (sign == RBIGNUM(y)->sign);
+ if (RBIGNUM(x)->sign != sign) {
+ if (sign) return bigsub(y, x);
+ return bigsub(x, y);
+ }
+
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ len = RBIGNUM(x)->len + 1;
+ z = x; x = y; y = z;
+ }
+ else {
+ len = RBIGNUM(y)->len + 1;
+ }
+ z = bignew(len, sign);
+
+ len = RBIGNUM(x)->len;
+ for (i = 0, num = 0; i < len; i++) {
+ num += (BDIGIT_DBL)BDIGITS(x)[i] + BDIGITS(y)[i];
+ BDIGITS(z)[i] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ len = RBIGNUM(y)->len;
+ while (num && i < len) {
+ num += BDIGITS(y)[i];
+ BDIGITS(z)[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (i < len) {
+ BDIGITS(z)[i] = BDIGITS(y)[i];
+ i++;
+ }
+ BDIGITS(z)[i] = (BDIGIT)num;
+
+ return z;
+}
+
+/*
+ * call-seq:
+ * big + other => Numeric
+ *
+ * Adds big and other, returning the result.
+ */
+
+VALUE
+rb_big_plus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bignorm(bigadd(x, y, 1));
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) + RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * big - other => Numeric
+ *
+ * Subtracts other from big, returning the result.
+ */
+
+VALUE
+rb_big_minus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bignorm(bigadd(x, y, 0));
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) - RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+VALUE
+rb_big_mul0(x, y)
+ VALUE x, y;
+{
+ long i, j;
+ BDIGIT_DBL n = 0;
+ VALUE z;
+ BDIGIT *zds;
+
+ if (FIXNUM_P(x)) x = rb_int2big(FIX2LONG(x));
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) * RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+
+ j = RBIGNUM(x)->len + RBIGNUM(y)->len + 1;
+ z = bignew(j, RBIGNUM(x)->sign==RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+ while (j--) zds[j] = 0;
+ for (i = 0; i < RBIGNUM(x)->len; i++) {
+ BDIGIT_DBL dd = BDIGITS(x)[i];
+ if (dd == 0) continue;
+ n = 0;
+ for (j = 0; j < RBIGNUM(y)->len; j++) {
+ BDIGIT_DBL ee = n + (BDIGIT_DBL)dd * BDIGITS(y)[j];
+ n = zds[i + j] + ee;
+ if (ee) zds[i + j] = BIGLO(n);
+ n = BIGDN(n);
+ }
+ if (n) {
+ zds[i + j] = n;
+ }
+ }
+
+ return z;
+}
+
+/*
+ * call-seq:
+ * big * other => Numeric
+ *
+ * Multiplies big and other, returning the result.
+ */
+
+VALUE
+rb_big_mul(x, y)
+ VALUE x, y;
+{
+ return bignorm(rb_big_mul0(x, y));
+}
+
+static void
+bigdivrem(x, y, divp, modp)
+ VALUE x, y;
+ VALUE *divp, *modp;
+{
+ long nx = RBIGNUM(x)->len, ny = RBIGNUM(y)->len;
+ long i, j;
+ VALUE yy, z;
+ BDIGIT *xds, *yds, *zds, *tds;
+ BDIGIT_DBL t2;
+ BDIGIT_DBL_SIGNED num;
+ BDIGIT dd, q;
+
+ if (BIGZEROP(y)) rb_num_zerodiv();
+ yds = BDIGITS(y);
+ if (nx < ny || (nx == ny && BDIGITS(x)[nx - 1] < BDIGITS(y)[ny - 1])) {
+ if (divp) *divp = rb_int2big(0);
+ if (modp) *modp = x;
+ return;
+ }
+ xds = BDIGITS(x);
+ if (ny == 1) {
+ dd = yds[0];
+ z = rb_big_clone(x);
+ zds = BDIGITS(z);
+ t2 = 0; i = nx;
+ while (i--) {
+ t2 = BIGUP(t2) + zds[i];
+ zds[i] = (BDIGIT)(t2 / dd);
+ t2 %= dd;
+ }
+ RBIGNUM(z)->sign = RBIGNUM(x)->sign==RBIGNUM(y)->sign;
+ if (modp) {
+ *modp = rb_uint2big((unsigned long)t2);
+ RBIGNUM(*modp)->sign = RBIGNUM(x)->sign;
+ }
+ if (divp) *divp = z;
+ return;
+ }
+ z = bignew(nx==ny?nx+2:nx+1, RBIGNUM(x)->sign==RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+ if (nx==ny) zds[nx+1] = 0;
+ while (!yds[ny-1]) ny--;
+
+ dd = 0;
+ q = yds[ny-1];
+ while ((q & (1<<(BITSPERDIG-1))) == 0) {
+ q <<= 1;
+ dd++;
+ }
+ if (dd) {
+ yy = rb_big_clone(y);
+ tds = BDIGITS(yy);
+ j = 0;
+ t2 = 0;
+ while (j<ny) {
+ t2 += (BDIGIT_DBL)yds[j]<<dd;
+ tds[j++] = BIGLO(t2);
+ t2 = BIGDN(t2);
+ }
+ yds = tds;
+ j = 0;
+ t2 = 0;
+ while (j<nx) {
+ t2 += (BDIGIT_DBL)xds[j]<<dd;
+ zds[j++] = BIGLO(t2);
+ t2 = BIGDN(t2);
+ }
+ zds[j] = (BDIGIT)t2;
+ }
+ else {
+ zds[nx] = 0;
+ j = nx;
+ while (j--) zds[j] = xds[j];
+ }
+
+ j = nx==ny?nx+1:nx;
+ do {
+ if (zds[j] == yds[ny-1]) q = BIGRAD-1;
+ else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
+ if (q) {
+ i = 0; num = 0; t2 = 0;
+ do { /* multiply and subtract */
+ BDIGIT_DBL ee;
+ t2 += (BDIGIT_DBL)yds[i] * q;
+ ee = num - BIGLO(t2);
+ num = (BDIGIT_DBL)zds[j - ny + i] + ee;
+ if (ee) zds[j - ny + i] = BIGLO(num);
+ num = BIGDN(num);
+ t2 = BIGDN(t2);
+ } while (++i < ny);
+ num += zds[j - ny + i] - t2;/* borrow from high digit; don't update */
+ while (num) { /* "add back" required */
+ i = 0; num = 0; q--;
+ do {
+ BDIGIT_DBL ee = num + yds[i];
+ num = (BDIGIT_DBL)zds[j - ny + i] + ee;
+ if (ee) zds[j - ny + i] = BIGLO(num);
+ num = BIGDN(num);
+ } while (++i < ny);
+ num--;
+ }
+ }
+ zds[j] = q;
+ } while (--j >= ny);
+ if (divp) { /* move quotient down in z */
+ *divp = rb_big_clone(z);
+ zds = BDIGITS(*divp);
+ j = (nx==ny ? nx+2 : nx+1) - ny;
+ for (i = 0;i < j;i++) zds[i] = zds[i+ny];
+ RBIGNUM(*divp)->len = i;
+ }
+ if (modp) { /* normalize remainder */
+ *modp = rb_big_clone(z);
+ zds = BDIGITS(*modp);
+ while (--ny && !zds[ny]); ++ny;
+ if (dd) {
+ t2 = 0; i = ny;
+ while(i--) {
+ t2 = (t2 | zds[i]) >> dd;
+ q = zds[i];
+ zds[i] = BIGLO(t2);
+ t2 = BIGUP(q);
+ }
+ }
+ RBIGNUM(*modp)->len = ny;
+ RBIGNUM(*modp)->sign = RBIGNUM(x)->sign;
+ }
+}
+
+static void
+bigdivmod(x, y, divp, modp)
+ VALUE x, y;
+ VALUE *divp, *modp;
+{
+ VALUE mod;
+
+ bigdivrem(x, y, divp, &mod);
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign && !BIGZEROP(mod)) {
+ if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
+ if (modp) *modp = bigadd(mod, y, 1);
+ }
+ else {
+ if (divp) *divp = *divp;
+ if (modp) *modp = mod;
+ }
+}
+
+/*
+ * call-seq:
+ * big / other => Numeric
+ * big.div(other) => Numeric
+ *
+ * Divides big by other, returning the result.
+ */
+
+static VALUE
+rb_big_div(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) / RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, &z, 0);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big % other => Numeric
+ * big.modulo(other) => Numeric
+ *
+ * Returns big modulo other. See Numeric.divmod for more
+ * information.
+ */
+
+static VALUE
+rb_big_modulo(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, 0, &z);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big.remainder(numeric) => number
+ *
+ * Returns the remainder after dividing <i>big</i> by <i>numeric</i>.
+ *
+ * -1234567890987654321.remainder(13731) #=> -6966
+ * -1234567890987654321.remainder(13731.24) #=> -9906.22531493148
+ */
+static VALUE
+rb_big_remainder(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivrem(x, y, 0, &z);
+
+ return bignorm(z);
+}
+
+static VALUE big_lshift _((VALUE, unsigned long));
+static VALUE big_rshift _((VALUE, unsigned long));
+
+static VALUE big_shift(x, n)
+ VALUE x;
+ int n;
+{
+ if (n < 0)
+ return big_lshift(x, (unsigned int)n);
+ else if (n > 0)
+ return big_rshift(x, (unsigned int)n);
+ return x;
+}
+
+/*
+ * call-seq:
+ * big.divmod(numeric) => array
+ *
+ * See <code>Numeric#divmod</code>.
+ *
+ */
+VALUE
+rb_big_divmod(x, y)
+ VALUE x, y;
+{
+ VALUE div, mod;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, &div, &mod);
+
+ return rb_assoc_new(bignorm(div), bignorm(mod));
+}
+
+/*
+ * call-seq:
+ * big.quo(numeric) -> float
+ *
+ * Returns the floating point result of dividing <i>big</i> by
+ * <i>numeric</i>.
+ *
+ * -1234567890987654321.quo(13731) #=> -89910996357705.5
+ * -1234567890987654321.quo(13731.24) #=> -89909424858035.7
+ *
+ */
+
+static VALUE
+rb_big_quo(x, y)
+ VALUE x, y;
+{
+ double dx = rb_big2dbl(x);
+ double dy;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ dy = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ dy = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ dy = RFLOAT(y)->value;
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ return rb_float_new(dx / dy);
+}
+
+/*
+ * call-seq:
+ * big ** exponent #=> numeric
+ *
+ * Raises _big_ to the _exponent_ power (which may be an integer, float,
+ * or anything that will coerce to a number). The result may be
+ * a Fixnum, Bignum, or Float
+ *
+ * 123456789 ** 2 #=> 15241578750190521
+ * 123456789 ** 1.2 #=> 5126464716.09932
+ * 123456789 ** -2 #=> 6.5610001194102e-17
+ */
+
+VALUE
+rb_big_pow(x, y)
+ VALUE x, y;
+{
+ double d;
+ long yy;
+
+ if (y == INT2FIX(0)) return INT2FIX(1);
+ switch (TYPE(y)) {
+ case T_FLOAT:
+ d = RFLOAT(y)->value;
+ break;
+
+ case T_BIGNUM:
+ rb_warn("in a**b, b may be too big");
+ d = rb_big2dbl(y);
+ break;
+
+ case T_FIXNUM:
+ yy = FIX2LONG(y);
+ if (yy > 0) {
+ VALUE z = x;
+ const long BIGLEN_LIMIT = 1024*1024 / SIZEOF_BDIGITS;
+
+ if ((RBIGNUM(x)->len > BIGLEN_LIMIT) ||
+ (RBIGNUM(x)->len > BIGLEN_LIMIT / yy)) {
+ rb_warn("in a**b, b may be too big");
+ d = (double)yy;
+ break;
+ }
+ for (;;) {
+ yy -= 1;
+ if (yy == 0) break;
+ while (yy % 2 == 0) {
+ yy /= 2;
+ x = rb_big_mul0(x, x);
+ bigtrunc(x);
+ }
+ z = rb_big_mul0(z, x);
+ bigtrunc(z);
+ }
+ return bignorm(z);
+ }
+ d = (double)yy;
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ return rb_float_new(pow(rb_big2dbl(x), d));
+}
+
+/*
+ * call-seq:
+ * big & numeric => integer
+ *
+ * Performs bitwise +and+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_and(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y, z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ z = bignew(l2, RBIGNUM(x)->sign || RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] & ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?0:ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z);
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big | numeric => integer
+ *
+ * Performs bitwise +or+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_or(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y, z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ z = bignew(l2, RBIGNUM(x)->sign && RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] | ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:(BIGRAD-1);
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big ^ numeric => integer
+ *
+ * Performs bitwise +exclusive or+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_xor(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y;
+ VALUE z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ RBIGNUM(x)->sign = RBIGNUM(x)->sign?1:0;
+ RBIGNUM(y)->sign = RBIGNUM(y)->sign?1:0;
+ z = bignew(l2, !(RBIGNUM(x)->sign ^ RBIGNUM(y)->sign));
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] ^ ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:~ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z);
+
+ return bignorm(z);
+}
+
+static VALUE
+check_shiftdown(VALUE y, VALUE x)
+{
+ if (!RBIGNUM(x)->len) return INT2FIX(0);
+ if (RBIGNUM(y)->len > SIZEOF_LONG / SIZEOF_BDIGITS) {
+ return RBIGNUM(x)->sign ? INT2FIX(0) : INT2FIX(-1);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * big << numeric => integer
+ *
+ * Shifts big left _numeric_ positions (right if _numeric_ is negative).
+ */
+
+VALUE
+rb_big_lshift(x, y)
+ VALUE x, y;
+{
+ long shift;
+ int neg = 0;
+
+ for (;;) {
+ if (FIXNUM_P(y)) {
+ shift = FIX2LONG(y);
+ if (shift < 0) {
+ neg = 1;
+ shift = -shift;
+ }
+ break;
+ }
+ else if (TYPE(y) == T_BIGNUM) {
+ if (!RBIGNUM(y)->sign) {
+ VALUE t = check_shiftdown(y, x);
+ if (!NIL_P(t)) return t;
+ neg = 1;
+ }
+ shift = big2ulong(y, "long", Qtrue);
+ break;
+ }
+ y = rb_to_int(y);
+ }
+
+ if (neg) return big_rshift(x, shift);
+ return big_lshift(x, shift);
+}
+
+static VALUE
+big_lshift(x, shift)
+ VALUE x;
+ unsigned long shift;
+{
+ BDIGIT *xds, *zds;
+ long s1 = shift/BITSPERDIG;
+ int s2 = shift%BITSPERDIG;
+ VALUE z;
+ BDIGIT_DBL num = 0;
+ long len, i;
+
+ len = RBIGNUM(x)->len;
+ z = bignew(len+s1+1, RBIGNUM(x)->sign);
+ zds = BDIGITS(z);
+ for (i=0; i<s1; i++) {
+ *zds++ = 0;
+ }
+ xds = BDIGITS(x);
+ for (i=0; i<len; i++) {
+ num = num | (BDIGIT_DBL)*xds++<<s2;
+ *zds++ = BIGLO(num);
+ num = BIGDN(num);
+ }
+ *zds = BIGLO(num);
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big >> numeric => integer
+ *
+ * Shifts big right _numeric_ positions (left if _numeric_ is negative).
+ */
+
+VALUE
+rb_big_rshift(x, y)
+ VALUE x, y;
+{
+ long shift;
+ int neg = 0;
+
+ for (;;) {
+ if (FIXNUM_P(y)) {
+ shift = FIX2LONG(y);
+ if (shift < 0) {
+ neg = 1;
+ shift = -shift;
+ }
+ break;
+ }
+ else if (TYPE(y) == T_BIGNUM) {
+ if (RBIGNUM(y)->sign) {
+ VALUE t = check_shiftdown(y, x);
+ if (!NIL_P(t)) return t;
+ }
+ else {
+ neg = 1;
+ }
+ shift = big2ulong(y, "long", Qtrue);
+ break;
+ }
+ y = rb_to_int(y);
+ }
+
+ if (neg) return big_lshift(x, shift);
+ return big_rshift(x, shift);
+}
+
+static VALUE
+big_rshift(x, shift)
+ VALUE x;
+ unsigned long shift;
+{
+ BDIGIT *xds, *zds;
+ long s1 = shift/BITSPERDIG;
+ int s2 = shift%BITSPERDIG;
+ VALUE z;
+ BDIGIT_DBL num = 0;
+ long i, j;
+ volatile VALUE save_x;
+
+ if (s1 > RBIGNUM(x)->len) {
+ if (RBIGNUM(x)->sign)
+ return INT2FIX(0);
+ else
+ return INT2FIX(-1);
+ }
+ if (!RBIGNUM(x)->sign) {
+ save_x = x = rb_big_clone(x);
+ get2comp(x);
+ }
+ xds = BDIGITS(x);
+ i = RBIGNUM(x)->len; j = i - s1;
+ if (j == 0) {
+ if (RBIGNUM(x)->sign) return INT2FIX(0);
+ else return INT2FIX(-1);
+ }
+ z = bignew(j, RBIGNUM(x)->sign);
+ if (!RBIGNUM(x)->sign) {
+ num = ((BDIGIT_DBL)~0) << BITSPERDIG;
+ }
+ zds = BDIGITS(z);
+ while (i--, j--) {
+ num = (num | xds[i]) >> s2;
+ zds[j] = BIGLO(num);
+ num = BIGUP(xds[i]);
+ }
+ if (!RBIGNUM(x)->sign) {
+ get2comp(z);
+ }
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big[n] -> 0, 1
+ *
+ * Bit Reference---Returns the <em>n</em>th bit in the (assumed) binary
+ * representation of <i>big</i>, where <i>big</i>[0] is the least
+ * significant bit.
+ *
+ * a = 9**15
+ * 50.downto(0) do |n|
+ * print a[n]
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 000101110110100000111000011110010100111100010111001
+ *
+ */
+
+static VALUE
+rb_big_aref(x, y)
+ VALUE x, y;
+{
+ BDIGIT *xds;
+ BDIGIT_DBL num;
+ unsigned long shift;
+ long i, s1, s2;
+
+ if (TYPE(y) == T_BIGNUM) {
+ if (!RBIGNUM(y)->sign)
+ return INT2FIX(0);
+ if (RBIGNUM(bigtrunc(y))->len > SIZEOF_LONG/SIZEOF_BDIGITS) {
+ out_of_range:
+ return RBIGNUM(x)->sign ? INT2FIX(0) : INT2FIX(1);
+ }
+ shift = big2ulong(y, "long", Qfalse);
+ }
+ else {
+ i = NUM2LONG(y);
+ if (i < 0) return INT2FIX(0);
+ shift = (VALUE)i;
+ }
+ s1 = shift/BITSPERDIG;
+ s2 = shift%BITSPERDIG;
+
+ if (s1 >= RBIGNUM(x)->len) goto out_of_range;
+ if (!RBIGNUM(x)->sign) {
+ xds = BDIGITS(x);
+ i = 0; num = 1;
+ while (num += ~xds[i], ++i <= s1) {
+ num = BIGDN(num);
+ }
+ }
+ else {
+ num = BDIGITS(x)[s1];
+ }
+ if (num & ((BDIGIT_DBL)1<<s2))
+ return INT2FIX(1);
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * big.hash => fixnum
+ *
+ * Compute a hash based on the value of _big_.
+ */
+
+static VALUE
+rb_big_hash(x)
+ VALUE x;
+{
+ long i, len, key;
+ BDIGIT *digits;
+
+ key = 0; digits = BDIGITS(x); len = RBIGNUM(x)->len;
+ for (i=0; i<len; i++) {
+ key ^= *digits++;
+ }
+ return LONG2FIX(key);
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+rb_big_coerce(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ return rb_assoc_new(rb_int2big(FIX2LONG(y)), x);
+ }
+ else if (TYPE(y) == T_BIGNUM) {
+ return rb_assoc_new(y, x);
+ }
+ else {
+ rb_raise(rb_eTypeError, "can't coerce %s to Bignum",
+ rb_obj_classname(y));
+ }
+ /* not reached */
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * big.abs -> aBignum
+ *
+ * Returns the absolute value of <i>big</i>.
+ *
+ * -1234567890987654321.abs #=> 1234567890987654321
+ */
+
+static VALUE
+rb_big_abs(x)
+ VALUE x;
+{
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ RBIGNUM(x)->sign = 1;
+ }
+ return x;
+}
+
+VALUE
+rb_big_rand(max, rand_buf)
+ VALUE max;
+ double *rand_buf;
+{
+ VALUE v;
+ long len = RBIGNUM(max)->len;
+
+ if (BIGZEROP(max)) {
+ return rb_float_new(rand_buf[0]);
+ }
+ v = bignew(len,1);
+ len--;
+ BDIGITS(v)[len] = BDIGITS(max)[len] * rand_buf[len];
+ while (len--) {
+ BDIGITS(v)[len] = ((BDIGIT)~0) * rand_buf[len];
+ }
+
+ return v;
+}
+
+/*
+ * call-seq:
+ * big.size -> integer
+ *
+ * Returns the number of bytes in the machine representation of
+ * <i>big</i>.
+ *
+ * (256**10 - 1).size #=> 12
+ * (256**20 - 1).size #=> 20
+ * (256**40 - 1).size #=> 40
+ */
+
+static VALUE
+rb_big_size(big)
+ VALUE big;
+{
+ return LONG2FIX(RBIGNUM(big)->len*SIZEOF_BDIGITS);
+}
+
+/*
+ * Bignum objects hold integers outside the range of
+ * Fixnum. Bignum objects are created
+ * automatically when integer calculations would otherwise overflow a
+ * Fixnum. When a calculation involving
+ * Bignum objects returns a result that will fit in a
+ * Fixnum, the result is automatically converted.
+ *
+ * For the purposes of the bitwise operations and <code>[]</code>, a
+ * Bignum is treated as if it were an infinite-length
+ * bitstring with 2's complement representation.
+ *
+ * While Fixnum values are immediate, Bignum
+ * objects are not---assignment and parameter passing work with
+ * references to objects, not the objects themselves.
+ *
+ */
+
+void
+Init_Bignum()
+{
+ rb_cBignum = rb_define_class("Bignum", rb_cInteger);
+
+ rb_define_method(rb_cBignum, "to_s", rb_big_to_s, -1);
+ rb_define_method(rb_cBignum, "coerce", rb_big_coerce, 1);
+ rb_define_method(rb_cBignum, "-@", rb_big_uminus, 0);
+ rb_define_method(rb_cBignum, "+", rb_big_plus, 1);
+ rb_define_method(rb_cBignum, "-", rb_big_minus, 1);
+ rb_define_method(rb_cBignum, "*", rb_big_mul, 1);
+ rb_define_method(rb_cBignum, "/", rb_big_div, 1);
+ rb_define_method(rb_cBignum, "%", rb_big_modulo, 1);
+ rb_define_method(rb_cBignum, "div", rb_big_div, 1);
+ rb_define_method(rb_cBignum, "divmod", rb_big_divmod, 1);
+ rb_define_method(rb_cBignum, "modulo", rb_big_modulo, 1);
+ rb_define_method(rb_cBignum, "remainder", rb_big_remainder, 1);
+ rb_define_method(rb_cBignum, "quo", rb_big_quo, 1);
+ rb_define_method(rb_cBignum, "**", rb_big_pow, 1);
+ rb_define_method(rb_cBignum, "&", rb_big_and, 1);
+ rb_define_method(rb_cBignum, "|", rb_big_or, 1);
+ rb_define_method(rb_cBignum, "^", rb_big_xor, 1);
+ rb_define_method(rb_cBignum, "~", rb_big_neg, 0);
+ rb_define_method(rb_cBignum, "<<", rb_big_lshift, 1);
+ rb_define_method(rb_cBignum, ">>", rb_big_rshift, 1);
+ rb_define_method(rb_cBignum, "[]", rb_big_aref, 1);
+
+ rb_define_method(rb_cBignum, "<=>", rb_big_cmp, 1);
+ rb_define_method(rb_cBignum, "==", rb_big_eq, 1);
+ rb_define_method(rb_cBignum, "eql?", rb_big_eql, 1);
+ rb_define_method(rb_cBignum, "hash", rb_big_hash, 0);
+ rb_define_method(rb_cBignum, "to_f", rb_big_to_f, 0);
+ rb_define_method(rb_cBignum, "abs", rb_big_abs, 0);
+ rb_define_method(rb_cBignum, "size", rb_big_size, 0);
+}
+/**********************************************************************
+
+ class.c -
+
+ $Author: shyouhei $
+ $Date: 2009-01-16 02:58:45 +0100 (Fri, 16 Jan 2009) $
+ created at: Tue Aug 10 15:05:44 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+#include "node.h"
+#include "st.h"
+#include <ctype.h>
+
+extern st_table *rb_class_tbl;
+
+VALUE
+rb_class_boot(super)
+ VALUE super;
+{
+ NEWOBJ(klass, struct RClass);
+ OBJSETUP(klass, rb_cClass, T_CLASS);
+
+ klass->super = super;
+ klass->iv_tbl = 0;
+ klass->m_tbl = 0; /* safe GC */
+ klass->m_tbl = st_init_numtable();
+
+ OBJ_INFECT(klass, super);
+ return (VALUE)klass;
+}
+
+VALUE
+rb_class_new(super)
+ VALUE super;
+{
+ Check_Type(super, T_CLASS);
+ if (super == rb_cClass) {
+ rb_raise(rb_eTypeError, "can't make subclass of Class");
+ }
+ if (FL_TEST(super, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "can't make subclass of virtual class");
+ }
+ return rb_class_boot(super);
+}
+
+struct clone_method_data {
+ st_table *tbl;
+ VALUE klass;
+};
+
+static int
+clone_method(mid, body, data)
+ ID mid;
+ NODE *body;
+ struct clone_method_data *data;
+{
+ NODE *fbody = body->nd_body;
+
+ if (fbody && nd_type(fbody) == NODE_SCOPE) {
+ NODE *cref = (NODE*)fbody->nd_rval;
+
+ if (cref) cref = cref->nd_next;
+ fbody = rb_copy_node_scope(fbody, NEW_CREF(data->klass, cref));
+ }
+ st_insert(data->tbl, mid, (st_data_t)NEW_METHOD(fbody, body->nd_noex));
+ return ST_CONTINUE;
+}
+
+/* :nodoc: */
+VALUE
+rb_mod_init_copy(clone, orig)
+ VALUE clone, orig;
+{
+ rb_obj_init_copy(clone, orig);
+ if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
+ RBASIC(clone)->klass = RBASIC(orig)->klass;
+ RBASIC(clone)->klass = rb_singleton_class_clone(clone);
+ }
+ RCLASS(clone)->super = RCLASS(orig)->super;
+ if (RCLASS(orig)->iv_tbl) {
+ ID id;
+
+ RCLASS(clone)->iv_tbl = st_copy(RCLASS(orig)->iv_tbl);
+ id = rb_intern("__classpath__");
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
+ id = rb_intern("__classid__");
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
+ }
+ if (RCLASS(orig)->m_tbl) {
+ struct clone_method_data data;
+
+ data.tbl = RCLASS(clone)->m_tbl = st_init_numtable();
+ data.klass = (VALUE)clone;
+
+ st_foreach(RCLASS(orig)->m_tbl, clone_method, (st_data_t)&data);
+ }
+
+ return clone;
+}
+
+/* :nodoc: */
+VALUE
+rb_class_init_copy(clone, orig)
+ VALUE clone, orig;
+{
+ if (RCLASS(clone)->super != 0) {
+ rb_raise(rb_eTypeError, "already initialized class");
+ }
+ if (FL_TEST(orig, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "can't copy singleton class");
+ }
+ return rb_mod_init_copy(clone, orig);
+}
+
+VALUE
+rb_singleton_class_clone(obj)
+ VALUE obj;
+{
+ VALUE klass = RBASIC(obj)->klass;
+
+ if (!FL_TEST(klass, FL_SINGLETON))
+ return klass;
+ else {
+ /* copy singleton(unnamed) class */
+ NEWOBJ(clone, struct RClass);
+ OBJSETUP(clone, 0, RBASIC(klass)->flags);
+
+ if (BUILTIN_TYPE(obj) == T_CLASS) {
+ RBASIC(clone)->klass = (VALUE)clone;
+ }
+ else {
+ RBASIC(clone)->klass = rb_singleton_class_clone(klass);
+ }
+
+ clone->super = RCLASS(klass)->super;
+ clone->iv_tbl = 0;
+ clone->m_tbl = 0;
+ if (RCLASS(klass)->iv_tbl) {
+ clone->iv_tbl = st_copy(RCLASS(klass)->iv_tbl);
+ }
+ {
+ struct clone_method_data data;
+
+ data.tbl = clone->m_tbl = st_init_numtable();
+ switch (TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ data.klass = obj;
+ break;
+ default:
+ data.klass = 0;
+ break;
+ }
+
+ st_foreach(RCLASS(klass)->m_tbl, clone_method, (st_data_t)&data);
+ }
+ rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
+ FL_SET(clone, FL_SINGLETON);
+ return (VALUE)clone;
+ }
+}
+
+void
+rb_singleton_class_attached(klass, obj)
+ VALUE klass, obj;
+{
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ if (!RCLASS(klass)->iv_tbl) {
+ RCLASS(klass)->iv_tbl = st_init_numtable();
+ }
+ st_insert(RCLASS(klass)->iv_tbl, rb_intern("__attached__"), obj);
+ }
+}
+
+VALUE
+rb_make_metaclass(obj, super)
+ VALUE obj, super;
+{
+ VALUE klass = rb_class_boot(super);
+ FL_SET(klass, FL_SINGLETON);
+ RBASIC(obj)->klass = klass;
+ rb_singleton_class_attached(klass, obj);
+ if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) {
+ RBASIC(klass)->klass = klass;
+ RCLASS(klass)->super = RBASIC(rb_class_real(RCLASS(obj)->super))->klass;
+ }
+ else {
+ VALUE metasuper = RBASIC(rb_class_real(super))->klass;
+
+ /* metaclass of a superclass may be NULL at boot time */
+ if (metasuper) {
+ RBASIC(klass)->klass = metasuper;
+ }
+ }
+
+ return klass;
+}
+
+VALUE
+rb_define_class_id(id, super)
+ ID id;
+ VALUE super;
+{
+ VALUE klass;
+
+ if (!super) super = rb_cObject;
+ klass = rb_class_new(super);
+ rb_make_metaclass(klass, RBASIC(super)->klass);
+
+ return klass;
+}
+
+void
+rb_check_inheritable(super)
+ VALUE super;
+{
+ if (TYPE(super) != T_CLASS) {
+ rb_raise(rb_eTypeError, "superclass must be a Class (%s given)",
+ rb_obj_classname(super));
+ }
+ if (RBASIC(super)->flags & FL_SINGLETON) {
+ rb_raise(rb_eTypeError, "can't make subclass of virtual class");
+ }
+}
+
+VALUE
+rb_class_inherited(super, klass)
+ VALUE super, klass;
+{
+ if (!super) super = rb_cObject;
+ return rb_funcall(super, rb_intern("inherited"), 1, klass);
+}
+
+VALUE
+rb_define_class(name, super)
+ const char *name;
+ VALUE super;
+{
+ VALUE klass;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined(rb_cObject, id)) {
+ klass = rb_const_get(rb_cObject, id);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class", name);
+ }
+ if (rb_class_real(RCLASS(klass)->super) != super) {
+ rb_name_error(id, "%s is already defined", name);
+ }
+ return klass;
+ }
+ if (!super) {
+ rb_warn("no super class for `%s', Object assumed", name);
+ }
+ klass = rb_define_class_id(id, super);
+ st_add_direct(rb_class_tbl, id, klass);
+ rb_name_class(klass, id);
+ rb_const_set(rb_cObject, id, klass);
+ rb_class_inherited(super, klass);
+
+ return klass;
+}
+
+VALUE
+rb_define_class_under(outer, name, super)
+ VALUE outer;
+ const char *name;
+ VALUE super;
+{
+ VALUE klass;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined_at(outer, id)) {
+ klass = rb_const_get_at(outer, id);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class", name);
+ }
+ if (rb_class_real(RCLASS(klass)->super) != super) {
+ rb_name_error(id, "%s is already defined", name);
+ }
+ return klass;
+ }
+ if (!super) {
+ rb_warn("no super class for `%s::%s', Object assumed",
+ rb_class2name(outer), name);
+ }
+ klass = rb_define_class_id(id, super);
+ rb_set_class_path(klass, outer, name);
+ rb_const_set(outer, id, klass);
+ rb_class_inherited(super, klass);
+
+ return klass;
+}
+
+VALUE
+rb_module_new()
+{
+ NEWOBJ(mdl, struct RClass);
+ OBJSETUP(mdl, rb_cModule, T_MODULE);
+
+ mdl->super = 0;
+ mdl->iv_tbl = 0;
+ mdl->m_tbl = 0;
+ mdl->m_tbl = st_init_numtable();
+
+ return (VALUE)mdl;
+}
+
+VALUE
+rb_define_module_id(id)
+ ID id;
+{
+ VALUE mdl;
+
+ mdl = rb_module_new();
+ rb_name_class(mdl, id);
+
+ return mdl;
+}
+
+VALUE
+rb_define_module(name)
+ const char *name;
+{
+ VALUE module;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined(rb_cObject, id)) {
+ module = rb_const_get(rb_cObject, id);
+ if (TYPE(module) == T_MODULE)
+ return module;
+ rb_raise(rb_eTypeError, "%s is not a module", rb_obj_classname(module));
+ }
+ module = rb_define_module_id(id);
+ st_add_direct(rb_class_tbl, id, module);
+ rb_const_set(rb_cObject, id, module);
+
+ return module;
+}
+
+VALUE
+rb_define_module_under(outer, name)
+ VALUE outer;
+ const char *name;
+{
+ VALUE module;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined_at(outer, id)) {
+ module = rb_const_get_at(outer, id);
+ if (TYPE(module) == T_MODULE)
+ return module;
+ rb_raise(rb_eTypeError, "%s::%s is not a module",
+ rb_class2name(outer), rb_obj_classname(module));
+ }
+ module = rb_define_module_id(id);
+ rb_const_set(outer, id, module);
+ rb_set_class_path(module, outer, name);
+
+ return module;
+}
+
+static VALUE
+include_class_new(module, super)
+ VALUE module, super;
+{
+ NEWOBJ(klass, struct RClass);
+ OBJSETUP(klass, rb_cClass, T_ICLASS);
+
+ if (BUILTIN_TYPE(module) == T_ICLASS) {
+ module = RBASIC(module)->klass;
+ }
+ if (!RCLASS(module)->iv_tbl) {
+ RCLASS(module)->iv_tbl = st_init_numtable();
+ }
+ klass->iv_tbl = RCLASS(module)->iv_tbl;
+ klass->m_tbl = RCLASS(module)->m_tbl;
+ klass->super = super;
+ if (TYPE(module) == T_ICLASS) {
+ RBASIC(klass)->klass = RBASIC(module)->klass;
+ }
+ else {
+ RBASIC(klass)->klass = module;
+ }
+ OBJ_INFECT(klass, module);
+ OBJ_INFECT(klass, super);
+
+ return (VALUE)klass;
+}
+
+void
+rb_include_module(klass, module)
+ VALUE klass, module;
+{
+ VALUE p, c;
+ int changed = 0;
+
+ rb_frozen_class_p(klass);
+ if (!OBJ_TAINTED(klass)) {
+ rb_secure(4);
+ }
+
+ if (TYPE(module) != T_MODULE) {
+ Check_Type(module, T_MODULE);
+ }
+
+ OBJ_INFECT(klass, module);
+ c = klass;
+ while (module) {
+ int superclass_seen = Qfalse;
+
+ if (RCLASS(klass)->m_tbl == RCLASS(module)->m_tbl)
+ rb_raise(rb_eArgError, "cyclic include detected");
+ /* ignore if the module included already in superclasses */
+ for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
+ switch (BUILTIN_TYPE(p)) {
+ case T_ICLASS:
+ if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
+ if (!superclass_seen) {
+ c = p; /* move insertion point */
+ }
+ goto skip;
+ }
+ break;
+ case T_CLASS:
+ superclass_seen = Qtrue;
+ break;
+ }
+ }
+ c = RCLASS(c)->super = include_class_new(module, RCLASS(c)->super);
+ changed = 1;
+ skip:
+ module = RCLASS(module)->super;
+ }
+ if (changed) rb_clear_cache();
+}
+
+/*
+ * call-seq:
+ * mod.included_modules -> array
+ *
+ * Returns the list of modules included in <i>mod</i>.
+ *
+ * module Mixin
+ * end
+ *
+ * module Outer
+ * include Mixin
+ * end
+ *
+ * Mixin.included_modules #=> []
+ * Outer.included_modules #=> [Mixin]
+ */
+
+VALUE
+rb_mod_included_modules(mod)
+ VALUE mod;
+{
+ VALUE ary = rb_ary_new();
+ VALUE p;
+
+ for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ rb_ary_push(ary, RBASIC(p)->klass);
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * mod.include?(module) => true or false
+ *
+ * Returns <code>true</code> if <i>module</i> is included in
+ * <i>mod</i> or one of <i>mod</i>'s ancestors.
+ *
+ * module A
+ * end
+ * class B
+ * include A
+ * end
+ * class C < B
+ * end
+ * B.include?(A) #=> true
+ * C.include?(A) #=> true
+ * A.include?(A) #=> false
+ */
+
+VALUE
+rb_mod_include_p(mod, mod2)
+ VALUE mod;
+ VALUE mod2;
+{
+ VALUE p;
+
+ Check_Type(mod2, T_MODULE);
+ for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ if (RBASIC(p)->klass == mod2) return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.ancestors -> array
+ *
+ * Returns a list of modules included in <i>mod</i> (including
+ * <i>mod</i> itself).
+ *
+ * module Mod
+ * include Math
+ * include Comparable
+ * end
+ *
+ * Mod.ancestors #=> [Mod, Comparable, Math]
+ * Math.ancestors #=> [Math]
+ */
+
+VALUE
+rb_mod_ancestors(mod)
+ VALUE mod;
+{
+ VALUE p, ary = rb_ary_new();
+
+ for (p = mod; p; p = RCLASS(p)->super) {
+ if (FL_TEST(p, FL_SINGLETON))
+ continue;
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ rb_ary_push(ary, RBASIC(p)->klass);
+ }
+ else {
+ rb_ary_push(ary, p);
+ }
+ }
+ return ary;
+}
+
+#define VISI(x) ((x)&NOEX_MASK)
+#define VISI_CHECK(x,f) (VISI(x) == (f))
+
+static int
+ins_methods_push(name, type, ary, visi)
+ ID name;
+ long type;
+ VALUE ary;
+ long visi;
+{
+ if (type == -1) return ST_CONTINUE;
+ switch (visi) {
+ case NOEX_PRIVATE:
+ case NOEX_PROTECTED:
+ case NOEX_PUBLIC:
+ visi = (type == visi);
+ break;
+ default:
+ visi = (type != NOEX_PRIVATE);
+ break;
+ }
+ if (visi) {
+ rb_ary_push(ary, rb_str_new2(rb_id2name(name)));
+ }
+ return ST_CONTINUE;
+}
+
+static int
+ins_methods_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, -1); /* everything but private */
+}
+
+static int
+ins_methods_prot_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PROTECTED);
+}
+
+static int
+ins_methods_priv_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PRIVATE);
+}
+
+static int
+ins_methods_pub_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PUBLIC);
+}
+
+static int
+method_entry(key, body, list)
+ ID key;
+ NODE *body;
+ st_table *list;
+{
+ long type;
+
+ if (key == ID_ALLOCATOR) return ST_CONTINUE;
+ if (!st_lookup(list, key, 0)) {
+ if (!body->nd_body) type = -1; /* none */
+ else type = VISI(body->nd_noex);
+ st_add_direct(list, key, type);
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+class_instance_method_list(argc, argv, mod, func)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+ int (*func) _((ID, long, VALUE));
+{
+ VALUE ary;
+ int recur;
+ st_table *list;
+
+ if (argc == 0) {
+ recur = Qtrue;
+ }
+ else {
+ VALUE r;
+ rb_scan_args(argc, argv, "01", &r);
+ recur = RTEST(r);
+ }
+
+ list = st_init_numtable();
+ for (; mod; mod = RCLASS(mod)->super) {
+ st_foreach(RCLASS(mod)->m_tbl, method_entry, (st_data_t)list);
+ if (BUILTIN_TYPE(mod) == T_ICLASS) continue;
+ if (FL_TEST(mod, FL_SINGLETON)) continue;
+ if (!recur) break;
+ }
+ ary = rb_ary_new();
+ st_foreach(list, func, ary);
+ st_free_table(list);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * mod.instance_methods(include_super=true) => array
+ *
+ * Returns an array containing the names of public instance methods in
+ * the receiver. For a module, these are the public methods; for a
+ * class, they are the instance (not singleton) methods. With no
+ * argument, or with an argument that is <code>false</code>, the
+ * instance methods in <i>mod</i> are returned, otherwise the methods
+ * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * def method3() end
+ * end
+ *
+ * A.instance_methods #=> ["method1"]
+ * B.instance_methods(false) #=> ["method2"]
+ * C.instance_methods(false) #=> ["method3"]
+ * C.instance_methods(true).length #=> 43
+ */
+
+VALUE
+rb_class_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_i);
+}
+
+/*
+ * call-seq:
+ * mod.protected_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the protected instance methods defined in
+ * <i>mod</i>. If the optional parameter is not <code>false</code>, the
+ * methods of any ancestors are included.
+ */
+
+VALUE
+rb_class_protected_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_prot_i);
+}
+
+/*
+ * call-seq:
+ * mod.private_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the private instance methods defined in
+ * <i>mod</i>. If the optional parameter is not <code>false</code>, the
+ * methods of any ancestors are included.
+ *
+ * module Mod
+ * def method1() end
+ * private :method1
+ * def method2() end
+ * end
+ * Mod.instance_methods #=> ["method2"]
+ * Mod.private_instance_methods #=> ["method1"]
+ */
+
+VALUE
+rb_class_private_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_priv_i);
+}
+
+/*
+ * call-seq:
+ * mod.public_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the public instance methods defined in <i>mod</i>.
+ * If the optional parameter is not <code>false</code>, the methods of
+ * any ancestors are included.
+ */
+
+VALUE
+rb_class_public_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_pub_i);
+}
+
+/*
+ * call-seq:
+ * obj.singleton_methods(all=true) => array
+ *
+ * Returns an array of the names of singleton methods for <i>obj</i>.
+ * If the optional <i>all</i> parameter is true, the list will include
+ * methods in modules included in <i>obj</i>.
+ *
+ * module Other
+ * def three() end
+ * end
+ *
+ * class Single
+ * def Single.four() end
+ * end
+ *
+ * a = Single.new
+ *
+ * def a.one()
+ * end
+ *
+ * class << a
+ * include Other
+ * def two()
+ * end
+ * end
+ *
+ * Single.singleton_methods #=> ["four"]
+ * a.singleton_methods(false) #=> ["two", "one"]
+ * a.singleton_methods #=> ["two", "one", "three"]
+ */
+
+VALUE
+rb_obj_singleton_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE recur, ary, klass;
+ st_table *list;
+
+ rb_scan_args(argc, argv, "01", &recur);
+ if (argc == 0) {
+ recur = Qtrue;
+ }
+ klass = CLASS_OF(obj);
+ list = st_init_numtable();
+ if (klass && FL_TEST(klass, FL_SINGLETON)) {
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
+ klass = RCLASS(klass)->super;
+ }
+ if (RTEST(recur)) {
+ while (klass && (FL_TEST(klass, FL_SINGLETON) || TYPE(klass) == T_ICLASS)) {
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
+ klass = RCLASS(klass)->super;
+ }
+ }
+ ary = rb_ary_new();
+ st_foreach(list, ins_methods_i, ary);
+ st_free_table(list);
+
+ return ary;
+}
+
+void
+rb_define_method_id(klass, name, func, argc)
+ VALUE klass;
+ ID name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC);
+}
+
+void
+rb_define_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ ID id = rb_intern(name);
+ int ex = NOEX_PUBLIC;
+
+
+ rb_add_method(klass, id, NEW_CFUNC(func, argc), ex);
+}
+
+void
+rb_define_protected_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PROTECTED);
+}
+
+void
+rb_define_private_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PRIVATE);
+}
+
+void
+rb_undef_method(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ rb_add_method(klass, rb_intern(name), 0, NOEX_UNDEF);
+}
+
+#define SPECIAL_SINGLETON(x,c) do {\
+ if (obj == (x)) {\
+ return c;\
+ }\
+} while (0)
+
+VALUE
+rb_singleton_class(obj)
+ VALUE obj;
+{
+ VALUE klass;
+
+ if (FIXNUM_P(obj) || SYMBOL_P(obj)) {
+ rb_raise(rb_eTypeError, "can't define singleton");
+ }
+ if (rb_special_const_p(obj)) {
+ SPECIAL_SINGLETON(Qnil, rb_cNilClass);
+ SPECIAL_SINGLETON(Qfalse, rb_cFalseClass);
+ SPECIAL_SINGLETON(Qtrue, rb_cTrueClass);
+ rb_bug("unknown immediate %ld", obj);
+ }
+
+ DEFER_INTS;
+ if (FL_TEST(RBASIC(obj)->klass, FL_SINGLETON) &&
+ rb_iv_get(RBASIC(obj)->klass, "__attached__") == obj) {
+ klass = RBASIC(obj)->klass;
+ }
+ else {
+ klass = rb_make_metaclass(obj, RBASIC(obj)->klass);
+ }
+ if (OBJ_TAINTED(obj)) {
+ OBJ_TAINT(klass);
+ }
+ else {
+ FL_UNSET(klass, FL_TAINT);
+ }
+ if (OBJ_FROZEN(obj)) OBJ_FREEZE(klass);
+ ALLOW_INTS;
+
+ return klass;
+}
+
+void
+rb_define_singleton_method(obj, name, func, argc)
+ VALUE obj;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_method(rb_singleton_class(obj), name, func, argc);
+}
+
+void
+rb_define_module_function(module, name, func, argc)
+ VALUE module;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_private_method(module, name, func, argc);
+ rb_define_singleton_method(module, name, func, argc);
+}
+
+void
+rb_define_global_function(name, func, argc)
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_module_function(rb_mKernel, name, func, argc);
+}
+
+void
+rb_define_alias(klass, name1, name2)
+ VALUE klass;
+ const char *name1, *name2;
+{
+ rb_alias(klass, rb_intern(name1), rb_intern(name2));
+}
+
+void
+rb_define_attr(klass, name, read, write)
+ VALUE klass;
+ const char *name;
+ int read, write;
+{
+ rb_attr(klass, rb_intern(name), read, write, Qfalse);
+}
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+int
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
+#else
+rb_scan_args(argc, argv, fmt, va_alist)
+ int argc;
+ const VALUE *argv;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ int n, i = 0;
+ const char *p = fmt;
+ VALUE *var;
+ va_list vargs;
+
+ va_init_list(vargs, fmt);
+
+ if (*p == '*') goto rest_arg;
+
+ if (ISDIGIT(*p)) {
+ n = *p - '0';
+ if (n > argc)
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
+ for (i=0; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ if (var) *var = argv[i];
+ }
+ p++;
+ }
+ else {
+ goto error;
+ }
+
+ if (ISDIGIT(*p)) {
+ n = i + *p - '0';
+ for (; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ if (argc > i) {
+ if (var) *var = argv[i];
+ }
+ else {
+ if (var) *var = Qnil;
+ }
+ }
+ p++;
+ }
+
+ if(*p == '*') {
+ rest_arg:
+ var = va_arg(vargs, VALUE*);
+ if (argc > i) {
+ if (var) *var = rb_ary_new4(argc-i, argv+i);
+ i = argc;
+ }
+ else {
+ if (var) *var = rb_ary_new();
+ }
+ p++;
+ }
+
+ if (*p == '&') {
+ var = va_arg(vargs, VALUE*);
+ if (rb_block_given_p()) {
+ *var = rb_block_proc();
+ }
+ else {
+ *var = Qnil;
+ }
+ p++;
+ }
+ va_end(vargs);
+
+ if (*p != '\0') {
+ goto error;
+ }
+
+ if (argc > i) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, i);
+ }
+
+ return argc;
+
+ error:
+ rb_fatal("bad scan arg format: %s", fmt);
+ return 0;
+}
+/**********************************************************************
+
+ compar.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Thu Aug 26 14:39:48 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+VALUE rb_mComparable;
+
+static ID cmp;
+
+int
+rb_cmpint(val, a, b)
+ VALUE val, a, b;
+{
+ if (NIL_P(val)) {
+ rb_cmperr(a, b);
+ }
+ if (FIXNUM_P(val)) return FIX2INT(val);
+ if (TYPE(val) == T_BIGNUM) {
+ if (RBIGNUM(val)->sign) return 1;
+ return -1;
+ }
+ if (RTEST(rb_funcall(val, '>', 1, INT2FIX(0)))) return 1;
+ if (RTEST(rb_funcall(val, '<', 1, INT2FIX(0)))) return -1;
+ return 0;
+}
+
+void
+rb_cmperr(x, y)
+ VALUE x, y;
+{
+ const char *classname;
+
+ if (SPECIAL_CONST_P(y)) {
+ y = rb_inspect(y);
+ classname = StringValuePtr(y);
+ }
+ else {
+ classname = rb_obj_classname(y);
+ }
+ rb_raise(rb_eArgError, "comparison of %s with %s failed",
+ rb_obj_classname(x), classname);
+}
+
+#define cmperr() (rb_cmperr(x, y), Qnil)
+
+static VALUE
+cmp_eq(a)
+ VALUE *a;
+{
+ VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
+
+ if (NIL_P(c)) return Qnil;
+ if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+cmp_failed()
+{
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * obj == other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 0. Also returns true if
+ * _obj_ and _other_ are the same object.
+ */
+
+static VALUE
+cmp_equal(x, y)
+ VALUE x, y;
+{
+ VALUE a[2];
+
+ if (x == y) return Qtrue;
+
+ a[0] = x; a[1] = y;
+ return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0);
+}
+
+/*
+ * call-seq:
+ * obj > other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 1.
+ */
+
+static VALUE
+cmp_gt(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) > 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj >= other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 0 or 1.
+ */
+
+static VALUE
+cmp_ge(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) >= 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj < other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns -1.
+ */
+
+static VALUE
+cmp_lt(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) < 0) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * obj <= other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns -1 or 0.
+ */
+
+static VALUE
+cmp_le(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) <= 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj.between?(min, max) => true or false
+ *
+ * Returns <code>false</code> if <i>obj</i> <code><=></code>
+ * <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
+ * <i>max</i> is greater than zero, <code>true</code> otherwise.
+ *
+ * 3.between?(1, 5) #=> true
+ * 6.between?(1, 5) #=> false
+ * 'cat'.between?('ant', 'dog') #=> true
+ * 'gnu'.between?('ant', 'dog') #=> false
+ *
+ */
+
+static VALUE
+cmp_between(x, min, max)
+ VALUE x, min, max;
+{
+ if (RTEST(cmp_lt(x, min))) return Qfalse;
+ if (RTEST(cmp_gt(x, max))) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * The <code>Comparable</code> mixin is used by classes whose objects
+ * may be ordered. The class must define the <code><=></code> operator,
+ * which compares the receiver against another object, returning -1, 0,
+ * or +1 depending on whether the receiver is less than, equal to, or
+ * greater than the other object. <code>Comparable</code> uses
+ * <code><=></code> to implement the conventional comparison operators
+ * (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
+ * and <code>></code>) and the method <code>between?</code>.
+ *
+ * class SizeMatters
+ * include Comparable
+ * attr :str
+ * def <=>(anOther)
+ * str.size <=> anOther.str.size
+ * end
+ * def initialize(str)
+ * @str = str
+ * end
+ * def inspect
+ * @str
+ * end
+ * end
+ *
+ * s1 = SizeMatters.new("Z")
+ * s2 = SizeMatters.new("YY")
+ * s3 = SizeMatters.new("XXX")
+ * s4 = SizeMatters.new("WWWW")
+ * s5 = SizeMatters.new("VVVVV")
+ *
+ * s1 < s2 #=> true
+ * s4.between?(s1, s3) #=> false
+ * s4.between?(s3, s5) #=> true
+ * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
+ *
+ */
+
+void
+Init_Comparable()
+{
+ rb_mComparable = rb_define_module("Comparable");
+ rb_define_method(rb_mComparable, "==", cmp_equal, 1);
+ rb_define_method(rb_mComparable, ">", cmp_gt, 1);
+ rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
+ rb_define_method(rb_mComparable, "<", cmp_lt, 1);
+ rb_define_method(rb_mComparable, "<=", cmp_le, 1);
+ rb_define_method(rb_mComparable, "between?", cmp_between, 2);
+
+ cmp = rb_intern("<=>");
+}
+/**********************************************************************
+
+ dir.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-04 06:26:31 +0100 (Wed, 04 Feb 2009) $
+ created at: Wed Jan 5 09:51:01 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined HAVE_DIRENT_H && !defined _WIN32
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#elif defined HAVE_DIRECT_H && !defined _WIN32
+# include <direct.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# if !defined __NeXT__
+# define NAMLEN(dirent) (dirent)->d_namlen
+# else
+# /* On some versions of NextStep, d_namlen is always zero, so avoid it. */
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+# endif
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# ifdef _WIN32
+# include "win32/dir.h"
+# endif
+#endif
+
+#include <errno.h>
+
+#ifndef HAVE_STDLIB_H
+char *getenv();
+#endif
+
+#ifndef HAVE_STRING_H
+char *strchr _((char*,char));
+#endif
+
+#include <ctype.h>
+
+#include "util.h"
+
+#if !defined HAVE_LSTAT && !defined lstat
+#define lstat stat
+#endif
+
+#ifndef CASEFOLD_FILESYSTEM
+# if defined DOSISH || defined __VMS
+# define CASEFOLD_FILESYSTEM 1
+# else
+# define CASEFOLD_FILESYSTEM 0
+# endif
+#endif
+
+#define FNM_NOESCAPE 0x01
+#define FNM_PATHNAME 0x02
+#define FNM_DOTMATCH 0x04
+#define FNM_CASEFOLD 0x08
+#if CASEFOLD_FILESYSTEM
+#define FNM_SYSCASE FNM_CASEFOLD
+#else
+#define FNM_SYSCASE 0
+#endif
+
+#define FNM_NOMATCH 1
+#define FNM_ERROR 2
+
+#define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
+#define compare(c1, c2) (((unsigned char)(c1)) - ((unsigned char)(c2)))
+
+/* caution: in case *p == '\0'
+ Next(p) == p + 1 in single byte environment
+ Next(p) == p in multi byte environment
+*/
+#if defined(CharNext)
+# define Next(p) CharNext(p)
+#elif defined(DJGPP)
+# define Next(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
+#elif defined(__EMX__)
+# define Next(p) ((p) + emx_mblen(p))
+static inline int
+emx_mblen(const char *p)
+{
+ int n = mblen(p, RUBY_MBCHAR_MAXSIZE);
+ return (n < 0) ? 1 : n;
+}
+#endif
+
+#ifndef Next /* single byte environment */
+# define Next(p) ((p) + 1)
+# define Inc(p) (++(p))
+# define Compare(p1, p2) (compare(downcase(*(p1)), downcase(*(p2))))
+#else /* multi byte environment */
+# define Inc(p) ((p) = Next(p))
+# define Compare(p1, p2) (CompareImpl(p1, p2, nocase))
+static int
+CompareImpl(const char *p1, const char *p2, int nocase)
+{
+ const int len1 = Next(p1) - p1;
+ const int len2 = Next(p2) - p2;
+#ifdef _WIN32
+ char buf1[10], buf2[10]; /* large enough? */
+#endif
+
+ if (len1 < 0 || len2 < 0) {
+ rb_fatal("CompareImpl: negative len");
+ }
+
+ if (len1 == 0) return len2;
+ if (len2 == 0) return -len1;
+
+#ifdef _WIN32
+ if (nocase && rb_w32_iswinnt()) {
+ if (len1 > 1) {
+ if (len1 >= sizeof(buf1)) {
+ rb_fatal("CompareImpl: too large len");
+ }
+ memcpy(buf1, p1, len1);
+ buf1[len1] = '\0';
+ CharLower(buf1);
+ p1 = buf1; /* trick */
+ }
+ if (len2 > 1) {
+ if (len2 >= sizeof(buf2)) {
+ rb_fatal("CompareImpl: too large len");
+ }
+ memcpy(buf2, p2, len2);
+ buf2[len2] = '\0';
+ CharLower(buf2);
+ p2 = buf2; /* trick */
+ }
+ }
+#endif
+ if (len1 == 1)
+ if (len2 == 1)
+ return compare(downcase(*p1), downcase(*p2));
+ else {
+ const int ret = compare(downcase(*p1), *p2);
+ return ret ? ret : -1;
+ }
+ else
+ if (len2 == 1) {
+ const int ret = compare(*p1, downcase(*p2));
+ return ret ? ret : 1;
+ }
+ else {
+ const int ret = memcmp(p1, p2, len1 < len2 ? len1 : len2);
+ return ret ? ret : len1 - len2;
+ }
+}
+#endif /* environment */
+
+static char *
+bracket(p, s, flags)
+ const char *p; /* pattern (next to '[') */
+ const char *s; /* string */
+ int flags;
+{
+ const int nocase = flags & FNM_CASEFOLD;
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ int ok = 0, not = 0;
+
+ if (*p == '!' || *p == '^') {
+ not = 1;
+ p++;
+ }
+
+ while (*p != ']') {
+ const char *t1 = p;
+ if (escape && *t1 == '\\')
+ t1++;
+ if (!*t1)
+ return NULL;
+ p = Next(t1);
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ if (escape && *t2 == '\\')
+ t2++;
+ if (!*t2)
+ return NULL;
+ p = Next(t2);
+ if (!ok && Compare(t1, s) <= 0 && Compare(s, t2) <= 0)
+ ok = 1;
+ }
+ else
+ if (!ok && Compare(t1, s) == 0)
+ ok = 1;
+ }
+
+ return ok == not ? NULL : (char *)p + 1;
+}
+
+/* If FNM_PATHNAME is set, only path element will be matched. (upto '/' or '\0')
+ Otherwise, entire string will be matched.
+ End marker itself won't be compared.
+ And if function succeeds, *pcur reaches end marker.
+*/
+#define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
+#define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
+#define RETURN(val) return *pcur = p, *scur = s, (val);
+
+static int
+fnmatch_helper(pcur, scur, flags)
+ const char **pcur; /* pattern */
+ const char **scur; /* string */
+ int flags;
+{
+ const int period = !(flags & FNM_DOTMATCH);
+ const int pathname = flags & FNM_PATHNAME;
+ const int escape = !(flags & FNM_NOESCAPE);
+ const int nocase = flags & FNM_CASEFOLD;
+
+ const char *ptmp = 0;
+ const char *stmp = 0;
+
+ const char *p = *pcur;
+ const char *s = *scur;
+
+ if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
+ RETURN(FNM_NOMATCH);
+
+ while (1) {
+ switch (*p) {
+ case '*':
+ do { p++; } while (*p == '*');
+ if (ISEND(UNESCAPE(p))) {
+ p = UNESCAPE(p);
+ RETURN(0);
+ }
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ ptmp = p;
+ stmp = s;
+ continue;
+
+ case '?':
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ p++;
+ Inc(s);
+ continue;
+
+ case '[': {
+ const char *t;
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ if (t = bracket(p + 1, s, flags)) {
+ p = t;
+ Inc(s);
+ continue;
+ }
+ goto failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p);
+ if (ISEND(s))
+ RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
+ if (ISEND(p))
+ goto failed;
+ if (Compare(p, s) != 0)
+ goto failed;
+ Inc(p);
+ Inc(s);
+ continue;
+
+ failed: /* try next '*' position */
+ if (ptmp && stmp) {
+ p = ptmp;
+ Inc(stmp); /* !ISEND(*stmp) */
+ s = stmp;
+ continue;
+ }
+ RETURN(FNM_NOMATCH);
+ }
+}
+
+static int
+fnmatch(p, s, flags)
+ const char *p; /* pattern */
+ const char *s; /* string */
+ int flags;
+{
+ const int period = !(flags & FNM_DOTMATCH);
+ const int pathname = flags & FNM_PATHNAME;
+
+ const char *ptmp = 0;
+ const char *stmp = 0;
+
+ if (pathname) {
+ while (1) {
+ if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ ptmp = p;
+ stmp = s;
+ }
+ if (fnmatch_helper(&p, &s, flags) == 0) {
+ while (*s && *s != '/') Inc(s);
+ if (*p && *s) {
+ p++;
+ s++;
+ continue;
+ }
+ if (!*p && !*s)
+ return 0;
+ }
+ /* failed : try next recursion */
+ if (ptmp && stmp && !(period && *stmp == '.')) {
+ while (*stmp && *stmp != '/') Inc(stmp);
+ if (*stmp) {
+ p = ptmp;
+ stmp++;
+ s = stmp;
+ continue;
+ }
+ }
+ return FNM_NOMATCH;
+ }
+ }
+ else
+ return fnmatch_helper(&p, &s, flags);
+}
+
+VALUE rb_cDir;
+
+struct dir_data {
+ DIR *dir;
+ char *path;
+};
+
+static void
+free_dir(dir)
+ struct dir_data *dir;
+{
+ if (dir) {
+ if (dir->dir) closedir(dir->dir);
+ if (dir->path) free(dir->path);
+ }
+ free(dir);
+}
+
+static VALUE dir_close _((VALUE));
+
+static VALUE dir_s_alloc _((VALUE));
+static VALUE
+dir_s_alloc(klass)
+ VALUE klass;
+{
+ struct dir_data *dirp;
+ VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
+
+ dirp->dir = NULL;
+ dirp->path = NULL;
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * Dir.new( string ) -> aDir
+ *
+ * Returns a new directory object for the named directory.
+ */
+static VALUE
+dir_initialize(dir, dirname)
+ VALUE dir, dirname;
+{
+ struct dir_data *dp;
+
+ SafeStringValue(dirname);
+ Data_Get_Struct(dir, struct dir_data, dp);
+ if (dp->dir) closedir(dp->dir);
+ if (dp->path) free(dp->path);
+ dp->dir = NULL;
+ dp->path = NULL;
+ dp->dir = opendir(RSTRING(dirname)->ptr);
+ if (dp->dir == NULL) {
+ if (errno == EMFILE || errno == ENFILE) {
+ rb_gc();
+ dp->dir = opendir(RSTRING(dirname)->ptr);
+ }
+ if (dp->dir == NULL) {
+ rb_sys_fail(RSTRING(dirname)->ptr);
+ }
+ }
+ dp->path = strdup(RSTRING(dirname)->ptr);
+
+ return dir;
+}
+
+/*
+ * call-seq:
+ * Dir.open( string ) => aDir
+ * Dir.open( string ) {| aDir | block } => anObject
+ *
+ * With no block, <code>open</code> is a synonym for
+ * <code>Dir::new</code>. If a block is present, it is passed
+ * <i>aDir</i> as a parameter. The directory is closed at the end of
+ * the block, and <code>Dir::open</code> returns the value of the
+ * block.
+ */
+
+static VALUE
+dir_s_open(klass, dirname)
+ VALUE klass, dirname;
+{
+ struct dir_data *dp;
+ VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
+
+ dir_initialize(dir, dirname);
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, dir, dir_close, dir);
+ }
+
+ return dir;
+}
+
+static void
+dir_closed()
+{
+ rb_raise(rb_eIOError, "closed directory");
+}
+
+static void
+dir_check(dir)
+ VALUE dir;
+{
+ if (!OBJ_TAINTED(dir) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted Dir");
+ rb_check_frozen(dir);
+}
+
+#define GetDIR(obj, dirp) do {\
+ dir_check(dir);\
+ Data_Get_Struct(obj, struct dir_data, dirp);\
+ if (dirp->dir == NULL) dir_closed();\
+} while (0)
+
+/*
+ * call-seq:
+ * dir.inspect => string
+ *
+ * Return a string describing this Dir object.
+ */
+static VALUE
+dir_inspect(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ if (dirp->path) {
+ char *c = rb_obj_classname(dir);
+ int len = strlen(c) + strlen(dirp->path) + 4;
+ VALUE s = rb_str_new(0, len);
+ snprintf(RSTRING_PTR(s), len+1, "#<%s:%s>", c, dirp->path);
+ return s;
+ }
+ return rb_funcall(dir, rb_intern("to_s"), 0, 0);
+}
+
+/*
+ * call-seq:
+ * dir.path => string or nil
+ *
+ * Returns the path parameter passed to <em>dir</em>'s constructor.
+ *
+ * d = Dir.new("..")
+ * d.path #=> ".."
+ */
+static VALUE
+dir_path(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ if (!dirp->path) return Qnil;
+ return rb_str_new2(dirp->path);
+}
+
+/*
+ * call-seq:
+ * dir.read => string or nil
+ *
+ * Reads the next entry from <em>dir</em> and returns it as a string.
+ * Returns <code>nil</code> at the end of the stream.
+ *
+ * d = Dir.new("testdir")
+ * d.read #=> "."
+ * d.read #=> ".."
+ * d.read #=> "config.h"
+ */
+static VALUE
+dir_read(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+ struct dirent *dp;
+
+ GetDIR(dir, dirp);
+ errno = 0;
+ dp = readdir(dirp->dir);
+ if (dp) {
+ return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
+ }
+ else if (errno == 0) { /* end of stream */
+ return Qnil;
+ }
+ else {
+ rb_sys_fail(0);
+ }
+ return Qnil; /* not reached */
+}
+
+/*
+ * call-seq:
+ * dir.each { |filename| block } => dir
+ *
+ * Calls the block once for each entry in this directory, passing the
+ * filename of each entry as a parameter to the block.
+ *
+ * d = Dir.new("testdir")
+ * d.each {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got .
+ * Got ..
+ * Got config.h
+ * Got main.rb
+ */
+static VALUE
+dir_each(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+ struct dirent *dp;
+
+ GetDIR(dir, dirp);
+ rewinddir(dirp->dir);
+ for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
+ rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
+ if (dirp->dir == NULL) dir_closed();
+ }
+ return dir;
+}
+
+/*
+ * call-seq:
+ * dir.pos => integer
+ * dir.tell => integer
+ *
+ * Returns the current position in <em>dir</em>. See also
+ * <code>Dir#seek</code>.
+ *
+ * d = Dir.new("testdir")
+ * d.tell #=> 0
+ * d.read #=> "."
+ * d.tell #=> 12
+ */
+static VALUE
+dir_tell(dir)
+ VALUE dir;
+{
+#ifdef HAVE_TELLDIR
+ struct dir_data *dirp;
+ long pos;
+
+ GetDIR(dir, dirp);
+ pos = telldir(dirp->dir);
+ return rb_int2inum(pos);
+#else
+ rb_notimplement();
+#endif
+}
+
+/*
+ * call-seq:
+ * dir.seek( integer ) => dir
+ *
+ * Seeks to a particular location in <em>dir</em>. <i>integer</i>
+ * must be a value returned by <code>Dir#tell</code>.
+ *
+ * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
+ * d.read #=> "."
+ * i = d.tell #=> 12
+ * d.read #=> ".."
+ * d.seek(i) #=> #<Dir:0x401b3c40>
+ * d.read #=> ".."
+ */
+static VALUE
+dir_seek(dir, pos)
+ VALUE dir, pos;
+{
+ struct dir_data *dirp;
+ off_t p = NUM2OFFT(pos);
+
+ GetDIR(dir, dirp);
+#ifdef HAVE_SEEKDIR
+ seekdir(dirp->dir, p);
+ return dir;
+#else
+ rb_notimplement();
+#endif
+}
+
+/*
+ * call-seq:
+ * dir.pos( integer ) => integer
+ *
+ * Synonym for <code>Dir#seek</code>, but returns the position
+ * parameter.
+ *
+ * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
+ * d.read #=> "."
+ * i = d.pos #=> 12
+ * d.read #=> ".."
+ * d.pos = i #=> 12
+ * d.read #=> ".."
+ */
+static VALUE
+dir_set_pos(dir, pos)
+ VALUE dir, pos;
+{
+ dir_seek(dir, pos);
+ return pos;
+}
+
+/*
+ * call-seq:
+ * dir.rewind => dir
+ *
+ * Repositions <em>dir</em> to the first entry.
+ *
+ * d = Dir.new("testdir")
+ * d.read #=> "."
+ * d.rewind #=> #<Dir:0x401b3fb0>
+ * d.read #=> "."
+ */
+static VALUE
+dir_rewind(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(dir)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't close");
+ }
+ GetDIR(dir, dirp);
+ rewinddir(dirp->dir);
+ return dir;
+}
+
+/*
+ * call-seq:
+ * dir.close => nil
+ *
+ * Closes the directory stream. Any further attempts to access
+ * <em>dir</em> will raise an <code>IOError</code>.
+ *
+ * d = Dir.new("testdir")
+ * d.close #=> nil
+ */
+static VALUE
+dir_close(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ closedir(dirp->dir);
+ dirp->dir = NULL;
+
+ return Qnil;
+}
+
+static void
+dir_chdir(path)
+ VALUE path;
+{
+ if (chdir(RSTRING(path)->ptr) < 0)
+ rb_sys_fail(RSTRING(path)->ptr);
+}
+
+static int chdir_blocking = 0;
+static VALUE chdir_thread = Qnil;
+
+struct chdir_data {
+ VALUE old_path, new_path;
+ int done;
+};
+
+static VALUE
+chdir_yield(args)
+ struct chdir_data *args;
+{
+ dir_chdir(args->new_path);
+ args->done = Qtrue;
+ chdir_blocking++;
+ if (chdir_thread == Qnil)
+ chdir_thread = rb_thread_current();
+ return rb_yield(args->new_path);
+}
+
+static VALUE
+chdir_restore(args)
+ struct chdir_data *args;
+{
+ if (args->done) {
+ chdir_blocking--;
+ if (chdir_blocking == 0)
+ chdir_thread = Qnil;
+ dir_chdir(args->old_path);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Dir.chdir( [ string] ) => 0
+ * Dir.chdir( [ string] ) {| path | block } => anObject
+ *
+ * Changes the current working directory of the process to the given
+ * string. When called without an argument, changes the directory to
+ * the value of the environment variable <code>HOME</code>, or
+ * <code>LOGDIR</code>. <code>SystemCallError</code> (probably
+ * <code>Errno::ENOENT</code>) if the target directory does not exist.
+ *
+ * If a block is given, it is passed the name of the new current
+ * directory, and the block is executed with that as the current
+ * directory. The original working directory is restored when the block
+ * exits. The return value of <code>chdir</code> is the value of the
+ * block. <code>chdir</code> blocks can be nested, but in a
+ * multi-threaded program an error will be raised if a thread attempts
+ * to open a <code>chdir</code> block while another thread has one
+ * open.
+ *
+ * Dir.chdir("/var/spool/mail")
+ * puts Dir.pwd
+ * Dir.chdir("/tmp") do
+ * puts Dir.pwd
+ * Dir.chdir("/usr") do
+ * puts Dir.pwd
+ * end
+ * puts Dir.pwd
+ * end
+ * puts Dir.pwd
+ *
+ * <em>produces:</em>
+ *
+ * /var/spool/mail
+ * /tmp
+ * /usr
+ * /tmp
+ * /var/spool/mail
+ */
+static VALUE
+dir_s_chdir(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE path = Qnil;
+
+ rb_secure(2);
+ if (rb_scan_args(argc, argv, "01", &path) == 1) {
+ SafeStringValue(path);
+ }
+ else {
+ const char *dist = getenv("HOME");
+ if (!dist) {
+ dist = getenv("LOGDIR");
+ if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
+ }
+ path = rb_str_new2(dist);
+ }
+
+ if (chdir_blocking > 0) {
+ if (!rb_block_given_p() || rb_thread_current() != chdir_thread)
+ rb_warn("conflicting chdir during another chdir block");
+ }
+
+ if (rb_block_given_p()) {
+ struct chdir_data args;
+ char *cwd = my_getcwd();
+
+ args.old_path = rb_tainted_str_new2(cwd); free(cwd);
+ args.new_path = path;
+ args.done = Qfalse;
+ return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
+ }
+ dir_chdir(path);
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * Dir.getwd => string
+ * Dir.pwd => string
+ *
+ * Returns the path to the current working directory of this process as
+ * a string.
+ *
+ * Dir.chdir("/tmp") #=> 0
+ * Dir.getwd #=> "/tmp"
+ */
+static VALUE
+dir_s_getwd(dir)
+ VALUE dir;
+{
+ char *path;
+ VALUE cwd;
+
+ rb_secure(4);
+ path = my_getcwd();
+ cwd = rb_tainted_str_new2(path);
+
+ free(path);
+ return cwd;
+}
+
+static void check_dirname _((volatile VALUE *));
+static void
+check_dirname(dir)
+ volatile VALUE *dir;
+{
+ char *path, *pend;
+
+ SafeStringValue(*dir);
+ rb_secure(2);
+ path = RSTRING(*dir)->ptr;
+ if (path && *(pend = rb_path_end(rb_path_skip_prefix(path)))) {
+ *dir = rb_str_new(path, pend - path);
+ }
+}
+
+/*
+ * call-seq:
+ * Dir.chroot( string ) => 0
+ *
+ * Changes this process's idea of the file system root. Only a
+ * privileged process may make this call. Not available on all
+ * platforms. On Unix systems, see <code>chroot(2)</code> for more
+ * information.
+ */
+static VALUE
+dir_s_chroot(dir, path)
+ VALUE dir, path;
+{
+#if defined(HAVE_CHROOT) && !defined(__CHECKER__)
+ check_dirname(&path);
+
+ if (chroot(RSTRING(path)->ptr) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * Dir.mkdir( string [, integer] ) => 0
+ *
+ * Makes a new directory named by <i>string</i>, with permissions
+ * specified by the optional parameter <i>anInteger</i>. The
+ * permissions may be modified by the value of
+ * <code>File::umask</code>, and are ignored on NT. Raises a
+ * <code>SystemCallError</code> if the directory cannot be created. See
+ * also the discussion of permissions in the class documentation for
+ * <code>File</code>.
+ *
+ */
+static VALUE
+dir_s_mkdir(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE path, vmode;
+ int mode;
+
+ if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
+ mode = NUM2INT(vmode);
+ }
+ else {
+ mode = 0777;
+ }
+
+ check_dirname(&path);
+ if (mkdir(RSTRING(path)->ptr, mode) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * Dir.delete( string ) => 0
+ * Dir.rmdir( string ) => 0
+ * Dir.unlink( string ) => 0
+ *
+ * Deletes the named directory. Raises a subclass of
+ * <code>SystemCallError</code> if the directory isn't empty.
+ */
+static VALUE
+dir_s_rmdir(obj, dir)
+ VALUE obj, dir;
+{
+ check_dirname(&dir);
+ if (rmdir(RSTRING(dir)->ptr) < 0)
+ rb_sys_fail(RSTRING(dir)->ptr);
+
+ return INT2FIX(0);
+}
+
+static void
+sys_warning_1(mesg)
+ const char* mesg;
+{
+ rb_sys_warning("%s", mesg);
+}
+
+#define GLOB_VERBOSE (1UL << (sizeof(int) * CHAR_BIT - 1))
+#define sys_warning(val) \
+ (void)((flags & GLOB_VERBOSE) && rb_protect((VALUE (*)_((VALUE)))sys_warning_1, (VALUE)(val), 0))
+
+#define GLOB_ALLOC(type) (type *)malloc(sizeof(type))
+#define GLOB_ALLOC_N(type, n) (type *)malloc(sizeof(type) * (n))
+#define GLOB_JUMP_TAG(status) ((status == -1) ? rb_memerror() : rb_jump_tag(status))
+
+/*
+ * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
+ * is not a directory.
+ */
+#define to_be_ignored(e) ((e) == ENOENT || (e) == ENOTDIR)
+
+/* System call with warning */
+static int
+do_stat(const char *path, struct stat *pst, int flags)
+
+{
+ int ret = stat(path, pst);
+ if (ret < 0 && !to_be_ignored(errno))
+ sys_warning(path);
+
+ return ret;
+}
+
+static int
+do_lstat(const char *path, struct stat *pst, int flags)
+{
+ int ret = lstat(path, pst);
+ if (ret < 0 && !to_be_ignored(errno))
+ sys_warning(path);
+
+ return ret;
+}
+
+static DIR *
+do_opendir(const char *path, int flags)
+{
+ DIR *dirp = opendir(path);
+ if (dirp == NULL && !to_be_ignored(errno))
+ sys_warning(path);
+
+ return dirp;
+}
+
+/* Return nonzero if S has any special globbing chars in it. */
+static int
+has_magic(s, flags)
+ const char *s;
+ int flags;
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+ const int nocase = flags & FNM_CASEFOLD;
+
+ register const char *p = s;
+ register char c;
+
+ while (c = *p++) {
+ switch (c) {
+ case '*':
+ case '?':
+ case '[':
+ return 1;
+
+ case '\\':
+ if (escape && !(c = *p++))
+ return 0;
+ continue;
+
+ default:
+ if (!FNM_SYSCASE && ISALPHA(c) && nocase)
+ return 1;
+ }
+
+ p = Next(p-1);
+ }
+
+ return 0;
+}
+
+/* Find separator in globbing pattern. */
+static char *
+find_dirsep(const char *s, int flags)
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ register const char *p = s;
+ register char c;
+ int open = 0;
+
+ while (c = *p++) {
+ switch (c) {
+ case '[':
+ open = 1;
+ continue;
+ case ']':
+ open = 0;
+ continue;
+
+ case '/':
+ if (!open)
+ return (char *)p-1;
+ continue;
+
+ case '\\':
+ if (escape && !(c = *p++))
+ return (char *)p-1;
+ continue;
+ }
+
+ p = Next(p-1);
+ }
+
+ return (char *)p-1;
+}
+
+/* Remove escaping backslashes */
+static void
+remove_backslashes(p)
+ char *p;
+{
+ char *t = p;
+ char *s = p;
+
+ while (*p) {
+ if (*p == '\\') {
+ if (t != s)
+ memmove(t, s, p - s);
+ t += p - s;
+ s = ++p;
+ if (!*p) break;
+ }
+ Inc(p);
+ }
+
+ while (*p++);
+
+ if (t != s)
+ memmove(t, s, p - s); /* move '\0' too */
+}
+
+/* Globing pattern */
+enum glob_pattern_type { PLAIN, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
+
+struct glob_pattern {
+ char *str;
+ enum glob_pattern_type type;
+ struct glob_pattern *next;
+};
+
+static void glob_free_pattern(struct glob_pattern *list);
+
+static struct glob_pattern *
+glob_make_pattern(const char *p, int flags)
+{
+ struct glob_pattern *list, *tmp, **tail = &list;
+ int dirsep = 0; /* pattern is terminated with '/' */
+
+ while (*p) {
+ tmp = GLOB_ALLOC(struct glob_pattern);
+ if (!tmp) goto error;
+ if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ /* fold continuous RECURSIVEs (needed in glob_helper) */
+ do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ tmp->type = RECURSIVE;
+ tmp->str = 0;
+ dirsep = 1;
+ }
+ else {
+ const char *m = find_dirsep(p, flags);
+ char *buf = GLOB_ALLOC_N(char, m-p+1);
+ if (!buf) {
+ free(tmp);
+ goto error;
+ }
+ memcpy(buf, p, m-p);
+ buf[m-p] = '\0';
+ tmp->type = has_magic(buf, flags) ? MAGICAL : PLAIN;
+ tmp->str = buf;
+ if (*m) {
+ dirsep = 1;
+ p = m + 1;
+ }
+ else {
+ dirsep = 0;
+ p = m;
+ }
+ }
+ *tail = tmp;
+ tail = &tmp->next;
+ }
+
+ tmp = GLOB_ALLOC(struct glob_pattern);
+ if (!tmp) {
+ error:
+ *tail = 0;
+ glob_free_pattern(list);
+ return 0;
+ }
+ tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
+ tmp->str = 0;
+ *tail = tmp;
+ tmp->next = 0;
+
+ return list;
+}
+
+static void
+glob_free_pattern(struct glob_pattern *list)
+{
+ while (list) {
+ struct glob_pattern *tmp = list;
+ list = list->next;
+ if (tmp->str)
+ free(tmp->str);
+ free(tmp);
+ }
+}
+
+static char *
+join_path(const char *path, int dirsep, const char *name)
+{
+ long len = strlen(path);
+ char *buf = GLOB_ALLOC_N(char, len+strlen(name)+(dirsep?1:0)+1);
+
+ if (!buf) return 0;
+ memcpy(buf, path, len);
+ if (dirsep) {
+ strcpy(buf+len, "/");
+ len++;
+ }
+ strcpy(buf+len, name);
+ return buf;
+}
+
+enum answer { YES, NO, UNKNOWN };
+
+#ifndef S_ISLNK
+# ifndef S_IFLNK
+# define S_ISLNK(m) (0)
+# else
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# endif
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+struct glob_args {
+ void (*func) _((const char*, VALUE));
+ const char *c;
+ VALUE v;
+};
+
+static VALUE glob_func_caller _((VALUE));
+
+static VALUE
+glob_func_caller(val)
+ VALUE val;
+{
+ struct glob_args *args = (struct glob_args *)val;
+
+ (*args->func)(args->c, args->v);
+ return Qnil;
+}
+
+#define glob_call_func(func, path, arg) (*func)(path, arg)
+
+static int glob_helper _((const char *, int, enum answer, enum answer, struct glob_pattern **, struct glob_pattern **, int, ruby_glob_func *, VALUE));
+
+static int
+glob_helper(path, dirsep, exist, isdir, beg, end, flags, func, arg)
+ const char *path;
+ int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
+ enum answer exist; /* Does 'path' indicate an existing entry? */
+ enum answer isdir; /* Does 'path' indicate a directory or a symlink to a directory? */
+ struct glob_pattern **beg;
+ struct glob_pattern **end;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ struct stat st;
+ int status = 0;
+ struct glob_pattern **cur, **new_beg, **new_end;
+ int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
+ int escape = !(flags & FNM_NOESCAPE);
+
+ for (cur = beg; cur < end; ++cur) {
+ struct glob_pattern *p = *cur;
+ if (p->type == RECURSIVE) {
+ recursive = 1;
+ p = p->next;
+ }
+ switch (p->type) {
+ case PLAIN:
+ plain = 1;
+ break;
+ case MAGICAL:
+ magical = 1;
+ break;
+ case MATCH_ALL:
+ match_all = 1;
+ break;
+ case MATCH_DIR:
+ match_dir = 1;
+ break;
+ case RECURSIVE:
+ rb_bug("continuous RECURSIVEs");
+ }
+ }
+
+ if (*path) {
+ if (match_all && exist == UNKNOWN) {
+ if (do_lstat(path, &st, flags) == 0) {
+ exist = YES;
+ isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
+ }
+ else {
+ exist = NO;
+ isdir = NO;
+ }
+ }
+ if (match_dir && isdir == UNKNOWN) {
+ if (do_stat(path, &st, flags) == 0) {
+ exist = YES;
+ isdir = S_ISDIR(st.st_mode) ? YES : NO;
+ }
+ else {
+ exist = NO;
+ isdir = NO;
+ }
+ }
+ if (match_all && exist == YES) {
+ status = glob_call_func(func, path, arg);
+ if (status) return status;
+ }
+ if (match_dir && isdir == YES) {
+ char *tmp = join_path(path, dirsep, "");
+ if (!tmp) return -1;
+ status = glob_call_func(func, tmp, arg);
+ free(tmp);
+ if (status) return status;
+ }
+ }
+
+ if (exist == NO || isdir == NO) return 0;
+
+ if (magical || recursive) {
+ struct dirent *dp;
+ DIR *dirp = do_opendir(*path ? path : ".", flags);
+ if (dirp == NULL) return 0;
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ char *buf = join_path(path, dirsep, dp->d_name);
+ enum answer new_isdir = UNKNOWN;
+
+ if (!buf) {
+ status = -1;
+ break;
+ }
+ if (recursive && strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0
+ && fnmatch("*", dp->d_name, flags) == 0) {
+#ifndef _WIN32
+ if (do_lstat(buf, &st, flags) == 0)
+ new_isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
+ else
+ new_isdir = NO;
+#else
+ new_isdir = dp->d_isdir ? (!dp->d_isrep ? YES : UNKNOWN) : NO;
+#endif
+ }
+
+ new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
+ if (!new_beg) {
+ status = -1;
+ break;
+ }
+
+ for (cur = beg; cur < end; ++cur) {
+ struct glob_pattern *p = *cur;
+ if (p->type == RECURSIVE) {
+ if (new_isdir == YES) /* not symlink but real directory */
+ *new_end++ = p; /* append recursive pattern */
+ p = p->next; /* 0 times recursion */
+ }
+ if (p->type == PLAIN || p->type == MAGICAL) {
+ if (fnmatch(p->str, dp->d_name, flags) == 0)
+ *new_end++ = p->next;
+ }
+ }
+
+ status = glob_helper(buf, 1, YES, new_isdir, new_beg, new_end, flags, func, arg);
+ free(buf);
+ free(new_beg);
+ if (status) break;
+ }
+
+ closedir(dirp);
+ }
+ else if (plain) {
+ struct glob_pattern **copy_beg, **copy_end, **cur2;
+
+ copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
+ if (!copy_beg) return -1;
+ for (cur = beg; cur < end; ++cur)
+ *copy_end++ = (*cur)->type == PLAIN ? *cur : 0;
+
+ for (cur = copy_beg; cur < copy_end; ++cur) {
+ if (*cur) {
+ char *buf;
+ char *name;
+ name = GLOB_ALLOC_N(char, strlen((*cur)->str) + 1);
+ if (!name) {
+ status = -1;
+ break;
+ }
+ strcpy(name, (*cur)->str);
+ if (escape) remove_backslashes(name);
+
+ new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
+ if (!new_beg) {
+ free(name);
+ status = -1;
+ break;
+ }
+ *new_end++ = (*cur)->next;
+ for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
+ if (*cur2 && fnmatch((*cur2)->str, name, flags) == 0) {
+ *new_end++ = (*cur2)->next;
+ *cur2 = 0;
+ }
+ }
+
+ buf = join_path(path, dirsep, name);
+ free(name);
+ if (!buf) {
+ free(new_beg);
+ status = -1;
+ break;
+ }
+ status = glob_helper(buf, 1, UNKNOWN, UNKNOWN, new_beg, new_end, flags, func, arg);
+ free(buf);
+ free(new_beg);
+ if (status) break;
+ }
+ }
+
+ free(copy_beg);
+ }
+
+ return status;
+}
+
+static int
+ruby_glob0(path, flags, func, arg)
+ const char *path;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ struct glob_pattern *list;
+ const char *root, *start;
+ char *buf;
+ int n;
+ int status;
+
+ start = root = path;
+ flags |= FNM_SYSCASE;
+#if defined DOSISH
+ root = rb_path_skip_prefix(root);
+#endif
+
+ if (root && *root == '/') root++;
+
+ n = root - start;
+ buf = GLOB_ALLOC_N(char, n + 1);
+ if (!buf) return -1;
+ MEMCPY(buf, start, char, n);
+ buf[n] = '\0';
+
+ list = glob_make_pattern(root, flags);
+ if (!list) {
+ free(buf);
+ return -1;
+ }
+ status = glob_helper(buf, 0, UNKNOWN, UNKNOWN, &list, &list + 1, flags, func, arg);
+ glob_free_pattern(list);
+ free(buf);
+
+ return status;
+}
+
+int
+ruby_glob(path, flags, func, arg)
+ const char *path;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg);
+}
+
+static int rb_glob_caller _((const char *, VALUE));
+
+static int
+rb_glob_caller(path, a)
+ const char *path;
+ VALUE a;
+{
+ int status;
+ struct glob_args *args = (struct glob_args *)a;
+
+ args->c = path;
+ rb_protect(glob_func_caller, a, &status);
+ return status;
+}
+
+static int
+rb_glob2(path, flags, func, arg)
+ const char *path;
+ int flags;
+ void (*func) _((const char *, VALUE));
+ VALUE arg;
+{
+ struct glob_args args;
+
+ args.func = func;
+ args.v = arg;
+
+ if (flags & FNM_SYSCASE) {
+ rb_warning("Dir.glob() ignores File::FNM_CASEFOLD");
+ }
+
+ return ruby_glob0(path, flags | GLOB_VERBOSE, rb_glob_caller, (VALUE)&args);
+}
+
+void
+rb_glob(path, func, arg)
+ const char *path;
+ void (*func) _((const char*, VALUE));
+ VALUE arg;
+{
+ int status = rb_glob2(path, 0, func, arg);
+ if (status) GLOB_JUMP_TAG(status);
+}
+
+static void push_pattern _((const char* path, VALUE ary));
+static void
+push_pattern(path, ary)
+ const char *path;
+ VALUE ary;
+{
+ rb_ary_push(ary, rb_tainted_str_new2(path));
+}
+
+int
+ruby_brace_expand(str, flags, func, arg)
+ const char *str;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+ const char *p = str;
+ const char *s = p;
+ const char *lbrace = 0, *rbrace = 0;
+ int nest = 0, status = 0;
+
+ while (*p) {
+ if (*p == '{' && nest++ == 0) {
+ lbrace = p;
+ }
+ if (*p == '}' && --nest <= 0) {
+ rbrace = p;
+ break;
+ }
+ if (*p == '\\' && escape) {
+ if (!*++p) break;
+ }
+ Inc(p);
+ }
+
+ if (lbrace && rbrace) {
+ char *buf = GLOB_ALLOC_N(char, strlen(s) + 1);
+ long shift;
+
+ if (!buf) return -1;
+ memcpy(buf, s, lbrace-s);
+ shift = (lbrace-s);
+ p = lbrace;
+ while (p < rbrace) {
+ const char *t = ++p;
+ nest = 0;
+ while (p < rbrace && !(*p == ',' && nest == 0)) {
+ if (*p == '{') nest++;
+ if (*p == '}') nest--;
+ if (*p == '\\' && escape) {
+ if (++p == rbrace) break;
+ }
+ Inc(p);
+ }
+ memcpy(buf+shift, t, p-t);
+ strcpy(buf+shift+(p-t), rbrace+1);
+ status = ruby_brace_expand(buf, flags, func, arg);
+ if (status) break;
+ }
+ free(buf);
+ }
+ else if (!lbrace && !rbrace) {
+ status = (*func)(s, arg);
+ }
+
+ return status;
+}
+
+struct brace_args {
+ ruby_glob_func *func;
+ VALUE value;
+ int flags;
+};
+
+static int glob_brace _((const char *, VALUE));
+static int
+glob_brace(path, val)
+ const char *path;
+ VALUE val;
+{
+ struct brace_args *arg = (struct brace_args *)val;
+
+ return ruby_glob0(path, arg->flags, arg->func, arg->value);
+}
+
+static int
+ruby_brace_glob0(str, flags, func, arg)
+ const char *str;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ struct brace_args args;
+
+ args.func = func;
+ args.value = arg;
+ args.flags = flags;
+ return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args);
+}
+
+int
+ruby_brace_glob(str, flags, func, arg)
+ const char *str;
+ int flags;
+ ruby_glob_func *func;
+ VALUE arg;
+{
+ return ruby_brace_glob0(str, flags & ~GLOB_VERBOSE, func, arg);
+}
+
+static int
+push_glob(VALUE ary, const char *str, int flags)
+{
+ struct glob_args args;
+
+ args.func = push_pattern;
+ args.v = ary;
+ return ruby_brace_glob0(str, flags | GLOB_VERBOSE, rb_glob_caller, (VALUE)&args);
+}
+
+static VALUE
+rb_push_glob(str, flags) /* '\0' is delimiter */
+ VALUE str;
+ int flags;
+{
+ long offset = 0;
+ VALUE ary;
+
+ ary = rb_ary_new();
+ SafeStringValue(str);
+
+ while (offset < RSTRING_LEN(str)) {
+ int status = push_glob(ary, RSTRING(str)->ptr + offset, flags);
+ char *p, *pend;
+ if (status) GLOB_JUMP_TAG(status);
+ if (offset >= RSTRING_LEN(str)) break;
+ p = RSTRING(str)->ptr + offset;
+ p += strlen(p) + 1;
+ pend = RSTRING(str)->ptr + RSTRING_LEN(str);
+ while (p < pend && !*p)
+ p++;
+ offset = p - RSTRING(str)->ptr;
+ }
+
+ return ary;
+}
+
+static VALUE
+dir_globs(argc, argv, flags)
+ long argc;
+ VALUE *argv;
+ int flags;
+{
+ VALUE ary = rb_ary_new();
+ long i;
+
+ for (i = 0; i < argc; ++i) {
+ int status;
+ VALUE str = argv[i];
+ SafeStringValue(str);
+ status = push_glob(ary, RSTRING(str)->ptr, flags);
+ if (status) GLOB_JUMP_TAG(status);
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * Dir[ array ] => array
+ * Dir[ string [, string ...] ] => array
+ *
+ * Equivalent to calling
+ * <code>Dir.glob(</code><i>array,</i><code>0)</code> and
+ * <code>Dir.glob([</code><i>string,...</i><code>],0)</code>.
+ *
+ */
+static VALUE
+dir_s_aref(int argc, VALUE *argv, VALUE obj)
+ {
+ if (argc == 1) {
+ return rb_push_glob(argv[0], 0);
+ }
+ return dir_globs(argc, argv, 0);
+ }
+
+/*
+ * call-seq:
+ * Dir.glob( pattern, [flags] ) => array
+ * Dir.glob( pattern, [flags] ) {| filename | block } => nil
+ *
+ * Returns the filenames found by expanding <i>pattern</i> which is
+ * an +Array+ of the patterns or the pattern +String+, either as an
+ * <i>array</i> or as parameters to the block. Note that this pattern
+ * is not a regexp (it's closer to a shell glob). See
+ * <code>File::fnmatch</code> for the meaning of the <i>flags</i>
+ * parameter. Note that case sensitivity depends on your system (so
+ * <code>File::FNM_CASEFOLD</code> is ignored)
+ *
+ * <code>*</code>:: Matches any file. Can be restricted by
+ * other values in the glob. <code>*</code>
+ * will match all files; <code>c*</code> will
+ * match all files beginning with
+ * <code>c</code>; <code>*c</code> will match
+ * all files ending with <code>c</code>; and
+ * <code>*c*</code> will match all files that
+ * have <code>c</code> in them (including at
+ * the beginning or end). Equivalent to
+ * <code>/ .* /x</code> in regexp.
+ * <code>**</code>:: Matches directories recursively.
+ * <code>?</code>:: Matches any one character. Equivalent to
+ * <code>/.{1}/</code> in regexp.
+ * <code>[set]</code>:: Matches any one character in +set+.
+ * Behaves exactly like character sets in
+ * Regexp, including set negation
+ * (<code>[^a-z]</code>).
+ * <code>{p,q}</code>:: Matches either literal <code>p</code> or
+ * literal <code>q</code>. Matching literals
+ * may be more than one character in length.
+ * More than two literals may be specified.
+ * Equivalent to pattern alternation in
+ * regexp.
+ * <code>\</code>:: Escapes the next metacharacter.
+ *
+ * Dir["config.?"] #=> ["config.h"]
+ * Dir.glob("config.?") #=> ["config.h"]
+ * Dir.glob("*.[a-z][a-z]") #=> ["main.rb"]
+ * Dir.glob("*.[^r]*") #=> ["config.h"]
+ * Dir.glob("*.{rb,h}") #=> ["main.rb", "config.h"]
+ * Dir.glob("*") #=> ["config.h", "main.rb"]
+ * Dir.glob("*", File::FNM_DOTMATCH) #=> [".", "..", "config.h", "main.rb"]
+ *
+ * rbfiles = File.join("**", "*.rb")
+ * Dir.glob(rbfiles) #=> ["main.rb",
+ * "lib/song.rb",
+ * "lib/song/karaoke.rb"]
+ * libdirs = File.join("**", "lib")
+ * Dir.glob(libdirs) #=> ["lib"]
+ *
+ * librbfiles = File.join("**", "lib", "**", "*.rb")
+ * Dir.glob(librbfiles) #=> ["lib/song.rb",
+ * "lib/song/karaoke.rb"]
+ *
+ * librbfiles = File.join("**", "lib", "*.rb")
+ * Dir.glob(librbfiles) #=> ["lib/song.rb"]
+ */
+static VALUE
+dir_s_glob(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE str, rflags, ary;
+ int flags;
+
+ if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
+ flags = NUM2INT(rflags);
+ else
+ flags = 0;
+
+ ary = rb_check_array_type(str);
+ if (NIL_P(ary)) {
+ ary = rb_push_glob(str, flags);
+ }
+ else {
+ volatile VALUE v = ary;
+ ary = dir_globs(RARRAY_LEN(v), RARRAY_PTR(v), flags);
+ }
+
+ if (rb_block_given_p()) {
+ rb_ary_each(ary);
+ return Qnil;
+ }
+ return ary;
+}
+
+static VALUE
+dir_open_dir(path)
+ VALUE path;
+{
+ VALUE dir = rb_funcall(rb_cDir, rb_intern("open"), 1, path);
+
+ if (TYPE(dir) != T_DATA ||
+ RDATA(dir)->dfree != (RUBY_DATA_FUNC)free_dir) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Dir)",
+ rb_obj_classname(dir));
+ }
+ return dir;
+}
+
+
+/*
+ * call-seq:
+ * Dir.foreach( dirname ) {| filename | block } => nil
+ *
+ * Calls the block once for each entry in the named directory, passing
+ * the filename of each entry as a parameter to the block.
+ *
+ * Dir.foreach("testdir") {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got .
+ * Got ..
+ * Got config.h
+ * Got main.rb
+ *
+ */
+static VALUE
+dir_foreach(io, dirname)
+ VALUE io, dirname;
+{
+ VALUE dir;
+
+ dir = dir_open_dir(dirname);
+ rb_ensure(dir_each, dir, dir_close, dir);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Dir.entries( dirname ) => array
+ *
+ * Returns an array containing all of the filenames in the given
+ * directory. Will raise a <code>SystemCallError</code> if the named
+ * directory doesn't exist.
+ *
+ * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
+ *
+ */
+static VALUE
+dir_entries(io, dirname)
+ VALUE io, dirname;
+{
+ VALUE dir;
+
+ dir = dir_open_dir(dirname);
+ return rb_ensure(rb_Array, dir, dir_close, dir);
+}
+
+/*
+ * call-seq:
+ * File.fnmatch( pattern, path, [flags] ) => (true or false)
+ * File.fnmatch?( pattern, path, [flags] ) => (true or false)
+ *
+ * Returns true if <i>path</i> matches against <i>pattern</i> The
+ * pattern is not a regular expression; instead it follows rules
+ * similar to shell filename globbing. It may contain the following
+ * metacharacters:
+ *
+ * <code>*</code>:: Matches any file. Can be restricted by
+ * other values in the glob. <code>*</code>
+ * will match all files; <code>c*</code> will
+ * match all files beginning with
+ * <code>c</code>; <code>*c</code> will match
+ * all files ending with <code>c</code>; and
+ * <code>*c*</code> will match all files that
+ * have <code>c</code> in them (including at
+ * the beginning or end). Equivalent to
+ * <code>/ .* /x</code> in regexp.
+ * <code>**</code>:: Matches directories recursively or files
+ * expansively.
+ * <code>?</code>:: Matches any one character. Equivalent to
+ * <code>/.{1}/</code> in regexp.
+ * <code>[set]</code>:: Matches any one character in +set+.
+ * Behaves exactly like character sets in
+ * Regexp, including set negation
+ * (<code>[^a-z]</code>).
+ * <code>\</code>:: Escapes the next metacharacter.
+ *
+ * <i>flags</i> is a bitwise OR of the <code>FNM_xxx</code>
+ * parameters. The same glob pattern and flags are used by
+ * <code>Dir::glob</code>.
+ *
+ * File.fnmatch('cat', 'cat') #=> true : match entire string
+ * File.fnmatch('cat', 'category') #=> false : only match partial string
+ * File.fnmatch('c{at,ub}s', 'cats') #=> false : { } isn't supported
+ *
+ * File.fnmatch('c?t', 'cat') #=> true : '?' match only 1 character
+ * File.fnmatch('c??t', 'cat') #=> false : ditto
+ * File.fnmatch('c*', 'cats') #=> true : '*' match 0 or more characters
+ * File.fnmatch('c*t', 'c/a/b/t') #=> true : ditto
+ * File.fnmatch('ca[a-z]', 'cat') #=> true : inclusive bracket expression
+ * File.fnmatch('ca[^t]', 'cat') #=> false : exclusive bracket expression ('^' or '!')
+ *
+ * File.fnmatch('cat', 'CAT') #=> false : case sensitive
+ * File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD) #=> true : case insensitive
+ *
+ * File.fnmatch('?', '/', File::FNM_PATHNAME) #=> false : wildcard doesn't match '/' on FNM_PATHNAME
+ * File.fnmatch('*', '/', File::FNM_PATHNAME) #=> false : ditto
+ * File.fnmatch('[/]', '/', File::FNM_PATHNAME) #=> false : ditto
+ *
+ * File.fnmatch('\?', '?') #=> true : escaped wildcard becomes ordinary
+ * File.fnmatch('\a', 'a') #=> true : escaped ordinary remains ordinary
+ * File.fnmatch('\a', '\a', File::FNM_NOESCAPE) #=> true : FNM_NOESACPE makes '\' ordinary
+ * File.fnmatch('[\?]', '?') #=> true : can escape inside bracket expression
+ *
+ * File.fnmatch('*', '.profile') #=> false : wildcard doesn't match leading
+ * File.fnmatch('*', '.profile', File::FNM_DOTMATCH) #=> true period by default.
+ * File.fnmatch('.*', '.profile') #=> true
+ *
+ * rbfiles = '**' '/' '*.rb' # you don't have to do like this. just write in single string.
+ * File.fnmatch(rbfiles, 'main.rb') #=> false
+ * File.fnmatch(rbfiles, './main.rb') #=> false
+ * File.fnmatch(rbfiles, 'lib/song.rb') #=> true
+ * File.fnmatch('**.rb', 'main.rb') #=> true
+ * File.fnmatch('**.rb', './main.rb') #=> false
+ * File.fnmatch('**.rb', 'lib/song.rb') #=> true
+ * File.fnmatch('*', 'dave/.profile') #=> true
+ *
+ * pattern = '*' '/' '*'
+ * File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME) #=> false
+ * File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
+ *
+ * pattern = '**' '/' 'foo'
+ * File.fnmatch(pattern, 'a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch(pattern, '/a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch(pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME) #=> false
+ * File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
+ */
+static VALUE
+file_s_fnmatch(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE pattern, path;
+ VALUE rflags;
+ int flags;
+
+ if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
+ flags = NUM2INT(rflags);
+ else
+ flags = 0;
+
+ StringValue(pattern);
+ StringValue(path);
+
+ if (fnmatch(RSTRING(pattern)->ptr, RSTRING(path)->ptr, flags) == 0)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * Objects of class <code>Dir</code> are directory streams representing
+ * directories in the underlying file system. They provide a variety of
+ * ways to list directories and their contents. See also
+ * <code>File</code>.
+ *
+ * The directory used in these examples contains the two regular files
+ * (<code>config.h</code> and <code>main.rb</code>), the parent
+ * directory (<code>..</code>), and the directory itself
+ * (<code>.</code>).
+ */
+void
+Init_Dir()
+{
+ rb_cDir = rb_define_class("Dir", rb_cObject);
+
+ rb_include_module(rb_cDir, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cDir, dir_s_alloc);
+ rb_define_singleton_method(rb_cDir, "open", dir_s_open, 1);
+ rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, 1);
+ rb_define_singleton_method(rb_cDir, "entries", dir_entries, 1);
+
+ rb_define_method(rb_cDir,"initialize", dir_initialize, 1);
+ rb_define_method(rb_cDir,"path", dir_path, 0);
+ rb_define_method(rb_cDir,"read", dir_read, 0);
+ rb_define_method(rb_cDir,"each", dir_each, 0);
+ rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
+ rb_define_method(rb_cDir,"tell", dir_tell, 0);
+ rb_define_method(rb_cDir,"seek", dir_seek, 1);
+ rb_define_method(rb_cDir,"pos", dir_tell, 0);
+ rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
+ rb_define_method(rb_cDir,"close", dir_close, 0);
+
+ rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
+ rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
+ rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
+ rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
+ rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
+ rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
+ rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
+ rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
+
+ rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1);
+ rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, -1);
+
+ rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
+ rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
+
+ rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
+ rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
+ rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
+ rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
+ rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
+}
+/**********************************************************************
+
+ dln.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-15 15:25:08 +0200 (Sun, 15 Jun 2008) $
+ created at: Tue Jan 18 17:05:06 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "dln.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef __CHECKER__
+#undef HAVE_DLOPEN
+#undef USE_DLN_A_OUT
+#undef USE_DLN_DLOPEN
+#endif
+
+#ifdef USE_DLN_A_OUT
+char *dln_argv0;
+#endif
+
+#if defined(HAVE_ALLOCA_H)
+#include <alloca.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef xmalloc
+void *xmalloc();
+void *xcalloc();
+void *xrealloc();
+#endif
+
+#include <stdio.h>
+#if defined(_WIN32) || defined(__VMS)
+#include "missing/file.h"
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 1024
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef _WIN32
+char *getenv();
+#endif
+
+#if defined(__VMS)
+#pragma builtins
+#include <dlfcn.h>
+#endif
+
+#ifdef __MACOS__
+# include <TextUtils.h>
+# include <CodeFragments.h>
+# include <Aliases.h>
+# include "macruby_private.h"
+#endif
+
+#ifdef __BEOS__
+# include <image.h>
+#endif
+
+#ifndef NO_DLN_LOAD
+
+#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) && !defined(_AIX) && !defined(__APPLE__) && !defined(_UNICOSMP)
+/* dynamic load with dlopen() */
+# define USE_DLN_DLOPEN
+#endif
+
+#ifndef FUNCNAME_PATTERN
+# if defined(__hp9000s300) || (defined(__NetBSD__) && !defined(__ELF__)) || defined(__BORLANDC__) || (defined(__FreeBSD__) && !defined(__ELF__)) || (defined(__OpenBSD__) && !defined(__ELF__)) || defined(NeXT) || defined(__WATCOMC__) || defined(__APPLE__)
+# define FUNCNAME_PATTERN "_Init_%s"
+# else
+# define FUNCNAME_PATTERN "Init_%s"
+# endif
+#endif
+
+static int
+init_funcname_len(buf, file)
+ char **buf;
+ const char *file;
+{
+ char *p;
+ const char *slash;
+ int len;
+
+ /* Load the file as an object one */
+ for (slash = file-1; *file; file++) /* Find position of last '/' */
+#ifdef __MACOS__
+ if (*file == ':') slash = file;
+#else
+ if (*file == '/') slash = file;
+#endif
+
+ len = strlen(FUNCNAME_PATTERN) + strlen(slash + 1);
+ *buf = xmalloc(len);
+ snprintf(*buf, len, FUNCNAME_PATTERN, slash + 1);
+ for (p = *buf; *p; p++) { /* Delete suffix if it exists */
+ if (*p == '.') {
+ *p = '\0'; break;
+ }
+ }
+ return p - *buf;
+}
+
+#define init_funcname(buf, file) do {\
+ int len = init_funcname_len(buf, file);\
+ char *tmp = ALLOCA_N(char, len+1);\
+ if (!tmp) {\
+ free(*buf);\
+ rb_memerror();\
+ }\
+ strcpy(tmp, *buf);\
+ free(*buf);\
+ *buf = tmp;\
+} while (0)
+
+#ifdef USE_DLN_A_OUT
+
+#ifndef LIBC_NAME
+# define LIBC_NAME "libc.a"
+#endif
+
+#ifndef DLN_DEFAULT_LIB_PATH
+# define DLN_DEFAULT_LIB_PATH "/lib:/usr/lib:/usr/local/lib:."
+#endif
+
+#include <errno.h>
+
+static int dln_errno;
+
+#define DLN_ENOEXEC ENOEXEC /* Exec format error */
+#define DLN_ECONFL 1201 /* Symbol name conflict */
+#define DLN_ENOINIT 1202 /* No initializer given */
+#define DLN_EUNDEF 1203 /* Undefine symbol remains */
+#define DLN_ENOTLIB 1204 /* Not a library file */
+#define DLN_EBADLIB 1205 /* Malformed library file */
+#define DLN_EINIT 1206 /* Not initialized */
+
+static int dln_init_p = 0;
+
+#include <ar.h>
+#include <a.out.h>
+#ifndef N_COMM
+# define N_COMM 0x12
+#endif
+#ifndef N_MAGIC
+# define N_MAGIC(x) (x).a_magic
+#endif
+
+#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC)
+
+#include "util.h"
+#include "st.h"
+
+static st_table *sym_tbl;
+static st_table *undef_tbl;
+
+static int load_lib();
+
+static int
+load_header(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ int size;
+
+ lseek(fd, disp, 0);
+ size = read(fd, hdrp, sizeof(struct exec));
+ if (size == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (size != sizeof(struct exec) || N_BADMAG(*hdrp)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ return 0;
+}
+
+#if defined(sequent)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr)
+#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr)
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#endif
+
+/* Default macros */
+#ifndef RELOC_ADDRESS
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) 0
+#define RELOC_PCREL_P(r) ((r)->r_pcrel)
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#endif
+
+#if defined(sun) && defined(sparc)
+/* Sparc (Sun 4) macros */
+# undef relocation_info
+# define relocation_info reloc_info_sparc
+# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type])
+# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type])
+# define R_LENGTH(r) (reloc_r_length[(r)->r_type])
+static int reloc_r_rightshift[] = {
+ 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
+};
+static int reloc_r_bitsize[] = {
+ 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
+};
+static int reloc_r_length[] = {
+ 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+};
+# define R_PCREL(r) \
+ ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
+# define R_SYMBOL(r) ((r)->r_index)
+#endif
+
+#if defined(sequent)
+#define R_SYMBOL(r) ((r)->r_symbolnum)
+#define R_MEMORY_SUB(r) ((r)->r_bsr)
+#define R_PCREL(r) ((r)->r_pcrel || (r)->r_bsr)
+#define R_LENGTH(r) ((r)->r_length)
+#endif
+
+#ifndef R_SYMBOL
+# define R_SYMBOL(r) ((r)->r_symbolnum)
+# define R_MEMORY_SUB(r) 0
+# define R_PCREL(r) ((r)->r_pcrel)
+# define R_LENGTH(r) ((r)->r_length)
+#endif
+
+static struct relocation_info *
+load_reloc(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct relocation_info *reloc;
+ int size;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0);
+ size = hdrp->a_trsize + hdrp->a_drsize;
+ reloc = (struct relocation_info*)xmalloc(size);
+ if (reloc == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ if (read(fd, reloc, size) != size) {
+ dln_errno = errno;
+ free(reloc);
+ return NULL;
+ }
+
+ return reloc;
+}
+
+static struct nlist *
+load_sym(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct nlist * buffer;
+ struct nlist * sym;
+ struct nlist * end;
+ long displ;
+ int size;
+
+ lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0);
+ if (read(fd, &size, sizeof(int)) != sizeof(int)) {
+ goto err_noexec;
+ }
+
+ buffer = (struct nlist*)xmalloc(hdrp->a_syms + size);
+ if (buffer == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ lseek(fd, disp + N_SYMOFF(*hdrp), 0);
+ if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) {
+ free(buffer);
+ goto err_noexec;
+ }
+
+ sym = buffer;
+ end = sym + hdrp->a_syms / sizeof(struct nlist);
+ displ = (long)buffer + (long)(hdrp->a_syms);
+
+ while (sym < end) {
+ sym->n_un.n_name = (char*)sym->n_un.n_strx + displ;
+ sym++;
+ }
+ return buffer;
+
+ err_noexec:
+ dln_errno = DLN_ENOEXEC;
+ return NULL;
+}
+
+static st_table *
+sym_hash(hdrp, syms)
+ struct exec *hdrp;
+ struct nlist *syms;
+{
+ st_table *tbl;
+ struct nlist *sym = syms;
+ struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist));
+
+ tbl = st_init_strtable();
+ if (tbl == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ while (sym < end) {
+ st_insert(tbl, sym->n_un.n_name, sym);
+ sym++;
+ }
+ return tbl;
+}
+
+static int
+dln_init(prog)
+ const char *prog;
+{
+ char *file;
+ int fd;
+ struct exec hdr;
+ struct nlist *syms;
+
+ if (dln_init_p == 1) return 0;
+
+ file = dln_find_exe(prog, NULL);
+ if (file == NULL || (fd = open(file, O_RDONLY)) < 0) {
+ dln_errno = errno;
+ return -1;
+ }
+
+ if (load_header(fd, &hdr, 0) == -1) return -1;
+ syms = load_sym(fd, &hdr, 0);
+ if (syms == NULL) {
+ close(fd);
+ return -1;
+ }
+ sym_tbl = sym_hash(&hdr, syms);
+ if (sym_tbl == NULL) { /* file may be start with #! */
+ char c = '\0';
+ char buf[MAXPATHLEN];
+ char *p;
+
+ free(syms);
+ lseek(fd, 0L, 0);
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '#') goto err_noexec;
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '!') goto err_noexec;
+
+ p = buf;
+ /* skip forwarding spaces */
+ while (read(fd, &c, 1) == 1) {
+ if (c == '\n') goto err_noexec;
+ if (c != '\t' && c != ' ') {
+ *p++ = c;
+ break;
+ }
+ }
+ /* read in command name */
+ while (read(fd, p, 1) == 1) {
+ if (*p == '\n' || *p == '\t' || *p == ' ') break;
+ p++;
+ if (p-buf >= MAXPATHLEN) {
+ dln_errno = ENAMETOOLONG;
+ return -1;
+ }
+ }
+ *p = '\0';
+
+ return dln_init(buf);
+ }
+ dln_init_p = 1;
+ undef_tbl = st_init_strtable();
+ close(fd);
+ return 0;
+
+ err_noexec:
+ close(fd);
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+}
+
+static long
+load_text_data(fd, hdrp, bss, disp)
+ int fd;
+ struct exec *hdrp;
+ int bss;
+ long disp;
+{
+ int size;
+ unsigned char* addr;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp), 0);
+ size = hdrp->a_text + hdrp->a_data;
+
+ if (bss == -1) size += hdrp->a_bss;
+ else if (bss > 1) size += bss;
+
+ addr = (unsigned char*)xmalloc(size);
+ if (addr == NULL) {
+ dln_errno = errno;
+ return 0;
+ }
+
+ if (read(fd, addr, size) != size) {
+ dln_errno = errno;
+ free(addr);
+ return 0;
+ }
+
+ if (bss == -1) {
+ memset(addr + hdrp->a_text + hdrp->a_data, 0, hdrp->a_bss);
+ }
+ else if (bss > 0) {
+ memset(addr + hdrp->a_text + hdrp->a_data, 0, bss);
+ }
+
+ return (long)addr;
+}
+
+static int
+undef_print(key, value)
+ char *key, *value;
+{
+ fprintf(stderr, " %s\n", key);
+ return ST_CONTINUE;
+}
+
+static void
+dln_print_undef()
+{
+ fprintf(stderr, " Undefined symbols:\n");
+ st_foreach(undef_tbl, undef_print, NULL);
+}
+
+static void
+dln_undefined()
+{
+ if (undef_tbl->num_entries > 0) {
+ fprintf(stderr, "dln: Calling undefined function\n");
+ dln_print_undef();
+ rb_exit(1);
+ }
+}
+
+struct undef {
+ char *name;
+ struct relocation_info reloc;
+ long base;
+ char *addr;
+ union {
+ char c;
+ short s;
+ long l;
+ } u;
+};
+
+static st_table *reloc_tbl = NULL;
+static void
+link_undef(name, base, reloc)
+ const char *name;
+ long base;
+ struct relocation_info *reloc;
+{
+ static int u_no = 0;
+ struct undef *obj;
+ char *addr = (char*)(reloc->r_address + base);
+
+ obj = (struct undef*)xmalloc(sizeof(struct undef));
+ obj->name = strdup(name);
+ obj->reloc = *reloc;
+ obj->base = base;
+ switch (R_LENGTH(reloc)) {
+ case 0: /* byte */
+ obj->u.c = *addr;
+ break;
+ case 1: /* word */
+ obj->u.s = *(short*)addr;
+ break;
+ case 2: /* long */
+ obj->u.l = *(long*)addr;
+ break;
+ }
+ if (reloc_tbl == NULL) {
+ reloc_tbl = st_init_numtable();
+ }
+ st_insert(reloc_tbl, u_no++, obj);
+}
+
+struct reloc_arg {
+ const char *name;
+ long value;
+};
+
+static int
+reloc_undef(no, undef, arg)
+ int no;
+ struct undef *undef;
+ struct reloc_arg *arg;
+{
+ int datum;
+ char *address;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE;
+ address = (char*)(undef->base + undef->reloc.r_address);
+ datum = arg->value;
+
+ if (R_PCREL(&(undef->reloc))) datum -= undef->base;
+#if defined(sun) && defined(sparc)
+ datum += undef->reloc.r_addend;
+ datum >>= R_RIGHTSHIFT(&(undef->reloc));
+ mask = (1 << R_BITSIZE(&(undef->reloc))) - 1;
+ mask |= mask -1;
+ datum &= mask;
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0:
+ *address = undef->u.c;
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address = undef->u.s;
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address = undef->u.l;
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0: /* byte */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *address = datum - *address;
+ else *address = undef->u.c + datum;
+ break;
+ case 1: /* word */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *(short*)address = datum - *(short*)address;
+ else *(short*)address = undef->u.s + datum;
+ break;
+ case 2: /* long */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *(long*)address = datum - *(long*)address;
+ else *(long*)address = undef->u.l + datum;
+ break;
+ }
+#endif
+ free(undef->name);
+ free(undef);
+ return ST_DELETE;
+}
+
+static void
+unlink_undef(name, value)
+ const char *name;
+ long value;
+{
+ struct reloc_arg arg;
+
+ arg.name = name;
+ arg.value = value;
+ st_foreach(reloc_tbl, reloc_undef, &arg);
+}
+
+#ifdef N_INDR
+struct indr_data {
+ char *name0, *name1;
+};
+
+static int
+reloc_repl(no, undef, data)
+ int no;
+ struct undef *undef;
+ struct indr_data *data;
+{
+ if (strcmp(data->name0, undef->name) == 0) {
+ free(undef->name);
+ undef->name = strdup(data->name1);
+ }
+ return ST_CONTINUE;
+}
+#endif
+
+static int
+load_1(fd, disp, need_init)
+ int fd;
+ long disp;
+ const char *need_init;
+{
+ static char *libc = LIBC_NAME;
+ struct exec hdr;
+ struct relocation_info *reloc = NULL;
+ long block = 0;
+ long new_common = 0; /* Length of new common */
+ struct nlist *syms = NULL;
+ struct nlist *sym;
+ struct nlist *end;
+ int init_p = 0;
+
+ if (load_header(fd, &hdr, disp) == -1) return -1;
+ if (INVALID_OBJECT(hdr)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ reloc = load_reloc(fd, &hdr, disp);
+ if (reloc == NULL) return -1;
+
+ syms = load_sym(fd, &hdr, disp);
+ if (syms == NULL) {
+ free(reloc);
+ return -1;
+ }
+
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ struct nlist *old_sym;
+ int value = sym->n_value;
+
+#ifdef N_INDR
+ if (sym->n_type == (N_INDR | N_EXT)) {
+ char *key = sym->n_un.n_name;
+
+ if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) {
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
+ unlink_undef(key, old_sym->n_value);
+ free(key);
+ }
+ }
+ else {
+ struct indr_data data;
+
+ data.name0 = sym->n_un.n_name;
+ data.name1 = sym[1].n_un.n_name;
+ st_foreach(reloc_tbl, reloc_repl, &data);
+
+ st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL);
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
+ free(key);
+ }
+ }
+ sym += 2;
+ continue;
+ }
+#endif
+ if (sym->n_type == (N_UNDF | N_EXT)) {
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) {
+ old_sym = NULL;
+ }
+
+ if (value) {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ int rnd =
+ value >= sizeof(double) ? sizeof(double) - 1
+ : value >= sizeof(long) ? sizeof(long) - 1
+ : sizeof(short) - 1;
+
+ sym->n_type = N_COMM;
+ new_common += rnd;
+ new_common &= ~(long)rnd;
+ sym->n_value = new_common;
+ new_common += value;
+ }
+ }
+ else {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ sym->n_value = (long)dln_undefined;
+ st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL);
+ }
+ }
+ }
+ sym++;
+ }
+
+ block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp);
+ if (block == 0) goto err_exit;
+
+ sym = syms;
+ while (sym < end) {
+ struct nlist *new_sym;
+ char *key;
+
+ switch (sym->n_type) {
+ case N_COMM:
+ sym->n_value += hdr.a_text + hdr.a_data;
+ case N_TEXT|N_EXT:
+ case N_DATA|N_EXT:
+
+ sym->n_value += block;
+
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0
+ && new_sym->n_value != (long)dln_undefined) {
+ dln_errno = DLN_ECONFL;
+ goto err_exit;
+ }
+
+ key = sym->n_un.n_name;
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL) != 0) {
+ unlink_undef(key, sym->n_value);
+ free(key);
+ }
+
+ new_sym = (struct nlist*)xmalloc(sizeof(struct nlist));
+ *new_sym = *sym;
+ new_sym->n_un.n_name = strdup(sym->n_un.n_name);
+ st_insert(sym_tbl, new_sym->n_un.n_name, new_sym);
+ break;
+
+ case N_TEXT:
+ case N_DATA:
+ sym->n_value += block;
+ break;
+ }
+ sym++;
+ }
+
+ /*
+ * First comes the text-relocation
+ */
+ {
+ struct relocation_info * rel = reloc;
+ struct relocation_info * rel_beg = reloc +
+ (hdr.a_trsize/sizeof(struct relocation_info));
+ struct relocation_info * rel_end = reloc +
+ (hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info);
+
+ while (rel < rel_end) {
+ char *address = (char*)(rel->r_address + block);
+ long datum = 0;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if(rel >= rel_beg)
+ address += hdr.a_text;
+
+ if (rel->r_extern) { /* Look it up in symbol-table */
+ sym = &(syms[R_SYMBOL(rel)]);
+ switch (sym->n_type) {
+ case N_EXT|N_UNDF:
+ link_undef(sym->n_un.n_name, block, rel);
+ case N_EXT|N_COMM:
+ case N_COMM:
+ datum = sym->n_value;
+ break;
+ default:
+ goto err_exit;
+ }
+ } /* end.. look it up */
+ else { /* is static */
+ switch (R_SYMBOL(rel)) {
+ case N_TEXT:
+ case N_DATA:
+ datum = block;
+ break;
+ case N_BSS:
+ datum = block + new_common;
+ break;
+ case N_ABS:
+ break;
+ }
+ } /* end .. is static */
+ if (R_PCREL(rel)) datum -= block;
+
+#if defined(sun) && defined(sparc)
+ datum += rel->r_addend;
+ datum >>= R_RIGHTSHIFT(rel);
+ mask = (1 << R_BITSIZE(rel)) - 1;
+ mask |= mask -1;
+ datum &= mask;
+
+ switch (R_LENGTH(rel)) {
+ case 0:
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(rel)) {
+ case 0: /* byte */
+ if (datum < -128 || datum > 127) goto err_exit;
+ *address += datum;
+ break;
+ case 1: /* word */
+ *(short *)address += datum;
+ break;
+ case 2: /* long */
+ *(long *)address += datum;
+ break;
+ }
+#endif
+ rel++;
+ }
+ }
+
+ if (need_init) {
+ int len;
+ char **libs_to_be_linked = 0;
+ char *buf;
+
+ if (undef_tbl->num_entries > 0) {
+ if (load_lib(libc) == -1) goto err_exit;
+ }
+
+ init_funcname(&buf, need_init);
+ len = strlen(buf);
+
+ for (sym = syms; sym<end; sym++) {
+ char *name = sym->n_un.n_name;
+ if (name[0] == '_' && sym->n_value >= block) {
+ if (strcmp(name+1, "dln_libs_to_be_linked") == 0) {
+ libs_to_be_linked = (char**)sym->n_value;
+ }
+ else if (strcmp(name+1, buf) == 0) {
+ init_p = 1;
+ ((int (*)())sym->n_value)();
+ }
+ }
+ }
+ if (libs_to_be_linked && undef_tbl->num_entries > 0) {
+ while (*libs_to_be_linked) {
+ load_lib(*libs_to_be_linked);
+ libs_to_be_linked++;
+ }
+ }
+ }
+ free(reloc);
+ free(syms);
+ if (need_init) {
+ if (init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+ if (undef_tbl->num_entries > 0) {
+ if (load_lib(libc) == -1) goto err_exit;
+ if (undef_tbl->num_entries > 0) {
+ dln_errno = DLN_EUNDEF;
+ return -1;
+ }
+ }
+ }
+ return 0;
+
+ err_exit:
+ if (syms) free(syms);
+ if (reloc) free(reloc);
+ if (block) free((char*)block);
+ return -1;
+}
+
+static int target_offset;
+static int
+search_undef(key, value, lib_tbl)
+ const char *key;
+ int value;
+ st_table *lib_tbl;
+{
+ long offset;
+
+ if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE;
+ target_offset = offset;
+ return ST_STOP;
+}
+
+struct symdef {
+ int rb_str_index;
+ int lib_offset;
+};
+
+char *dln_librrb_ary_path = DLN_DEFAULT_LIB_PATH;
+
+static int
+load_lib(lib)
+ const char *lib;
+{
+ char *path, *file;
+ char armagic[SARMAG];
+ int fd, size;
+ struct ar_hdr ahdr;
+ st_table *lib_tbl = NULL;
+ int *data, nsym;
+ struct symdef *base;
+ char *name_base;
+
+ if (dln_init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+
+ if (undef_tbl->num_entries == 0) return 0;
+ dln_errno = DLN_EBADLIB;
+
+ if (lib[0] == '-' && lib[1] == 'l') {
+ long len = strlen(lib) + 4;
+ char *p = alloca(len);
+ snprintf(p, len, "lib%s.a", lib+2);
+ lib = p;
+ }
+
+ /* library search path: */
+ /* look for environment variable DLN_LIBRARY_PATH first. */
+ /* then variable dln_librrb_ary_path. */
+ /* if path is still NULL, use "." for path. */
+ path = getenv("DLN_LIBRARY_PATH");
+ if (path == NULL) path = dln_librrb_ary_path;
+
+ file = dln_find_file(lib, path);
+ fd = open(file, O_RDONLY);
+ if (fd == -1) goto syserr;
+ size = read(fd, armagic, SARMAG);
+ if (size == -1) goto syserr;
+
+ if (size != SARMAG) {
+ dln_errno = DLN_ENOTLIB;
+ goto badlib;
+ }
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+
+ if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) {
+ /* make hash table from __.SYMDEF */
+
+ lib_tbl = st_init_strtable();
+ data = (int*)xmalloc(size);
+ if (data == NULL) goto syserr;
+ size = read(fd, data, size);
+ nsym = *data / sizeof(struct symdef);
+ base = (struct symdef*)(data + 1);
+ name_base = (char*)(base + nsym) + sizeof(int);
+ while (nsym > 0) {
+ char *name = name_base + base->rb_str_index;
+
+ st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr));
+ nsym--;
+ base++;
+ }
+ for (;;) {
+ target_offset = -1;
+ st_foreach(undef_tbl, search_undef, lib_tbl);
+ if (target_offset == -1) break;
+ if (load_1(fd, target_offset, 0) == -1) {
+ st_free_table(lib_tbl);
+ free(data);
+ goto badlib;
+ }
+ if (undef_tbl->num_entries == 0) break;
+ }
+ free(data);
+ st_free_table(lib_tbl);
+ }
+ else {
+ /* linear library, need to scan (FUTURE) */
+
+ for (;;) {
+ int offset = SARMAG;
+ int found = 0;
+ struct exec hdr;
+ struct nlist *syms, *sym, *end;
+
+ while (undef_tbl->num_entries > 0) {
+ found = 0;
+ lseek(fd, offset, 0);
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size == 0) break;
+ if (size != sizeof(ahdr)
+ || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+ offset += sizeof(ahdr);
+ if (load_header(fd, &hdr, offset) == -1)
+ goto badlib;
+ syms = load_sym(fd, &hdr, offset);
+ if (syms == NULL) goto badlib;
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ if (sym->n_type == N_EXT|N_TEXT
+ && st_lookup(undef_tbl, sym->n_un.n_name, NULL)) {
+ break;
+ }
+ sym++;
+ }
+ if (sym < end) {
+ found++;
+ free(syms);
+ if (load_1(fd, offset, 0) == -1) {
+ goto badlib;
+ }
+ }
+ offset += size;
+ if (offset & 1) offset++;
+ }
+ if (found) break;
+ }
+ }
+ close(fd);
+ return 0;
+
+ syserr:
+ dln_errno = errno;
+ badlib:
+ if (fd >= 0) close(fd);
+ return -1;
+}
+
+static int
+load(file)
+ const char *file;
+{
+ int fd;
+ int result;
+
+ if (dln_init_p == 0) {
+ if (dln_init(dln_argv0) == -1) return -1;
+ }
+ result = strlen(file);
+ if (file[result-1] == 'a') {
+ return load_lib(file);
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ result = load_1(fd, 0, file);
+ close(fd);
+
+ return result;
+}
+
+void*
+dln_sym(name)
+ const char *name;
+{
+ struct nlist *sym;
+
+ if (st_lookup(sym_tbl, name, &sym))
+ return (void*)sym->n_value;
+ return NULL;
+}
+
+#endif /* USE_DLN_A_OUT */
+
+#ifdef USE_DLN_DLOPEN
+# if defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ < 105000000
+# include <nlist.h>
+# include <link.h>
+# else
+# include <dlfcn.h>
+# endif
+#endif
+
+#ifdef __hpux
+#include <errno.h>
+#include "dl.h"
+#endif
+
+#if defined(_AIX)
+#include <ctype.h> /* for isdigit() */
+#include <errno.h> /* for global errno */
+#include <sys/ldr.h>
+#endif
+
+#ifdef NeXT
+#if NS_TARGET_MAJOR < 4
+#include <mach-o/rld.h>
+#else
+#include <mach-o/dyld.h>
+#ifndef NSLINKMODULE_OPTION_BINDNOW
+#define NSLINKMODULE_OPTION_BINDNOW 1
+#endif
+#endif
+#else
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+#include <windows.h>
+#endif
+
+#ifdef _WIN32_WCE
+#undef FormatMessage
+#define FormatMessage FormatMessageA
+#undef LoadLibrary
+#define LoadLibrary LoadLibraryA
+#undef GetProcAddress
+#define GetProcAddress GetProcAddressA
+#endif
+
+static const char *
+dln_strerror()
+{
+#ifdef USE_DLN_A_OUT
+ char *strerror();
+
+ switch (dln_errno) {
+ case DLN_ECONFL:
+ return "Symbol name conflict";
+ case DLN_ENOINIT:
+ return "No initializer given";
+ case DLN_EUNDEF:
+ return "Unresolved symbols";
+ case DLN_ENOTLIB:
+ return "Not a library file";
+ case DLN_EBADLIB:
+ return "Malformed library file";
+ case DLN_EINIT:
+ return "Not initialized";
+ default:
+ return strerror(dln_errno);
+ }
+#endif
+
+#ifdef USE_DLN_DLOPEN
+ return (char*)dlerror();
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+ static char message[1024];
+ int error = GetLastError();
+ char *p = message;
+ p += sprintf(message, "%d: ", error);
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ p,
+ sizeof message - strlen(message),
+ NULL);
+
+ for (p = message; *p; p++) {
+ if (*p == '\n' || *p == '\r')
+ *p = ' ';
+ }
+ return message;
+#endif
+}
+
+
+#if defined(_AIX) && ! defined(_IA64)
+static void
+aix_loaderror(const char *pathname)
+{
+ char *message[8], errbuf[1024];
+ int i,j;
+
+ struct errtab {
+ int errnum;
+ char *errstr;
+ } load_errtab[] = {
+ {L_ERROR_TOOMANY, "too many errors, rest skipped."},
+ {L_ERROR_NOLIB, "can't load library:"},
+ {L_ERROR_UNDEF, "can't find symbol in library:"},
+ {L_ERROR_RLDBAD,
+ "RLD index out of range or bad relocation type:"},
+ {L_ERROR_FORMAT, "not a valid, executable xcoff file:"},
+ {L_ERROR_MEMBER,
+ "file not an archive or does not contain requested member:"},
+ {L_ERROR_TYPE, "symbol table mismatch:"},
+ {L_ERROR_ALIGN, "text alignment in file is wrong."},
+ {L_ERROR_SYSTEM, "System error:"},
+ {L_ERROR_ERRNO, NULL}
+ };
+
+#define LOAD_ERRTAB_LEN (sizeof(load_errtab)/sizeof(load_errtab[0]))
+#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1)
+
+ snprintf(errbuf, 1024, "load failed - %s ", pathname);
+
+ if (!loadquery(1, &message[0], sizeof(message)))
+ ERRBUF_APPEND(strerror(errno));
+ for(i = 0; message[i] && *message[i]; i++) {
+ int nerr = atoi(message[i]);
+ for (j=0; j<LOAD_ERRTAB_LEN; j++) {
+ if (nerr == load_errtab[i].errnum && load_errtab[i].errstr)
+ ERRBUF_APPEND(load_errtab[i].errstr);
+ }
+ while (isdigit(*message[i])) message[i]++;
+ ERRBUF_APPEND(message[i]);
+ ERRBUF_APPEND("\n");
+ }
+ errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */
+ rb_loaderror(errbuf);
+ return;
+}
+#endif
+
+#if defined(__VMS)
+#include <starlet.h>
+#include <rms.h>
+#include <stsdef.h>
+#include <unixlib.h>
+#include <descrip.h>
+#include <lib$routines.h>
+
+static char *vms_filespec;
+static int vms_fileact(char *filespec, int type);
+static long vms_fisexh(long *sigarr, long *mecarr);
+#endif
+
+#endif /* NO_DLN_LOAD */
+
+void*
+dln_load(file)
+ const char *file;
+{
+#ifdef NO_DLN_LOAD
+ rb_raise(rb_eLoadError, "this executable file can't load extension libraries");
+#else
+
+#if !defined(_AIX) && !defined(NeXT)
+ const char *error = 0;
+#define DLN_ERROR() (error = dln_strerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+ HINSTANCE handle;
+ char winfile[MAXPATHLEN];
+ void (*init_fct)();
+ char *buf;
+
+ if (strlen(file) >= MAXPATHLEN) rb_loaderror("filename too long");
+
+ /* Load the file as an object one */
+ init_funcname(&buf, file);
+
+ strcpy(winfile, file);
+
+ /* Load file */
+ if ((handle = LoadLibrary(winfile)) == NULL) {
+ error = dln_strerror();
+ goto failed;
+ }
+
+ if ((init_fct = (void(*)())GetProcAddress(handle, buf)) == NULL) {
+ rb_loaderror("%s - %s\n%s", dln_strerror(), buf, file);
+ }
+
+ /* Call the init code */
+ (*init_fct)();
+ return handle;
+#else
+#ifdef USE_DLN_A_OUT
+ if (load(file) == -1) {
+ error = dln_strerror();
+ goto failed;
+ }
+ return 0;
+#else
+
+ char *buf;
+ /* Load the file as an object one */
+ init_funcname(&buf, file);
+
+#ifdef USE_DLN_DLOPEN
+#define DLN_DEFINED
+ {
+ void *handle;
+ void (*init_fct)();
+
+#ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+#endif
+#ifdef __INTERIX
+# undef RTLD_GLOBAL
+#endif
+#ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+#endif
+
+ /* Load file */
+ if ((handle = (void*)dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) {
+ error = dln_strerror();
+ goto failed;
+ }
+
+ init_fct = (void(*)())dlsym(handle, buf);
+ if (init_fct == NULL) {
+ error = DLN_ERROR();
+ dlclose(handle);
+ goto failed;
+ }
+ /* Call the init code */
+ (*init_fct)();
+
+ return handle;
+ }
+#endif /* USE_DLN_DLOPEN */
+
+#ifdef __hpux
+#define DLN_DEFINED
+ {
+ shl_t lib = NULL;
+ int flags;
+ void (*init_fct)();
+
+ flags = BIND_DEFERRED;
+ lib = shl_load(file, flags, 0);
+ if (lib == NULL) {
+ extern int errno;
+ rb_loaderror("%s - %s", strerror(errno), file);
+ }
+ shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct);
+ if (init_fct == NULL) {
+ shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct);
+ if (init_fct == NULL) {
+ errno = ENOSYM;
+ rb_loaderror("%s - %s", strerror(ENOSYM), file);
+ }
+ }
+ (*init_fct)();
+ return (void*)lib;
+ }
+#endif /* hpux */
+
+#if defined(_AIX) && ! defined(_IA64)
+#define DLN_DEFINED
+ {
+ void (*init_fct)();
+
+ init_fct = (void(*)())load((char*)file, 1, 0);
+ if (init_fct == NULL) {
+ aix_loaderror(file);
+ }
+ if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
+ aix_loaderror(file);
+ }
+ (*init_fct)();
+ return (void*)init_fct;
+ }
+#endif /* _AIX */
+
+#if defined(NeXT) || defined(__APPLE__)
+#define DLN_DEFINED
+/*----------------------------------------------------
+ By SHIROYAMA Takayuki Psi@fortune.nest.or.jp
+
+ Special Thanks...
+ Yu tomoak-i@is.aist-nara.ac.jp,
+ Mi hisho@tasihara.nest.or.jp,
+ sunshine@sunshineco.com,
+ and... Miss ARAI Akino(^^;)
+ ----------------------------------------------------*/
+#if defined(NeXT) && (NS_TARGET_MAJOR < 4)/* NeXTSTEP rld functions */
+
+ {
+ NXStream* s;
+ unsigned long init_address;
+ char *object_files[2] = {NULL, NULL};
+
+ void (*init_fct)();
+
+ object_files[0] = (char*)file;
+
+ s = NXOpenFile(2,NX_WRITEONLY);
+
+ /* Load object file, if return value ==0 , load failed*/
+ if(rld_load(s, NULL, object_files, NULL) == 0) {
+ NXFlush(s);
+ NXClose(s);
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ /* lookup the initial function */
+ if(rld_lookup(s, buf, &init_address) == 0) {
+ NXFlush(s);
+ NXClose(s);
+ rb_loaderror("Failed to lookup Init function %.200s", file);
+ }
+
+ NXFlush(s);
+ NXClose(s);
+
+ /* Cannot call *init_address directory, so copy this value to
+ funtion pointer */
+ init_fct = (void(*)())init_address;
+ (*init_fct)();
+ return (void*)init_address;
+ }
+#else/* OPENSTEP dyld functions */
+ {
+ int dyld_result;
+ NSObjectFileImage obj_file; /* handle, but not use it */
+ /* "file" is module file name .
+ "buf" is pointer to initial function name with "_" . */
+
+ void (*init_fct)();
+
+
+ dyld_result = NSCreateObjectFileImageFromFile(file, &obj_file);
+
+ if (dyld_result != NSObjectFileImageSuccess) {
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ NSLinkModule(obj_file, file, NSLINKMODULE_OPTION_BINDNOW);
+
+ /* lookup the initial function */
+ if(!NSIsSymbolNameDefined(buf)) {
+ rb_loaderror("Failed to lookup Init function %.200s",file);
+ }
+ init_fct = NSAddressOfSymbol(NSLookupAndBindSymbol(buf));
+ (*init_fct)();
+
+ return (void*)init_fct;
+ }
+#endif /* rld or dyld */
+#endif
+
+#ifdef __BEOS__
+# define DLN_DEFINED
+ {
+ status_t err_stat; /* BeOS error status code */
+ image_id img_id; /* extention module unique id */
+ void (*init_fct)(); /* initialize function for extention module */
+
+ /* load extention module */
+ img_id = load_add_on(file);
+ if (img_id <= 0) {
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ /* find symbol for module initialize function. */
+ /* The Be Book KernelKit Images section described to use
+ B_SYMBOL_TYPE_TEXT for symbol of function, not
+ B_SYMBOL_TYPE_CODE. Why ? */
+ /* strcat(init_fct_symname, "__Fv"); */ /* parameter nothing. */
+ /* "__Fv" dont need! The Be Book Bug ? */
+ err_stat = get_image_symbol(img_id, buf,
+ B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
+
+ if (err_stat != B_NO_ERROR) {
+ char real_name[MAXPATHLEN];
+
+ strcpy(real_name, buf);
+ strcat(real_name, "__Fv");
+ err_stat = get_image_symbol(img_id, real_name,
+ B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
+ }
+
+ if ((B_BAD_IMAGE_ID == err_stat) || (B_BAD_INDEX == err_stat)) {
+ unload_add_on(img_id);
+ rb_loaderror("Failed to lookup Init function %.200s", file);
+ }
+ else if (B_NO_ERROR != err_stat) {
+ char errmsg[] = "Internal of BeOS version. %.200s (symbol_name = %s)";
+ unload_add_on(img_id);
+ rb_loaderror(errmsg, strerror(err_stat), buf);
+ }
+
+ /* call module initialize function. */
+ (*init_fct)();
+ return (void*)img_id;
+ }
+#endif /* __BEOS__*/
+
+#ifdef __MACOS__
+# define DLN_DEFINED
+ {
+ OSErr err;
+ FSSpec libspec;
+ CFragConnectionID connID;
+ Ptr mainAddr;
+ char errMessage[1024];
+ Boolean isfolder, didsomething;
+ Str63 fragname;
+ Ptr symAddr;
+ CFragSymbolClass class;
+ void (*init_fct)();
+ char fullpath[MAXPATHLEN];
+
+ strcpy(fullpath, file);
+
+ /* resolve any aliases to find the real file */
+ c2pstr(fullpath);
+ (void)FSMakeFSSpec(0, 0, fullpath, &libspec);
+ err = ResolveAliasFile(&libspec, 1, &isfolder, &didsomething);
+ if (err) {
+ rb_loaderror("Unresolved Alias - %s", file);
+ }
+
+ /* Load the fragment (or return the connID if it is already loaded */
+ fragname[0] = 0;
+ err = GetDiskFragment(&libspec, 0, 0, fragname,
+ kLoadCFrag, &connID, &mainAddr,
+ errMessage);
+ if (err) {
+ p2cstr(errMessage);
+ rb_loaderror("%s - %s",errMessage , file);
+ }
+
+ /* Locate the address of the correct init function */
+ c2pstr(buf);
+ err = FindSymbol(connID, buf, &symAddr, &class);
+ if (err) {
+ rb_loaderror("Unresolved symbols - %s" , file);
+ }
+ init_fct = (void (*)())symAddr;
+ (*init_fct)();
+ return (void*)init_fct;
+ }
+#endif /* __MACOS__ */
+
+#if defined(__VMS)
+#define DLN_DEFINED
+ {
+ long status;
+ void (*init_fct)();
+ char *fname, *p1, *p2;
+
+ $DESCRIPTOR(fname_d, "");
+ $DESCRIPTOR(image_d, "");
+ $DESCRIPTOR(buf_d, "");
+
+ decc$to_vms(file, vms_fileact, 0, 0);
+
+ fname = (char *)__alloca(strlen(file)+1);
+ strcpy(fname,file);
+ if (p1 = strrchr(fname,'/'))
+ fname = p1 + 1;
+ if (p2 = strrchr(fname,'.'))
+ *p2 = '\0';
+
+ fname_d.dsc$w_length = strlen(fname);
+ fname_d.dsc$a_pointer = fname;
+ image_d.dsc$w_length = strlen(vms_filespec);
+ image_d.dsc$a_pointer = vms_filespec;
+ buf_d.dsc$w_length = strlen(buf);
+ buf_d.dsc$a_pointer = buf;
+
+ lib$establish(vms_fisexh);
+
+ status = lib$find_image_symbol (
+ &fname_d,
+ &buf_d,
+ &init_fct,
+ &image_d);
+
+ lib$establish(0);
+
+ if (status == RMS$_FNF) {
+ error = dln_strerror();
+ goto failed;
+ } else if (!$VMS_STATUS_SUCCESS(status)) {
+ error = DLN_ERROR();
+ goto failed;
+ }
+
+ /* Call the init code */
+ (*init_fct)();
+
+ return 1;
+ }
+#endif /* __VMS */
+
+#ifndef DLN_DEFINED
+ rb_notimplement();
+#endif
+
+#endif /* USE_DLN_A_OUT */
+#endif
+#if !defined(_AIX) && !defined(NeXT)
+ failed:
+ rb_loaderror("%s - %s", error, file);
+#endif
+
+#endif /* NO_DLN_LOAD */
+ return 0; /* dummy return */
+}
+
+static char *dln_find_1();
+
+char *
+dln_find_exe(fname, path)
+ const char *fname;
+ const char *path;
+{
+ if (!path) {
+ path = getenv(PATH_ENV);
+ }
+
+ if (!path) {
+#if defined(MSDOS) || defined(_WIN32) || defined(__human68k__) || defined(__MACOS__)
+ path = "/usr/local/bin;/usr/ucb;/usr/bin;/bin;.";
+#else
+ path = "/usr/local/bin:/usr/ucb:/usr/bin:/bin:.";
+#endif
+ }
+ return dln_find_1(fname, path, 1);
+}
+
+char *
+dln_find_file(fname, path)
+ const char *fname;
+ const char *path;
+{
+#ifndef __MACOS__
+ if (!path) path = ".";
+ return dln_find_1(fname, path, 0);
+#else
+ if (!path) path = ".";
+ return _macruby_path_conv_posix_to_macos(dln_find_1(fname, path, 0));
+#endif
+}
+
+static char fbuf[MAXPATHLEN];
+
+static char *
+dln_find_1(fname, path, exe_flag)
+ char *fname;
+ char *path;
+ int exe_flag; /* non 0 if looking for executable. */
+{
+ register char *dp;
+ register char *ep;
+ register char *bp;
+ struct stat st;
+#ifdef __MACOS__
+ const char* mac_fullpath;
+#endif
+
+ if (!fname) return fname;
+ if (fname[0] == '/') return fname;
+ if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0)
+ return fname;
+ if (exe_flag && strchr(fname, '/')) return fname;
+#ifdef DOSISH
+ if (fname[0] == '\\') return fname;
+# ifdef DOSISH_DRIVE_LETTER
+ if (strlen(fname) > 2 && fname[1] == ':') return fname;
+# endif
+ if (strncmp(".\\", fname, 2) == 0 || strncmp("..\\", fname, 3) == 0)
+ return fname;
+ if (exe_flag && strchr(fname, '\\')) return fname;
+#endif
+
+ for (dp = path;; dp = ++ep) {
+ register int l;
+ int i;
+ int fspace;
+
+ /* extract a component */
+ ep = strchr(dp, PATH_SEP[0]);
+ if (ep == NULL)
+ ep = dp+strlen(dp);
+
+ /* find the length of that component */
+ l = ep - dp;
+ bp = fbuf;
+ fspace = sizeof fbuf - 2;
+ if (l > 0) {
+ /*
+ ** If the length of the component is zero length,
+ ** start from the current directory. If the
+ ** component begins with "~", start from the
+ ** user's $HOME environment variable. Otherwise
+ ** take the path literally.
+ */
+
+ if (*dp == '~' && (l == 1 ||
+#if defined(DOSISH)
+ dp[1] == '\\' ||
+#endif
+ dp[1] == '/')) {
+ char *home;
+
+ home = getenv("HOME");
+ if (home != NULL) {
+ i = strlen(home);
+ if ((fspace -= i) < 0)
+ goto toolong;
+ memcpy(bp, home, i);
+ bp += i;
+ }
+ dp++;
+ l--;
+ }
+ if (l > 0) {
+ if ((fspace -= l) < 0)
+ goto toolong;
+ memcpy(bp, dp, l);
+ bp += l;
+ }
+
+ /* add a "/" between directory and filename */
+ if (ep[-1] != '/')
+ *bp++ = '/';
+ }
+
+ /* now append the file name */
+ i = strlen(fname);
+ if ((fspace -= i) < 0) {
+ toolong:
+ fprintf(stderr, "openpath: pathname too long (ignored)\n");
+ *bp = '\0';
+ fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
+ fprintf(stderr, "\tFile \"%s\"\n", fname);
+ goto next;
+ }
+ memcpy(bp, fname, i + 1);
+
+#if defined(DOSISH)
+ if (exe_flag) {
+ static const char extension[][5] = {
+#if defined(MSDOS)
+ ".com", ".exe", ".bat",
+#if defined(DJGPP)
+ ".btm", ".sh", ".ksh", ".pl", ".sed",
+#endif
+#elif defined(__EMX__) || defined(_WIN32)
+ ".exe", ".com", ".cmd", ".bat",
+/* end of __EMX__ or _WIN32 */
+#else
+ ".r", ".R", ".x", ".X", ".bat", ".BAT",
+/* __human68k__ */
+#endif
+ };
+ int j;
+
+ for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) {
+ if (fspace < strlen(extension[j])) {
+ fprintf(stderr, "openpath: pathname too long (ignored)\n");
+ fprintf(stderr, "\tDirectory \"%.*s\"\n", (int) (bp - fbuf), fbuf);
+ fprintf(stderr, "\tFile \"%s%s\"\n", fname, extension[j]);
+ continue;
+ }
+ strcpy(bp + i, extension[j]);
+#ifndef __MACOS__
+ if (stat(fbuf, &st) == 0)
+ return fbuf;
+#else
+ if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf))
+ return mac_fullpath;
+
+#endif
+ }
+ goto next;
+ }
+#endif /* MSDOS or _WIN32 or __human68k__ or __EMX__ */
+
+#ifndef __MACOS__
+ if (stat(fbuf, &st) == 0) {
+ if (exe_flag == 0) return fbuf;
+ /* looking for executable */
+ if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0)
+ return fbuf;
+ }
+#else
+ if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf)) {
+ if (exe_flag == 0) return mac_fullpath;
+ /* looking for executable */
+ if (stat(mac_fullpath, &st) == 0) {
+ if (!S_ISDIR(st.st_mode) && eaccess(mac_fullpath, X_OK) == 0)
+ return mac_fullpath;
+ }
+ }
+#endif
+
+ next:
+ /* if not, and no other alternatives, life is bleak */
+ if (*ep == '\0') {
+ return NULL;
+ }
+
+ /* otherwise try the next component in the search path */
+ }
+}
+
+#if defined(__VMS)
+
+/* action routine for decc$to_vms */
+static int vms_fileact(char *filespec, int type)
+{
+ if (vms_filespec)
+ free(vms_filespec);
+ vms_filespec = malloc(strlen(filespec)+1);
+ strcpy(vms_filespec, filespec);
+ return 1;
+}
+
+/* exception handler for LIB$FIND_IMAGE_SYMBOL */
+static long vms_fisexh(long *sigarr, long *mecarr)
+{
+ sys$unwind(1, 0);
+ return 1;
+}
+
+#endif /* __VMS */
+#define NO_DLN_LOAD 1
+#include "dln.c"
+void
+Init_ext()
+{
+}
+/**********************************************************************
+
+ enum.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Fri Oct 1 15:15:19 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "node.h"
+#include "util.h"
+
+VALUE rb_mEnumerable;
+static ID id_each, id_eqq, id_cmp;
+
+VALUE
+rb_each(obj)
+ VALUE obj;
+{
+ return rb_funcall(obj, id_each, 0, 0);
+}
+
+static VALUE
+grep_i(i, arg)
+ VALUE i, *arg;
+{
+ if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) {
+ rb_ary_push(arg[1], i);
+ }
+ return Qnil;
+}
+
+static VALUE
+grep_iter_i(i, arg)
+ VALUE i, *arg;
+{
+ if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) {
+ rb_ary_push(arg[1], rb_yield(i));
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_grep(obj, pat)
+ VALUE obj, pat;
+{
+ VALUE ary = rb_ary_new();
+ VALUE arg[2];
+
+ arg[0] = pat;
+ arg[1] = ary;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)arg);
+
+ return ary;
+}
+
+static VALUE
+find_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (RTEST(rb_yield(i))) {
+ *memo = i;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_find(argc, argv, obj)
+ int argc;
+ VALUE* argv;
+ VALUE obj;
+{
+ VALUE memo = Qundef;
+ VALUE if_none;
+
+ rb_scan_args(argc, argv, "01", &if_none);
+ rb_iterate(rb_each, obj, find_i, (VALUE)&memo);
+ if (memo != Qundef) {
+ return memo;
+ }
+ if (!NIL_P(if_none)) {
+ return rb_funcall(if_none, rb_intern("call"), 0, 0);
+ }
+ return Qnil;
+}
+
+static VALUE
+find_all_i(i, ary)
+ VALUE i, ary;
+{
+ if (RTEST(rb_yield(i))) {
+ rb_ary_push(ary, i);
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_find_all(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, find_all_i, ary);
+
+ return ary;
+}
+
+static VALUE
+reject_i(i, ary)
+ VALUE i, ary;
+{
+ if (!RTEST(rb_yield(i))) {
+ rb_ary_push(ary, i);
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_reject(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, reject_i, ary);
+
+ return ary;
+}
+
+static VALUE
+collect_i(i, ary)
+ VALUE i, ary;
+{
+ rb_ary_push(ary, rb_yield(i));
+
+ return Qnil;
+}
+
+static VALUE
+collect_all(i, ary)
+ VALUE i, ary;
+{
+ rb_ary_push(ary, i);
+
+ return Qnil;
+}
+
+/*
+ * 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
+enum_collect(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? collect_i : collect_all, ary);
+
+ return ary;
+}
+
+/*
+ * 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]]
+ */
+static VALUE
+enum_to_a(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, collect_all, ary);
+
+ return ary;
+}
+
+static VALUE
+inject_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (*memo == Qundef) {
+ *memo = i;
+ }
+ else {
+ *memo = rb_yield_values(2, *memo, i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.inject(initial) {| memo, obj | block } => obj
+ * enum.inject {| memo, obj | block } => obj
+ *
+ * Combines the elements of <i>enum</i> by applying the block to an
+ * accumulator value (<i>memo</i>) and each element in turn. At each
+ * step, <i>memo</i> is set to the value returned by the block. The
+ * first form lets you supply an initial value for <i>memo</i>. The
+ * second form uses the first element of the collection as a the
+ * initial value (and skips that element while iterating).
+ *
+ * # Sum some numbers
+ * (5..10).inject {|sum, n| sum + n } #=> 45
+ * # Multiply some numbers
+ * (5..10).inject(1) {|product, n| product * n } #=> 151200
+ *
+ * # find the longest word
+ * longest = %w{ cat sheep bear }.inject do |memo,word|
+ * memo.length > word.length ? memo : word
+ * end
+ * longest #=> "sheep"
+ *
+ * # find the length of the longest word
+ * longest = %w{ cat sheep bear }.inject(0) do |memo,word|
+ * memo >= word.length ? memo : word.length
+ * end
+ * longest #=> 5
+ *
+ */
+
+static VALUE
+enum_inject(argc, argv, obj)
+ int argc;
+ VALUE *argv, obj;
+{
+ VALUE memo = Qundef;
+
+ if (rb_scan_args(argc, argv, "01", &memo) == 0)
+ memo = Qundef;
+ rb_iterate(rb_each, obj, inject_i, (VALUE)&memo);
+ if (memo == Qundef) return Qnil;
+ return memo;
+}
+
+static VALUE
+partition_i(i, ary)
+ VALUE i, *ary;
+{
+ if (RTEST(rb_yield(i))) {
+ rb_ary_push(ary[0], i);
+ }
+ else {
+ rb_ary_push(ary[1], i);
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_partition(obj)
+ VALUE obj;
+{
+ VALUE ary[2];
+
+ ary[0] = rb_ary_new();
+ ary[1] = rb_ary_new();
+ rb_iterate(rb_each, obj, partition_i, (VALUE)ary);
+
+ return rb_assoc_new(ary[0], ary[1]);
+}
+
+/*
+ * 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
+ * +1 depending on the comparison between <i>a</i> and <i>b</i>. As of
+ * 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]
+ */
+
+static VALUE
+enum_sort(obj)
+ VALUE obj;
+{
+ return rb_ary_sort(enum_to_a(obj));
+}
+
+static VALUE
+sort_by_i(i, ary)
+ VALUE i, ary;
+{
+ VALUE v;
+ NODE *memo;
+
+ v = rb_yield(i);
+ if (RBASIC(ary)->klass) {
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
+ }
+ memo = rb_node_newnode(NODE_MEMO, v, i, 0);
+ rb_ary_push(ary, (VALUE)memo);
+ return Qnil;
+}
+
+static int
+sort_by_cmp(aa, bb)
+ NODE **aa, **bb;
+{
+ VALUE a = aa[0]->u1.value;
+ VALUE b = bb[0]->u1.value;
+
+ return rb_cmpint(rb_funcall(a, id_cmp, 1, b), a, b);
+}
+
+/*
+ * 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
+ * approach a Schwartzian Transform, after Randal Schwartz. We
+ * 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"]
+ */
+
+static VALUE
+enum_sort_by(obj)
+ VALUE obj;
+{
+ VALUE ary;
+ long i;
+
+ if (TYPE(obj) == T_ARRAY) {
+ ary = rb_ary_new2(RARRAY(obj)->len);
+ }
+ else {
+ ary = rb_ary_new();
+ }
+ RBASIC(ary)->klass = 0;
+ rb_iterate(rb_each, obj, sort_by_i, ary);
+ if (RARRAY(ary)->len > 1) {
+ qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE), sort_by_cmp, 0);
+ }
+ if (RBASIC(ary)->klass) {
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
+ }
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ RARRAY(ary)->ptr[i] = RNODE(RARRAY(ary)->ptr[i])->u2.value;
+ }
+ RBASIC(ary)->klass = rb_cArray;
+ return ary;
+}
+
+static VALUE
+all_iter_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (!RTEST(rb_yield(i))) {
+ *memo = Qfalse;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
+all_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (!RTEST(i)) {
+ *memo = Qfalse;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_all(obj)
+ VALUE obj;
+{
+ VALUE result = Qtrue;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? all_iter_i : all_i, (VALUE)&result);
+ return result;
+}
+
+static VALUE
+any_iter_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (RTEST(rb_yield(i))) {
+ *memo = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
+any_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ if (RTEST(i)) {
+ *memo = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+ * given, Ruby adds an implicit block of <code>{|obj| obj}</code> (that
+ * 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
+enum_any(obj)
+ VALUE obj;
+{
+ VALUE result = Qfalse;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? any_iter_i : any_i, (VALUE)&result);
+ return result;
+}
+
+static VALUE
+min_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ VALUE cmp;
+
+ if (*memo == Qundef) {
+ *memo = i;
+ }
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, *memo);
+ if (rb_cmpint(cmp, i, *memo) < 0) {
+ *memo = i;
+ }
+ }
+ return Qnil;
+}
+
+static VALUE
+min_ii(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ VALUE cmp;
+
+ if (*memo == Qundef) {
+ *memo = i;
+ }
+ else {
+ cmp = rb_yield_values(2, i, *memo);
+ if (rb_cmpint(cmp, i, *memo) < 0) {
+ *memo = i;
+ }
+ }
+ return Qnil;
+}
+
+
+/*
+ * 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"
+ */
+
+static VALUE
+enum_min(obj)
+ VALUE obj;
+{
+ VALUE result = Qundef;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? min_ii : min_i, (VALUE)&result);
+ if (result == Qundef) return Qnil;
+ return result;
+}
+
+/*
+ * 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"
+ */
+
+static VALUE
+max_i(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ VALUE cmp;
+
+ if (*memo == Qundef) {
+ *memo = i;
+ }
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, *memo);
+ if (rb_cmpint(cmp, i, *memo) > 0) {
+ *memo = i;
+ }
+ }
+ return Qnil;
+}
+
+static VALUE
+max_ii(i, memo)
+ VALUE i;
+ VALUE *memo;
+{
+ VALUE cmp;
+
+ if (*memo == Qundef) {
+ *memo = i;
+ }
+ else {
+ cmp = rb_yield_values(2, i, *memo);
+ if (rb_cmpint(cmp, i, *memo) > 0) {
+ *memo = i;
+ }
+ }
+ return Qnil;
+}
+
+/*
+ * 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)
+ VALUE obj;
+{
+ VALUE result = Qundef;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? max_ii : max_i, (VALUE)&result);
+ if (result == Qundef) return Qnil;
+ return result;
+}
+
+static VALUE
+member_i(item, memo)
+ VALUE item;
+ VALUE *memo;
+{
+ if (rb_equal(item, memo[0])) {
+ memo[1] = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+enum_member(obj, val)
+ VALUE obj, val;
+{
+ VALUE memo[2];
+
+ memo[0] = val;
+ memo[1] = Qfalse;
+ rb_iterate(rb_each, obj, member_i, (VALUE)memo);
+ return memo[1];
+}
+
+static VALUE
+each_with_index_i(val, memo)
+ VALUE val;
+ VALUE *memo;
+{
+ rb_yield_values(2, val, INT2FIX(*memo));
+ ++*memo;
+ return Qnil;
+}
+
+/*
+ * 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
+enum_each_with_index(obj)
+ VALUE obj;
+{
+ VALUE memo = 0;
+
+ rb_need_block();
+ rb_iterate(rb_each, obj, each_with_index_i, (VALUE)&memo);
+ return obj;
+}
+
+static VALUE
+zip_i(val, memo)
+ VALUE val;
+ VALUE *memo;
+{
+ VALUE result = memo[0];
+ VALUE args = memo[1];
+ int idx = memo[2]++;
+ VALUE tmp;
+ int i;
+
+ tmp = rb_ary_new2(RARRAY(args)->len + 1);
+ rb_ary_store(tmp, 0, val);
+ for (i=0; i<RARRAY(args)->len; i++) {
+ rb_ary_push(tmp, rb_ary_entry(RARRAY(args)->ptr[i], idx));
+ }
+ if (rb_block_given_p()) {
+ rb_yield(tmp);
+ }
+ else {
+ rb_ary_push(result, tmp);
+ }
+ return Qnil;
+}
+
+/*
+ * 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
+ * arrays, where <em>n</em> is one more that the count of arguments. If
+ * the size of any argument is less than <code>enum#size</code>,
+ * <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
+enum_zip(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ int i;
+ VALUE result;
+ VALUE memo[3];
+
+ for (i=0; i<argc; i++) {
+ argv[i] = rb_convert_type(argv[i], T_ARRAY, "Array", "to_a");
+ }
+ result = rb_block_given_p() ? Qnil : rb_ary_new();
+ memo[0] = result;
+ memo[1] = rb_ary_new4(argc, argv);
+ memo[2] = 0;
+ rb_iterate(rb_each, obj, zip_i, (VALUE)memo);
+
+ return result;
+}
+
+/*
+ * The <code>Enumerable</code> mixin provides collection classes with
+ * several traversal and searching methods, and with the ability to
+ * sort. The class must provide a method <code>each</code>, which
+ * yields successive members of the collection. If
+ * <code>Enumerable#max</code>, <code>#min</code>, or
+ * <code>#sort</code> is used, the objects in the collection must also
+ * implement a meaningful <code><=></code> operator, as these methods
+ * rely on an ordering between members of the collection.
+ */
+
+void
+Init_Enumerable()
+{
+ rb_mEnumerable = rb_define_module("Enumerable");
+
+ rb_define_method(rb_mEnumerable,"to_a", enum_to_a, 0);
+ rb_define_method(rb_mEnumerable,"entries", enum_to_a, 0);
+
+ rb_define_method(rb_mEnumerable,"sort", enum_sort, 0);
+ rb_define_method(rb_mEnumerable,"sort_by", enum_sort_by, 0);
+ rb_define_method(rb_mEnumerable,"grep", enum_grep, 1);
+ rb_define_method(rb_mEnumerable,"find", enum_find, -1);
+ rb_define_method(rb_mEnumerable,"detect", enum_find, -1);
+ rb_define_method(rb_mEnumerable,"find_all", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable,"select", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable,"reject", enum_reject, 0);
+ rb_define_method(rb_mEnumerable,"collect", enum_collect, 0);
+ rb_define_method(rb_mEnumerable,"map", enum_collect, 0);
+ rb_define_method(rb_mEnumerable,"inject", enum_inject, -1);
+ rb_define_method(rb_mEnumerable,"partition", enum_partition, 0);
+ rb_define_method(rb_mEnumerable,"all?", enum_all, 0);
+ rb_define_method(rb_mEnumerable,"any?", enum_any, 0);
+ rb_define_method(rb_mEnumerable,"min", enum_min, 0);
+ rb_define_method(rb_mEnumerable,"max", enum_max, 0);
+ rb_define_method(rb_mEnumerable,"member?", enum_member, 1);
+ rb_define_method(rb_mEnumerable,"include?", enum_member, 1);
+ rb_define_method(rb_mEnumerable,"each_with_index", enum_each_with_index, 0);
+ rb_define_method(rb_mEnumerable, "zip", enum_zip, -1);
+
+ id_eqq = rb_intern("===");
+ id_each = rb_intern("each");
+ id_cmp = rb_intern("<=>");
+}
+
+/**********************************************************************
+
+ error.c -
+
+ $Author: shyouhei $
+ $Date: 2008-08-04 05:16:55 +0200 (Mon, 04 Aug 2008) $
+ created at: Mon Aug 9 16:11:34 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "st.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+extern const char ruby_version[], ruby_release_date[], ruby_platform[];
+
+int ruby_nerrs;
+
+static int
+err_position(buf, len)
+ char *buf;
+ long len;
+{
+ ruby_set_current_source();
+ if (!ruby_sourcefile) {
+ return 0;
+ }
+ else if (ruby_sourceline == 0) {
+ return snprintf(buf, len, "%s: ", ruby_sourcefile);
+ }
+ else {
+ return snprintf(buf, len, "%s:%d: ", ruby_sourcefile, ruby_sourceline);
+ }
+}
+
+static void
+err_snprintf(buf, len, fmt, args)
+ char *buf;
+ long len;
+ const char *fmt;
+ va_list args;
+{
+ long n;
+
+ n = err_position(buf, len);
+ if (len > n) {
+ vsnprintf((char*)buf+n, len-n, fmt, args);
+ }
+}
+
+static void err_append _((const char*));
+static void
+err_print(fmt, args)
+ const char *fmt;
+ va_list args;
+{
+ char buf[BUFSIZ];
+
+ err_snprintf(buf, BUFSIZ, fmt, args);
+ err_append(buf);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_compile_error(const char *fmt, ...)
+#else
+rb_compile_error(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+
+ va_init_list(args, fmt);
+ err_print(fmt, args);
+ va_end(args);
+ ruby_nerrs++;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_compile_error_append(const char *fmt, ...)
+#else
+rb_compile_error_append(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ err_append(buf);
+}
+
+static void
+warn_print(fmt, args)
+ const char *fmt;
+ va_list args;
+{
+ char buf[BUFSIZ];
+ int len;
+
+ err_snprintf(buf, BUFSIZ, fmt, args);
+ len = strlen(buf);
+ buf[len++] = '\n';
+ rb_write_error2(buf, len);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_warn(const char *fmt, ...)
+#else
+rb_warn(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ if (NIL_P(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+}
+
+/* rb_warning() reports only in verbose mode */
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_warning(const char *fmt, ...)
+#else
+rb_warning(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ if (!RTEST(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+}
+
+/*
+ * call-seq:
+ * warn(msg) => nil
+ *
+ * Display the given message (followed by a newline) on STDERR unless
+ * warnings are disabled (for example with the <code>-W0</code> flag).
+ */
+
+static VALUE
+rb_warn_m(self, mesg)
+ VALUE self, mesg;
+{
+ if (!NIL_P(ruby_verbose)) {
+ rb_io_write(rb_stderr, mesg);
+ rb_io_write(rb_stderr, rb_default_rs);
+ }
+ return Qnil;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_bug(const char *fmt, ...)
+#else
+rb_bug(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+ FILE *out = stderr;
+ int len = err_position(buf, BUFSIZ);
+
+ if (fwrite(buf, 1, len, out) == len ||
+ fwrite(buf, 1, len, (out = stdout)) == len) {
+ fputs("[BUG] ", out);
+ va_init_list(args, fmt);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ fprintf(out, "\nruby %s (%s) [%s]\n\n",
+ ruby_version, ruby_release_date, ruby_platform);
+ }
+ abort();
+}
+
+static struct types {
+ int type;
+ const char *name;
+} builtin_types[] = {
+ {T_NIL, "nil"},
+ {T_OBJECT, "Object"},
+ {T_CLASS, "Class"},
+ {T_ICLASS, "iClass"}, /* internal use: mixed-in module holder */
+ {T_MODULE, "Module"},
+ {T_FLOAT, "Float"},
+ {T_STRING, "String"},
+ {T_REGEXP, "Regexp"},
+ {T_ARRAY, "Array"},
+ {T_FIXNUM, "Fixnum"},
+ {T_HASH, "Hash"},
+ {T_STRUCT, "Struct"},
+ {T_BIGNUM, "Bignum"},
+ {T_FILE, "File"},
+ {T_TRUE, "true"},
+ {T_FALSE, "false"},
+ {T_SYMBOL, "Symbol"}, /* :symbol */
+ {T_DATA, "Data"}, /* internal use: wrapped C pointers */
+ {T_MATCH, "MatchData"}, /* data of $~ */
+ {T_VARMAP, "Varmap"}, /* internal use: dynamic variables */
+ {T_SCOPE, "Scope"}, /* internal use: variable scope */
+ {T_NODE, "Node"}, /* internal use: syntax tree node */
+ {T_UNDEF, "undef"}, /* internal use: #undef; should not happen */
+ {-1, 0}
+};
+
+void
+rb_check_type(x, t)
+ VALUE x;
+ int t;
+{
+ struct types *type = builtin_types;
+
+ if (x == Qundef) {
+ rb_bug("undef leaked to the Ruby space");
+ }
+
+ if (TYPE(x) != t) {
+ while (type->type >= 0) {
+ if (type->type == t) {
+ char *etype;
+
+ if (NIL_P(x)) {
+ etype = "nil";
+ }
+ else if (FIXNUM_P(x)) {
+ etype = "Fixnum";
+ }
+ else if (SYMBOL_P(x)) {
+ etype = "Symbol";
+ }
+ else if (rb_special_const_p(x)) {
+ etype = RSTRING(rb_obj_as_string(x))->ptr;
+ }
+ else {
+ etype = rb_obj_classname(x);
+ }
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
+ etype, type->name);
+ }
+ type++;
+ }
+ rb_bug("unknown type 0x%x", t);
+ }
+}
+
+/* exception classes */
+#include <errno.h>
+
+VALUE rb_eException;
+VALUE rb_eSystemExit;
+VALUE rb_eInterrupt;
+VALUE rb_eSignal;
+VALUE rb_eFatal;
+VALUE rb_eStandardError;
+VALUE rb_eRuntimeError;
+VALUE rb_eTypeError;
+VALUE rb_eArgError;
+VALUE rb_eIndexError;
+VALUE rb_eRangeError;
+VALUE rb_eNameError;
+VALUE rb_eNoMethodError;
+VALUE rb_eSecurityError;
+VALUE rb_eNotImpError;
+VALUE rb_eNoMemError;
+VALUE rb_cNameErrorMesg;
+
+VALUE rb_eScriptError;
+VALUE rb_eSyntaxError;
+VALUE rb_eLoadError;
+
+VALUE rb_eSystemCallError;
+VALUE rb_mErrno;
+
+VALUE
+rb_exc_new(etype, ptr, len)
+ VALUE etype;
+ const char *ptr;
+ long len;
+{
+ return rb_funcall(etype, rb_intern("new"), 1, rb_str_new(ptr, len));
+}
+
+VALUE
+rb_exc_new2(etype, s)
+ VALUE etype;
+ const char *s;
+{
+ return rb_exc_new(etype, s, strlen(s));
+}
+
+VALUE
+rb_exc_new3(etype, str)
+ VALUE etype, str;
+{
+ StringValue(str);
+ return rb_funcall(etype, rb_intern("new"), 1, str);
+}
+
+/*
+ * call-seq:
+ * Exception.new(msg = nil) => exception
+ *
+ * Construct a new Exception object, optionally passing in
+ * a message.
+ */
+
+static VALUE
+exc_initialize(argc, argv, exc)
+ int argc;
+ VALUE *argv;
+ VALUE exc;
+{
+ VALUE arg;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ rb_iv_set(exc, "mesg", arg);
+ rb_iv_set(exc, "bt", Qnil);
+
+ return exc;
+}
+
+/*
+ * Document-method: exception
+ *
+ * call-seq:
+ * exc.exception(string) -> an_exception or exc
+ *
+ * With no argument, or if the argument is the same as the receiver,
+ * return the receiver. Otherwise, create a new
+ * exception object of the same class as the receiver, but with a
+ * message equal to <code>string.to_str</code>.
+ *
+ */
+
+static VALUE
+exc_exception(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE exc;
+
+ if (argc == 0) return self;
+ if (argc == 1 && self == argv[0]) return self;
+ exc = rb_obj_clone(self);
+ exc_initialize(argc, argv, exc);
+
+ return exc;
+}
+
+/*
+ * call-seq:
+ * exception.to_s => string
+ *
+ * Returns exception's message (or the name of the exception if
+ * no message is set).
+ */
+
+static VALUE
+exc_to_s(exc)
+ VALUE exc;
+{
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
+
+ if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
+ if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/*
+ * call-seq:
+ * exception.message => string
+ * exception.to_str => string
+ *
+ * Returns the result of invoking <code>exception.to_s</code>.
+ * Normally this returns the exception's message or name. By
+ * supplying a to_str method, exceptions are agreeing to
+ * be used where Strings are expected.
+ */
+
+static VALUE
+exc_to_str(exc)
+ VALUE exc;
+{
+ return rb_funcall(exc, rb_intern("to_s"), 0, 0);
+}
+
+/*
+ * call-seq:
+ * exception.inspect => string
+ *
+ * Return this exception's class name an message
+ */
+
+static VALUE
+exc_inspect(exc)
+ VALUE exc;
+{
+ VALUE str, klass;
+
+ klass = CLASS_OF(exc);
+ exc = rb_obj_as_string(exc);
+ if (RSTRING(exc)->len == 0) {
+ return rb_str_dup(rb_class_name(klass));
+ }
+
+ str = rb_str_buf_new2("#<");
+ klass = rb_class_name(klass);
+ rb_str_buf_append(str, klass);
+ rb_str_buf_cat(str, ": ", 2);
+ rb_str_buf_append(str, exc);
+ rb_str_buf_cat(str, ">", 1);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * exception.backtrace => array
+ *
+ * Returns any backtrace associated with the exception. The backtrace
+ * is an array of strings, each containing either ``filename:lineNo: in
+ * `method''' or ``filename:lineNo.''
+ *
+ * def a
+ * raise "boom"
+ * end
+ *
+ * def b
+ * a()
+ * end
+ *
+ * begin
+ * b()
+ * rescue => detail
+ * print detail.backtrace.join("\n")
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:2:in `a'
+ * prog.rb:6:in `b'
+ * prog.rb:10
+*/
+
+static VALUE
+exc_backtrace(exc)
+ VALUE exc;
+{
+ static ID bt;
+
+ if (!bt) bt = rb_intern("bt");
+ return rb_attr_get(exc, bt);
+}
+
+VALUE
+rb_check_backtrace(bt)
+ VALUE bt;
+{
+ long i;
+ static char *err = "backtrace must be Array of String";
+
+ if (!NIL_P(bt)) {
+ int t = TYPE(bt);
+
+ if (t == T_STRING) return rb_ary_new3(1, bt);
+ if (t != T_ARRAY) {
+ rb_raise(rb_eTypeError, err);
+ }
+ for (i=0;i<RARRAY(bt)->len;i++) {
+ if (TYPE(RARRAY(bt)->ptr[i]) != T_STRING) {
+ rb_raise(rb_eTypeError, err);
+ }
+ }
+ }
+ return bt;
+}
+
+/*
+ * call-seq:
+ * exc.set_backtrace(array) => array
+ *
+ * Sets the backtrace information associated with <i>exc</i>. The
+ * argument must be an array of <code>String</code> objects in the
+ * format described in <code>Exception#backtrace</code>.
+ *
+ */
+
+static VALUE
+exc_set_backtrace(exc, bt)
+ VALUE exc;
+ VALUE bt;
+{
+ return rb_iv_set(exc, "bt", rb_check_backtrace(bt));
+}
+
+/*
+ * call-seq:
+ * SystemExit.new(status=0) => system_exit
+ *
+ * Create a new +SystemExit+ exception with the given status.
+ */
+
+static VALUE
+exit_initialize(argc, argv, exc)
+ int argc;
+ VALUE *argv;
+ VALUE exc;
+{
+ VALUE status = INT2FIX(EXIT_SUCCESS);
+ if (argc > 0 && FIXNUM_P(argv[0])) {
+ status = *argv++;
+ --argc;
+ }
+ rb_call_super(argc, argv);
+ rb_iv_set(exc, "status", status);
+ return exc;
+}
+
+
+/*
+ * call-seq:
+ * system_exit.status => fixnum
+ *
+ * Return the status value associated with this system exit.
+ */
+
+static VALUE
+exit_status(exc)
+ VALUE exc;
+{
+ return rb_attr_get(exc, rb_intern("status"));
+}
+
+
+/*
+ * call-seq:
+ * system_exit.success? => true or false
+ *
+ * Returns +true+ if exiting successful, +false+ if not.
+ */
+
+static VALUE
+exit_success_p(exc)
+ VALUE exc;
+{
+ VALUE status = rb_attr_get(exc, rb_intern("status"));
+ if (NIL_P(status)) return Qtrue;
+ if (status == INT2FIX(EXIT_SUCCESS)) return Qtrue;
+ return Qfalse;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_name_error(ID id, const char *fmt, ...)
+#else
+rb_name_error(id, fmt, va_alist)
+ ID id;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ VALUE exc, argv[2];
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+
+ argv[0] = rb_str_new2(buf);
+ argv[1] = ID2SYM(id);
+ exc = rb_class_new_instance(2, argv, rb_eNameError);
+ rb_exc_raise(exc);
+}
+
+/*
+ * call-seq:
+ * NameError.new(msg [, name]) => name_error
+ *
+ * Construct a new NameError exception. If given the <i>name</i>
+ * parameter may subsequently be examined using the <code>NameError.name</code>
+ * method.
+ */
+
+static VALUE
+name_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE name;
+
+ name = (argc > 1) ? argv[--argc] : Qnil;
+ rb_call_super(argc, argv);
+ rb_iv_set(self, "name", name);
+ return self;
+}
+
+/*
+ * call-seq:
+ * name_error.name => string or nil
+ *
+ * Return the name associated with this NameError exception.
+ */
+
+static VALUE
+name_err_name(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("name"));
+}
+
+/*
+ * call-seq:
+ * name_error.to_s => string
+ *
+ * Produce a nicely-formated string representing the +NameError+.
+ */
+
+static VALUE
+name_err_to_s(exc)
+ VALUE exc;
+{
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg")), str = mesg;
+
+ if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
+ StringValue(str);
+ if (str != mesg) {
+ rb_iv_set(exc, "mesg", mesg = str);
+ }
+ if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/*
+ * call-seq:
+ * NoMethodError.new(msg, name [, args]) => no_method_error
+ *
+ * Construct a NoMethodError exception for a method of the given name
+ * called with the given arguments. The name may be accessed using
+ * the <code>#name</code> method on the resulting object, and the
+ * arguments using the <code>#args</code> method.
+ */
+
+static VALUE
+nometh_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE args = (argc > 2) ? argv[--argc] : Qnil;
+ name_err_initialize(argc, argv, self);
+ rb_iv_set(self, "args", args);
+ return self;
+}
+
+/* :nodoc: */
+static void
+name_err_mesg_mark(ptr)
+ VALUE *ptr;
+{
+ rb_gc_mark_locations(ptr, ptr+3);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_new(obj, mesg, recv, method)
+ VALUE obj, mesg, recv, method;
+{
+ VALUE *ptr = ALLOC_N(VALUE, 3);
+
+ ptr[0] = mesg;
+ ptr[1] = recv;
+ ptr[2] = method;
+ return Data_Wrap_Struct(rb_cNameErrorMesg, name_err_mesg_mark, -1, ptr);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_to_str(obj)
+ VALUE obj;
+{
+ VALUE *ptr, mesg;
+ Data_Get_Struct(obj, VALUE, ptr);
+
+ mesg = ptr[0];
+ if (NIL_P(mesg)) return Qnil;
+ else {
+ char *desc = 0;
+ VALUE d = 0, args[3];
+
+ obj = ptr[1];
+ switch (TYPE(obj)) {
+ case T_NIL:
+ desc = "nil";
+ break;
+ case T_TRUE:
+ desc = "true";
+ break;
+ case T_FALSE:
+ desc = "false";
+ break;
+ default:
+ d = rb_protect(rb_inspect, obj, 0);
+ if (NIL_P(d) || RSTRING(d)->len > 65) {
+ d = rb_any_to_s(obj);
+ }
+ desc = RSTRING(d)->ptr;
+ break;
+ }
+ if (desc && desc[0] != '#') {
+ d = rb_str_new2(desc);
+ rb_str_cat2(d, ":");
+ rb_str_cat2(d, rb_obj_classname(obj));
+ }
+ args[0] = mesg;
+ args[1] = ptr[2];
+ args[2] = d;
+ mesg = rb_f_sprintf(3, args);
+ }
+ if (OBJ_TAINTED(obj)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_load(klass, str)
+ VALUE klass, str;
+{
+ return str;
+}
+
+/*
+ * call-seq:
+ * no_method_error.args => obj
+ *
+ * Return the arguments passed in as the third parameter to
+ * the constructor.
+ */
+
+static VALUE
+nometh_err_args(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("args"));
+}
+
+void
+rb_invalid_str(str, type)
+ const char *str, *type;
+{
+ VALUE s = rb_str_inspect(rb_str_new2(str));
+
+ rb_raise(rb_eArgError, "invalid value for %s: %s", type, RSTRING(s)->ptr);
+}
+
+/*
+ * Document-module: Errno
+ *
+ * Ruby exception objects are subclasses of <code>Exception</code>.
+ * However, operating systems typically report errors using plain
+ * integers. Module <code>Errno</code> is created dynamically to map
+ * these operating system errors to Ruby classes, with each error
+ * number generating its own subclass of <code>SystemCallError</code>.
+ * As the subclass is created in module <code>Errno</code>, its name
+ * will start <code>Errno::</code>.
+ *
+ * The names of the <code>Errno::</code> classes depend on
+ * the environment in which Ruby runs. On a typical Unix or Windows
+ * platform, there are <code>Errno</code> classes such as
+ * <code>Errno::EACCES</code>, <code>Errno::EAGAIN</code>,
+ * <code>Errno::EINTR</code>, and so on.
+ *
+ * The integer operating system error number corresponding to a
+ * particular error is available as the class constant
+ * <code>Errno::</code><em>error</em><code>::Errno</code>.
+ *
+ * Errno::EACCES::Errno #=> 13
+ * Errno::EAGAIN::Errno #=> 11
+ * Errno::EINTR::Errno #=> 4
+ *
+ * The full list of operating system errors on your particular platform
+ * are available as the constants of <code>Errno</code>.
+ *
+ * Errno.constants #=> E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, ...
+ */
+
+static st_table *syserr_tbl;
+
+static VALUE
+set_syserr(n, name)
+ int n;
+ const char *name;
+{
+ VALUE error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+ rb_define_const(error, "Errno", INT2NUM(n));
+ st_add_direct(syserr_tbl, n, error);
+ }
+ else {
+ rb_define_const(rb_mErrno, name, error);
+ }
+ return error;
+}
+
+static VALUE
+get_syserr(n)
+ int n;
+{
+ VALUE error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ char name[8]; /* some Windows' errno have 5 digits. */
+
+ snprintf(name, sizeof(name), "E%03d", n);
+ error = set_syserr(n, name);
+ }
+ return error;
+}
+
+/*
+ * call-seq:
+ * SystemCallError.new(msg, errno) => system_call_error_subclass
+ *
+ * If _errno_ corresponds to a known system error code, constructs
+ * the appropriate <code>Errno</code> class for that error, otherwise
+ * constructs a generic <code>SystemCallError</code> object. The
+ * error number is subsequently available via the <code>errno</code>
+ * method.
+ */
+
+static VALUE
+syserr_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+#if !defined(_WIN32) && !defined(__VMS)
+ char *strerror();
+#endif
+ char *err;
+ VALUE mesg, error;
+ VALUE klass = rb_obj_class(self);
+
+ if (klass == rb_eSystemCallError) {
+ rb_scan_args(argc, argv, "11", &mesg, &error);
+ if (argc == 1 && FIXNUM_P(mesg)) {
+ error = mesg; mesg = Qnil;
+ }
+ if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &klass)) {
+ /* change class */
+ if (TYPE(self) != T_OBJECT) { /* insurance to avoid type crash */
+ rb_raise(rb_eTypeError, "invalid instance type");
+ }
+ RBASIC(self)->klass = klass;
+ }
+ }
+ else {
+ rb_scan_args(argc, argv, "01", &mesg);
+ error = rb_const_get(klass, rb_intern("Errno"));
+ }
+ if (!NIL_P(error)) err = strerror(NUM2LONG(error));
+ else err = "unknown error";
+ if (!NIL_P(mesg)) {
+ VALUE str = mesg;
+ size_t len;
+
+ StringValue(str);
+ len = strlen(err)+RSTRING(str)->len+3;
+ mesg = rb_str_new(0, len);
+ snprintf(RSTRING(mesg)->ptr, len+1, "%s - %.*s", err,
+ (int)RSTRING(str)->len, RSTRING(str)->ptr);
+ rb_str_resize(mesg, strlen(RSTRING(mesg)->ptr));
+ }
+ else {
+ mesg = rb_str_new2(err);
+ }
+ rb_call_super(1, &mesg);
+ rb_iv_set(self, "errno", error);
+ return self;
+}
+
+/*
+ * call-seq:
+ * system_call_error.errno => fixnum
+ *
+ * Return this SystemCallError's error number.
+ */
+
+static VALUE
+syserr_errno(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("errno"));
+}
+
+/*
+ * call-seq:
+ * system_call_error === other => true or false
+ *
+ * Return +true+ if the receiver is a generic +SystemCallError+, or
+ * if the error numbers _self_ and _other_ are the same.
+ */
+
+
+static VALUE
+syserr_eqq(self, exc)
+ VALUE self, exc;
+{
+ VALUE num, e;
+
+ if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) return Qfalse;
+ if (self == rb_eSystemCallError) return Qtrue;
+
+ num = rb_attr_get(exc, rb_intern("errno"));
+ if (NIL_P(num)) {
+ VALUE klass = CLASS_OF(exc);
+
+ while (TYPE(klass) == T_ICLASS || FL_TEST(klass, FL_SINGLETON)) {
+ klass = (VALUE)RCLASS(klass)->super;
+ }
+ num = rb_const_get(klass, rb_intern("Errno"));
+ }
+ e = rb_const_get(self, rb_intern("Errno"));
+ if (FIXNUM_P(num) ? num == e : rb_equal(num, e))
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * Descendents of class <code>Exception</code> are used to communicate
+ * between <code>raise</code> methods and <code>rescue</code>
+ * statements in <code>begin/end</code> blocks. <code>Exception</code>
+ * objects carry information about the exception---its type (the
+ * exception's class name), an optional descriptive string, and
+ * optional traceback information. Programs may subclass
+ * <code>Exception</code> to add additional information.
+ */
+
+void
+Init_Exception()
+{
+ rb_eException = rb_define_class("Exception", rb_cObject);
+ rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
+ rb_define_method(rb_eException, "exception", exc_exception, -1);
+ rb_define_method(rb_eException, "initialize", exc_initialize, -1);
+ rb_define_method(rb_eException, "to_s", exc_to_s, 0);
+ rb_define_method(rb_eException, "to_str", exc_to_str, 0);
+ rb_define_method(rb_eException, "message", exc_to_str, 0);
+ rb_define_method(rb_eException, "inspect", exc_inspect, 0);
+ rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
+ rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1);
+
+ rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
+ rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);
+ rb_define_method(rb_eSystemExit, "status", exit_status, 0);
+ rb_define_method(rb_eSystemExit, "success?", exit_success_p, 0);
+
+ rb_eFatal = rb_define_class("fatal", rb_eException);
+ rb_eSignal = rb_define_class("SignalException", rb_eException);
+ rb_eInterrupt = rb_define_class("Interrupt", rb_eSignal);
+
+ rb_eStandardError = rb_define_class("StandardError", rb_eException);
+ rb_eTypeError = rb_define_class("TypeError", rb_eStandardError);
+ rb_eArgError = rb_define_class("ArgumentError", rb_eStandardError);
+ rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
+ rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
+ rb_eNameError = rb_define_class("NameError", rb_eStandardError);
+ rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
+ rb_define_method(rb_eNameError, "name", name_err_name, 0);
+ rb_define_method(rb_eNameError, "to_s", name_err_to_s, 0);
+ rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cData);
+ rb_define_singleton_method(rb_cNameErrorMesg, "!", name_err_mesg_new, 3);
+ rb_define_method(rb_cNameErrorMesg, "to_str", name_err_mesg_to_str, 0);
+ rb_define_method(rb_cNameErrorMesg, "_dump", name_err_mesg_to_str, 1);
+ rb_define_singleton_method(rb_cNameErrorMesg, "_load", name_err_mesg_load, 1);
+ rb_eNoMethodError = rb_define_class("NoMethodError", rb_eNameError);
+ rb_define_method(rb_eNoMethodError, "initialize", nometh_err_initialize, -1);
+ rb_define_method(rb_eNoMethodError, "args", nometh_err_args, 0);
+
+ rb_eScriptError = rb_define_class("ScriptError", rb_eException);
+ rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
+ rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
+ rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
+
+ rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);
+ rb_eSecurityError = rb_define_class("SecurityError", rb_eStandardError);
+ rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
+
+ syserr_tbl = st_init_numtable();
+ rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
+ rb_define_method(rb_eSystemCallError, "initialize", syserr_initialize, -1);
+ rb_define_method(rb_eSystemCallError, "errno", syserr_errno, 0);
+ rb_define_singleton_method(rb_eSystemCallError, "===", syserr_eqq, 1);
+
+ rb_mErrno = rb_define_module("Errno");
+
+ rb_define_global_function("warn", rb_warn_m, 1);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_raise(VALUE exc, const char *fmt, ...)
+#else
+rb_raise(exc, fmt, va_alist)
+ VALUE exc;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args,fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_exc_raise(rb_exc_new2(exc, buf));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_loaderror(const char *fmt, ...)
+#else
+rb_loaderror(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_exc_raise(rb_exc_new2(rb_eLoadError, buf));
+}
+
+void
+rb_notimplement()
+{
+ rb_raise(rb_eNotImpError,
+ "%s() function is unimplemented on this machine",
+ rb_id2name(ruby_frame->last_func));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_fatal(const char *fmt, ...)
+#else
+rb_fatal(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+
+ ruby_in_eval = 0;
+ rb_exc_fatal(rb_exc_new2(rb_eFatal, buf));
+}
+
+void
+rb_sys_fail(mesg)
+ const char *mesg;
+{
+ int n = errno;
+ VALUE arg;
+
+ errno = 0;
+ if (n == 0) {
+ rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
+ }
+
+ arg = mesg ? rb_str_new2(mesg) : Qnil;
+ rb_exc_raise(rb_class_new_instance(1, &arg, get_syserr(n)));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_sys_warning(const char *fmt, ...)
+#else
+rb_sys_warning(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+ int errno_save;
+
+ errno_save = errno;
+
+ if (!RTEST(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+ snprintf(buf+strlen(buf), BUFSIZ-strlen(buf), ": %s", strerror(errno_save));
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+ errno = errno_save;
+}
+
+void
+rb_load_fail(path)
+ const char *path;
+{
+ rb_loaderror("%s -- %s", strerror(errno), path);
+}
+
+void
+rb_error_frozen(what)
+ const char *what;
+{
+ rb_raise(rb_eTypeError, "can't modify frozen %s", what);
+}
+
+void
+rb_check_frozen(obj)
+ VALUE obj;
+{
+ if (OBJ_FROZEN(obj)) rb_error_frozen(rb_obj_classname(obj));
+}
+
+void
+Init_syserr()
+{
+#ifdef EPERM
+ set_syserr(EPERM, "EPERM");
+#endif
+#ifdef ENOENT
+ set_syserr(ENOENT, "ENOENT");
+#endif
+#ifdef ESRCH
+ set_syserr(ESRCH, "ESRCH");
+#endif
+#ifdef EINTR
+ set_syserr(EINTR, "EINTR");
+#endif
+#ifdef EIO
+ set_syserr(EIO, "EIO");
+#endif
+#ifdef ENXIO
+ set_syserr(ENXIO, "ENXIO");
+#endif
+#ifdef E2BIG
+ set_syserr(E2BIG, "E2BIG");
+#endif
+#ifdef ENOEXEC
+ set_syserr(ENOEXEC, "ENOEXEC");
+#endif
+#ifdef EBADF
+ set_syserr(EBADF, "EBADF");
+#endif
+#ifdef ECHILD
+ set_syserr(ECHILD, "ECHILD");
+#endif
+#ifdef EAGAIN
+ set_syserr(EAGAIN, "EAGAIN");
+#endif
+#ifdef ENOMEM
+ set_syserr(ENOMEM, "ENOMEM");
+#endif
+#ifdef EACCES
+ set_syserr(EACCES, "EACCES");
+#endif
+#ifdef EFAULT
+ set_syserr(EFAULT, "EFAULT");
+#endif
+#ifdef ENOTBLK
+ set_syserr(ENOTBLK, "ENOTBLK");
+#endif
+#ifdef EBUSY
+ set_syserr(EBUSY, "EBUSY");
+#endif
+#ifdef EEXIST
+ set_syserr(EEXIST, "EEXIST");
+#endif
+#ifdef EXDEV
+ set_syserr(EXDEV, "EXDEV");
+#endif
+#ifdef ENODEV
+ set_syserr(ENODEV, "ENODEV");
+#endif
+#ifdef ENOTDIR
+ set_syserr(ENOTDIR, "ENOTDIR");
+#endif
+#ifdef EISDIR
+ set_syserr(EISDIR, "EISDIR");
+#endif
+#ifdef EINVAL
+ set_syserr(EINVAL, "EINVAL");
+#endif
+#ifdef ENFILE
+ set_syserr(ENFILE, "ENFILE");
+#endif
+#ifdef EMFILE
+ set_syserr(EMFILE, "EMFILE");
+#endif
+#ifdef ENOTTY
+ set_syserr(ENOTTY, "ENOTTY");
+#endif
+#ifdef ETXTBSY
+ set_syserr(ETXTBSY, "ETXTBSY");
+#endif
+#ifdef EFBIG
+ set_syserr(EFBIG, "EFBIG");
+#endif
+#ifdef ENOSPC
+ set_syserr(ENOSPC, "ENOSPC");
+#endif
+#ifdef ESPIPE
+ set_syserr(ESPIPE, "ESPIPE");
+#endif
+#ifdef EROFS
+ set_syserr(EROFS, "EROFS");
+#endif
+#ifdef EMLINK
+ set_syserr(EMLINK, "EMLINK");
+#endif
+#ifdef EPIPE
+ set_syserr(EPIPE, "EPIPE");
+#endif
+#ifdef EDOM
+ set_syserr(EDOM, "EDOM");
+#endif
+#ifdef ERANGE
+ set_syserr(ERANGE, "ERANGE");
+#endif
+#ifdef EDEADLK
+ set_syserr(EDEADLK, "EDEADLK");
+#endif
+#ifdef ENAMETOOLONG
+ set_syserr(ENAMETOOLONG, "ENAMETOOLONG");
+#endif
+#ifdef ENOLCK
+ set_syserr(ENOLCK, "ENOLCK");
+#endif
+#ifdef ENOSYS
+ set_syserr(ENOSYS, "ENOSYS");
+#endif
+#ifdef ENOTEMPTY
+ set_syserr(ENOTEMPTY, "ENOTEMPTY");
+#endif
+#ifdef ELOOP
+ set_syserr(ELOOP, "ELOOP");
+#endif
+#ifdef EWOULDBLOCK
+ set_syserr(EWOULDBLOCK, "EWOULDBLOCK");
+#endif
+#ifdef ENOMSG
+ set_syserr(ENOMSG, "ENOMSG");
+#endif
+#ifdef EIDRM
+ set_syserr(EIDRM, "EIDRM");
+#endif
+#ifdef ECHRNG
+ set_syserr(ECHRNG, "ECHRNG");
+#endif
+#ifdef EL2NSYNC
+ set_syserr(EL2NSYNC, "EL2NSYNC");
+#endif
+#ifdef EL3HLT
+ set_syserr(EL3HLT, "EL3HLT");
+#endif
+#ifdef EL3RST
+ set_syserr(EL3RST, "EL3RST");
+#endif
+#ifdef ELNRNG
+ set_syserr(ELNRNG, "ELNRNG");
+#endif
+#ifdef EUNATCH
+ set_syserr(EUNATCH, "EUNATCH");
+#endif
+#ifdef ENOCSI
+ set_syserr(ENOCSI, "ENOCSI");
+#endif
+#ifdef EL2HLT
+ set_syserr(EL2HLT, "EL2HLT");
+#endif
+#ifdef EBADE
+ set_syserr(EBADE, "EBADE");
+#endif
+#ifdef EBADR
+ set_syserr(EBADR, "EBADR");
+#endif
+#ifdef EXFULL
+ set_syserr(EXFULL, "EXFULL");
+#endif
+#ifdef ENOANO
+ set_syserr(ENOANO, "ENOANO");
+#endif
+#ifdef EBADRQC
+ set_syserr(EBADRQC, "EBADRQC");
+#endif
+#ifdef EBADSLT
+ set_syserr(EBADSLT, "EBADSLT");
+#endif
+#ifdef EDEADLOCK
+ set_syserr(EDEADLOCK, "EDEADLOCK");
+#endif
+#ifdef EBFONT
+ set_syserr(EBFONT, "EBFONT");
+#endif
+#ifdef ENOSTR
+ set_syserr(ENOSTR, "ENOSTR");
+#endif
+#ifdef ENODATA
+ set_syserr(ENODATA, "ENODATA");
+#endif
+#ifdef ETIME
+ set_syserr(ETIME, "ETIME");
+#endif
+#ifdef ENOSR
+ set_syserr(ENOSR, "ENOSR");
+#endif
+#ifdef ENONET
+ set_syserr(ENONET, "ENONET");
+#endif
+#ifdef ENOPKG
+ set_syserr(ENOPKG, "ENOPKG");
+#endif
+#ifdef EREMOTE
+ set_syserr(EREMOTE, "EREMOTE");
+#endif
+#ifdef ENOLINK
+ set_syserr(ENOLINK, "ENOLINK");
+#endif
+#ifdef EADV
+ set_syserr(EADV, "EADV");
+#endif
+#ifdef ESRMNT
+ set_syserr(ESRMNT, "ESRMNT");
+#endif
+#ifdef ECOMM
+ set_syserr(ECOMM, "ECOMM");
+#endif
+#ifdef EPROTO
+ set_syserr(EPROTO, "EPROTO");
+#endif
+#ifdef EMULTIHOP
+ set_syserr(EMULTIHOP, "EMULTIHOP");
+#endif
+#ifdef EDOTDOT
+ set_syserr(EDOTDOT, "EDOTDOT");
+#endif
+#ifdef EBADMSG
+ set_syserr(EBADMSG, "EBADMSG");
+#endif
+#ifdef EOVERFLOW
+ set_syserr(EOVERFLOW, "EOVERFLOW");
+#endif
+#ifdef ENOTUNIQ
+ set_syserr(ENOTUNIQ, "ENOTUNIQ");
+#endif
+#ifdef EBADFD
+ set_syserr(EBADFD, "EBADFD");
+#endif
+#ifdef EREMCHG
+ set_syserr(EREMCHG, "EREMCHG");
+#endif
+#ifdef ELIBACC
+ set_syserr(ELIBACC, "ELIBACC");
+#endif
+#ifdef ELIBBAD
+ set_syserr(ELIBBAD, "ELIBBAD");
+#endif
+#ifdef ELIBSCN
+ set_syserr(ELIBSCN, "ELIBSCN");
+#endif
+#ifdef ELIBMAX
+ set_syserr(ELIBMAX, "ELIBMAX");
+#endif
+#ifdef ELIBEXEC
+ set_syserr(ELIBEXEC, "ELIBEXEC");
+#endif
+#ifdef EILSEQ
+ set_syserr(EILSEQ, "EILSEQ");
+#endif
+#ifdef ERESTART
+ set_syserr(ERESTART, "ERESTART");
+#endif
+#ifdef ESTRPIPE
+ set_syserr(ESTRPIPE, "ESTRPIPE");
+#endif
+#ifdef EUSERS
+ set_syserr(EUSERS, "EUSERS");
+#endif
+#ifdef ENOTSOCK
+ set_syserr(ENOTSOCK, "ENOTSOCK");
+#endif
+#ifdef EDESTADDRREQ
+ set_syserr(EDESTADDRREQ, "EDESTADDRREQ");
+#endif
+#ifdef EMSGSIZE
+ set_syserr(EMSGSIZE, "EMSGSIZE");
+#endif
+#ifdef EPROTOTYPE
+ set_syserr(EPROTOTYPE, "EPROTOTYPE");
+#endif
+#ifdef ENOPROTOOPT
+ set_syserr(ENOPROTOOPT, "ENOPROTOOPT");
+#endif
+#ifdef EPROTONOSUPPORT
+ set_syserr(EPROTONOSUPPORT, "EPROTONOSUPPORT");
+#endif
+#ifdef ESOCKTNOSUPPORT
+ set_syserr(ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT");
+#endif
+#ifdef EOPNOTSUPP
+ set_syserr(EOPNOTSUPP, "EOPNOTSUPP");
+#endif
+#ifdef EPFNOSUPPORT
+ set_syserr(EPFNOSUPPORT, "EPFNOSUPPORT");
+#endif
+#ifdef EAFNOSUPPORT
+ set_syserr(EAFNOSUPPORT, "EAFNOSUPPORT");
+#endif
+#ifdef EADDRINUSE
+ set_syserr(EADDRINUSE, "EADDRINUSE");
+#endif
+#ifdef EADDRNOTAVAIL
+ set_syserr(EADDRNOTAVAIL, "EADDRNOTAVAIL");
+#endif
+#ifdef ENETDOWN
+ set_syserr(ENETDOWN, "ENETDOWN");
+#endif
+#ifdef ENETUNREACH
+ set_syserr(ENETUNREACH, "ENETUNREACH");
+#endif
+#ifdef ENETRESET
+ set_syserr(ENETRESET, "ENETRESET");
+#endif
+#ifdef ECONNABORTED
+ set_syserr(ECONNABORTED, "ECONNABORTED");
+#endif
+#ifdef ECONNRESET
+ set_syserr(ECONNRESET, "ECONNRESET");
+#endif
+#ifdef ENOBUFS
+ set_syserr(ENOBUFS, "ENOBUFS");
+#endif
+#ifdef EISCONN
+ set_syserr(EISCONN, "EISCONN");
+#endif
+#ifdef ENOTCONN
+ set_syserr(ENOTCONN, "ENOTCONN");
+#endif
+#ifdef ESHUTDOWN
+ set_syserr(ESHUTDOWN, "ESHUTDOWN");
+#endif
+#ifdef ETOOMANYREFS
+ set_syserr(ETOOMANYREFS, "ETOOMANYREFS");
+#endif
+#ifdef ETIMEDOUT
+ set_syserr(ETIMEDOUT, "ETIMEDOUT");
+#endif
+#ifdef ECONNREFUSED
+ set_syserr(ECONNREFUSED, "ECONNREFUSED");
+#endif
+#ifdef EHOSTDOWN
+ set_syserr(EHOSTDOWN, "EHOSTDOWN");
+#endif
+#ifdef EHOSTUNREACH
+ set_syserr(EHOSTUNREACH, "EHOSTUNREACH");
+#endif
+#ifdef EALREADY
+ set_syserr(EALREADY, "EALREADY");
+#endif
+#ifdef EINPROGRESS
+ set_syserr(EINPROGRESS, "EINPROGRESS");
+#endif
+#ifdef ESTALE
+ set_syserr(ESTALE, "ESTALE");
+#endif
+#ifdef EUCLEAN
+ set_syserr(EUCLEAN, "EUCLEAN");
+#endif
+#ifdef ENOTNAM
+ set_syserr(ENOTNAM, "ENOTNAM");
+#endif
+#ifdef ENAVAIL
+ set_syserr(ENAVAIL, "ENAVAIL");
+#endif
+#ifdef EISNAM
+ set_syserr(EISNAM, "EISNAM");
+#endif
+#ifdef EREMOTEIO
+ set_syserr(EREMOTEIO, "EREMOTEIO");
+#endif
+#ifdef EDQUOT
+ set_syserr(EDQUOT, "EDQUOT");
+#endif
+}
+
+static void
+err_append(s)
+ const char *s;
+{
+ extern VALUE ruby_errinfo;
+
+ if (ruby_in_eval) {
+ if (NIL_P(ruby_errinfo)) {
+ ruby_errinfo = rb_exc_new2(rb_eSyntaxError, s);
+ }
+ else {
+ VALUE str = rb_obj_as_string(ruby_errinfo);
+
+ rb_str_cat2(str, "\n");
+ rb_str_cat2(str, s);
+ ruby_errinfo = rb_exc_new3(rb_eSyntaxError, str);
+ }
+ }
+ else {
+ rb_write_error(s);
+ rb_write_error("\n");
+ }
+}
+/**********************************************************************
+
+ eval.c -
+
+ $Author: wyhaines $
+ $Date: 2009-08-20 19:22:00 +0200 (Thu, 20 Aug 2009) $
+ created at: Thu Jun 10 14:22:17 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "node.h"
+#include "env.h"
+#include "util.h"
+#include "rubysig.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#include <stdio.h>
+
+#include "st.h"
+#include "dln.h"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+#endif
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+# ifndef atarist
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# endif /* atarist */
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifndef _AIX
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+void *alloca ();
+# endif
+# endif /* AIX */
+# endif /* HAVE_ALLOCA_H */
+#endif /* __GNUC__ */
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+#ifndef HAVE_STRING_H
+char *strrchr _((const char*,const char));
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <time.h>
+
+#if defined(HAVE_FCNTL_H) || defined(_WIN32)
+#include <fcntl.h>
+#elif defined(HAVE_SYS_FCNTL_H)
+#include <sys/fcntl.h>
+#endif
+#ifdef __CYGWIN__
+#include <io.h>
+#endif
+
+#if defined(__BEOS__) && !defined(BONE)
+#include <net/socket.h>
+#endif
+
+#ifdef __MACOS__
+#include "macruby_private.h"
+#endif
+
+#ifdef __VMS
+#include "vmsruby_private.h"
+#endif
+
+#ifdef USE_CONTEXT
+
+NORETURN(static void rb_jump_context(rb_jmpbuf_t, int));
+static inline void
+rb_jump_context(env, val)
+ rb_jmpbuf_t env;
+ int val;
+{
+ env->status = val;
+ setcontext(&env->context);
+ abort(); /* ensure noreturn */
+}
+/*
+ * PRE_GETCONTEXT and POST_GETCONTEXT is a magic for getcontext, gcc,
+ * IA64 register stack and SPARC register window combination problem.
+ *
+ * Assume following code sequence.
+ *
+ * 1. set a register in the register stack/window such as r32/l0.
+ * 2. call getcontext.
+ * 3. use the register.
+ * 4. update the register for other use.
+ * 5. call setcontext indirectly (or directly).
+ *
+ * This code should be run as 1->2->3->4->5->3->4.
+ * But after second getcontext return (second 3),
+ * the register is broken (updated).
+ * It's because getcontext/setcontext doesn't preserve the content of the
+ * register stack/window.
+ *
+ * setjmp also doesn't preserve the content of the register stack/window.
+ * But it has not the problem because gcc knows setjmp may return twice.
+ * gcc detects setjmp and generates setjmp safe code.
+ *
+ * So setjmp calls before and after the getcontext call makes the code
+ * somewhat safe.
+ * It fix the problem on IA64.
+ * It is not required that setjmp is called at run time, since the problem is
+ * register usage.
+ *
+ * Since the magic setjmp is not enough for SPARC,
+ * inline asm is used to prohibit registers in register windows.
+ *
+ * Since the problem is fixed at gcc 4.0.3, the magic is applied only for
+ * prior versions of gcc.
+ * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21957
+ * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22127
+ */
+# define GCC_VERSION_BEFORE(major, minor, patchlevel) \
+ (defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
+ ((__GNUC__ < (major)) || \
+ (__GNUC__ == (major) && __GNUC_MINOR__ < (minor)) || \
+ (__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ < (patchlevel))))
+# if GCC_VERSION_BEFORE(4,0,3) && (defined(sparc) || defined(__sparc__))
+# ifdef __pic__
+/*
+ * %l7 is excluded for PIC because it is PIC register.
+ * http://lists.freebsd.org/pipermail/freebsd-sparc64/2006-January/003739.html
+ */
+# define PRE_GETCONTEXT \
+ ({ __asm__ volatile ("" : : : \
+ "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o7", \
+ "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", \
+ "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%i7"); })
+# else
+# define PRE_GETCONTEXT \
+ ({ __asm__ volatile ("" : : : \
+ "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o7", \
+ "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", \
+ "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%i7"); })
+# endif
+# define POST_GETCONTEXT PRE_GETCONTEXT
+# elif GCC_VERSION_BEFORE(4,0,3) && defined(__ia64)
+static jmp_buf function_call_may_return_twice_jmp_buf;
+int function_call_may_return_twice_false_1 = 0;
+int function_call_may_return_twice_false_2 = 0;
+# define PRE_GETCONTEXT \
+ (function_call_may_return_twice_false_1 ? \
+ setjmp(function_call_may_return_twice_jmp_buf) : \
+ 0)
+# define POST_GETCONTEXT \
+ (function_call_may_return_twice_false_2 ? \
+ setjmp(function_call_may_return_twice_jmp_buf) : \
+ 0)
+# elif defined(__FreeBSD__) && __FreeBSD__ < 7
+/*
+ * workaround for FreeBSD/i386 getcontext/setcontext bug.
+ * clear the carry flag by (0 ? ... : ...).
+ * FreeBSD PR 92110 http://www.freebsd.org/cgi/query-pr.cgi?pr=92110
+ * [ruby-dev:28263]
+ */
+static int volatile freebsd_clear_carry_flag = 0;
+# define PRE_GETCONTEXT \
+ (freebsd_clear_carry_flag ? (freebsd_clear_carry_flag = 0) : 0)
+# endif
+# ifndef PRE_GETCONTEXT
+# define PRE_GETCONTEXT 0
+# endif
+# ifndef POST_GETCONTEXT
+# define POST_GETCONTEXT 0
+# endif
+# define ruby_longjmp(env, val) rb_jump_context(env, val)
+# define ruby_setjmp(just_before_setjmp, j) ((j)->status = 0, \
+ (just_before_setjmp), \
+ PRE_GETCONTEXT, \
+ getcontext(&(j)->context), \
+ POST_GETCONTEXT, \
+ (j)->status)
+#else
+# if !defined(setjmp) && defined(HAVE__SETJMP)
+# define ruby_setjmp(just_before_setjmp, env) \
+ ((just_before_setjmp), _setjmp(env))
+# define ruby_longjmp(env,val) _longjmp(env,val)
+# else
+# define ruby_setjmp(just_before_setjmp, env) \
+ ((just_before_setjmp), setjmp(env))
+# define ruby_longjmp(env,val) longjmp(env,val)
+# endif
+#endif
+
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined(__VMS)
+#pragma nostandard
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <sys/stat.h>
+
+VALUE rb_cProc;
+VALUE rb_cBinding;
+static VALUE proc_invoke _((VALUE,VALUE,VALUE,VALUE));
+static VALUE rb_f_binding _((VALUE));
+static void rb_f_END _((void));
+static VALUE rb_f_block_given_p _((void));
+static VALUE block_pass _((VALUE,NODE*));
+
+VALUE rb_cMethod;
+static VALUE method_call _((int, VALUE*, VALUE));
+VALUE rb_cUnboundMethod;
+static VALUE umethod_bind _((VALUE, VALUE));
+static VALUE rb_mod_define_method _((int, VALUE*, VALUE));
+NORETURN(static void rb_raise_jump _((VALUE)));
+static VALUE rb_make_exception _((int argc, VALUE *argv));
+
+static int scope_vmode;
+#define SCOPE_PUBLIC 0
+#define SCOPE_PRIVATE 1
+#define SCOPE_PROTECTED 2
+#define SCOPE_MODFUNC 5
+#define SCOPE_MASK 7
+#define SCOPE_SET(f) (scope_vmode=(f))
+#define SCOPE_TEST(f) (scope_vmode&(f))
+
+VALUE (*ruby_sandbox_save)_((rb_thread_t));
+VALUE (*ruby_sandbox_restore)_((rb_thread_t));
+NODE* ruby_current_node;
+int ruby_safe_level = 0;
+/* safe-level:
+ 0 - strings from streams/environment/ARGV are tainted (default)
+ 1 - no dangerous operation by tainted value
+ 2 - process/file operations prohibited
+ 3 - all generated objects are tainted
+ 4 - no global (non-tainted) variable modification/no direct output
+*/
+
+static VALUE safe_getter _((void));
+static void safe_setter _((VALUE val));
+
+void
+rb_secure(level)
+ int level;
+{
+ if (level <= ruby_safe_level) {
+ if (ruby_frame->last_func) {
+ rb_raise(rb_eSecurityError, "Insecure operation `%s' at level %d",
+ rb_id2name(ruby_frame->last_func), ruby_safe_level);
+ }
+ else {
+ rb_raise(rb_eSecurityError, "Insecure operation at level %d", ruby_safe_level);
+ }
+ }
+}
+
+void
+rb_secure_update(obj)
+ VALUE obj;
+{
+ if (!OBJ_TAINTED(obj)) rb_secure(4);
+}
+
+void
+rb_check_safe_obj(x)
+ VALUE x;
+{
+ if (ruby_safe_level > 0 && OBJ_TAINTED(x)){
+ if (ruby_frame->last_func) {
+ rb_raise(rb_eSecurityError, "Insecure operation - %s",
+ rb_id2name(ruby_frame->last_func));
+ }
+ else {
+ rb_raise(rb_eSecurityError, "Insecure operation: -r");
+ }
+ }
+ rb_secure(4);
+}
+
+void
+rb_check_safe_str(x)
+ VALUE x;
+{
+ rb_check_safe_obj(x);
+ if (TYPE(x)!= T_STRING) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected String)",
+ rb_obj_classname(x));
+ }
+}
+
+NORETURN(static void print_undef _((VALUE, ID)));
+static void
+print_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ rb_name_error(id, "undefined method `%s' for %s `%s'",
+ rb_id2name(id),
+ (TYPE(klass) == T_MODULE) ? "module" : "class",
+ rb_class2name(klass));
+}
+
+static ID removed, singleton_removed, undefined, singleton_undefined;
+
+#define CACHE_SIZE 0x800
+#define CACHE_MASK 0x7ff
+#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
+
+struct cache_entry { /* method hash table. */
+ ID mid; /* method's id */
+ ID mid0; /* method's original id */
+ VALUE klass; /* receiver's class */
+ VALUE origin; /* where method defined */
+ NODE *method;
+ int noex;
+};
+
+static struct cache_entry cache[CACHE_SIZE];
+static int ruby_running = 0;
+
+void
+rb_clear_cache()
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ ent->mid = 0;
+ ent++;
+ }
+}
+
+static void
+rb_clear_cache_for_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->mid == id &&
+ RCLASS(ent->origin)->m_tbl == RCLASS(klass)->m_tbl) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+static void
+rb_clear_cache_by_id(id)
+ ID id;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->mid == id) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+void
+rb_clear_cache_by_class(klass)
+ VALUE klass;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->klass == klass || ent->origin == klass) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+static ID init, eqq, each, aref, aset, match, missing;
+static ID added, singleton_added;
+static ID __id__, __send__, respond_to;
+
+#define NOEX_TAINTED 8
+#define NOEX_SAFE(n) ((n) >> 4)
+#define NOEX_WITH(n, v) ((n) | (v) << 4)
+#define NOEX_WITH_SAFE(n) NOEX_WITH(n, ruby_safe_level)
+
+void
+rb_add_method(klass, mid, node, noex)
+ VALUE klass;
+ ID mid;
+ NODE *node;
+ int noex;
+{
+ NODE *body;
+
+ if (NIL_P(klass)) klass = rb_cObject;
+ if (ruby_safe_level >= 4 && (klass == rb_cObject || !OBJ_TAINTED(klass))) {
+ rb_raise(rb_eSecurityError, "Insecure: can't define method");
+ }
+ if (!FL_TEST(klass, FL_SINGLETON) &&
+ node && nd_type(node) != NODE_ZSUPER &&
+ (mid == rb_intern("initialize" )|| mid == rb_intern("initialize_copy"))) {
+ noex = NOEX_PRIVATE | noex;
+ }
+ else if (FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) == NODE_CFUNC &&
+ mid == rb_intern("allocate")) {
+ rb_warn("defining %s.allocate is deprecated; use rb_define_alloc_func()",
+ rb_class2name(rb_iv_get(klass, "__attached__")));
+ mid = ID_ALLOCATOR;
+ }
+ if (OBJ_FROZEN(klass)) rb_error_frozen("class/module");
+ rb_clear_cache_by_id(mid);
+ body = NEW_METHOD(node, NOEX_WITH_SAFE(noex));
+ st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t)body);
+ if (node && mid != ID_ALLOCATOR && ruby_running) {
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1, ID2SYM(mid));
+ }
+ else {
+ rb_funcall(klass, added, 1, ID2SYM(mid));
+ }
+ }
+}
+
+void
+rb_define_alloc_func(klass, func)
+ VALUE klass;
+ VALUE (*func) _((VALUE));
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR, NEW_CFUNC(func, 0),
+ NOEX_PRIVATE);
+}
+
+void
+rb_undef_alloc_func(klass)
+ VALUE klass;
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR, 0, NOEX_UNDEF);
+}
+
+static NODE*
+search_method(klass, id, origin)
+ VALUE klass, *origin;
+ ID id;
+{
+ st_data_t body;
+
+ if (!klass) return 0;
+ while (!st_lookup(RCLASS(klass)->m_tbl, id, &body)) {
+ klass = RCLASS(klass)->super;
+ if (!klass) return 0;
+ }
+
+ if (origin) *origin = klass;
+ return (NODE *)body;
+}
+
+static NODE*
+rb_get_method_body(klassp, idp, noexp)
+ VALUE *klassp;
+ ID *idp;
+ int *noexp;
+{
+ ID id = *idp;
+ VALUE klass = *klassp;
+ VALUE origin;
+ NODE * volatile body;
+ struct cache_entry *ent;
+
+ if ((body = search_method(klass, id, &origin)) == 0 || !body->nd_body) {
+ /* store empty info in cache */
+ ent = cache + EXPR1(klass, id);
+ ent->klass = klass;
+ ent->origin = klass;
+ ent->mid = ent->mid0 = id;
+ ent->noex = 0;
+ ent->method = 0;
+
+ return 0;
+ }
+
+ if (ruby_running) {
+ /* store in cache */
+ ent = cache + EXPR1(klass, id);
+ ent->klass = klass;
+ ent->noex = body->nd_noex;
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ ent->mid = id;
+ *klassp = body->nd_orig;
+ ent->origin = body->nd_orig;
+ *idp = ent->mid0 = body->nd_mid;
+ body = ent->method = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ ent->origin = origin;
+ ent->mid = ent->mid0 = id;
+ ent->method = body;
+ }
+ }
+ else {
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ *klassp = body->nd_orig;
+ *idp = body->nd_mid;
+ body = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ }
+ }
+
+ return body;
+}
+
+NODE*
+rb_method_node(klass, id)
+ VALUE klass;
+ ID id;
+{
+ int noex;
+
+ return rb_get_method_body(&klass, &id, &noex);
+}
+
+static void
+remove_method(klass, mid)
+ VALUE klass;
+ ID mid;
+{
+ st_data_t data;
+ NODE *body = 0;
+
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(klass)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't remove method");
+ }
+ if (OBJ_FROZEN(klass)) rb_error_frozen("class/module");
+ if (mid == __id__ || mid == __send__ || mid == init) {
+ rb_warn("removing `%s' may cause serious problem", rb_id2name(mid));
+ }
+ if (st_lookup(RCLASS(klass)->m_tbl, mid, &data)) {
+ body = (NODE *)data;
+ if (!body || !body->nd_body) body = 0;
+ else {
+ st_delete(RCLASS(klass)->m_tbl, &mid, &data);
+ }
+ }
+ if (!body) {
+ rb_name_error(mid, "method `%s' not defined in %s",
+ rb_id2name(mid), rb_class2name(klass));
+ }
+ rb_clear_cache_for_undef(klass, mid);
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1, ID2SYM(mid));
+ }
+ else {
+ rb_funcall(klass, removed, 1, ID2SYM(mid));
+ }
+}
+
+void
+rb_remove_method(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ remove_method(klass, rb_intern(name));
+}
+
+/*
+ * call-seq:
+ * remove_method(symbol) => self
+ *
+ * Removes the method identified by _symbol_ from the current
+ * class. For an example, see <code>Module.undef_method</code>.
+ */
+
+static VALUE
+rb_mod_remove_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ remove_method(mod, rb_to_id(argv[i]));
+ }
+ return mod;
+}
+
+#undef rb_disable_super
+#undef rb_enable_super
+
+void
+rb_disable_super(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ /* obsolete - no use */
+}
+
+void
+rb_enable_super(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ rb_warn("rb_enable_super() is obsolete");
+}
+
+static void
+rb_export_method(klass, name, noex)
+ VALUE klass;
+ ID name;
+ ID noex;
+{
+ NODE *body;
+ VALUE origin;
+
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ body = search_method(klass, name, &origin);
+ if (!body && TYPE(klass) == T_MODULE) {
+ body = search_method(rb_cObject, name, &origin);
+ }
+ if (!body || !body->nd_body) {
+ print_undef(klass, name);
+ }
+ if (body->nd_noex != noex) {
+ if (klass == origin) {
+ body->nd_noex = noex;
+ }
+ else {
+ rb_add_method(klass, name, NEW_ZSUPER(), noex);
+ }
+ }
+}
+
+int
+rb_method_boundp(klass, id, ex)
+ VALUE klass;
+ ID id;
+ int ex;
+{
+ struct cache_entry *ent;
+ int noex;
+
+ /* is it in the method cache? */
+ ent = cache + EXPR1(klass, id);
+ if (ent->mid == id && ent->klass == klass) {
+ if (ex && (ent->noex & NOEX_PRIVATE))
+ return Qfalse;
+ if (!ent->method) return Qfalse;
+ return Qtrue;
+ }
+ if (rb_get_method_body(&klass, &id, &noex)) {
+ if (ex && (noex & NOEX_PRIVATE))
+ return Qfalse;
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+void
+rb_attr(klass, id, read, write, ex)
+ VALUE klass;
+ ID id;
+ int read, write, ex;
+{
+ const char *name;
+ char *buf;
+ ID attriv;
+ int noex;
+ size_t len;
+
+ if (!ex) noex = NOEX_PUBLIC;
+ else {
+ if (SCOPE_TEST(SCOPE_PRIVATE)) {
+ noex = NOEX_PRIVATE;
+ rb_warning((scope_vmode == SCOPE_MODFUNC) ?
+ "attribute accessor as module_function" :
+ "private attribute?");
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ else {
+ noex = NOEX_PUBLIC;
+ }
+ }
+
+ if (!rb_is_local_id(id) && !rb_is_const_id(id)) {
+ rb_name_error(id, "invalid attribute name `%s'", rb_id2name(id));
+ }
+ name = rb_id2name(id);
+ if (!name) {
+ rb_raise(rb_eArgError, "argument needs to be symbol or string");
+ }
+ len = strlen(name)+2;
+ buf = ALLOCA_N(char,len);
+ snprintf(buf, len, "@%s", name);
+ attriv = rb_intern(buf);
+ if (read) {
+ rb_add_method(klass, id, NEW_IVAR(attriv), noex);
+ }
+ if (write) {
+ rb_add_method(klass, rb_id_attrset(id), NEW_ATTRSET(attriv), noex);
+ }
+}
+
+extern int ruby_in_compile;
+
+VALUE ruby_errinfo = Qnil;
+extern NODE *ruby_eval_tree_begin;
+extern NODE *ruby_eval_tree;
+extern int ruby_nerrs;
+
+VALUE rb_eLocalJumpError;
+VALUE rb_eSysStackError;
+
+extern VALUE ruby_top_self;
+
+struct FRAME *ruby_frame;
+struct SCOPE *ruby_scope;
+static struct FRAME *top_frame;
+static struct SCOPE *top_scope;
+
+static unsigned long frame_unique = 0;
+
+#define PUSH_FRAME() do { \
+ volatile struct FRAME _frame; \
+ _frame.prev = ruby_frame; \
+ _frame.tmp = 0; \
+ _frame.node = ruby_current_node; \
+ _frame.iter = ruby_iter->iter; \
+ _frame.argc = 0; \
+ _frame.flags = 0; \
+ _frame.uniq = frame_unique++; \
+ ruby_frame = &_frame
+
+#define POP_FRAME() \
+ ruby_current_node = _frame.node; \
+ ruby_frame = _frame.prev; \
+} while (0)
+
+struct BLOCK {
+ NODE *var;
+ NODE *body;
+ VALUE self;
+ struct FRAME frame;
+ struct SCOPE *scope;
+ VALUE klass;
+ NODE *cref;
+ int iter;
+ int vmode;
+ int flags;
+ int uniq;
+ struct RVarmap *dyna_vars;
+ VALUE orig_thread;
+ VALUE wrapper;
+ VALUE block_obj;
+ struct BLOCK *outer;
+ struct BLOCK *prev;
+};
+
+#define BLOCK_D_SCOPE 1
+#define BLOCK_LAMBDA 2
+
+static struct BLOCK *ruby_block;
+static unsigned long block_unique = 1;
+
+#define PUSH_BLOCK(v,b) do { \
+ struct BLOCK _block; \
+ _block.var = (v); \
+ _block.body = (b); \
+ _block.self = self; \
+ _block.frame = *ruby_frame; \
+ _block.klass = ruby_class; \
+ _block.cref = ruby_cref; \
+ _block.frame.node = ruby_current_node;\
+ _block.scope = ruby_scope; \
+ _block.prev = ruby_block; \
+ _block.outer = ruby_block; \
+ _block.iter = ruby_iter->iter; \
+ _block.vmode = scope_vmode; \
+ _block.flags = BLOCK_D_SCOPE; \
+ _block.dyna_vars = ruby_dyna_vars; \
+ _block.wrapper = ruby_wrapper; \
+ _block.block_obj = 0; \
+ _block.uniq = (b)?block_unique++:0; \
+ if (b) { \
+ prot_tag->blkid = _block.uniq; \
+ } \
+ ruby_block = &_block
+
+#define POP_BLOCK() \
+ ruby_block = _block.prev; \
+} while (0)
+
+struct RVarmap *ruby_dyna_vars;
+#define PUSH_VARS() do { \
+ struct RVarmap * volatile _old; \
+ _old = ruby_dyna_vars; \
+ ruby_dyna_vars = 0
+
+#define POP_VARS() \
+ if (_old && (ruby_scope->flags & SCOPE_DONT_RECYCLE)) {\
+ if (RBASIC(_old)->flags) /* unless it's already recycled */ \
+ FL_SET(_old, DVAR_DONT_RECYCLE); \
+ }\
+ ruby_dyna_vars = _old; \
+} while (0)
+
+#define DVAR_DONT_RECYCLE FL_USER2
+
+#define DMETHOD_P() (ruby_frame->flags & FRAME_DMETH)
+
+static struct RVarmap*
+new_dvar(id, value, prev)
+ ID id;
+ VALUE value;
+ struct RVarmap *prev;
+{
+ NEWOBJ(vars, struct RVarmap);
+ OBJSETUP(vars, 0, T_VARMAP);
+ vars->id = id;
+ vars->val = value;
+ vars->next = prev;
+
+ return vars;
+}
+
+VALUE
+rb_dvar_defined(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) return Qtrue;
+ vars = vars->next;
+ }
+ return Qfalse;
+}
+
+VALUE
+rb_dvar_curr(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == 0) break;
+ if (vars->id == id) return Qtrue;
+ vars = vars->next;
+ }
+ return Qfalse;
+}
+
+VALUE
+rb_dvar_ref(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) {
+ return vars->val;
+ }
+ vars = vars->next;
+ }
+ return Qnil;
+}
+
+void
+rb_dvar_push(id, value)
+ ID id;
+ VALUE value;
+{
+ ruby_dyna_vars = new_dvar(id, value, ruby_dyna_vars);
+}
+
+static void
+dvar_asgn_internal(id, value, curr)
+ ID id;
+ VALUE value;
+ int curr;
+{
+ int n = 0;
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (curr && vars->id == 0) {
+ /* first null is a dvar header */
+ n++;
+ if (n == 2) break;
+ }
+ if (vars->id == id) {
+ vars->val = value;
+ return;
+ }
+ vars = vars->next;
+ }
+ if (!ruby_dyna_vars) {
+ ruby_dyna_vars = new_dvar(id, value, 0);
+ }
+ else {
+ vars = new_dvar(id, value, ruby_dyna_vars->next);
+ ruby_dyna_vars->next = vars;
+ }
+}
+
+static inline void
+dvar_asgn(id, value)
+ ID id;
+ VALUE value;
+{
+ dvar_asgn_internal(id, value, 0);
+}
+
+static inline void
+dvar_asgn_curr(id, value)
+ ID id;
+ VALUE value;
+{
+ dvar_asgn_internal(id, value, 1);
+}
+
+VALUE *
+rb_svar(cnt)
+ int cnt;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+ ID id;
+
+ if (!ruby_scope->local_tbl) return NULL;
+ if (cnt >= ruby_scope->local_tbl[0]) return NULL;
+ id = ruby_scope->local_tbl[cnt+1];
+ while (vars) {
+ if (vars->id == id) return &vars->val;
+ vars = vars->next;
+ }
+ if (ruby_scope->local_vars == 0) return NULL;
+ return &ruby_scope->local_vars[cnt];
+}
+
+struct iter {
+ int iter;
+ struct iter *prev;
+};
+static struct iter *ruby_iter;
+
+#define ITER_NOT 0
+#define ITER_PRE 1
+#define ITER_CUR 2
+#define ITER_PAS 3
+
+#define PUSH_ITER(i) do { \
+ struct iter _iter; \
+ _iter.prev = ruby_iter; \
+ _iter.iter = (i); \
+ ruby_iter = &_iter
+
+#define POP_ITER() \
+ ruby_iter = _iter.prev; \
+} while (0)
+
+struct tag {
+ rb_jmpbuf_t buf;
+ struct FRAME *frame;
+ struct iter *iter;
+ VALUE tag;
+ VALUE retval;
+ struct SCOPE *scope;
+ VALUE dst;
+ struct tag *prev;
+ int blkid;
+};
+static struct tag *prot_tag;
+
+#define PUSH_TAG(ptag) do { \
+ struct tag _tag; \
+ _tag.retval = Qnil; \
+ _tag.frame = ruby_frame; \
+ _tag.iter = ruby_iter; \
+ _tag.prev = prot_tag; \
+ _tag.scope = ruby_scope; \
+ _tag.tag = ptag; \
+ _tag.dst = 0; \
+ _tag.blkid = 0; \
+ prot_tag = &_tag
+
+#define PROT_NONE Qfalse /* 0 */
+#define PROT_THREAD Qtrue /* 2 */
+#define PROT_FUNC INT2FIX(0) /* 1 */
+#define PROT_LOOP INT2FIX(1) /* 3 */
+#define PROT_LAMBDA INT2FIX(2) /* 5 */
+#define PROT_YIELD INT2FIX(3) /* 7 */
+
+#define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, ruby_setjmp(((void)0), prot_tag->buf))
+
+#define JUMP_TAG(st) do { \
+ ruby_frame = prot_tag->frame; \
+ ruby_iter = prot_tag->iter; \
+ ruby_longjmp(prot_tag->buf,(st)); \
+} while (0)
+
+#define POP_TAG() \
+ prot_tag = _tag.prev; \
+} while (0)
+
+#define TAG_DST() (_tag.dst == (VALUE)ruby_frame->uniq)
+
+#define TAG_RETURN 0x1
+#define TAG_BREAK 0x2
+#define TAG_NEXT 0x3
+#define TAG_RETRY 0x4
+#define TAG_REDO 0x5
+#define TAG_RAISE 0x6
+#define TAG_THROW 0x7
+#define TAG_FATAL 0x8
+#define TAG_MASK 0xf
+
+VALUE ruby_class;
+static VALUE ruby_wrapper; /* security wrapper */
+
+#define PUSH_CLASS(c) do { \
+ volatile VALUE _class = ruby_class; \
+ ruby_class = (c)
+
+#define POP_CLASS() ruby_class = _class; \
+} while (0)
+
+NODE *ruby_cref = 0;
+NODE *ruby_top_cref;
+#define PUSH_CREF(c) ruby_cref = NEW_CREF(c,ruby_cref)
+#define POP_CREF() ruby_cref = ruby_cref->nd_next
+
+#define PUSH_SCOPE() do { \
+ volatile int _vmode = scope_vmode; \
+ struct SCOPE * volatile _old; \
+ NEWOBJ(_scope, struct SCOPE); \
+ OBJSETUP(_scope, 0, T_SCOPE); \
+ _scope->local_tbl = 0; \
+ _scope->local_vars = 0; \
+ _scope->flags = 0; \
+ _old = ruby_scope; \
+ ruby_scope = _scope; \
+ scope_vmode = SCOPE_PUBLIC
+
+rb_thread_t rb_curr_thread;
+rb_thread_t rb_main_thread;
+#define main_thread rb_main_thread
+#define curr_thread rb_curr_thread
+
+static void scope_dup _((struct SCOPE *));
+
+#define POP_SCOPE() \
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE) {\
+ if (_old) scope_dup(_old); \
+ } \
+ if (!(ruby_scope->flags & SCOPE_MALLOC)) {\
+ ruby_scope->local_vars = 0; \
+ ruby_scope->local_tbl = 0; \
+ if (!(ruby_scope->flags & SCOPE_DONT_RECYCLE) && \
+ ruby_scope != top_scope) { \
+ rb_gc_force_recycle((VALUE)ruby_scope);\
+ } \
+ } \
+ ruby_scope->flags |= SCOPE_NOSTACK; \
+ ruby_scope = _old; \
+ scope_vmode = _vmode; \
+} while (0)
+
+static VALUE rb_eval _((VALUE,NODE*));
+static VALUE eval _((VALUE,VALUE,VALUE,char*,int));
+static NODE *compile _((VALUE, char*, int));
+
+static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
+
+#define YIELD_LAMBDA_CALL 1
+#define YIELD_PROC_CALL 2
+#define YIELD_PUBLIC_DEF 4
+#define YIELD_FUNC_AVALUE 1
+#define YIELD_FUNC_SVALUE 2
+
+static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int,VALUE));
+static VALUE module_setup _((VALUE,NODE*));
+
+static VALUE massign _((VALUE,NODE*,VALUE,int));
+static void assign _((VALUE,NODE*,VALUE,int));
+
+typedef struct event_hook {
+ rb_event_hook_func_t func;
+ rb_event_t events;
+ struct event_hook *next;
+} rb_event_hook_t;
+
+static rb_event_hook_t *event_hooks;
+
+#define EXEC_EVENT_HOOK(event, node, self, id, klass) \
+ do { \
+ rb_event_hook_t *hook = event_hooks; \
+ rb_event_hook_func_t hook_func; \
+ rb_event_t events; \
+ \
+ while (hook) { \
+ hook_func = hook->func; \
+ events = hook->events; \
+ hook = hook->next; \
+ if (events & event) \
+ (*hook_func)(event, node, self, id, klass); \
+ } \
+ } while (0)
+
+static VALUE trace_func = 0;
+static int tracing = 0;
+static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
+
+#if 0
+#define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
+ ruby_sourceline = nd_line(ruby_current_node))
+#else
+#define SET_CURRENT_SOURCE() ((void)0)
+#endif
+
+void
+ruby_set_current_source()
+{
+ if (ruby_current_node) {
+ ruby_sourcefile = ruby_current_node->nd_file;
+ ruby_sourceline = nd_line(ruby_current_node);
+ }
+}
+
+static void
+#ifdef HAVE_STDARG_PROTOTYPES
+warn_printf(const char *fmt, ...)
+#else
+warn_printf(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_write_error(buf);
+}
+
+#define warn_print(x) rb_write_error(x)
+#define warn_print2(x,l) rb_write_error2(x,l)
+
+static void
+error_pos()
+{
+ ruby_set_current_source();
+ if (ruby_sourcefile) {
+ if (ruby_frame->last_func) {
+ warn_printf("%s:%d:in `%s'", ruby_sourcefile, ruby_sourceline,
+ rb_id2name(ruby_frame->orig_func));
+ }
+ else if (ruby_sourceline == 0) {
+ warn_printf("%s", ruby_sourcefile);
+ }
+ else {
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
+ }
+ }
+}
+
+VALUE rb_check_backtrace(VALUE);
+
+static VALUE
+get_backtrace(info)
+ VALUE info;
+{
+ if (NIL_P(info)) return Qnil;
+ info = rb_funcall(info, rb_intern("backtrace"), 0);
+ if (NIL_P(info)) return Qnil;
+ return rb_check_backtrace(info);
+}
+
+static void
+set_backtrace(info, bt)
+ VALUE info, bt;
+{
+ rb_funcall(info, rb_intern("set_backtrace"), 1, bt);
+}
+
+static void
+error_print()
+{
+ VALUE errat = Qnil; /* OK */
+ volatile VALUE eclass, e;
+ char *einfo;
+ long elen;
+
+ if (NIL_P(ruby_errinfo)) return;
+
+ PUSH_TAG(PROT_NONE);
+ if (EXEC_TAG() == 0) {
+ errat = get_backtrace(ruby_errinfo);
+ }
+ else {
+ errat = Qnil;
+ }
+ if (EXEC_TAG()) goto error;
+ if (NIL_P(errat)){
+ ruby_set_current_source();
+ if (ruby_sourcefile)
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
+ else
+ warn_printf("%d", ruby_sourceline);
+ }
+ else if (RARRAY(errat)->len == 0) {
+ error_pos();
+ }
+ else {
+ VALUE mesg = RARRAY(errat)->ptr[0];
+
+ if (NIL_P(mesg)) error_pos();
+ else {
+ warn_print2(RSTRING(mesg)->ptr, RSTRING(mesg)->len);
+ }
+ }
+
+ eclass = CLASS_OF(ruby_errinfo);
+ if (EXEC_TAG() == 0) {
+ e = rb_funcall(ruby_errinfo, rb_intern("message"), 0, 0);
+ StringValue(e);
+ einfo = RSTRING(e)->ptr;
+ elen = RSTRING(e)->len;
+ }
+ else {
+ einfo = "";
+ elen = 0;
+ }
+ if (EXEC_TAG()) goto error;
+ if (eclass == rb_eRuntimeError && elen == 0) {
+ warn_print(": unhandled exception\n");
+ }
+ else {
+ VALUE epath;
+
+ epath = rb_class_name(eclass);
+ if (elen == 0) {
+ warn_print(": ");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
+ warn_print("\n");
+ }
+ else {
+ char *tail = 0;
+ long len = elen;
+
+ if (RSTRING(epath)->ptr[0] == '#') epath = 0;
+ if ((tail = memchr(einfo, '\n', elen)) != 0) {
+ len = tail - einfo;
+ tail++; /* skip newline */
+ }
+ warn_print(": ");
+ warn_print2(einfo, len);
+ if (epath) {
+ warn_print(" (");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
+ warn_print(")\n");
+ }
+ if (tail && elen>len+1) {
+ warn_print2(tail, elen-len-1);
+ if (einfo[elen-1] != '\n') warn_print2("\n", 1);
+ }
+ }
+ }
+
+ if (!NIL_P(errat)) {
+ long i;
+ struct RArray *ep = RARRAY(errat);
+
+#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
+#define TRACE_HEAD 8
+#define TRACE_TAIL 5
+
+ ep = RARRAY(errat);
+ for (i=1; i<ep->len; i++) {
+ if (TYPE(ep->ptr[i]) == T_STRING) {
+ warn_printf("\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
+ }
+ if (i == TRACE_HEAD && ep->len > TRACE_MAX) {
+ warn_printf("\t ... %ld levels...\n",
+ ep->len - TRACE_HEAD - TRACE_TAIL);
+ i = ep->len - TRACE_TAIL;
+ }
+ }
+ }
+ error:
+ POP_TAG();
+}
+
+#if defined(__APPLE__)
+#define environ (*_NSGetEnviron())
+#elif !defined(_WIN32) && !defined(__MACOS__) || defined(_WIN32_WCE)
+extern char **environ;
+#endif
+char **rb_origenviron;
+
+void rb_call_inits _((void));
+void Init_stack _((VALUE*));
+void Init_heap _((void));
+void Init_ext _((void));
+
+#ifdef HAVE_NATIVETHREAD
+static rb_nativethread_t ruby_thid;
+int
+is_ruby_native_thread() {
+ return NATIVETHREAD_EQUAL(ruby_thid, NATIVETHREAD_CURRENT());
+}
+
+# ifdef HAVE_NATIVETHREAD_KILL
+void
+ruby_native_thread_kill(sig)
+ int sig;
+{
+ NATIVETHREAD_KILL(ruby_thid, sig);
+}
+# endif
+#endif
+
+void
+ruby_init()
+{
+ static int initialized = 0;
+ static struct FRAME frame;
+ static struct iter iter;
+ int state;
+
+ if (initialized)
+ return;
+ initialized = 1;
+#ifdef HAVE_NATIVETHREAD
+ ruby_thid = NATIVETHREAD_CURRENT();
+#endif
+
+ ruby_frame = top_frame = &frame;
+ ruby_iter = &iter;
+
+#ifdef __MACOS__
+ rb_origenviron = 0;
+#else
+ rb_origenviron = environ;
+#endif
+
+ Init_stack((void*)&state);
+ Init_heap();
+ PUSH_SCOPE();
+ ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ top_scope = ruby_scope;
+ /* default visibility is private at toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_call_inits();
+ ruby_class = rb_cObject;
+ ruby_frame->self = ruby_top_self;
+ ruby_top_cref = rb_node_newnode(NODE_CREF,rb_cObject,0,0);
+ ruby_cref = ruby_top_cref;
+ rb_define_global_const("TOPLEVEL_BINDING", rb_f_binding(ruby_top_self));
+#ifdef __MACOS__
+ _macruby_init();
+#elif defined(__VMS)
+ _vmsruby_init();
+#endif
+ ruby_prog_init();
+ ALLOW_INTS;
+ }
+ POP_TAG();
+ if (state) {
+ error_print();
+ exit(EXIT_FAILURE);
+ }
+ POP_SCOPE();
+ ruby_scope = top_scope;
+ top_scope->flags &= ~SCOPE_NOSTACK;
+ ruby_running = 1;
+}
+
+static VALUE
+eval_node(self, node)
+ VALUE self;
+ NODE *node;
+{
+ NODE *beg_tree = ruby_eval_tree_begin;
+
+ ruby_eval_tree_begin = 0;
+ if (beg_tree) {
+ rb_eval(self, beg_tree);
+ }
+
+ if (!node) return Qnil;
+ return rb_eval(self, node);
+}
+
+int ruby_in_eval;
+
+static void rb_thread_cleanup _((void));
+static void rb_thread_wait_other_threads _((void));
+
+static int thread_no_ensure _((void));
+
+static VALUE exception_error;
+static VALUE sysstack_error;
+
+static int
+sysexit_status(err)
+ VALUE err;
+{
+ VALUE st = rb_iv_get(err, "status");
+ return NUM2INT(st);
+}
+
+static int
+error_handle(ex)
+ int ex;
+{
+ int status = EXIT_FAILURE;
+ rb_thread_t th = curr_thread;
+
+ if (rb_thread_set_raised(th))
+ return EXIT_FAILURE;
+ switch (ex & TAG_MASK) {
+ case 0:
+ status = EXIT_SUCCESS;
+ break;
+
+ case TAG_RETURN:
+ error_pos();
+ warn_print(": unexpected return\n");
+ break;
+ case TAG_NEXT:
+ error_pos();
+ warn_print(": unexpected next\n");
+ break;
+ case TAG_BREAK:
+ error_pos();
+ warn_print(": unexpected break\n");
+ break;
+ case TAG_REDO:
+ error_pos();
+ warn_print(": unexpected redo\n");
+ break;
+ case TAG_RETRY:
+ error_pos();
+ warn_print(": retry outside of rescue clause\n");
+ break;
+ case TAG_THROW:
+ if (prot_tag && prot_tag->frame && prot_tag->frame->node) {
+ NODE *tag = prot_tag->frame->node;
+ warn_printf("%s:%d: uncaught throw\n",
+ tag->nd_file, nd_line(tag));
+ }
+ else {
+ error_pos();
+ warn_printf(": unexpected throw\n");
+ }
+ break;
+ case TAG_RAISE:
+ case TAG_FATAL:
+ if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ status = sysexit_status(ruby_errinfo);
+ }
+ else if (rb_obj_is_instance_of(ruby_errinfo, rb_eSignal)) {
+ /* no message when exiting by signal */
+ }
+ else {
+ error_print();
+ }
+ break;
+ default:
+ rb_bug("Unknown longjmp status %d", ex);
+ break;
+ }
+ rb_thread_reset_raised(th);
+ return status;
+}
+
+void
+ruby_options(argc, argv)
+ int argc;
+ char **argv;
+{
+ int state;
+
+ Init_stack((void*)&state);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ ruby_process_options(argc, argv);
+ }
+ else {
+ trace_func = 0;
+ tracing = 0;
+ exit(error_handle(state));
+ }
+ POP_TAG();
+}
+
+void rb_exec_end_proc _((void));
+
+static void
+ruby_finalize_0()
+{
+ PUSH_TAG(PROT_NONE);
+ if (EXEC_TAG() == 0) {
+ rb_trap_exit();
+ }
+ POP_TAG();
+ rb_exec_end_proc();
+}
+
+static void
+ruby_finalize_1()
+{
+ signal(SIGINT, SIG_DFL);
+ ruby_errinfo = 0;
+ rb_gc_call_finalizer_at_exit();
+ trace_func = 0;
+ tracing = 0;
+}
+
+void
+ruby_finalize()
+{
+ ruby_finalize_0();
+ ruby_finalize_1();
+}
+
+int
+ruby_cleanup(ex)
+ int ex;
+{
+ int state;
+ volatile VALUE errs[2];
+ int nerr;
+
+ errs[1] = ruby_errinfo;
+ ruby_safe_level = 0;
+ Init_stack((void *)&state);
+ ruby_finalize_0();
+ errs[0] = ruby_errinfo;
+ PUSH_TAG(PROT_NONE);
+ PUSH_ITER(ITER_NOT);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_thread_cleanup();
+ rb_thread_wait_other_threads();
+ }
+ else if (ex == 0) {
+ ex = state;
+ }
+ POP_ITER();
+ ruby_errinfo = errs[1];
+ ex = error_handle(ex);
+ ruby_finalize_1();
+ POP_TAG();
+
+ for (nerr = 0; nerr < sizeof(errs) / sizeof(errs[0]); ++nerr) {
+ VALUE err = errs[nerr];
+
+ if (!RTEST(err)) continue;
+
+ if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
+ return sysexit_status(err);
+ }
+ else if (rb_obj_is_kind_of(err, rb_eSignal)) {
+ VALUE sig = rb_iv_get(err, "signo");
+ ruby_default_signal(NUM2INT(sig));
+ }
+ else if (ex == 0) {
+ ex = 1;
+ }
+ }
+
+#if EXIT_SUCCESS != 0 || EXIT_FAILURE != 1
+ switch (ex) {
+#if EXIT_SUCCESS != 0
+ case 0: return EXIT_SUCCESS;
+#endif
+#if EXIT_FAILURE != 1
+ case 1: return EXIT_FAILURE;
+#endif
+ }
+#endif
+
+ return ex;
+}
+
+static int
+ruby_exec_internal()
+{
+ int state;
+
+ PUSH_TAG(PROT_NONE);
+ PUSH_ITER(ITER_NOT);
+ /* default visibility is private at toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+ if ((state = EXEC_TAG()) == 0) {
+ eval_node(ruby_top_self, ruby_eval_tree);
+ }
+ POP_ITER();
+ POP_TAG();
+ return state;
+}
+
+void
+ruby_stop(ex)
+ int ex;
+{
+ exit(ruby_cleanup(ex));
+}
+
+int
+ruby_exec()
+{
+ volatile NODE *tmp;
+
+ Init_stack((void*)&tmp);
+ return ruby_exec_internal();
+}
+
+void
+ruby_run()
+{
+ int state;
+ static int ex;
+
+ if (ruby_nerrs > 0) exit(EXIT_FAILURE);
+ state = ruby_exec();
+ if (state && !ex) ex = state;
+ ruby_stop(ex);
+}
+
+static void
+compile_error(at)
+ const char *at;
+{
+ VALUE str;
+
+ ruby_nerrs = 0;
+ str = rb_str_buf_new2("compile error");
+ if (at) {
+ rb_str_buf_cat2(str, " in ");
+ rb_str_buf_cat2(str, at);
+ }
+ rb_str_buf_cat(str, "\n", 1);
+ if (!NIL_P(ruby_errinfo)) {
+ rb_str_append(str, rb_obj_as_string(ruby_errinfo));
+ }
+ rb_exc_raise(rb_exc_new3(rb_eSyntaxError, str));
+}
+
+VALUE
+rb_eval_string(str)
+ const char *str;
+{
+ VALUE v;
+ NODE *oldsrc = ruby_current_node;
+
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename("(eval)");
+ v = eval(ruby_top_self, rb_str_new2(str), Qnil, 0, 0);
+ ruby_current_node = oldsrc;
+
+ return v;
+}
+
+VALUE
+rb_eval_string_protect(str, state)
+ const char *str;
+ int *state;
+{
+ return rb_protect((VALUE (*)_((VALUE)))rb_eval_string, (VALUE)str, state);
+}
+
+VALUE
+rb_eval_string_wrap(str, state)
+ const char *str;
+ int *state;
+{
+ int status;
+ VALUE self = ruby_top_self;
+ VALUE wrapper = ruby_wrapper;
+ VALUE val;
+
+ PUSH_CLASS(ruby_wrapper = rb_module_new());
+ ruby_top_self = rb_obj_clone(ruby_top_self);
+ rb_extend_object(ruby_top_self, ruby_wrapper);
+ PUSH_FRAME();
+ ruby_frame->last_func = 0;
+ ruby_frame->last_class = 0;
+ ruby_frame->self = self;
+ PUSH_CREF(ruby_wrapper);
+ PUSH_SCOPE();
+
+ val = rb_eval_string_protect(str, &status);
+ ruby_top_self = self;
+
+ POP_SCOPE();
+ POP_FRAME();
+ POP_CLASS();
+ ruby_wrapper = wrapper;
+ if (state) {
+ *state = status;
+ }
+ else if (status) {
+ JUMP_TAG(status);
+ }
+ return val;
+}
+
+NORETURN(static void localjump_error(const char*, VALUE, int));
+static void
+localjump_error(mesg, value, reason)
+ const char *mesg;
+ VALUE value;
+ int reason;
+{
+ VALUE exc = rb_exc_new2(rb_eLocalJumpError, mesg);
+ ID id;
+
+ rb_iv_set(exc, "@exit_value", value);
+ switch (reason) {
+ case TAG_BREAK:
+ id = rb_intern("break"); break;
+ case TAG_REDO:
+ id = rb_intern("redo"); break;
+ case TAG_RETRY:
+ id = rb_intern("retry"); break;
+ case TAG_NEXT:
+ id = rb_intern("next"); break;
+ case TAG_RETURN:
+ id = rb_intern("return"); break;
+ default:
+ id = rb_intern("noreason"); break;
+ }
+ rb_iv_set(exc, "@reason", ID2SYM(id));
+ rb_exc_raise(exc);
+}
+
+/*
+ * call_seq:
+ * local_jump_error.exit_value => obj
+ *
+ * Returns the exit value associated with this +LocalJumpError+.
+ */
+static VALUE
+localjump_xvalue(exc)
+ VALUE exc;
+{
+ return rb_iv_get(exc, "@exit_value");
+}
+
+/*
+ * call-seq:
+ * local_jump_error.reason => symbol
+ *
+ * The reason this block was terminated:
+ * :break, :redo, :retry, :next, :return, or :noreason.
+ */
+
+static VALUE
+localjump_reason(exc)
+ VALUE exc;
+{
+ return rb_iv_get(exc, "@reason");
+}
+
+NORETURN(static void jump_tag_but_local_jump _((int,VALUE)));
+static void
+jump_tag_but_local_jump(state, val)
+ int state;
+ VALUE val;
+{
+
+ if (val == Qundef) val = prot_tag->retval;
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETURN:
+ localjump_error("unexpected return", val, state);
+ break;
+ case TAG_BREAK:
+ localjump_error("unexpected break", val, state);
+ break;
+ case TAG_NEXT:
+ localjump_error("unexpected next", val, state);
+ break;
+ case TAG_REDO:
+ localjump_error("unexpected redo", Qnil, state);
+ break;
+ case TAG_RETRY:
+ localjump_error("retry outside of rescue clause", Qnil, state);
+ break;
+ default:
+ break;
+ }
+ JUMP_TAG(state);
+}
+
+VALUE
+rb_eval_cmd(cmd, arg, level)
+ VALUE cmd, arg;
+ int level;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+ struct SCOPE *saved_scope;
+ volatile int safe = ruby_safe_level;
+
+ if (OBJ_TAINTED(cmd)) {
+ level = 4;
+ }
+ if (TYPE(cmd) != T_STRING) {
+ PUSH_ITER(ITER_NOT);
+ PUSH_TAG(PROT_NONE);
+ ruby_safe_level = level;
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_funcall2(cmd, rb_intern("call"), RARRAY(arg)->len, RARRAY(arg)->ptr);
+ }
+ ruby_safe_level = safe;
+ POP_TAG();
+ POP_ITER();
+ if (state) JUMP_TAG(state);
+ return val;
+ }
+
+ saved_scope = ruby_scope;
+ ruby_scope = top_scope;
+ PUSH_FRAME();
+ ruby_frame->last_func = 0;
+ ruby_frame->last_class = 0;
+ ruby_frame->self = ruby_top_self;
+ PUSH_CREF(ruby_wrapper ? ruby_wrapper : rb_cObject);
+
+ ruby_safe_level = level;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = eval(ruby_top_self, cmd, Qnil, 0, 0);
+ }
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE)
+ scope_dup(saved_scope);
+ ruby_scope = saved_scope;
+ ruby_safe_level = safe;
+ POP_TAG();
+ POP_FRAME();
+
+ if (state) jump_tag_but_local_jump(state, val);
+ return val;
+}
+
+#define ruby_cbase (ruby_cref->nd_clss)
+
+static VALUE
+ev_const_defined(cref, id, self)
+ NODE *cref;
+ ID id;
+ VALUE self;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_next) {
+ struct RClass *klass = RCLASS(cbase->nd_clss);
+
+ if (!NIL_P(klass)) {
+ if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, &result)) {
+ if (result == Qundef && NIL_P(rb_autoload_p((VALUE)klass, id))) {
+ return Qfalse;
+ }
+ return Qtrue;
+ }
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_defined(cref->nd_clss, id);
+}
+
+static VALUE
+ev_const_get(cref, id, self)
+ NODE *cref;
+ ID id;
+ VALUE self;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_next) {
+ VALUE klass = cbase->nd_clss;
+
+ if (!NIL_P(klass)) {
+ while (RCLASS(klass)->iv_tbl &&
+ st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
+ if (result == Qundef) {
+ if (!RTEST(rb_autoload_load(klass, id))) break;
+ continue;
+ }
+ return result;
+ }
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_get(NIL_P(cref->nd_clss) ? CLASS_OF(self): cref->nd_clss, id);
+}
+
+static VALUE
+cvar_cbase()
+{
+ NODE *cref = ruby_cref;
+
+ while (cref && cref->nd_next && (NIL_P(cref->nd_clss) || FL_TEST(cref->nd_clss, FL_SINGLETON))) {
+ cref = cref->nd_next;
+ if (!cref->nd_next) {
+ rb_warn("class variable access from toplevel singleton method");
+ }
+ }
+ if (NIL_P(cref->nd_clss)) {
+ rb_raise(rb_eTypeError, "no class variables available");
+ }
+ return cref->nd_clss;
+}
+
+/*
+ * call-seq:
+ * Module.nesting => array
+ *
+ * Returns the list of +Modules+ nested at the point of call.
+ *
+ * module M1
+ * module M2
+ * $a = Module.nesting
+ * end
+ * end
+ * $a #=> [M1::M2, M1]
+ * $a[0].name #=> "M1::M2"
+ */
+
+static VALUE
+rb_mod_nesting()
+{
+ NODE *cbase = ruby_cref;
+ VALUE ary = rb_ary_new();
+
+ while (cbase && cbase->nd_next) {
+ if (!NIL_P(cbase->nd_clss)) rb_ary_push(ary, cbase->nd_clss);
+ cbase = cbase->nd_next;
+ }
+ if (ruby_wrapper && RARRAY(ary)->len == 0) {
+ rb_ary_push(ary, ruby_wrapper);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * Module.constants => array
+ *
+ * Returns an array of the names of all constants defined in the
+ * system. This list includes the names of all modules and classes.
+ *
+ * p Module.constants.sort[1..5]
+ *
+ * <em>produces:</em>
+ *
+ * ["ARGV", "ArgumentError", "Array", "Bignum", "Binding"]
+ */
+
+static VALUE
+rb_mod_s_constants()
+{
+ NODE *cbase = ruby_cref;
+ void *data = 0;
+
+ while (cbase) {
+ if (!NIL_P(cbase->nd_clss)) {
+ data = rb_mod_const_at(cbase->nd_clss, data);
+ }
+ cbase = cbase->nd_next;
+ }
+
+ if (!NIL_P(ruby_cbase)) {
+ data = rb_mod_const_of(ruby_cbase, data);
+ }
+ return rb_const_list(data);
+}
+
+void
+rb_frozen_class_p(klass)
+ VALUE klass;
+{
+ char *desc = "something(?!)";
+
+ if (OBJ_FROZEN(klass)) {
+ if (FL_TEST(klass, FL_SINGLETON))
+ desc = "object";
+ else {
+ switch (TYPE(klass)) {
+ case T_MODULE:
+ case T_ICLASS:
+ desc = "module"; break;
+ case T_CLASS:
+ desc = "class"; break;
+ }
+ }
+ rb_error_frozen(desc);
+ }
+}
+
+void
+rb_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ VALUE origin;
+ NODE *body;
+
+ if (ruby_cbase == rb_cObject && klass == rb_cObject) {
+ rb_secure(4);
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(klass)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id));
+ }
+ rb_frozen_class_p(klass);
+ if (id == __id__ || id == __send__ || id == init) {
+ rb_warn("undefining `%s' may cause serious problem", rb_id2name(id));
+ }
+ body = search_method(klass, id, &origin);
+ if (!body || !body->nd_body) {
+ char *s0 = " class";
+ VALUE c = klass;
+
+ if (FL_TEST(c, FL_SINGLETON)) {
+ VALUE obj = rb_iv_get(klass, "__attached__");
+
+ switch (TYPE(obj)) {
+ case T_MODULE:
+ case T_CLASS:
+ c = obj;
+ s0 = "";
+ }
+ }
+ else if (TYPE(c) == T_MODULE) {
+ s0 = " module";
+ }
+ rb_name_error(id, "undefined method `%s' for%s `%s'",
+ rb_id2name(id),s0,rb_class2name(c));
+ }
+ rb_add_method(klass, id, 0, NOEX_PUBLIC);
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"),
+ singleton_undefined, 1, ID2SYM(id));
+ }
+ else {
+ rb_funcall(klass, undefined, 1, ID2SYM(id));
+ }
+}
+
+/*
+ * call-seq:
+ * undef_method(symbol) => self
+ *
+ * Prevents the current class from responding to calls to the named
+ * method. Contrast this with <code>remove_method</code>, which deletes
+ * the method from the particular class; Ruby will still search
+ * superclasses and mixed-in modules for a possible receiver.
+ *
+ * class Parent
+ * def hello
+ * puts "In parent"
+ * end
+ * end
+ * class Child < Parent
+ * def hello
+ * puts "In child"
+ * end
+ * end
+ *
+ *
+ * c = Child.new
+ * c.hello
+ *
+ *
+ * class Child
+ * remove_method :hello # remove from child, still in parent
+ * end
+ * c.hello
+ *
+ *
+ * class Child
+ * undef_method :hello # prevent any calls to 'hello'
+ * end
+ * c.hello
+ *
+ * <em>produces:</em>
+ *
+ * In child
+ * In parent
+ * prog.rb:23: undefined method `hello' for #<Child:0x401b3bb4> (NoMethodError)
+ */
+
+static VALUE
+rb_mod_undef_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_undef(mod, rb_to_id(argv[i]));
+ }
+ return mod;
+}
+
+void
+rb_alias(klass, name, def)
+ VALUE klass;
+ ID name, def;
+{
+ VALUE origin;
+ NODE *orig, *body, *node;
+ VALUE singleton = 0;
+ st_data_t data;
+
+ rb_frozen_class_p(klass);
+ if (name == def) return;
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ orig = search_method(klass, def, &origin);
+ if (!orig || !orig->nd_body) {
+ if (TYPE(klass) == T_MODULE) {
+ orig = search_method(rb_cObject, def, &origin);
+ }
+ }
+ if (!orig || !orig->nd_body) {
+ print_undef(klass, def);
+ }
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ singleton = rb_iv_get(klass, "__attached__");
+ }
+ body = orig->nd_body;
+ orig->nd_cnt++;
+ if (nd_type(body) == NODE_FBODY) { /* was alias */
+ def = body->nd_mid;
+ origin = body->nd_orig;
+ body = body->nd_head;
+ }
+
+ rb_clear_cache_by_id(name);
+ if (RTEST(ruby_verbose) && st_lookup(RCLASS(klass)->m_tbl, name, &data)) {
+ node = (NODE *)data;
+ if (node->nd_cnt == 0 && node->nd_body) {
+ rb_warning("discarding old %s", rb_id2name(name));
+ }
+ }
+ st_insert(RCLASS(klass)->m_tbl, name,
+ (st_data_t)NEW_METHOD(NEW_FBODY(body, def, origin),
+ NOEX_WITH_SAFE(orig->nd_noex)));
+
+ if (!ruby_running) return;
+
+ if (singleton) {
+ rb_funcall(singleton, singleton_added, 1, ID2SYM(name));
+ }
+ else {
+ rb_funcall(klass, added, 1, ID2SYM(name));
+ }
+}
+
+/*
+ * call-seq:
+ * alias_method(new_name, old_name) => self
+ *
+ * Makes <i>new_name</i> a new copy of the method <i>old_name</i>. This can
+ * be used to retain access to methods that are overridden.
+ *
+ * module Mod
+ * alias_method :orig_exit, :exit
+ * def exit(code=0)
+ * puts "Exiting with code #{code}"
+ * orig_exit(code)
+ * end
+ * end
+ * include Mod
+ * exit(99)
+ *
+ * <em>produces:</em>
+ *
+ * Exiting with code 99
+ */
+
+static VALUE
+rb_mod_alias_method(mod, newname, oldname)
+ VALUE mod, newname, oldname;
+{
+ rb_alias(mod, rb_to_id(newname), rb_to_id(oldname));
+ return mod;
+}
+
+NODE *
+rb_copy_node_scope(node, rval)
+ NODE *node;
+ NODE *rval;
+{
+ NODE *copy = NEW_NODE(NODE_SCOPE,0,rval,node->nd_next);
+
+ if (node->nd_tbl) {
+ copy->nd_tbl = ALLOC_N(ID, node->nd_tbl[0]+1);
+ MEMCPY(copy->nd_tbl, node->nd_tbl, ID, node->nd_tbl[0]+1);
+ }
+ else {
+ copy->nd_tbl = 0;
+ }
+ return copy;
+}
+
+#ifdef C_ALLOCA
+# define TMP_PROTECT NODE * volatile tmp__protect_tmp=0
+# define TMP_ALLOC(n) \
+ (tmp__protect_tmp = NEW_NODE(NODE_ALLOCA, \
+ ALLOC_N(VALUE,n),tmp__protect_tmp,n), \
+ (void*)tmp__protect_tmp->nd_head)
+#else
+# define TMP_PROTECT typedef int foobazzz
+# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
+#endif
+
+#define SETUP_ARGS0(anode,extra) do {\
+ NODE *n = anode;\
+ if (!n) {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ else if (nd_type(n) == NODE_ARRAY) {\
+ argc=anode->nd_alen;\
+ if (argc > 0) {\
+ int i;\
+ n = anode;\
+ argv = TMP_ALLOC(argc+extra);\
+ for (i=0;i<argc;i++) {\
+ argv[i] = rb_eval(self,n->nd_head);\
+ n=n->nd_next;\
+ }\
+ }\
+ else {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ }\
+ else {\
+ VALUE args = rb_eval(self,n);\
+ if (TYPE(args) != T_ARRAY)\
+ args = rb_ary_to_ary(args);\
+ argc = RARRAY(args)->len;\
+ argv = TMP_ALLOC(argc+extra);\
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\
+ }\
+} while (0)
+
+#define SETUP_ARGS(anode) SETUP_ARGS0(anode,0)
+
+#define BEGIN_CALLARGS do {\
+ struct BLOCK *tmp_block = ruby_block;\
+ int tmp_iter = ruby_iter->iter;\
+ switch (tmp_iter) {\
+ case ITER_PRE:\
+ if (ruby_block) ruby_block = ruby_block->outer;\
+ case ITER_PAS:\
+ tmp_iter = ITER_NOT;\
+ }\
+ PUSH_ITER(tmp_iter)
+
+#define END_CALLARGS \
+ ruby_block = tmp_block;\
+ POP_ITER();\
+} while (0)
+
+#define MATCH_DATA *rb_svar(node->nd_cnt)
+
+static char* is_defined _((VALUE, NODE*, char*));
+
+static char*
+arg_defined(self, node, buf, type)
+ VALUE self;
+ NODE *node;
+ char *buf;
+ char *type;
+{
+ int argc;
+ int i;
+
+ if (!node) return type; /* no args */
+ if (nd_type(node) == NODE_ARRAY) {
+ argc=node->nd_alen;
+ if (argc > 0) {
+ for (i=0;i<argc;i++) {
+ if (!is_defined(self, node->nd_head, buf))
+ return 0;
+ node = node->nd_next;
+ }
+ }
+ }
+ else if (!is_defined(self, node, buf)) {
+ return 0;
+ }
+ return type;
+}
+
+static char*
+is_defined(self, node, buf)
+ VALUE self;
+ NODE *node; /* OK */
+ char *buf;
+{
+ VALUE val; /* OK */
+ int state;
+
+ again:
+ if (!node) return "expression";
+ switch (nd_type(node)) {
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ if (ruby_frame->last_func == 0) return 0;
+ else if (ruby_frame->last_class == 0) return 0;
+ val = ruby_frame->last_class;
+ if (rb_method_boundp(RCLASS(val)->super, ruby_frame->orig_func, 0)) {
+ if (nd_type(node) == NODE_SUPER) {
+ return arg_defined(self, node->nd_args, buf, "super");
+ }
+ return "super";
+ }
+ break;
+
+ case NODE_VCALL:
+ case NODE_FCALL:
+ val = self;
+ goto check_bound;
+
+ case NODE_ATTRASGN:
+ val = self;
+ if (node->nd_recv == (NODE *)1) goto check_bound;
+ case NODE_CALL:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node->nd_recv);
+ }
+ POP_TAG();
+ if (state) {
+ ruby_errinfo = Qnil;
+ return 0;
+ }
+ check_bound:
+ {
+ int call = nd_type(node)==NODE_CALL;
+
+ val = CLASS_OF(val);
+ if (call) {
+ int noex;
+ ID id = node->nd_mid;
+
+ if (!rb_get_method_body(&val, &id, &noex))
+ break;
+ if ((noex & NOEX_PRIVATE))
+ break;
+ if ((noex & NOEX_PROTECTED) &&
+ !rb_obj_is_kind_of(self, rb_class_real(val)))
+ break;
+ }
+ else if (!rb_method_boundp(val, node->nd_mid, call))
+ break;
+ return arg_defined(self, node->nd_args, buf,
+ nd_type(node) == NODE_ATTRASGN ?
+ "assignment" : "method");
+ }
+ break;
+
+ case NODE_MATCH2:
+ case NODE_MATCH3:
+ return "method";
+
+ case NODE_YIELD:
+ if (rb_block_given_p()) {
+ return "yield";
+ }
+ break;
+
+ case NODE_SELF:
+ return "self";
+
+ case NODE_NIL:
+ return "nil";
+
+ case NODE_TRUE:
+ return "true";
+
+ case NODE_FALSE:
+ return "false";
+
+ 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:
+ case NODE_DASGN_CURR:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ case NODE_CDECL:
+ case NODE_CVDECL:
+ case NODE_CVASGN:
+ return "assignment";
+
+ case NODE_LVAR:
+ return "local-variable";
+ case NODE_DVAR:
+ return "local-variable(in-block)";
+
+ case NODE_GVAR:
+ if (rb_gvar_defined(node->nd_entry)) {
+ return "global-variable";
+ }
+ break;
+
+ case NODE_IVAR:
+ if (rb_ivar_defined(self, node->nd_vid)) {
+ return "instance-variable";
+ }
+ break;
+
+ case NODE_CONST:
+ if (ev_const_defined(ruby_cref, node->nd_vid, self)) {
+ return "constant";
+ }
+ break;
+
+ case NODE_CVAR:
+ if (rb_cvar_defined(cvar_cbase(), node->nd_vid)) {
+ return "class variable";
+ }
+ break;
+
+ case NODE_COLON2:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ if (state) {
+ ruby_errinfo = Qnil;
+ return 0;
+ }
+ else {
+ switch (TYPE(val)) {
+ case T_CLASS:
+ case T_MODULE:
+ if (rb_const_defined_from(val, node->nd_mid))
+ return "constant";
+ break;
+ default:
+ if (rb_method_boundp(CLASS_OF(val), node->nd_mid, 1)) {
+ return "method";
+ }
+ }
+ }
+ break;
+
+ case NODE_COLON3:
+ if (rb_const_defined_from(rb_cObject, node->nd_mid)) {
+ return "constant";
+ }
+ break;
+
+ case NODE_NTH_REF:
+ if (RTEST(rb_reg_nth_defined(node->nd_nth, MATCH_DATA))) {
+ sprintf(buf, "$%d", (int)node->nd_nth);
+ return buf;
+ }
+ break;
+
+ case NODE_BACK_REF:
+ if (RTEST(rb_reg_nth_defined(0, MATCH_DATA))) {
+ sprintf(buf, "$%c", (char)node->nd_nth);
+ return buf;
+ }
+ break;
+
+ case NODE_NEWLINE:
+ node = node->nd_next;
+ goto again;
+
+ default:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval(self, node);
+ }
+ POP_TAG();
+ if (!state) {
+ return "expression";
+ }
+ ruby_errinfo = Qnil;
+ break;
+ }
+ return 0;
+}
+
+static int handle_rescue _((VALUE,NODE*));
+
+static void blk_free();
+
+static VALUE
+rb_obj_is_proc(proc)
+ VALUE proc;
+{
+ if (TYPE(proc) == T_DATA && RDATA(proc)->dfree == (RUBY_DATA_FUNC)blk_free) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+void
+rb_add_event_hook(func, events)
+ rb_event_hook_func_t func;
+ rb_event_t events;
+{
+ rb_event_hook_t *hook;
+
+ hook = ALLOC(rb_event_hook_t);
+ hook->func = func;
+ hook->events = events;
+ hook->next = event_hooks;
+ event_hooks = hook;
+}
+
+int
+rb_remove_event_hook(func)
+ rb_event_hook_func_t func;
+{
+ rb_event_hook_t *prev, *hook;
+
+ prev = NULL;
+ hook = event_hooks;
+ while (hook) {
+ if (hook->func == func) {
+ if (prev) {
+ prev->next = hook->next;
+ }
+ else {
+ event_hooks = hook->next;
+ }
+ xfree(hook);
+ return 0;
+ }
+ prev = hook;
+ hook = hook->next;
+ }
+ return -1;
+}
+
+/*
+ * call-seq:
+ * set_trace_func(proc) => proc
+ * set_trace_func(nil) => nil
+ *
+ * Establishes _proc_ as the handler for tracing, or disables
+ * tracing if the parameter is +nil+. _proc_ takes up
+ * to six parameters: an event name, a filename, a line number, an
+ * object id, a binding, and the name of a class. _proc_ is
+ * invoked whenever an event occurs. Events are: <code>c-call</code>
+ * (call a C-language routine), <code>c-return</code> (return from a
+ * C-language routine), <code>call</code> (call a Ruby method),
+ * <code>class</code> (start a class or module definition),
+ * <code>end</code> (finish a class or module definition),
+ * <code>line</code> (execute code on a new line), <code>raise</code>
+ * (raise an exception), and <code>return</code> (return from a Ruby
+ * method). Tracing is disabled within the context of _proc_.
+ *
+ * class Test
+ * def test
+ * a = 1
+ * b = 2
+ * end
+ * end
+ *
+ * set_trace_func proc { |event, file, line, id, binding, classname|
+ * printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
+ * }
+ * t = Test.new
+ * t.test
+ *
+ * line prog.rb:11 false
+ * c-call prog.rb:11 new Class
+ * c-call prog.rb:11 initialize Object
+ * c-return prog.rb:11 initialize Object
+ * c-return prog.rb:11 new Class
+ * line prog.rb:12 false
+ * call prog.rb:2 test Test
+ * line prog.rb:3 test Test
+ * line prog.rb:4 test Test
+ * return prog.rb:4 test Test
+ */
+
+
+static VALUE
+set_trace_func(obj, trace)
+ VALUE obj, trace;
+{
+ rb_event_hook_t *hook;
+
+ rb_secure(4);
+ if (NIL_P(trace)) {
+ trace_func = 0;
+ rb_remove_event_hook(call_trace_func);
+ return Qnil;
+ }
+ if (!rb_obj_is_proc(trace)) {
+ rb_raise(rb_eTypeError, "trace_func needs to be Proc");
+ }
+ trace_func = trace;
+ for (hook = event_hooks; hook; hook = hook->next) {
+ if (hook->func == call_trace_func)
+ return trace;
+ }
+ rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL);
+ return trace;
+}
+
+static char *
+get_event_name(rb_event_t event)
+{
+ switch (event) {
+ case RUBY_EVENT_LINE:
+ return "line";
+ case RUBY_EVENT_CLASS:
+ return "class";
+ case RUBY_EVENT_END:
+ return "end";
+ case RUBY_EVENT_CALL:
+ return "call";
+ case RUBY_EVENT_RETURN:
+ return "return";
+ case RUBY_EVENT_C_CALL:
+ return "c-call";
+ case RUBY_EVENT_C_RETURN:
+ return "c-return";
+ case RUBY_EVENT_RAISE:
+ return "raise";
+ default:
+ return "unknown";
+ }
+}
+
+static void
+call_trace_func(event, node, self, id, klass)
+ rb_event_t event;
+ NODE *node;
+ VALUE self;
+ ID id;
+ VALUE klass; /* OK */
+{
+ int state, raised;
+ struct FRAME *prev;
+ NODE *node_save;
+ VALUE srcfile;
+ char *event_name;
+ rb_thread_t th = curr_thread;
+
+ if (!trace_func) return;
+ if (tracing) return;
+ if (ruby_in_compile) return;
+ if (id == ID_ALLOCATOR) return;
+
+ if (!(node_save = ruby_current_node)) {
+ node_save = NEW_NEWLINE(0);
+ }
+ tracing = 1;
+ prev = ruby_frame;
+ PUSH_FRAME();
+ *ruby_frame = *prev;
+ ruby_frame->prev = prev;
+ ruby_frame->iter = 0; /* blocks not available anyway */
+
+ if (node) {
+ ruby_current_node = node;
+ ruby_frame->node = node;
+ ruby_sourcefile = node->nd_file;
+ ruby_sourceline = nd_line(node);
+ }
+ if (klass) {
+ if (TYPE(klass) == T_ICLASS) {
+ klass = RBASIC(klass)->klass;
+ }
+ else if (FL_TEST(klass, FL_SINGLETON)) {
+ klass = rb_iv_get(klass, "__attached__");
+ }
+ }
+ PUSH_TAG(PROT_NONE);
+ raised = rb_thread_reset_raised(th);
+ if ((state = EXEC_TAG()) == 0) {
+ srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)");
+ event_name = get_event_name(event);
+ proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event_name),
+ srcfile,
+ INT2FIX(ruby_sourceline),
+ id?ID2SYM(id):Qnil,
+ self?rb_f_binding(self):Qnil,
+ klass),
+ Qundef, 0);
+ }
+ if (raised) rb_thread_set_raised(th);
+ POP_TAG();
+ POP_FRAME();
+
+ tracing = 0;
+ ruby_current_node = node_save;
+ SET_CURRENT_SOURCE();
+ if (state) JUMP_TAG(state);
+}
+
+static VALUE
+avalue_to_svalue(v)
+ VALUE v;
+{
+ VALUE tmp, top;
+
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return v;
+ }
+ if (RARRAY(tmp)->len == 0) {
+ return Qundef;
+ }
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (NIL_P(top)) {
+ return RARRAY(tmp)->ptr[0];
+ }
+ if (RARRAY(top)->len > 1) {
+ return v;
+ }
+ return top;
+ }
+ return tmp;
+}
+
+static VALUE
+svalue_to_avalue(v)
+ VALUE v;
+{
+ VALUE tmp, top;
+
+ if (v == Qundef) return rb_ary_new2(0);
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, v);
+ }
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (!NIL_P(top) && RARRAY(top)->len > 1) {
+ return tmp;
+ }
+ return rb_ary_new3(1, v);
+ }
+ return tmp;
+}
+
+static VALUE
+svalue_to_mrhs(v, lhs)
+ VALUE v;
+ NODE *lhs;
+{
+ VALUE tmp;
+
+ if (v == Qundef) return rb_ary_new2(0);
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, v);
+ }
+ /* no lhs means splat lhs only */
+ if (!lhs) {
+ return rb_ary_new3(1, v);
+ }
+ return tmp;
+}
+
+static VALUE
+avalue_splat(v)
+ VALUE v;
+{
+ if (RARRAY(v)->len == 0) {
+ return Qundef;
+ }
+ if (RARRAY(v)->len == 1) {
+ return RARRAY(v)->ptr[0];
+ }
+ return v;
+}
+
+#if 1
+VALUE
+rb_Array(val)
+ VALUE val;
+{
+ VALUE tmp = rb_check_array_type(val);
+
+ if (NIL_P(tmp)) {
+ /* hack to avoid invoke Object#to_a */
+ VALUE origin;
+ ID id = rb_intern("to_a");
+
+ if (search_method(CLASS_OF(val), id, &origin) &&
+ RCLASS(origin)->m_tbl != RCLASS(rb_mKernel)->m_tbl) { /* exclude Kernel#to_a */
+ val = rb_funcall(val, id, 0);
+ if (TYPE(val) != T_ARRAY) {
+ rb_raise(rb_eTypeError, "`to_a' did not return Array");
+ }
+ return val;
+ }
+ else {
+ return rb_ary_new3(1, val);
+ }
+ }
+ return tmp;
+}
+#endif
+
+static VALUE
+splat_value(v)
+ VALUE v;
+{
+ if (NIL_P(v)) return rb_ary_new3(1, Qnil);
+ return rb_Array(v);
+}
+
+static VALUE
+class_prefix(self, cpath)
+ VALUE self;
+ NODE *cpath;
+{
+ if (!cpath) {
+ rb_bug("class path missing");
+ }
+ if (cpath->nd_head) {
+ VALUE c = rb_eval(self, cpath->nd_head);
+ switch (TYPE(c)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(c))->ptr);
+ }
+ return c;
+ }
+ else if (nd_type(cpath) == NODE_COLON2) {
+ return ruby_cbase;
+ }
+ else if (ruby_wrapper) {
+ return ruby_wrapper;
+ }
+ else {
+ return rb_cObject;
+ }
+}
+
+#define return_value(v) do {\
+ if ((prot_tag->retval = (v)) == Qundef) {\
+ prot_tag->retval = Qnil;\
+ }\
+} while (0)
+
+NORETURN(static void return_jump _((VALUE)));
+NORETURN(static void break_jump _((VALUE)));
+NORETURN(static void next_jump _((VALUE)));
+NORETURN(static void unknown_node _((NODE * volatile)));
+
+static void
+unknown_node(node)
+ NODE *volatile node;
+{
+ ruby_current_node = 0;
+ if (node->flags == 0) {
+ rb_bug("terminated node (0x%lx)", node);
+ }
+ else if (BUILTIN_TYPE(node) != T_NODE) {
+ rb_bug("not a node 0x%02lx (0x%lx)", BUILTIN_TYPE(node), node);
+ }
+ else {
+ rb_bug("unknown node type %d (0x%lx)", nd_type(node), node);
+ }
+}
+
+static VALUE
+rb_eval(self, n)
+ VALUE self;
+ NODE *n;
+{
+ NODE * volatile contnode = 0;
+ NODE * volatile node = n;
+ int state;
+ volatile VALUE result = Qnil;
+ st_data_t data;
+
+#define RETURN(v) do { \
+ result = (v); \
+ goto finish; \
+} while (0)
+
+ again:
+ if (!node) RETURN(Qnil);
+
+ ruby_current_node = node;
+ switch (nd_type(node)) {
+ case NODE_BLOCK:
+ if (contnode) {
+ result = rb_eval(self, node);
+ break;
+ }
+ contnode = node->nd_next;
+ node = node->nd_head;
+ goto again;
+
+ case NODE_POSTEXE:
+ rb_f_END();
+ nd_set_type(node, NODE_NIL); /* exec just once */
+ result = Qnil;
+ break;
+
+ /* begin .. end without clauses */
+ case NODE_BEGIN:
+ node = node->nd_body;
+ goto again;
+
+ /* nodes for speed-up(default match) */
+ case NODE_MATCH:
+ result = rb_reg_match2(node->nd_lit);
+ break;
+
+ /* nodes for speed-up(literal match) */
+ case NODE_MATCH2:
+ {
+ VALUE l = rb_eval(self,node->nd_recv);
+ VALUE r = rb_eval(self,node->nd_value);
+ result = rb_reg_match(l, r);
+ }
+ break;
+
+ /* nodes for speed-up(literal match) */
+ case NODE_MATCH3:
+ {
+ VALUE r = rb_eval(self,node->nd_recv);
+ VALUE l = rb_eval(self,node->nd_value);
+ if (TYPE(l) == T_STRING) {
+ result = rb_reg_match(r, l);
+ }
+ else {
+ result = rb_funcall(l, match, 1, r);
+ }
+ }
+ break;
+
+ /* node for speed-up(top-level loop for -n/-p) */
+ case NODE_OPT_N:
+ PUSH_TAG(PROT_LOOP);
+ switch (state = EXEC_TAG()) {
+ case 0:
+ opt_n_next:
+ while (!NIL_P(rb_gets())) {
+ opt_n_redo:
+ rb_eval(self, node->nd_body);
+ }
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto opt_n_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto opt_n_next;
+ case TAG_BREAK:
+ state = 0;
+ default:
+ break;
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(Qnil);
+
+ case NODE_SELF:
+ RETURN(self);
+
+ case NODE_NIL:
+ RETURN(Qnil);
+
+ case NODE_TRUE:
+ RETURN(Qtrue);
+
+ case NODE_FALSE:
+ RETURN(Qfalse);
+
+ case NODE_IF:
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
+ if (RTEST(rb_eval(self, node->nd_cond))) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ goto again;
+
+ case NODE_WHEN:
+ while (node) {
+ NODE *tag;
+
+ if (nd_type(node) != NODE_WHEN) goto again;
+ tag = node->nd_head;
+ while (tag) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
+ VALUE v = rb_eval(self, tag->nd_head->nd_head);
+ long i;
+
+ if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v);
+ for (i=0; i<RARRAY(v)->len; i++) {
+ if (RTEST(RARRAY(v)->ptr[i])) {
+ node = node->nd_body;
+ goto again;
+ }
+ }
+ tag = tag->nd_next;
+ continue;
+ }
+ if (RTEST(rb_eval(self, tag->nd_head))) {
+ node = node->nd_body;
+ goto again;
+ }
+ tag = tag->nd_next;
+ }
+ node = node->nd_next;
+ }
+ RETURN(Qnil);
+
+ case NODE_CASE:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_head);
+ node = node->nd_body;
+ while (node) {
+ NODE *tag;
+
+ if (nd_type(node) != NODE_WHEN) {
+ goto again;
+ }
+ tag = node->nd_head;
+ while (tag) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
+ VALUE v = rb_eval(self, tag->nd_head->nd_head);
+ long i;
+
+ if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v);
+ for (i=0; i<RARRAY(v)->len; i++) {
+ if (RTEST(rb_funcall2(RARRAY(v)->ptr[i], eqq, 1, &val))){
+ node = node->nd_body;
+ goto again;
+ }
+ }
+ tag = tag->nd_next;
+ continue;
+ }
+ if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head), eqq, 1, &val))) {
+ node = node->nd_body;
+ goto again;
+ }
+ tag = tag->nd_next;
+ }
+ node = node->nd_next;
+ }
+ }
+ RETURN(Qnil);
+
+ case NODE_WHILE:
+ PUSH_TAG(PROT_LOOP);
+ result = Qnil;
+ switch (state = EXEC_TAG()) {
+ case 0:
+ if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond)))
+ goto while_out;
+ do {
+ while_redo:
+ rb_eval(self, node->nd_body);
+ while_next:
+ ;
+ } while (RTEST(rb_eval(self, node->nd_cond)));
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto while_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto while_next;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ state = 0;
+ result = prot_tag->retval;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ while_out:
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(result);
+
+ case NODE_UNTIL:
+ PUSH_TAG(PROT_LOOP);
+ result = Qnil;
+ switch (state = EXEC_TAG()) {
+ case 0:
+ if (node->nd_state && RTEST(rb_eval(self, node->nd_cond)))
+ goto until_out;
+ do {
+ until_redo:
+ rb_eval(self, node->nd_body);
+ until_next:
+ ;
+ } while (!RTEST(rb_eval(self, node->nd_cond)));
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto until_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto until_next;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ state = 0;
+ result = prot_tag->retval;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ until_out:
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(result);
+
+ case NODE_BLOCK_PASS:
+ result = block_pass(self, node);
+ break;
+
+ case NODE_ITER:
+ case NODE_FOR:
+ {
+ PUSH_TAG(PROT_LOOP);
+ PUSH_BLOCK(node->nd_var, node->nd_body);
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ iter_retry:
+ PUSH_ITER(ITER_PRE);
+ if (nd_type(node) == NODE_ITER) {
+ result = rb_eval(self, node->nd_iter);
+ }
+ else {
+ VALUE recv;
+
+ _block.flags &= ~BLOCK_D_SCOPE;
+ BEGIN_CALLARGS;
+ recv = rb_eval(self, node->nd_iter);
+ END_CALLARGS;
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(recv),recv,each,0,0,0,self);
+ }
+ POP_ITER();
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY) {
+ state = 0;
+ goto iter_retry;
+ }
+ POP_BLOCK();
+ POP_TAG();
+ switch (state) {
+ case 0:
+ break;
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ break;
+
+ case NODE_BREAK:
+ break_jump(rb_eval(self, node->nd_stts));
+ break;
+
+ case NODE_NEXT:
+ CHECK_INTS;
+ next_jump(rb_eval(self, node->nd_stts));
+ break;
+
+ case NODE_REDO:
+ CHECK_INTS;
+ JUMP_TAG(TAG_REDO);
+ break;
+
+ case NODE_RETRY:
+ CHECK_INTS;
+ JUMP_TAG(TAG_RETRY);
+ break;
+
+ case NODE_SPLAT:
+ result = splat_value(rb_eval(self, node->nd_head));
+ break;
+
+ case NODE_TO_ARY:
+ result = rb_ary_to_ary(rb_eval(self, node->nd_head));
+ break;
+
+ case NODE_SVALUE:
+ result = avalue_splat(rb_eval(self, node->nd_head));
+ if (result == Qundef) result = Qnil;
+ break;
+
+ case NODE_YIELD:
+ if (node->nd_head) {
+ result = rb_eval(self, node->nd_head);
+ ruby_current_node = node;
+ }
+ else {
+ result = Qundef; /* no arg */
+ }
+ SET_CURRENT_SOURCE();
+ result = rb_yield_0(result, 0, 0, 0, node->nd_state);
+ break;
+
+ case NODE_RESCUE:
+ {
+ volatile VALUE e_info = ruby_errinfo;
+ volatile int rescuing = 0;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ retry_entry:
+ result = rb_eval(self, node->nd_head);
+ }
+ else if (rescuing) {
+ if (rescuing < 0) {
+ /* in rescue argument, just reraise */
+ }
+ else if (state == TAG_RETRY) {
+ rescuing = state = 0;
+ ruby_errinfo = e_info;
+ goto retry_entry;
+ }
+ else if (state != TAG_RAISE) {
+ result = prot_tag->retval;
+ }
+ }
+ else if (state == TAG_RAISE) {
+ NODE *resq = node->nd_resq;
+
+ rescuing = -1;
+ while (resq) {
+ ruby_current_node = resq;
+ if (handle_rescue(self, resq)) {
+ state = 0;
+ rescuing = 1;
+ result = rb_eval(self, resq->nd_body);
+ break;
+ }
+ resq = resq->nd_head; /* next rescue */
+ }
+ }
+ else {
+ result = prot_tag->retval;
+ }
+ POP_TAG();
+ if (state != TAG_RAISE) ruby_errinfo = e_info;
+ if (state) {
+ JUMP_TAG(state);
+ }
+ /* no exception raised */
+ if (!rescuing && (node = node->nd_else)) { /* else clause given */
+ goto again;
+ }
+ }
+ break;
+
+ case NODE_ENSURE:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ if (node->nd_ensr && !thread_no_ensure()) {
+ VALUE retval = prot_tag->retval; /* save retval */
+ VALUE errinfo = ruby_errinfo;
+
+ rb_eval(self, node->nd_ensr);
+ return_value(retval);
+ ruby_errinfo = errinfo;
+ }
+ if (state) JUMP_TAG(state);
+ break;
+
+ case NODE_AND:
+ result = rb_eval(self, node->nd_1st);
+ if (!RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_OR:
+ result = rb_eval(self, node->nd_1st);
+ if (RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_NOT:
+ if (RTEST(rb_eval(self, node->nd_body))) result = Qfalse;
+ else result = Qtrue;
+ break;
+
+ case NODE_DOT2:
+ case NODE_DOT3:
+ {
+ VALUE beg = rb_eval(self, node->nd_beg);
+ VALUE end = rb_eval(self, node->nd_end);
+ result = rb_range_new(beg, end, nd_type(node) == NODE_DOT3);
+ }
+ break;
+
+ case NODE_FLIP2: /* like AWK */
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ *flip = RTEST(rb_eval(self, node->nd_end))?Qfalse:Qtrue;
+ result = Qtrue;
+ }
+ else {
+ result = Qfalse;
+ }
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
+ }
+ }
+ break;
+
+ case NODE_FLIP3: /* like SED */
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ result = RTEST(rb_eval(self, node->nd_beg)) ? Qtrue : Qfalse;
+ *flip = result;
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
+ }
+ }
+ break;
+
+ case NODE_RETURN:
+ return_jump(rb_eval(self, node->nd_stts));
+ break;
+
+ case NODE_ARGSCAT:
+ {
+ VALUE args = rb_eval(self, node->nd_head);
+ result = rb_ary_concat(args, splat_value(rb_eval(self, node->nd_body)));
+ }
+ break;
+
+ case NODE_ARGSPUSH:
+ {
+ VALUE args = rb_ary_dup(rb_eval(self, node->nd_head));
+ result = rb_ary_push(args, rb_eval(self, node->nd_body));
+ }
+ break;
+
+ case NODE_ATTRASGN:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ int scope;
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ if (node->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, node->nd_recv);
+ scope = 0;
+ }
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope,self);
+ result = argv[argc-1];
+ }
+ break;
+
+ case NODE_CALL:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ recv = rb_eval(self, node->nd_recv);
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0,self);
+ }
+ break;
+
+ case NODE_FCALL:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1,self);
+ }
+ break;
+
+ case NODE_VCALL:
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,0,0,2,self);
+ break;
+
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ if (ruby_frame->last_class == 0) {
+ if (ruby_frame->last_func) {
+ rb_name_error(ruby_frame->last_func,
+ "superclass method `%s' disabled",
+ rb_id2name(ruby_frame->orig_func));
+ }
+ else {
+ rb_raise(rb_eNoMethodError, "super called outside of method");
+ }
+ }
+ if (nd_type(node) == NODE_ZSUPER) {
+ argc = ruby_frame->argc;
+ if (argc && DMETHOD_P()) {
+ if (TYPE(RBASIC(ruby_scope)->klass) != T_ARRAY ||
+ RARRAY(RBASIC(ruby_scope)->klass)->len != argc) {
+ rb_raise(rb_eRuntimeError,
+ "super: specify arguments explicitly");
+ }
+ argv = RARRAY(RBASIC(ruby_scope)->klass)->ptr;
+ }
+ else if (!ruby_scope->local_vars) {
+ argc = 0;
+ argv = 0;
+ }
+ else {
+ argv = ruby_scope->local_vars + 2;
+ }
+ }
+ else {
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+ ruby_current_node = node;
+ }
+
+ SET_CURRENT_SOURCE();
+ result = rb_call_super(argc, argv);
+ }
+ break;
+
+ case NODE_SCOPE:
+ {
+ struct FRAME frame;
+ NODE *saved_cref = 0;
+
+ frame = *ruby_frame;
+ frame.tmp = ruby_frame;
+ ruby_frame = &frame;
+
+ PUSH_SCOPE();
+ PUSH_TAG(PROT_NONE);
+ if (node->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)node->nd_rval;
+ }
+ if (node->nd_tbl) {
+ VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ ruby_scope->local_vars = vars;
+ rb_mem_clear(ruby_scope->local_vars, node->nd_tbl[0]);
+ ruby_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_next);
+ }
+ POP_TAG();
+ POP_SCOPE();
+ ruby_frame = frame.tmp;
+ if (saved_cref)
+ ruby_cref = saved_cref;
+ if (state) JUMP_TAG(state);
+ }
+ break;
+
+ case NODE_OP_ASGN1:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ VALUE recv, val, tmp;
+ NODE *rval;
+ TMP_PROTECT;
+
+ recv = rb_eval(self, node->nd_recv);
+ rval = node->nd_args->nd_head;
+ SETUP_ARGS0(node->nd_args->nd_body, 1);
+ val = rb_funcall3(recv, aref, argc, argv);
+ switch (node->nd_mid) {
+ case 0: /* OR */
+ if (RTEST(val)) RETURN(val);
+ val = rb_eval(self, rval);
+ break;
+ case 1: /* AND */
+ if (!RTEST(val)) RETURN(val);
+ val = rb_eval(self, rval);
+ break;
+ default:
+ tmp = rb_eval(self, rval);
+ val = rb_funcall3(val, node->nd_mid, 1, &tmp);
+ }
+ argv[argc] = val;
+ rb_funcall2(recv, aset, argc+1, argv);
+ result = val;
+ }
+ break;
+
+ case NODE_OP_ASGN2:
+ {
+ ID id = node->nd_next->nd_vid;
+ VALUE recv, val, tmp;
+
+ recv = rb_eval(self, node->nd_recv);
+ val = rb_funcall3(recv, id, 0, 0);
+ switch (node->nd_next->nd_mid) {
+ case 0: /* OR */
+ if (RTEST(val)) RETURN(val);
+ val = rb_eval(self, node->nd_value);
+ break;
+ case 1: /* AND */
+ if (!RTEST(val)) RETURN(val);
+ val = rb_eval(self, node->nd_value);
+ break;
+ default:
+ tmp = rb_eval(self, node->nd_value);
+ val = rb_funcall3(val, node->nd_next->nd_mid, 1, &tmp);
+ }
+
+ rb_funcall2(recv, node->nd_next->nd_aid, 1, &val);
+ result = val;
+ }
+ break;
+
+ case NODE_OP_ASGN_AND:
+ result = rb_eval(self, node->nd_head);
+ if (!RTEST(result)) break;
+ node = node->nd_value;
+ goto again;
+
+ case NODE_OP_ASGN_OR:
+ if ((node->nd_aid && !is_defined(self, node->nd_head, 0)) ||
+ !RTEST(result = rb_eval(self, node->nd_head))) {
+ node = node->nd_value;
+ goto again;
+ }
+ break;
+
+ case NODE_MASGN:
+ result = massign(self, node, rb_eval(self, node->nd_value), 0);
+ break;
+
+ case NODE_LASGN:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected local variable assignment");
+ result = rb_eval(self, node->nd_value);
+ ruby_scope->local_vars[node->nd_cnt] = result;
+ break;
+
+ case NODE_DASGN:
+ result = rb_eval(self, node->nd_value);
+ dvar_asgn(node->nd_vid, result);
+ break;
+
+ case NODE_DASGN_CURR:
+ result = rb_eval(self, node->nd_value);
+ dvar_asgn_curr(node->nd_vid, result);
+ break;
+
+ case NODE_GASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_gvar_set(node->nd_entry, result);
+ break;
+
+ case NODE_IASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_ivar_set(self, node->nd_vid, result);
+ break;
+
+ case NODE_CDECL:
+ result = rb_eval(self, node->nd_value);
+ if (node->nd_vid == 0) {
+ rb_const_set(class_prefix(self, node->nd_else), node->nd_else->nd_mid, result);
+ }
+ else {
+ rb_const_set(ruby_cbase, node->nd_vid, result);
+ }
+ break;
+
+ case NODE_CVDECL:
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no class/module to define class variable");
+ }
+ result = rb_eval(self, node->nd_value);
+ rb_cvar_set(cvar_cbase(), node->nd_vid, result, Qtrue);
+ break;
+
+ case NODE_CVASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_cvar_set(cvar_cbase(), node->nd_vid, result, Qfalse);
+ break;
+
+ case NODE_LVAR:
+ if (ruby_scope->local_vars == 0) {
+ rb_bug("unexpected local variable");
+ }
+ result = ruby_scope->local_vars[node->nd_cnt];
+ break;
+
+ case NODE_DVAR:
+ result = rb_dvar_ref(node->nd_vid);
+ break;
+
+ case NODE_GVAR:
+ result = rb_gvar_get(node->nd_entry);
+ break;
+
+ case NODE_IVAR:
+ result = rb_ivar_get(self, node->nd_vid);
+ break;
+
+ case NODE_CONST:
+ result = ev_const_get(ruby_cref, node->nd_vid, self);
+ break;
+
+ case NODE_CVAR:
+ result = rb_cvar_get(cvar_cbase(), node->nd_vid);
+ break;
+
+ case NODE_BLOCK_ARG:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected block argument");
+ if (rb_block_given_p()) {
+ result = rb_block_proc();
+ ruby_scope->local_vars[node->nd_cnt] = result;
+ }
+ else {
+ result = Qnil;
+ }
+ break;
+
+ case NODE_COLON2:
+ {
+ VALUE klass;
+
+ klass = rb_eval(self, node->nd_head);
+ if (rb_is_const_id(node->nd_mid)) {
+ switch (TYPE(klass)) {
+ case T_CLASS:
+ case T_MODULE:
+ result = rb_const_get_from(klass, node->nd_mid);
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(klass))->ptr);
+ break;
+ }
+ }
+ else {
+ result = rb_funcall(klass, node->nd_mid, 0, 0);
+ }
+ }
+ break;
+
+ case NODE_COLON3:
+ result = rb_const_get_from(rb_cObject, node->nd_mid);
+ break;
+
+ case NODE_NTH_REF:
+ result = rb_reg_nth_match(node->nd_nth, MATCH_DATA);
+ break;
+
+ case NODE_BACK_REF:
+ switch (node->nd_nth) {
+ case '&':
+ result = rb_reg_last_match(MATCH_DATA);
+ break;
+ case '`':
+ result = rb_reg_match_pre(MATCH_DATA);
+ break;
+ case '\'':
+ result = rb_reg_match_post(MATCH_DATA);
+ break;
+ case '+':
+ result = rb_reg_match_last(MATCH_DATA);
+ break;
+ default:
+ rb_bug("unexpected back-ref");
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ NODE *list;
+ VALUE hash = rb_hash_new();
+ VALUE key, val;
+
+ list = node->nd_head;
+ while (list) {
+ key = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ if (list == 0)
+ rb_bug("odd number list for Hash");
+ val = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ rb_hash_aset(hash, key, val);
+ }
+ result = hash;
+ }
+ break;
+
+ case NODE_ZARRAY: /* zero length list */
+ result = rb_ary_new();
+ break;
+
+ case NODE_ARRAY:
+ {
+ VALUE ary;
+ long i;
+
+ i = node->nd_alen;
+ ary = rb_ary_new2(i);
+ for (i=0;node;node=node->nd_next) {
+ RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head);
+ RARRAY(ary)->len = i;
+ }
+
+ result = ary;
+ }
+ break;
+
+ case NODE_STR:
+ result = rb_str_new3(node->nd_lit);
+ break;
+
+ case NODE_EVSTR:
+ result = rb_obj_as_string(rb_eval(self, node->nd_body));
+ break;
+
+ case NODE_DSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ case NODE_DSYM:
+ {
+ VALUE str, str2;
+ NODE *list = node->nd_next;
+
+ str = rb_str_new3(node->nd_lit);
+ while (list) {
+ if (list->nd_head) {
+ switch (nd_type(list->nd_head)) {
+ case NODE_STR:
+ str2 = list->nd_head->nd_lit;
+ break;
+ default:
+ str2 = rb_eval(self, list->nd_head);
+ break;
+ }
+ rb_str_append(str, str2);
+ OBJ_INFECT(str, str2);
+ }
+ list = list->nd_next;
+ }
+ switch (nd_type(node)) {
+ case NODE_DREGX:
+ result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ break;
+ case NODE_DREGX_ONCE: /* regexp expand once */
+ result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ nd_set_type(node, NODE_LIT);
+ node->nd_lit = result;
+ break;
+ case NODE_LIT:
+ /* other thread may replace NODE_DREGX_ONCE to NODE_LIT */
+ goto again;
+ case NODE_DXSTR:
+ result = rb_funcall(self, '`', 1, str);
+ break;
+ case NODE_DSYM:
+ result = rb_str_intern(str);
+ break;
+ default:
+ result = str;
+ break;
+ }
+ }
+ break;
+
+ case NODE_XSTR:
+ result = rb_funcall(self, '`', 1, rb_str_new3(node->nd_lit));
+ break;
+
+ case NODE_LIT:
+ result = node->nd_lit;
+ break;
+
+ case NODE_DEFN:
+ if (node->nd_defn) {
+ NODE *body, *defn;
+ VALUE origin;
+ int noex;
+
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class/module to add method");
+ }
+ if (ruby_class == rb_cObject && node->nd_mid == init) {
+ rb_warn("redefining Object#initialize may cause infinite loop");
+ }
+ if (node->nd_mid == __id__ || node->nd_mid == __send__) {
+ rb_warn("redefining `%s' may cause serious problem",
+ rb_id2name(node->nd_mid));
+ }
+ rb_frozen_class_p(ruby_class);
+ body = search_method(ruby_class, node->nd_mid, &origin);
+ if (body){
+ if (RTEST(ruby_verbose) && ruby_class == origin && body->nd_cnt == 0 && body->nd_body) {
+ rb_warning("method redefined; discarding old %s", rb_id2name(node->nd_mid));
+ }
+ }
+
+ if (SCOPE_TEST(SCOPE_PRIVATE) || node->nd_mid == init) {
+ noex = NOEX_PRIVATE;
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ else {
+ noex = NOEX_PUBLIC;
+ }
+ if (body && origin == ruby_class && body->nd_body == 0) {
+ noex |= NOEX_NOSUPER;
+ }
+
+ defn = rb_copy_node_scope(node->nd_defn, ruby_cref);
+ rb_add_method(ruby_class, node->nd_mid, defn, noex);
+ if (scope_vmode == SCOPE_MODFUNC) {
+ rb_add_method(rb_singleton_class(ruby_class),
+ node->nd_mid, defn, NOEX_PUBLIC);
+ }
+ result = Qnil;
+ }
+ break;
+
+ case NODE_DEFS:
+ if (node->nd_defn) {
+ VALUE recv = rb_eval(self, node->nd_recv);
+ VALUE klass;
+ NODE *body = 0, *defn;
+
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(recv)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't define singleton method");
+ }
+ if (FIXNUM_P(recv) || SYMBOL_P(recv)) {
+ rb_raise(rb_eTypeError,
+ "can't define singleton method \"%s\" for %s",
+ rb_id2name(node->nd_mid),
+ rb_obj_classname(recv));
+ }
+
+ if (OBJ_FROZEN(recv)) rb_error_frozen("object");
+ klass = rb_singleton_class(recv);
+ if (st_lookup(RCLASS(klass)->m_tbl, node->nd_mid, &data)) {
+ body = (NODE *)data;
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "redefining method prohibited");
+ }
+ if (RTEST(ruby_verbose)) {
+ rb_warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ }
+ defn = rb_copy_node_scope(node->nd_defn, ruby_cref);
+ rb_add_method(klass, node->nd_mid, defn,
+ NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0));
+ result = Qnil;
+ }
+ break;
+
+ case NODE_UNDEF:
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class to undef method");
+ }
+ rb_undef(ruby_class, rb_to_id(rb_eval(self, node->u2.node)));
+ result = Qnil;
+ break;
+
+ case NODE_ALIAS:
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class to make alias");
+ }
+ rb_alias(ruby_class, rb_to_id(rb_eval(self, node->u1.node)),
+ rb_to_id(rb_eval(self, node->u2.node)));
+ result = Qnil;
+ break;
+
+ case NODE_VALIAS:
+ rb_alias_variable(node->u1.id, node->u2.id);
+ result = Qnil;
+ break;
+
+ case NODE_CLASS:
+ {
+ VALUE super, klass, tmp, cbase;
+ ID cname;
+ int gen = Qfalse;
+
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no outer class/module");
+ }
+ if (node->nd_super) {
+ super = rb_eval(self, node->nd_super);
+ rb_check_inheritable(super);
+ }
+ else {
+ super = 0;
+ }
+
+ if (rb_const_defined_at(cbase, cname)) {
+ klass = rb_const_get_at(cbase, cname);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class",
+ rb_id2name(cname));
+ }
+ if (super) {
+ tmp = rb_class_real(RCLASS(klass)->super);
+ if (tmp != super) {
+ rb_raise(rb_eTypeError, "superclass mismatch for class %s",
+ rb_id2name(cname));
+ }
+ super = 0;
+ }
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "extending class prohibited");
+ }
+ }
+ else {
+ if (!super) super = rb_cObject;
+ klass = rb_define_class_id(cname, super);
+ rb_set_class_path(klass, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, klass);
+ gen = Qtrue;
+ }
+ if (ruby_wrapper) {
+ rb_extend_object(klass, ruby_wrapper);
+ rb_include_module(klass, ruby_wrapper);
+ }
+ if (super && gen) {
+ rb_class_inherited(super, klass);
+ }
+ result = module_setup(klass, node);
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ VALUE module, cbase;
+ ID cname;
+
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no outer class/module");
+ }
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+ if (rb_const_defined_at(cbase, cname)) {
+ module = rb_const_get_at(cbase, cname);
+ if (TYPE(module) != T_MODULE) {
+ rb_raise(rb_eTypeError, "%s is not a module",
+ rb_id2name(cname));
+ }
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "extending module prohibited");
+ }
+ }
+ else {
+ module = rb_define_module_id(cname);
+ rb_set_class_path(module, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, module);
+ }
+ if (ruby_wrapper) {
+ rb_extend_object(module, ruby_wrapper);
+ rb_include_module(module, ruby_wrapper);
+ }
+
+ result = module_setup(module, node);
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ VALUE klass;
+
+ result = rb_eval(self, node->nd_recv);
+ if (FIXNUM_P(result) || SYMBOL_P(result)) {
+ rb_raise(rb_eTypeError, "no virtual class for %s",
+ rb_obj_classname(result));
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(result))
+ rb_raise(rb_eSecurityError, "Insecure: can't extend object");
+ klass = rb_singleton_class(result);
+
+ if (ruby_wrapper) {
+ rb_extend_object(klass, ruby_wrapper);
+ rb_include_module(klass, ruby_wrapper);
+ }
+
+ result = module_setup(klass, node);
+ }
+ break;
+
+ case NODE_DEFINED:
+ {
+ char buf[20];
+ char *desc = is_defined(self, node->nd_head, buf);
+
+ if (desc) result = rb_str_new2(desc);
+ else result = Qnil;
+ }
+ break;
+
+ case NODE_NEWLINE:
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
+ node = node->nd_next;
+ goto again;
+
+ default:
+ unknown_node(node);
+ }
+ finish:
+ CHECK_INTS;
+ if (contnode) {
+ node = contnode;
+ contnode = 0;
+ goto again;
+ }
+ return result;
+}
+
+static VALUE
+module_setup(module, n)
+ VALUE module;
+ NODE *n;
+{
+ NODE * volatile node = n->nd_body;
+ int state;
+ struct FRAME frame;
+ VALUE result = Qnil; /* OK */
+ TMP_PROTECT;
+
+ frame = *ruby_frame;
+ frame.tmp = ruby_frame;
+ ruby_frame = &frame;
+
+ PUSH_CLASS(module);
+ PUSH_SCOPE();
+ PUSH_VARS();
+
+ if (node->nd_tbl) {
+ VALUE *vars = TMP_ALLOC(node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ ruby_scope->local_vars = vars;
+ rb_mem_clear(ruby_scope->local_vars, node->nd_tbl[0]);
+ ruby_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+
+ PUSH_CREF(module);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase,
+ ruby_frame->last_func, ruby_frame->last_class);
+ result = rb_eval(ruby_cbase, node->nd_next);
+ }
+ POP_TAG();
+ POP_CREF();
+ POP_VARS();
+ POP_SCOPE();
+ POP_CLASS();
+
+ ruby_frame = frame.tmp;
+ EXEC_EVENT_HOOK(RUBY_EVENT_END, n, 0,
+ ruby_frame->last_func, ruby_frame->last_class);
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+static NODE *basic_respond_to = 0;
+
+int
+rb_obj_respond_to(obj, id, priv)
+ VALUE obj;
+ ID id;
+ int priv;
+{
+ VALUE klass = CLASS_OF(obj);
+
+ if (rb_method_node(klass, respond_to) == basic_respond_to) {
+ return rb_method_boundp(klass, id, !priv);
+ }
+ else {
+ VALUE args[2];
+ int n = 0;
+ args[n++] = ID2SYM(id);
+ if (priv) args[n++] = Qtrue;
+ return RTEST(rb_funcall2(obj, respond_to, n, args));
+ }
+}
+
+int
+rb_respond_to(obj, id)
+ VALUE obj;
+ ID id;
+{
+ return rb_obj_respond_to(obj, id, Qfalse);
+}
+
+/*
+ * call-seq:
+ * obj.respond_to?(symbol, include_private=false) => true or false
+ *
+ * Returns +true+> if _obj_ responds to the given
+ * method. Private methods are included in the search only if the
+ * optional second parameter evaluates to +true+.
+ */
+
+static VALUE
+obj_respond_to(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE mid, priv;
+ ID id;
+
+ rb_scan_args(argc, argv, "11", &mid, &priv);
+ id = rb_to_id(mid);
+ if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named method is defined by
+ * _mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors). Public and protected methods are matched.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.method_defined? "method1" #=> true
+ * C.method_defined? "method2" #=> true
+ * C.method_defined? "method3" #=> true
+ * C.method_defined? "method4" #=> false
+ */
+
+static VALUE
+rb_mod_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ return rb_method_boundp(mod, rb_to_id(mid), 1);
+}
+
+#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f))
+
+/*
+ * call-seq:
+ * mod.public_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named public method is defined by
+ * _mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * protected
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.public_method_defined? "method1" #=> true
+ * C.public_method_defined? "method2" #=> false
+ * C.method_defined? "method2" #=> true
+ */
+
+static VALUE
+rb_mod_public_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PUBLIC))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.private_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named private method is defined by
+ * _ mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * private
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.private_method_defined? "method1" #=> false
+ * C.private_method_defined? "method2" #=> true
+ * C.method_defined? "method2" #=> false
+ */
+
+static VALUE
+rb_mod_private_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PRIVATE))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.protected_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named protected method is defined
+ * by _mod_ (or its included modules and, if _mod_ is a
+ * class, its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * protected
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.protected_method_defined? "method1" #=> false
+ * C.protected_method_defined? "method2" #=> true
+ * C.method_defined? "method2" #=> true
+ */
+
+static VALUE
+rb_mod_protected_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PROTECTED))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+NORETURN(static VALUE terminate_process _((int, VALUE)));
+static VALUE
+terminate_process(status, mesg)
+ int status;
+ VALUE mesg;
+{
+ VALUE args[2];
+ args[0] = INT2NUM(status);
+ args[1] = mesg;
+
+ rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
+}
+
+void
+rb_exit(status)
+ int status;
+{
+ if (prot_tag) {
+ terminate_process(status, rb_str_new("exit", 4));
+ }
+ ruby_finalize();
+ exit(status);
+}
+
+
+/*
+ * call-seq:
+ * exit(integer=0)
+ * Kernel::exit(integer=0)
+ * Process::exit(integer=0)
+ *
+ * Initiates the termination of the Ruby script by raising the
+ * <code>SystemExit</code> exception. This exception may be caught. The
+ * optional parameter is used to return a status code to the invoking
+ * environment.
+ *
+ * begin
+ * exit
+ * puts "never get here"
+ * rescue SystemExit
+ * puts "rescued a SystemExit exception"
+ * end
+ * puts "after begin block"
+ *
+ * <em>produces:</em>
+ *
+ * rescued a SystemExit exception
+ * after begin block
+ *
+ * Just prior to termination, Ruby executes any <code>at_exit</code> functions
+ * (see Kernel::at_exit) and runs any object finalizers (see
+ * ObjectSpace::define_finalizer).
+ *
+ * at_exit { puts "at_exit function" }
+ * ObjectSpace.define_finalizer("string", proc { puts "in finalizer" })
+ * exit
+ *
+ * <em>produces:</em>
+ *
+ * at_exit function
+ * in finalizer
+ */
+
+VALUE
+rb_f_exit(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE status;
+ int istatus;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "01", &status) == 1) {
+ switch (status) {
+ case Qtrue:
+ istatus = EXIT_SUCCESS;
+ break;
+ case Qfalse:
+ istatus = EXIT_FAILURE;
+ break;
+ default:
+ istatus = NUM2INT(status);
+#if EXIT_SUCCESS != 0
+ if (istatus == 0) istatus = EXIT_SUCCESS;
+#endif
+ break;
+ }
+ }
+ else {
+ istatus = EXIT_SUCCESS;
+ }
+ rb_exit(istatus);
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * call-seq:
+ * abort
+ * Kernel::abort
+ * Process::abort
+ *
+ * Terminate execution immediately, effectively by calling
+ * <code>Kernel.exit(1)</code>. If _msg_ is given, it is written
+ * to STDERR prior to terminating.
+ */
+
+VALUE
+rb_f_abort(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_secure(4);
+ if (argc == 0) {
+ if (!NIL_P(ruby_errinfo)) {
+ error_print();
+ }
+ rb_exit(EXIT_FAILURE);
+ }
+ else {
+ VALUE mesg;
+
+ rb_scan_args(argc, argv, "1", &mesg);
+ StringValue(mesg);
+ rb_io_puts(1, &mesg, rb_stderr);
+ terminate_process(EXIT_FAILURE, mesg);
+ }
+ return Qnil; /* not reached */
+}
+
+void
+rb_iter_break()
+{
+ break_jump(Qnil);
+}
+
+NORETURN(static void rb_longjmp _((int, VALUE)));
+static VALUE make_backtrace _((void));
+
+static void
+rb_longjmp(tag, mesg)
+ int tag;
+ VALUE mesg;
+{
+ VALUE at;
+ rb_thread_t th = curr_thread;
+
+ if (rb_thread_set_raised(th)) {
+ ruby_errinfo = exception_error;
+ JUMP_TAG(TAG_FATAL);
+ }
+ if (NIL_P(mesg)) mesg = ruby_errinfo;
+ if (NIL_P(mesg)) {
+ mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
+ }
+
+ ruby_set_current_source();
+ if (ruby_sourcefile && !NIL_P(mesg)) {
+ at = get_backtrace(mesg);
+ if (NIL_P(at)) {
+ at = make_backtrace();
+ if (OBJ_FROZEN(mesg)) {
+ mesg = rb_obj_dup(mesg);
+ }
+ set_backtrace(mesg, at);
+ }
+ }
+ if (!NIL_P(mesg)) {
+ ruby_errinfo = mesg;
+ }
+
+ if (RTEST(ruby_debug) && !NIL_P(ruby_errinfo)
+ && !rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ VALUE e = ruby_errinfo;
+ int status;
+
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ StringValue(e);
+ warn_printf("Exception `%s' at %s:%d - %s\n",
+ rb_obj_classname(ruby_errinfo),
+ ruby_sourcefile, ruby_sourceline,
+ RSTRING(e)->ptr);
+ }
+ POP_TAG();
+ if (status == TAG_FATAL && ruby_errinfo == exception_error) {
+ ruby_errinfo = mesg;
+ }
+ else if (status) {
+ rb_thread_reset_raised(th);
+ JUMP_TAG(status);
+ }
+ }
+
+ rb_trap_restore_mask();
+ if (tag != TAG_FATAL) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RAISE, ruby_current_node,
+ ruby_frame->self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
+ }
+ if (!prot_tag) {
+ error_print();
+ }
+ rb_thread_raised_clear(th);
+ JUMP_TAG(tag);
+}
+
+void
+rb_exc_jump(mesg)
+ VALUE mesg;
+{
+ rb_thread_raised_clear(rb_curr_thread);
+ ruby_errinfo = mesg;
+ JUMP_TAG(TAG_RAISE);
+}
+
+void
+rb_exc_raise(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_RAISE, mesg);
+}
+
+void
+rb_exc_fatal(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_FATAL, mesg);
+}
+
+void
+rb_interrupt()
+{
+ rb_raise(rb_eInterrupt, "");
+}
+
+/*
+ * call-seq:
+ * raise
+ * raise(string)
+ * raise(exception [, string [, array]])
+ * fail
+ * fail(string)
+ * fail(exception [, string [, array]])
+ *
+ * With no arguments, raises the exception in <code>$!</code> or raises
+ * a <code>RuntimeError</code> if <code>$!</code> is +nil+.
+ * With a single +String+ argument, raises a
+ * +RuntimeError+ with the string as a message. Otherwise,
+ * the first parameter should be the name of an +Exception+
+ * class (or an object that returns an +Exception+ object when sent
+ * an +exception+ message). The optional second parameter sets the
+ * message associated with the exception, and the third parameter is an
+ * array of callback information. Exceptions are caught by the
+ * +rescue+ clause of <code>begin...end</code> blocks.
+ *
+ * raise "Failed to create socket"
+ * raise ArgumentError, "No parameters", caller
+ */
+
+static VALUE
+rb_f_raise(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_raise_jump(rb_make_exception(argc, argv));
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_make_exception(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE mesg;
+ ID exception;
+ int n;
+
+ mesg = Qnil;
+ switch (argc) {
+ case 0:
+ mesg = Qnil;
+ break;
+ case 1:
+ if (NIL_P(argv[0])) break;
+ if (TYPE(argv[0]) == T_STRING) {
+ mesg = rb_exc_new3(rb_eRuntimeError, argv[0]);
+ break;
+ }
+ n = 0;
+ goto exception_call;
+
+ case 2:
+ case 3:
+ n = 1;
+ exception_call:
+ exception = rb_intern("exception");
+ if (!rb_respond_to(argv[0], exception)) {
+ rb_raise(rb_eTypeError, "exception class/object expected");
+ }
+ mesg = rb_funcall(argv[0], exception, n, argv[1]);
+ break;
+ default:
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ break;
+ }
+ if (argc > 0) {
+ if (!rb_obj_is_kind_of(mesg, rb_eException))
+ rb_raise(rb_eTypeError, "exception object expected");
+ if (argc>2)
+ set_backtrace(mesg, argv[2]);
+ }
+
+ return mesg;
+}
+
+static void
+rb_raise_jump(mesg)
+ VALUE mesg;
+{
+ if (ruby_frame != top_frame) {
+ PUSH_FRAME(); /* fake frame */
+ *ruby_frame = *_frame.prev->prev;
+ rb_longjmp(TAG_RAISE, mesg);
+ POP_FRAME();
+ }
+ rb_longjmp(TAG_RAISE, mesg);
+}
+
+void
+rb_jump_tag(tag)
+ int tag;
+{
+ JUMP_TAG(tag);
+}
+
+int
+rb_block_given_p()
+{
+ if (ruby_frame->iter == ITER_CUR && ruby_block)
+ return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_iterator_p()
+{
+ return rb_block_given_p();
+}
+
+/*
+ * call-seq:
+ * block_given? => true or false
+ * iterator? => true or false
+ *
+ * Returns <code>true</code> if <code>yield</code> would execute a
+ * block in the current context. The <code>iterator?</code> form
+ * is mildly deprecated.
+ *
+ * def try
+ * if block_given?
+ * yield
+ * else
+ * "no block"
+ * end
+ * end
+ * try #=> "no block"
+ * try { "hello" } #=> "hello"
+ * try do "hello" end #=> "hello"
+ */
+
+
+static VALUE
+rb_f_block_given_p()
+{
+ if (ruby_frame->prev && ruby_frame->prev->iter == ITER_CUR && ruby_block)
+ return Qtrue;
+ return Qfalse;
+}
+
+VALUE rb_eThreadError;
+
+NORETURN(static void proc_jump_error(int, VALUE));
+static void
+proc_jump_error(state, result)
+ int state;
+ VALUE result;
+{
+ char mesg[32];
+ char *statement;
+
+ switch (state) {
+ case TAG_BREAK:
+ statement = "break"; break;
+ case TAG_RETURN:
+ statement = "return"; break;
+ case TAG_RETRY:
+ statement = "retry"; break;
+ default:
+ statement = "local-jump"; break; /* should not happen */
+ }
+ snprintf(mesg, sizeof mesg, "%s from proc-closure", statement);
+ localjump_error(mesg, result, state);
+}
+
+static void
+return_jump(retval)
+ VALUE retval;
+{
+ struct tag *tt = prot_tag;
+ int yield = Qfalse;
+
+ if (retval == Qundef) retval = Qnil;
+ while (tt) {
+ if (tt->tag == PROT_YIELD) {
+ yield = Qtrue;
+ tt = tt->prev;
+ }
+ if (tt->tag == PROT_FUNC && tt->frame->uniq == ruby_frame->uniq) {
+ tt->dst = (VALUE)ruby_frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_RETURN);
+ }
+ if (tt->tag == PROT_LAMBDA && !yield) {
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_RETURN);
+ }
+ if (tt->tag == PROT_THREAD) {
+ rb_raise(rb_eThreadError, "return can't jump across threads");
+ }
+ tt = tt->prev;
+ }
+ localjump_error("unexpected return", retval, TAG_RETURN);
+}
+
+static void
+break_jump(retval)
+ VALUE retval;
+{
+ struct tag *tt = prot_tag;
+
+ if (retval == Qundef) retval = Qnil;
+ while (tt) {
+ switch (tt->tag) {
+ case PROT_THREAD:
+ case PROT_YIELD:
+ case PROT_LOOP:
+ case PROT_LAMBDA:
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_BREAK);
+ break;
+ case PROT_FUNC:
+ tt = 0;
+ continue;
+ default:
+ break;
+ }
+ tt = tt->prev;
+ }
+ localjump_error("unexpected break", retval, TAG_BREAK);
+}
+
+static void
+next_jump(retval)
+ VALUE retval;
+{
+ struct tag *tt = prot_tag;
+
+ if (retval == Qundef) retval = Qnil;
+ while (tt) {
+ switch (tt->tag) {
+ case PROT_THREAD:
+ case PROT_YIELD:
+ case PROT_LOOP:
+ case PROT_LAMBDA:
+ case PROT_FUNC:
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_NEXT);
+ break;
+ default:
+ break;
+ }
+ tt = tt->prev;
+ }
+ localjump_error("unexpected next", retval, TAG_NEXT);
+}
+
+void
+rb_need_block()
+{
+ if (!rb_block_given_p()) {
+ localjump_error("no block given", Qnil, 0);
+ }
+}
+
+static VALUE
+rb_yield_0(val, self, klass, flags, avalue)
+ VALUE val, self, klass; /* OK */
+ int flags, avalue;
+{
+ NODE *node;
+ volatile VALUE result = Qnil;
+ volatile VALUE old_cref;
+ volatile VALUE old_wrapper;
+ struct BLOCK * volatile block;
+ struct SCOPE * volatile old_scope;
+ int old_vmode;
+ struct FRAME frame;
+ NODE *cnode = ruby_current_node;
+ int lambda = flags & YIELD_LAMBDA_CALL;
+ int state;
+
+ rb_need_block();
+
+ PUSH_VARS();
+ block = ruby_block;
+ frame = block->frame;
+ frame.prev = ruby_frame;
+ frame.node = cnode;
+ ruby_frame = &(frame);
+ old_cref = (VALUE)ruby_cref;
+ ruby_cref = block->cref;
+ old_wrapper = ruby_wrapper;
+ ruby_wrapper = block->wrapper;
+ old_scope = ruby_scope;
+ ruby_scope = block->scope;
+ old_vmode = scope_vmode;
+ scope_vmode = (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC : block->vmode;
+ ruby_block = block->prev;
+ if (block->flags & BLOCK_D_SCOPE) {
+ /* put place holder for dynamic (in-block) local variables */
+ ruby_dyna_vars = new_dvar(0, 0, block->dyna_vars);
+ }
+ else {
+ /* FOR does not introduce new scope */
+ ruby_dyna_vars = block->dyna_vars;
+ }
+ PUSH_CLASS(klass ? klass : block->klass);
+ if (!klass) {
+ self = block->self;
+ }
+ node = block->body;
+
+ if (block->var) {
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ if (block->var == (NODE*)1) { /* no parameter || */
+ if (lambda && RARRAY(val)->len != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
+ RARRAY(val)->len);
+ }
+ }
+ else if (block->var == (NODE*)2) {
+ if (TYPE(val) == T_ARRAY && RARRAY(val)->len != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
+ RARRAY(val)->len);
+ }
+ }
+ else if (nd_type(block->var) == NODE_MASGN) {
+ if (!avalue) {
+ val = svalue_to_mrhs(val, block->var->nd_head);
+ }
+ massign(self, block->var, val, lambda);
+ }
+ else {
+ int len = 0;
+ if (avalue) {
+ len = RARRAY(val)->len;
+ if (len == 0) {
+ goto zero_arg;
+ }
+ if (len == 1) {
+ val = RARRAY(val)->ptr[0];
+ }
+ else {
+ goto multi_values;
+ }
+ }
+ else if (val == Qundef) {
+ zero_arg:
+ val = Qnil;
+ multi_values:
+ {
+ ruby_current_node = block->var;
+ rb_warn("multiple values for a block parameter (%d for 1)\n\tfrom %s:%d",
+ len, cnode->nd_file, nd_line(cnode));
+ ruby_current_node = cnode;
+ }
+ }
+ assign(self, block->var, val, lambda);
+ }
+ }
+ POP_TAG();
+ if (state) goto pop_state;
+ }
+ if (!node) {
+ state = 0;
+ goto pop_state;
+ }
+ ruby_current_node = node;
+
+ PUSH_ITER(block->iter);
+ PUSH_TAG(lambda ? PROT_NONE : PROT_YIELD);
+ if ((state = EXEC_TAG()) == 0) {
+ redo:
+ if (nd_type(node) == NODE_CFUNC || nd_type(node) == NODE_IFUNC) {
+ if (node->nd_state == YIELD_FUNC_AVALUE) {
+ if (!avalue) {
+ val = svalue_to_avalue(val);
+ }
+ }
+ else {
+ if (avalue) {
+ val = avalue_to_svalue(val);
+ }
+ if (val == Qundef && node->nd_state != YIELD_FUNC_SVALUE)
+ val = Qnil;
+ }
+ result = (*node->nd_cfnc)(val, node->nd_tval, self);
+ }
+ else {
+ result = rb_eval(self, node);
+ }
+ }
+ else {
+ switch (state) {
+ case TAG_REDO:
+ state = 0;
+ CHECK_INTS;
+ goto redo;
+ case TAG_NEXT:
+ if (!lambda) {
+ state = 0;
+ result = prot_tag->retval;
+ }
+ break;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ result = prot_tag->retval;
+ }
+ else {
+ lambda = Qtrue; /* just pass TAG_BREAK */
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ POP_TAG();
+ POP_ITER();
+ pop_state:
+ POP_CLASS();
+ if (ruby_dyna_vars && (block->flags & BLOCK_D_SCOPE) &&
+ !FL_TEST(ruby_dyna_vars, DVAR_DONT_RECYCLE)) {
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ if (ruby_dyna_vars->id == 0) {
+ vars = ruby_dyna_vars->next;
+ rb_gc_force_recycle((VALUE)ruby_dyna_vars);
+ while (vars && vars->id != 0 && vars != block->dyna_vars) {
+ struct RVarmap *tmp = vars->next;
+ rb_gc_force_recycle((VALUE)vars);
+ vars = tmp;
+ }
+ }
+ }
+ POP_VARS();
+ ruby_block = block;
+ ruby_frame = ruby_frame->prev;
+ ruby_cref = (NODE*)old_cref;
+ ruby_wrapper = old_wrapper;
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE)
+ scope_dup(old_scope);
+ ruby_scope = old_scope;
+ scope_vmode = old_vmode;
+ switch (state) {
+ case 0:
+ break;
+ case TAG_BREAK:
+ if (!lambda) {
+ struct tag *tt = prot_tag;
+
+ while (tt) {
+ if (tt->tag == PROT_LOOP && tt->blkid == ruby_block->uniq) {
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = result;
+ JUMP_TAG(TAG_BREAK);
+ }
+ tt = tt->prev;
+ }
+ proc_jump_error(TAG_BREAK, result);
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ break;
+ }
+ ruby_current_node = cnode;
+ return result;
+}
+
+VALUE
+rb_yield(val)
+ VALUE val;
+{
+ return rb_yield_0(val, 0, 0, 0, Qfalse);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_yield_values(int n, ...)
+#else
+rb_yield_values(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list args;
+ VALUE ary;
+
+ if (n == 0) {
+ return rb_yield_0(Qundef, 0, 0, 0, Qfalse);
+ }
+ ary = rb_ary_new2(n);
+ va_init_list(args, n);
+ while (n--) {
+ rb_ary_push(ary, va_arg(args, VALUE));
+ }
+ va_end(args);
+ return rb_yield_0(ary, 0, 0, 0, Qtrue);
+}
+
+VALUE
+rb_yield_splat(values)
+ VALUE values;
+{
+ int avalue = Qfalse;
+
+ if (TYPE(values) == T_ARRAY) {
+ if (RARRAY(values)->len == 0) {
+ values = Qundef;
+ }
+ else {
+ avalue = Qtrue;
+ }
+ }
+ return rb_yield_0(values, 0, 0, 0, avalue);
+}
+
+/*
+ * call-seq:
+ * loop {|| block }
+ *
+ * Repeatedly executes the block.
+ *
+ * loop do
+ * print "Input: "
+ * line = gets
+ * break if !line or line =~ /^qQ/
+ * # ...
+ * end
+ */
+
+static VALUE
+rb_f_loop()
+{
+ for (;;) {
+ rb_yield_0(Qundef, 0, 0, 0, Qfalse);
+ CHECK_INTS;
+ }
+ return Qnil; /* dummy */
+}
+
+static VALUE
+massign(self, node, val, pcall)
+ VALUE self;
+ NODE *node;
+ VALUE val;
+ int pcall;
+{
+ NODE *list;
+ long i = 0, len;
+
+ len = RARRAY(val)->len;
+ list = node->nd_head;
+ for (; list && i<len; i++) {
+ assign(self, list->nd_head, RARRAY(val)->ptr[i], pcall);
+ list = list->nd_next;
+ }
+ if (pcall && list) goto arg_error;
+ if (node->nd_args) {
+ if ((long)(node->nd_args) == -1) {
+ /* no check for mere `*' */
+ }
+ else if (!list && i<len) {
+ assign(self, node->nd_args, rb_ary_new4(len-i, RARRAY(val)->ptr+i), pcall);
+ }
+ else {
+ assign(self, node->nd_args, rb_ary_new2(0), pcall);
+ }
+ }
+ else if (pcall && i < len) {
+ goto arg_error;
+ }
+
+ while (list) {
+ i++;
+ assign(self, list->nd_head, Qnil, pcall);
+ list = list->nd_next;
+ }
+ return val;
+
+ arg_error:
+ while (list) {
+ i++;
+ list = list->nd_next;
+ }
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for %ld)", len, i);
+}
+
+static void
+assign(self, lhs, val, pcall)
+ VALUE self;
+ NODE *lhs;
+ VALUE val;
+ int pcall;
+{
+ ruby_current_node = lhs;
+ if (val == Qundef) {
+ rb_warning("assigning void value");
+ val = Qnil;
+ }
+ switch (nd_type(lhs)) {
+ case NODE_GASGN:
+ rb_gvar_set(lhs->nd_entry, val);
+ break;
+
+ case NODE_IASGN:
+ rb_ivar_set(self, lhs->nd_vid, val);
+ break;
+
+ case NODE_LASGN:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected local variable assignment");
+ ruby_scope->local_vars[lhs->nd_cnt] = val;
+ break;
+
+ case NODE_DASGN:
+ dvar_asgn(lhs->nd_vid, val);
+ break;
+
+ case NODE_DASGN_CURR:
+ dvar_asgn_curr(lhs->nd_vid, val);
+ break;
+
+ case NODE_CDECL:
+ if (lhs->nd_vid == 0) {
+ rb_const_set(class_prefix(self, lhs->nd_else), lhs->nd_else->nd_mid, val);
+ }
+ else {
+ rb_const_set(ruby_cbase, lhs->nd_vid, val);
+ }
+ break;
+
+ case NODE_CVDECL:
+ if (RTEST(ruby_verbose) && FL_TEST(ruby_cbase, FL_SINGLETON)) {
+ rb_warn("declaring singleton class variable");
+ }
+ rb_cvar_set(cvar_cbase(), lhs->nd_vid, val, Qtrue);
+ break;
+
+ case NODE_CVASGN:
+ rb_cvar_set(cvar_cbase(), lhs->nd_vid, val, Qfalse);
+ break;
+
+ case NODE_MASGN:
+ massign(self, lhs, svalue_to_mrhs(val, lhs->nd_head), pcall);
+ break;
+
+ case NODE_CALL:
+ case NODE_ATTRASGN:
+ {
+ VALUE recv;
+ int scope;
+ if (lhs->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, lhs->nd_recv);
+ scope = 0;
+ }
+ if (!lhs->nd_args) {
+ /* attr set */
+ ruby_current_node = lhs;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, scope, self);
+ }
+ else {
+ /* array set */
+ VALUE args;
+
+ args = rb_eval(self, lhs->nd_args);
+ rb_ary_push(args, val);
+ ruby_current_node = lhs;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv), recv, lhs->nd_mid,
+ RARRAY(args)->len, RARRAY(args)->ptr, scope, self);
+ }
+ }
+ break;
+
+ default:
+ rb_bug("bug in variable assignment");
+ break;
+ }
+}
+
+VALUE
+rb_iterate(it_proc, data1, bl_proc, data2)
+ VALUE (*it_proc) _((VALUE)), (*bl_proc)(ANYARGS);
+ VALUE data1, data2;
+{
+ int state;
+ volatile VALUE retval = Qnil;
+ NODE *node = NEW_IFUNC(bl_proc, data2);
+ VALUE self = ruby_top_self;
+
+ PUSH_TAG(PROT_LOOP);
+ PUSH_BLOCK(0, node);
+ PUSH_ITER(ITER_PRE);
+ state = EXEC_TAG();
+ if (state == 0) {
+ iter_retry:
+ retval = (*it_proc)(data1);
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ retval = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY) {
+ state = 0;
+ goto iter_retry;
+ }
+ POP_ITER();
+ POP_BLOCK();
+ POP_TAG();
+
+ switch (state) {
+ case 0:
+ break;
+ default:
+ JUMP_TAG(state);
+ }
+ return retval;
+}
+
+static int
+handle_rescue(self, node)
+ VALUE self;
+ NODE *node;
+{
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ if (!node->nd_args) {
+ return rb_obj_is_kind_of(ruby_errinfo, rb_eStandardError);
+ }
+
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ while (argc--) {
+ if (!rb_obj_is_kind_of(argv[0], rb_cModule)) {
+ rb_raise(rb_eTypeError, "class or module required for rescue clause");
+ }
+ if (RTEST(rb_funcall(*argv, eqq, 1, ruby_errinfo))) return 1;
+ argv++;
+ }
+ return 0;
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_rescue2(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*r_proc)(ANYARGS), VALUE data2, ...)
+#else
+rb_rescue2(b_proc, data1, r_proc, data2, va_alist)
+ VALUE (*b_proc)(ANYARGS), (*r_proc)(ANYARGS);
+ VALUE data1, data2;
+ va_dcl
+#endif
+{
+ int state;
+ volatile VALUE result;
+ volatile VALUE e_info = ruby_errinfo;
+ volatile int handle = Qfalse;
+ VALUE eclass;
+ va_list args;
+
+ PUSH_TAG(PROT_NONE);
+ switch (state = EXEC_TAG()) {
+ case TAG_RETRY:
+ if (!handle) break;
+ handle = Qfalse;
+ state = 0;
+ ruby_errinfo = Qnil;
+ case 0:
+ result = (*b_proc)(data1);
+ break;
+ case TAG_RAISE:
+ if (handle) break;
+ handle = Qfalse;
+ va_init_list(args, data2);
+ while ((eclass = va_arg(args, VALUE)) != 0) {
+ if (rb_obj_is_kind_of(ruby_errinfo, eclass)) {
+ handle = Qtrue;
+ break;
+ }
+ }
+ va_end(args);
+
+ if (handle) {
+ state = 0;
+ if (r_proc) {
+ result = (*r_proc)(data2, ruby_errinfo);
+ }
+ else {
+ result = Qnil;
+ }
+ ruby_errinfo = e_info;
+ }
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+VALUE
+rb_rescue(b_proc, data1, r_proc, data2)
+ VALUE (*b_proc)(), (*r_proc)();
+ VALUE data1, data2;
+{
+ return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
+}
+
+static VALUE cont_protect;
+
+VALUE
+rb_protect(proc, data, state)
+ VALUE (*proc) _((VALUE));
+ VALUE data;
+ int *state;
+{
+ VALUE result = Qnil; /* OK */
+ int status;
+
+ PUSH_TAG(PROT_NONE);
+ cont_protect = (VALUE)rb_node_newnode(NODE_MEMO, cont_protect, 0, 0);
+ if ((status = EXEC_TAG()) == 0) {
+ result = (*proc)(data);
+ }
+ cont_protect = ((NODE *)cont_protect)->u1.value;
+ POP_TAG();
+ if (state) {
+ *state = status;
+ }
+ if (status != 0) {
+ return Qnil;
+ }
+
+ return result;
+}
+
+VALUE
+rb_ensure(b_proc, data1, e_proc, data2)
+ VALUE (*b_proc)();
+ VALUE data1;
+ VALUE (*e_proc)();
+ VALUE data2;
+{
+ int state;
+ volatile VALUE result = Qnil;
+ VALUE retval;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*b_proc)(data1);
+ }
+ POP_TAG();
+ retval = prot_tag ? prot_tag->retval : Qnil; /* save retval */
+ if (!thread_no_ensure()) {
+ (*e_proc)(data2);
+ }
+ if (prot_tag) return_value(retval);
+ if (state) JUMP_TAG(state);
+ return result;
+}
+
+VALUE
+rb_with_disable_interrupt(proc, data)
+ VALUE (*proc)();
+ VALUE data;
+{
+ VALUE result = Qnil; /* OK */
+ int status;
+
+ DEFER_INTS;
+ {
+ int thr_critical = rb_thread_critical;
+
+ rb_thread_critical = Qtrue;
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ result = (*proc)(data);
+ }
+ POP_TAG();
+ rb_thread_critical = thr_critical;
+ }
+ ENABLE_INTS;
+ if (status) JUMP_TAG(status);
+
+ return result;
+}
+
+static void
+stack_check()
+{
+ rb_thread_t th = rb_curr_thread;
+
+ if (!rb_thread_raised_p(th, RAISED_STACKOVERFLOW) && ruby_stack_check()) {
+ rb_thread_raised_set(th, RAISED_STACKOVERFLOW);
+ rb_exc_raise(sysstack_error);
+ }
+}
+
+static int last_call_status;
+
+#define CSTAT_PRIV 1
+#define CSTAT_PROT 2
+#define CSTAT_VCALL 4
+#define CSTAT_SUPER 8
+
+/*
+ * call-seq:
+ * obj.method_missing(symbol [, *args] ) => result
+ *
+ * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
+ * <i>symbol</i> is the symbol for the method called, and <i>args</i>
+ * are any arguments that were passed to it. By default, the interpreter
+ * raises an error when this method is called. However, it is possible
+ * to override the method to provide more dynamic behavior.
+ * The example below creates
+ * a class <code>Roman</code>, which responds to methods with names
+ * consisting of roman numerals, returning the corresponding integer
+ * values.
+ *
+ * class Roman
+ * def romanToInt(str)
+ * # ...
+ * end
+ * def method_missing(methId)
+ * str = methId.id2name
+ * romanToInt(str)
+ * end
+ * end
+ *
+ * r = Roman.new
+ * r.iv #=> 4
+ * r.xxiii #=> 23
+ * r.mm #=> 2000
+ */
+
+static VALUE
+rb_method_missing(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ ID id;
+ VALUE exc = rb_eNoMethodError;
+ char *format = 0;
+ NODE *cnode = ruby_current_node;
+
+ if (argc == 0 || !SYMBOL_P(argv[0])) {
+ rb_raise(rb_eArgError, "no id given");
+ }
+
+ stack_check();
+
+ id = SYM2ID(argv[0]);
+
+ if (last_call_status & CSTAT_PRIV) {
+ format = "private method `%s' called for %s";
+ }
+ else if (last_call_status & CSTAT_PROT) {
+ format = "protected method `%s' called for %s";
+ }
+ else if (last_call_status & CSTAT_VCALL) {
+ format = "undefined local variable or method `%s' for %s";
+ exc = rb_eNameError;
+ }
+ else if (last_call_status & CSTAT_SUPER) {
+ format = "super: no superclass method `%s'";
+ }
+ if (!format) {
+ format = "undefined method `%s' for %s";
+ }
+
+ ruby_current_node = cnode;
+ {
+ int n = 0;
+ VALUE args[3];
+
+ args[n++] = rb_funcall(rb_const_get(exc, rb_intern("message")), '!',
+ 3, rb_str_new2(format), obj, argv[0]);
+ args[n++] = argv[0];
+ if (exc == rb_eNoMethodError) {
+ args[n++] = rb_ary_new4(argc-1, argv+1);
+ }
+ exc = rb_class_new_instance(n, args, exc);
+ ruby_frame = ruby_frame->prev; /* pop frame for "method_missing" */
+ rb_exc_raise(exc);
+ }
+
+ return Qnil; /* not reached */
+}
+
+static VALUE
+method_missing(obj, id, argc, argv, call_status)
+ VALUE obj;
+ ID id;
+ int argc;
+ const VALUE *argv;
+ int call_status;
+{
+ VALUE *nargv;
+
+ last_call_status = call_status;
+
+ if (id == missing) {
+ PUSH_FRAME();
+ rb_method_missing(argc, argv, obj);
+ POP_FRAME();
+ }
+ else if (id == ID_ALLOCATOR) {
+ rb_raise(rb_eTypeError, "allocator undefined for %s", rb_class2name(obj));
+ }
+ if (argc < 0) {
+ VALUE tmp;
+
+ argc = -argc-1;
+ tmp = splat_value(argv[argc]);
+ nargv = ALLOCA_N(VALUE, argc + RARRAY(tmp)->len + 1);
+ MEMCPY(nargv+1, argv, VALUE, argc);
+ MEMCPY(nargv+1+argc, RARRAY(tmp)->ptr, VALUE, RARRAY(tmp)->len);
+ argc += RARRAY(tmp)->len;
+ }
+ else {
+ nargv = ALLOCA_N(VALUE, argc+1);
+ MEMCPY(nargv+1, argv, VALUE, argc);
+ }
+ nargv[0] = ID2SYM(id);
+ return rb_funcall2(obj, missing, argc+1, nargv);
+}
+
+static inline VALUE
+call_cfunc(func, recv, len, argc, argv)
+ VALUE (*func)();
+ VALUE recv;
+ int len, argc;
+ VALUE *argv;
+{
+ if (len >= 0 && argc != len) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, len);
+ }
+
+ switch (len) {
+ case -2:
+ return (*func)(recv, rb_ary_new4(argc, argv));
+ break;
+ case -1:
+ return (*func)(argc, argv, recv);
+ break;
+ case 0:
+ return (*func)(recv);
+ break;
+ case 1:
+ return (*func)(recv, argv[0]);
+ break;
+ case 2:
+ return (*func)(recv, argv[0], argv[1]);
+ break;
+ case 3:
+ return (*func)(recv, argv[0], argv[1], argv[2]);
+ break;
+ case 4:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3]);
+ break;
+ case 5:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4]);
+ break;
+ case 6:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5]);
+ break;
+ case 7:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6]);
+ break;
+ case 8:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7]);
+ break;
+ case 9:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8]);
+ break;
+ case 10:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9]);
+ break;
+ case 11:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
+ break;
+ case 12:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9],
+ argv[10], argv[11]);
+ break;
+ case 13:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12]);
+ break;
+ case 14:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12], argv[13]);
+ break;
+ case 15:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12], argv[13], argv[14]);
+ break;
+ default:
+ rb_raise(rb_eArgError, "too many arguments (%d)", len);
+ break;
+ }
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_call0(klass, recv, id, oid, argc, argv, body, flags)
+ VALUE klass, recv;
+ ID id;
+ ID oid;
+ int argc; /* OK */
+ VALUE *argv; /* OK */
+ NODE * volatile body;
+ int flags;
+{
+ NODE *b2; /* OK */
+ volatile VALUE result = Qnil;
+ int itr;
+ static int tick;
+ TMP_PROTECT;
+ volatile int safe = -1;
+
+ if (NOEX_SAFE(flags) > ruby_safe_level && NOEX_SAFE(flags) > 2) {
+ rb_raise(rb_eSecurityError, "calling insecure method: %s",
+ rb_id2name(id));
+ }
+ switch (ruby_iter->iter) {
+ case ITER_PRE:
+ case ITER_PAS:
+ itr = ITER_CUR;
+ break;
+ case ITER_CUR:
+ default:
+ itr = ITER_NOT;
+ break;
+ }
+
+ if ((++tick & 0xff) == 0) {
+ CHECK_INTS; /* better than nothing */
+ stack_check();
+ rb_gc_finalize_deferred();
+ }
+ if (argc < 0) {
+ VALUE tmp;
+ VALUE *nargv;
+
+ argc = -argc-1;
+ tmp = splat_value(argv[argc]);
+ nargv = TMP_ALLOC(argc + RARRAY(tmp)->len);
+ MEMCPY(nargv, argv, VALUE, argc);
+ MEMCPY(nargv+argc, RARRAY(tmp)->ptr, VALUE, RARRAY(tmp)->len);
+ argc += RARRAY(tmp)->len;
+ argv = nargv;
+ }
+ PUSH_ITER(itr);
+ PUSH_FRAME();
+
+ ruby_frame->last_func = id;
+ ruby_frame->orig_func = oid;
+ ruby_frame->last_class = (flags & NOEX_NOSUPER)?0:klass;
+ ruby_frame->self = recv;
+ ruby_frame->argc = argc;
+ ruby_frame->flags = 0;
+
+ switch (nd_type(body)) {
+ case NODE_CFUNC:
+ {
+ int len = body->nd_argc;
+
+ if (len < -2) {
+ rb_bug("bad argc (%d) specified for `%s(%s)'",
+ len, rb_class2name(klass), rb_id2name(id));
+ }
+ if (event_hooks) {
+ int state;
+
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_CALL, ruby_current_node,
+ recv, id, klass);
+ PUSH_TAG(PROT_FUNC);
+ if ((state = EXEC_TAG()) == 0) {
+ result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
+ }
+ POP_TAG();
+ ruby_current_node = ruby_frame->node;
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_RETURN, ruby_current_node,
+ recv, id, klass);
+ if (state) JUMP_TAG(state);
+ }
+ else {
+ result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
+ }
+ }
+ break;
+
+ /* for attr get/set */
+ case NODE_IVAR:
+ if (argc != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ result = rb_attr_get(recv, body->nd_vid);
+ break;
+
+ case NODE_ATTRSET:
+ if (argc != 1)
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ result = rb_ivar_set(recv, body->nd_vid, argv[0]);
+ break;
+
+ case NODE_ZSUPER:
+ result = rb_call_super(argc, argv);
+ break;
+
+ case NODE_DMETHOD:
+ result = method_call(argc, argv, umethod_bind(body->nd_cval, recv));
+ break;
+
+ case NODE_BMETHOD:
+ ruby_frame->flags |= FRAME_DMETH;
+ if (event_hooks) {
+ struct BLOCK *data;
+ Data_Get_Struct(body->nd_cval, struct BLOCK, data);
+ EXEC_EVENT_HOOK(RUBY_EVENT_CALL, data->body, recv, id, klass);
+ }
+ result = proc_invoke(body->nd_cval, rb_ary_new4(argc, argv), recv, klass);
+ if (event_hooks) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
+ }
+ break;
+
+ case NODE_SCOPE:
+ {
+ int state;
+ VALUE *local_vars; /* OK */
+ NODE *saved_cref = 0;
+
+ PUSH_SCOPE();
+ if (body->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)body->nd_rval;
+ }
+ PUSH_CLASS(ruby_cbase);
+ if (body->nd_tbl) {
+ local_vars = TMP_ALLOC(body->nd_tbl[0]+1);
+ *local_vars++ = (VALUE)body;
+ rb_mem_clear(local_vars, body->nd_tbl[0]);
+ ruby_scope->local_tbl = body->nd_tbl;
+ ruby_scope->local_vars = local_vars;
+ }
+ else {
+ local_vars = ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+ b2 = body = body->nd_next;
+
+ if (NOEX_SAFE(flags) > ruby_safe_level) {
+ safe = ruby_safe_level;
+ ruby_safe_level = NOEX_SAFE(flags);
+ }
+ PUSH_VARS();
+ PUSH_TAG(PROT_FUNC);
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *node = 0;
+ int i, nopt = 0;
+
+ if (nd_type(body) == NODE_ARGS) {
+ node = body;
+ body = 0;
+ }
+ else if (nd_type(body) == NODE_BLOCK) {
+ node = body->nd_head;
+ body = body->nd_next;
+ }
+ if (node) {
+ if (nd_type(node) != NODE_ARGS) {
+ rb_bug("no argument-node");
+ }
+
+ i = node->nd_cnt;
+ if (i > argc) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, i);
+ }
+ if (!node->nd_rest) {
+ NODE *optnode = node->nd_opt;
+
+ nopt = i;
+ while (optnode) {
+ nopt++;
+ optnode = optnode->nd_next;
+ }
+ if (nopt < argc) {
+ rb_raise(rb_eArgError,
+ "wrong number of arguments (%d for %d)",
+ argc, nopt);
+ }
+ }
+ if (local_vars) {
+ if (i > 0) {
+ /* +2 for $_ and $~ */
+ MEMCPY(local_vars+2, argv, VALUE, i);
+ }
+ }
+ argv += i; argc -= i;
+ if (node->nd_opt) {
+ NODE *opt = node->nd_opt;
+
+ while (opt && argc) {
+ assign(recv, opt->nd_head, *argv, 1);
+ argv++; argc--;
+ ++i;
+ opt = opt->nd_next;
+ }
+ if (opt) {
+ rb_eval(recv, opt);
+ while (opt) {
+ opt = opt->nd_next;
+ ++i;
+ }
+ }
+ }
+ if (!node->nd_rest) {
+ i = nopt;
+ }
+ else {
+ VALUE v;
+
+ if (argc > 0) {
+ v = rb_ary_new4(argc,argv);
+ i = -i - 1;
+ }
+ else {
+ v = rb_ary_new2(0);
+ }
+ assign(recv, node->nd_rest, v, 1);
+ }
+ ruby_frame->argc = i;
+ }
+ if (event_hooks) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_CALL, b2, recv, id, klass);
+ }
+ result = rb_eval(recv, body);
+ }
+ else if (state == TAG_RETURN && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ POP_TAG();
+ if (event_hooks) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
+ }
+ POP_VARS();
+ POP_CLASS();
+ POP_SCOPE();
+ ruby_cref = saved_cref;
+ if (safe >= 0) ruby_safe_level = safe;
+ switch (state) {
+ case 0:
+ break;
+
+ case TAG_BREAK:
+ case TAG_RETURN:
+ JUMP_TAG(state);
+ break;
+
+ case TAG_RETRY:
+ if (rb_block_given_p()) JUMP_TAG(state);
+ /* fall through */
+ default:
+ jump_tag_but_local_jump(state, result);
+ break;
+ }
+ }
+ break;
+
+ default:
+ unknown_node(body);
+ break;
+ }
+ POP_FRAME();
+ POP_ITER();
+ return result;
+}
+
+static VALUE
+rb_call(klass, recv, mid, argc, argv, scope, self)
+ VALUE klass, recv;
+ ID mid;
+ int argc; /* OK */
+ const VALUE *argv; /* OK */
+ int scope;
+ VALUE self;
+{
+ NODE *body; /* OK */
+ int noex;
+ ID id = mid;
+ struct cache_entry *ent;
+
+ if (!klass) {
+ rb_raise(rb_eNotImpError, "method `%s' called on terminated object (0x%lx)",
+ rb_id2name(mid), recv);
+ }
+ /* is it in the method cache? */
+ ent = cache + EXPR1(klass, mid);
+ if (ent->mid == mid && ent->klass == klass) {
+ if (!ent->method)
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ klass = ent->origin;
+ id = ent->mid0;
+ noex = ent->noex;
+ body = ent->method;
+ }
+ else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
+ if (scope == 3) {
+ return method_missing(recv, mid, argc, argv, CSTAT_SUPER);
+ }
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ }
+
+ if (mid != missing && scope == 0) {
+ /* receiver specified form for private method */
+ if (noex & NOEX_PRIVATE)
+ return method_missing(recv, mid, argc, argv, CSTAT_PRIV);
+
+ /* self must be kind of a specified form for protected method */
+ if (noex & NOEX_PROTECTED) {
+ VALUE defined_class = klass;
+
+ if (self == Qundef) self = ruby_frame->self;
+ if (TYPE(defined_class) == T_ICLASS) {
+ defined_class = RBASIC(defined_class)->klass;
+ }
+ if (!rb_obj_is_kind_of(self, rb_class_real(defined_class)))
+ return method_missing(recv, mid, argc, argv, CSTAT_PROT);
+ }
+ }
+
+ return rb_call0(klass, recv, mid, id, argc, argv, body, noex);
+}
+
+VALUE
+rb_apply(recv, mid, args)
+ VALUE recv;
+ ID mid;
+ VALUE args;
+{
+ int argc;
+ VALUE *argv;
+
+ argc = RARRAY(args)->len; /* Assigns LONG, but argc is INT */
+ argv = ALLOCA_N(VALUE, argc);
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1, Qundef);
+}
+
+/*
+ * call-seq:
+ * obj.send(symbol [, args...]) => obj
+ * obj.__send__(symbol [, args...]) => obj
+ *
+ * Invokes the method identified by _symbol_, passing it any
+ * arguments specified. You can use <code>\_\_send__</code> if the name
+ * +send+ clashes with an existing method in _obj_.
+ *
+ * class Klass
+ * def hello(*args)
+ * "Hello " + args.join(' ')
+ * end
+ * end
+ * k = Klass.new
+ * k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
+ */
+
+static VALUE
+rb_f_send(argc, argv, recv)
+ int argc;
+ VALUE *argv;
+ VALUE recv;
+{
+ VALUE vid;
+
+ if (argc == 0) rb_raise(rb_eArgError, "no method name given");
+
+ vid = *argv++; argc--;
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ vid = rb_call(CLASS_OF(recv), recv, rb_to_id(vid), argc, argv, 1, Qundef);
+ POP_ITER();
+
+ return vid;
+}
+
+static VALUE
+vafuncall(recv, mid, n, ar)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_list *ar;
+{
+ VALUE *argv;
+
+ if (n > 0) {
+ long i;
+
+ argv = ALLOCA_N(VALUE, n);
+
+ for (i=0;i<n;i++) {
+ argv[i] = va_arg(*ar, VALUE);
+ }
+ va_end(*ar);
+ }
+ else {
+ argv = 0;
+ }
+
+ return rb_call(CLASS_OF(recv), recv, mid, n, argv, 1, Qundef);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_funcall(VALUE recv, ID mid, int n, ...)
+#else
+rb_funcall(recv, mid, n, va_alist)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_dcl
+#endif
+{
+ va_list ar;
+ va_init_list(ar, n);
+
+ return vafuncall(recv, mid, n, &ar);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_funcall_rescue(VALUE recv, ID mid, int n, ...)
+#else
+rb_funcall_rescue(recv, mid, n, va_alist)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_dcl
+#endif
+{
+ VALUE result = Qnil; /* OK */
+ int status;
+ va_list ar;
+
+ va_init_list(ar, n);
+
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ result = vafuncall(recv, mid, n, &ar);
+ }
+ POP_TAG();
+ switch (status) {
+ case 0:
+ return result;
+ case TAG_RAISE:
+ return Qundef;
+ default:
+ JUMP_TAG(status);
+ }
+}
+
+VALUE
+rb_funcall2(recv, mid, argc, argv)
+ VALUE recv;
+ ID mid;
+ int argc;
+ const VALUE *argv;
+{
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1, Qundef);
+}
+
+VALUE
+rb_funcall3(recv, mid, argc, argv)
+ VALUE recv;
+ ID mid;
+ int argc;
+ const VALUE *argv;
+{
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 0, Qundef);
+}
+
+VALUE
+rb_call_super(argc, argv)
+ int argc;
+ const VALUE *argv;
+{
+ VALUE result, self, klass;
+
+ if (ruby_frame->last_class == 0) {
+ rb_name_error(ruby_frame->last_func, "calling `super' from `%s' is prohibited",
+ rb_id2name(ruby_frame->orig_func));
+ }
+
+ self = ruby_frame->self;
+ klass = ruby_frame->last_class;
+ if (RCLASS(klass)->super == 0) {
+ return method_missing(self, ruby_frame->orig_func, argc, argv, CSTAT_SUPER);
+ }
+
+ PUSH_ITER(ruby_iter->iter ? ITER_PRE : ITER_NOT);
+ result = rb_call(RCLASS(klass)->super, self, ruby_frame->orig_func, argc, argv, 3, Qundef);
+ POP_ITER();
+
+ return result;
+}
+
+static VALUE
+backtrace(lev)
+ int lev;
+{
+ struct FRAME *frame = ruby_frame;
+ char buf[BUFSIZ];
+ VALUE ary;
+ NODE *n;
+
+ ary = rb_ary_new();
+ if (frame->last_func == ID_ALLOCATOR) {
+ frame = frame->prev;
+ }
+ if (lev < 0) {
+ ruby_set_current_source();
+ if (frame->last_func) {
+ snprintf(buf, BUFSIZ, "%s:%d:in `%s'",
+ ruby_sourcefile, ruby_sourceline,
+ rb_id2name(frame->last_func));
+ }
+ else if (ruby_sourceline == 0) {
+ snprintf(buf, BUFSIZ, "%s", ruby_sourcefile);
+ }
+ else {
+ snprintf(buf, BUFSIZ, "%s:%d", ruby_sourcefile, ruby_sourceline);
+ }
+ rb_ary_push(ary, rb_str_new2(buf));
+ if (lev < -1) return ary;
+ }
+ else {
+ while (lev-- > 0) {
+ frame = frame->prev;
+ if (!frame) {
+ ary = Qnil;
+ break;
+ }
+ }
+ }
+ for (; frame && (n = frame->node); frame = frame->prev) {
+ if (frame->prev && frame->prev->last_func) {
+ if (frame->prev->node == n) {
+ if (frame->prev->last_func == frame->last_func) continue;
+ }
+ snprintf(buf, BUFSIZ, "%s:%d:in `%s'",
+ n->nd_file, nd_line(n),
+ rb_id2name(frame->prev->last_func));
+ }
+ else {
+ snprintf(buf, BUFSIZ, "%s:%d", n->nd_file, nd_line(n));
+ }
+ rb_ary_push(ary, rb_str_new2(buf));
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * caller(start=1) => array
+ *
+ * Returns the current execution stack---an array containing strings in
+ * the form ``<em>file:line</em>'' or ``<em>file:line: in
+ * `method'</em>''. The optional _start_ parameter
+ * determines the number of initial stack entries to omit from the
+ * result.
+ *
+ * def a(skip)
+ * caller(skip)
+ * end
+ * def b(skip)
+ * a(skip)
+ * end
+ * def c(skip)
+ * b(skip)
+ * end
+ * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
+ * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
+ * c(2) #=> ["prog:8:in `c'", "prog:12"]
+ * c(3) #=> ["prog:13"]
+ */
+
+static VALUE
+rb_f_caller(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE level;
+ int lev;
+
+ rb_scan_args(argc, argv, "01", &level);
+
+ if (NIL_P(level)) lev = 1;
+ else lev = NUM2INT(level);
+ if (lev < 0) rb_raise(rb_eArgError, "negative level (%d)", lev);
+
+ return backtrace(lev);
+}
+
+void
+rb_backtrace()
+{
+ long i;
+ VALUE ary;
+
+ ary = backtrace(-1);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr[i])->ptr);
+ }
+}
+
+static VALUE
+make_backtrace()
+{
+ return backtrace(-1);
+}
+
+ID
+rb_frame_last_func()
+{
+ return ruby_frame->last_func;
+}
+
+static NODE*
+compile(src, file, line)
+ VALUE src;
+ char *file;
+ int line;
+{
+ NODE *node;
+ int critical;
+
+ ruby_nerrs = 0;
+ StringValue(src);
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
+ node = rb_compile_string(file, src, line);
+ rb_thread_critical = critical;
+
+ if (ruby_nerrs == 0) return node;
+ return 0;
+}
+
+static VALUE
+eval(self, src, scope, file, line)
+ VALUE self, src, scope;
+ char *file;
+ int line;
+{
+ struct BLOCK *data = NULL;
+ volatile VALUE result = Qnil;
+ struct SCOPE * volatile old_scope;
+ struct BLOCK * volatile old_block;
+ struct RVarmap * volatile old_dyna_vars;
+ VALUE volatile old_cref;
+ int volatile old_vmode;
+ volatile VALUE old_wrapper;
+ struct FRAME frame;
+ NODE *nodesave = ruby_current_node;
+ volatile int iter = ruby_frame->iter;
+ volatile int safe = ruby_safe_level;
+ int state;
+
+ if (!NIL_P(scope)) {
+ if (!rb_obj_is_proc(scope)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Binding)",
+ rb_obj_classname(scope));
+ }
+
+ Data_Get_Struct(scope, struct BLOCK, data);
+ /* PUSH BLOCK from data */
+ frame = data->frame;
+ frame.tmp = ruby_frame; /* gc protection */
+ ruby_frame = &(frame);
+ old_scope = ruby_scope;
+ ruby_scope = data->scope;
+ old_block = ruby_block;
+ ruby_block = data->prev;
+ old_dyna_vars = ruby_dyna_vars;
+ ruby_dyna_vars = data->dyna_vars;
+ old_vmode = scope_vmode;
+ scope_vmode = data->vmode;
+ old_cref = (VALUE)ruby_cref;
+ ruby_cref = data->cref;
+ old_wrapper = ruby_wrapper;
+ ruby_wrapper = data->wrapper;
+ if ((file == 0 || (line == 1 && strcmp(file, "(eval)") == 0)) && data->frame.node) {
+ file = data->frame.node->nd_file;
+ if (!file) file = "__builtin__";
+ line = nd_line(data->frame.node);
+ }
+
+ self = data->self;
+ ruby_frame->iter = data->iter;
+ }
+ else {
+ if (ruby_frame->prev) {
+ ruby_frame->iter = ruby_frame->prev->iter;
+ }
+ }
+ if (file == 0) {
+ ruby_set_current_source();
+ file = ruby_sourcefile;
+ line = ruby_sourceline;
+ }
+ PUSH_CLASS(data ? data->klass : ruby_class);
+ ruby_in_eval++;
+ if (TYPE(ruby_class) == T_ICLASS) {
+ ruby_class = RBASIC(ruby_class)->klass;
+ }
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *node;
+
+ ruby_safe_level = 0;
+ result = ruby_errinfo;
+ ruby_errinfo = Qnil;
+ node = compile(src, file, line);
+ ruby_safe_level = safe;
+ if (ruby_nerrs > 0) {
+ compile_error(0);
+ }
+ if (!NIL_P(result)) ruby_errinfo = result;
+ result = eval_node(self, node);
+ }
+ POP_TAG();
+ POP_CLASS();
+ ruby_in_eval--;
+ if (!NIL_P(scope)) {
+ int dont_recycle = ruby_scope->flags & SCOPE_DONT_RECYCLE;
+
+ ruby_wrapper = old_wrapper;
+ ruby_cref = (NODE*)old_cref;
+ ruby_frame = frame.tmp;
+ ruby_scope = old_scope;
+ ruby_block = old_block;
+ ruby_dyna_vars = old_dyna_vars;
+ data->vmode = scope_vmode; /* write back visibility mode */
+ scope_vmode = old_vmode;
+ if (dont_recycle) {
+ struct tag *tag;
+ struct RVarmap *vars;
+
+ scope_dup(ruby_scope);
+ for (tag=prot_tag; tag; tag=tag->prev) {
+ scope_dup(tag->scope);
+ }
+ for (vars = ruby_dyna_vars; vars; vars = vars->next) {
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ }
+ else {
+ ruby_frame->iter = iter;
+ }
+ ruby_current_node = nodesave;
+ ruby_set_current_source();
+ if (state) {
+ if (state == TAG_RAISE) {
+ if (strcmp(file, "(eval)") == 0) {
+ VALUE mesg, errat, bt2;
+
+ errat = get_backtrace(ruby_errinfo);
+ mesg = rb_attr_get(ruby_errinfo, rb_intern("mesg"));
+ if (!NIL_P(errat) && TYPE(errat) == T_ARRAY &&
+ (bt2 = backtrace(-2), RARRAY(bt2)->len > 0)) {
+ if (!NIL_P(mesg) && TYPE(mesg) == T_STRING) {
+ rb_str_update(mesg, 0, 0, rb_str_new2(": "));
+ rb_str_update(mesg, 0, 0, RARRAY(errat)->ptr[0]);
+ }
+ RARRAY(errat)->ptr[0] = RARRAY(bt2)->ptr[0];
+ }
+ }
+ rb_exc_raise(ruby_errinfo);
+ }
+ JUMP_TAG(state);
+ }
+
+ return result;
+}
+
+/*
+ * call-seq:
+ * eval(string [, binding [, filename [,lineno]]]) => obj
+ *
+ * Evaluates the Ruby expression(s) in <em>string</em>. If
+ * <em>binding</em> is given, the evaluation is performed in its
+ * context. The binding may be a <code>Binding</code> object or a
+ * <code>Proc</code> object. If the optional <em>filename</em> and
+ * <em>lineno</em> parameters are present, they will be used when
+ * reporting syntax errors.
+ *
+ * def getBinding(str)
+ * return binding
+ * end
+ * str = "hello"
+ * eval "str + ' Fred'" #=> "hello Fred"
+ * eval "str + ' Fred'", getBinding("bye") #=> "bye Fred"
+ */
+
+static VALUE
+rb_f_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE src, scope, vfile, vline;
+ char *file = "(eval)";
+ int line = 1;
+
+ rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
+ if (ruby_safe_level >= 4) {
+ StringValue(src);
+ if (!NIL_P(scope) && !OBJ_TAINTED(scope)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't modify trusted binding");
+ }
+ }
+ else {
+ SafeStringValue(src);
+ }
+ if (argc >= 3) {
+ StringValue(vfile);
+ }
+ if (argc >= 4) {
+ line = NUM2INT(vline);
+ }
+
+ if (!NIL_P(vfile)) file = RSTRING(vfile)->ptr;
+ if (NIL_P(scope) && ruby_frame->prev) {
+ struct FRAME *prev;
+ VALUE val;
+
+ prev = ruby_frame;
+ PUSH_FRAME();
+ *ruby_frame = *prev->prev;
+ ruby_frame->prev = prev;
+ val = eval(self, src, scope, file, line);
+ POP_FRAME();
+
+ return val;
+ }
+ return eval(self, src, scope, file, line);
+}
+
+/* function to call func under the specified class/module context */
+static VALUE
+exec_under(func, under, cbase, args)
+ VALUE (*func)();
+ VALUE under, cbase;
+ void *args;
+{
+ VALUE val = Qnil; /* OK */
+ int state;
+ int mode;
+ struct FRAME *f = ruby_frame;
+
+ PUSH_CLASS(under);
+ PUSH_FRAME();
+ ruby_frame->self = f->self;
+ ruby_frame->last_func = f->last_func;
+ ruby_frame->orig_func = f->orig_func;
+ ruby_frame->last_class = f->last_class;
+ ruby_frame->argc = f->argc;
+ if (cbase) {
+ PUSH_CREF(cbase);
+ }
+
+ mode = scope_vmode;
+ SCOPE_SET(SCOPE_PUBLIC);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = (*func)(args);
+ }
+ POP_TAG();
+ if (cbase) POP_CREF();
+ SCOPE_SET(mode);
+ POP_FRAME();
+ POP_CLASS();
+ if (state) JUMP_TAG(state);
+
+ return val;
+}
+
+static VALUE
+eval_under_i(args)
+ VALUE *args;
+{
+ struct FRAME *f = ruby_frame;
+
+ if (f && (f = f->prev) && (f = f->prev)) {
+ ruby_frame = f;
+ }
+ return eval(args[0], args[1], Qnil, (char*)args[2], (int)args[3]);
+}
+
+/* string eval under the class/module context */
+static VALUE
+eval_under(under, self, src, file, line)
+ VALUE under, self, src;
+ const char *file;
+ int line;
+{
+ VALUE args[4];
+
+ if (ruby_safe_level >= 4) {
+ StringValue(src);
+ }
+ else {
+ SafeStringValue(src);
+ }
+ args[0] = self;
+ args[1] = src;
+ args[2] = (VALUE)file;
+ args[3] = (VALUE)line;
+ return exec_under(eval_under_i, under, under, args);
+}
+
+static VALUE
+yield_under_i(self)
+ VALUE self;
+{
+ return rb_yield_0(self, self, ruby_class, YIELD_PUBLIC_DEF, Qfalse);
+}
+
+/* block eval under the class/module context */
+static VALUE
+yield_under(under, self)
+ VALUE under, self;
+{
+ return exec_under(yield_under_i, under, 0, self);
+}
+
+static VALUE
+specific_eval(argc, argv, klass, self)
+ int argc;
+ VALUE *argv;
+ VALUE klass, self;
+{
+ if (rb_block_given_p()) {
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ return yield_under(klass, self);
+ }
+ else {
+ char *file = "(eval)";
+ int line = 1;
+
+ if (argc == 0) {
+ rb_raise(rb_eArgError, "block not supplied");
+ }
+ else {
+ if (ruby_safe_level >= 4) {
+ StringValue(argv[0]);
+ }
+ else {
+ SafeStringValue(argv[0]);
+ }
+ if (argc > 3) {
+ rb_raise(rb_eArgError, "wrong number of arguments: %s(src) or %s{..}",
+ rb_id2name(ruby_frame->last_func),
+ rb_id2name(ruby_frame->last_func));
+ }
+ if (argc > 2) line = NUM2INT(argv[2]);
+ if (argc > 1) {
+ file = StringValuePtr(argv[1]);
+ }
+ }
+ return eval_under(klass, self, argv[0], file, line);
+ }
+}
+
+/*
+ * call-seq:
+ * obj.instance_eval(string [, filename [, lineno]] ) => obj
+ * obj.instance_eval {| | block } => obj
+ *
+ * Evaluates a string containing Ruby source code, or the given block,
+ * within the context of the receiver (_obj_). In order to set the
+ * context, the variable +self+ is set to _obj_ while
+ * the code is executing, giving the code access to _obj_'s
+ * instance variables. In the version of <code>instance_eval</code>
+ * that takes a +String+, the optional second and third
+ * parameters supply a filename and starting line number that are used
+ * when reporting compilation errors.
+ *
+ * class Klass
+ * def initialize
+ * @secret = 99
+ * end
+ * end
+ * k = Klass.new
+ * k.instance_eval { @secret } #=> 99
+ */
+
+VALUE
+rb_obj_instance_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE klass;
+
+ if (SPECIAL_CONST_P(self)) {
+ klass = Qnil;
+ }
+ else {
+ klass = rb_singleton_class(self);
+ }
+ return specific_eval(argc, argv, klass, self);
+}
+
+/*
+ * call-seq:
+ * mod.class_eval(string [, filename [, lineno]]) => obj
+ * mod.module_eval {|| block } => obj
+ *
+ * Evaluates the string or block in the context of _mod_. This can
+ * be used to add methods to a class. <code>module_eval</code> returns
+ * the result of evaluating its argument. The optional _filename_
+ * and _lineno_ parameters set the text for error messages.
+ *
+ * class Thing
+ * end
+ * a = %q{def hello() "Hello there!" end}
+ * Thing.module_eval(a)
+ * puts Thing.new.hello()
+ * Thing.module_eval("invalid code", "dummy", 123)
+ *
+ * <em>produces:</em>
+ *
+ * Hello there!
+ * dummy:123:in `module_eval': undefined local variable
+ * or method `code' for Thing:Class
+ */
+
+VALUE
+rb_mod_module_eval(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return specific_eval(argc, argv, mod, mod);
+}
+
+VALUE rb_load_path;
+
+NORETURN(static void load_failed _((VALUE)));
+
+void
+rb_load(fname, wrap)
+ VALUE fname;
+ int wrap;
+{
+ VALUE tmp;
+ int state;
+ volatile int prohibit_int = rb_prohibit_interrupt;
+ volatile ID last_func;
+ volatile VALUE wrapper = ruby_wrapper;
+ volatile VALUE self = ruby_top_self;
+ NODE *volatile last_node;
+ NODE *saved_cref = ruby_cref;
+
+ if (wrap && ruby_safe_level >= 4) {
+ StringValue(fname);
+ }
+ else {
+ SafeStringValue(fname);
+ }
+ fname = rb_str_new4(fname);
+ tmp = rb_find_file(fname);
+ if (!tmp) {
+ load_failed(fname);
+ }
+ fname = tmp;
+
+ ruby_errinfo = Qnil; /* ensure */
+ PUSH_VARS();
+ PUSH_CLASS(ruby_wrapper);
+ ruby_cref = ruby_top_cref;
+ if (!wrap) {
+ rb_secure(4); /* should alter global state */
+ ruby_class = rb_cObject;
+ ruby_wrapper = 0;
+ }
+ else {
+ /* load in anonymous module as toplevel */
+ ruby_class = ruby_wrapper = rb_module_new();
+ self = rb_obj_clone(ruby_top_self);
+ rb_extend_object(self, ruby_wrapper);
+ PUSH_CREF(ruby_wrapper);
+ }
+ PUSH_ITER(ITER_NOT);
+ PUSH_FRAME();
+ ruby_frame->last_func = 0;
+ ruby_frame->last_class = 0;
+ ruby_frame->self = self;
+ PUSH_SCOPE();
+ /* default visibility is private at loading toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+ PUSH_TAG(PROT_NONE);
+ state = EXEC_TAG();
+ last_func = ruby_frame->last_func;
+ last_node = ruby_current_node;
+ if (!ruby_current_node && ruby_sourcefile) {
+ last_node = NEW_NEWLINE(0);
+ }
+ ruby_current_node = 0;
+ if (state == 0) {
+ NODE *node;
+ volatile int critical;
+
+ DEFER_INTS;
+ ruby_in_eval++;
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
+ rb_load_file(RSTRING(fname)->ptr);
+ ruby_in_eval--;
+ node = ruby_eval_tree;
+ rb_thread_critical = critical;
+ ALLOW_INTS;
+ if (ruby_nerrs == 0) {
+ eval_node(self, node);
+ }
+ }
+ ruby_frame->last_func = last_func;
+ ruby_current_node = last_node;
+ ruby_sourcefile = 0;
+ ruby_set_current_source();
+ if (ruby_scope->flags == SCOPE_ALLOCA && ruby_class == rb_cObject) {
+ if (ruby_scope->local_tbl) /* toplevel was empty */
+ free(ruby_scope->local_tbl);
+ }
+ POP_TAG();
+ rb_prohibit_interrupt = prohibit_int;
+ ruby_cref = saved_cref;
+ POP_SCOPE();
+ POP_FRAME();
+ POP_ITER();
+ POP_CLASS();
+ POP_VARS();
+ ruby_wrapper = wrapper;
+ if (ruby_nerrs > 0) {
+ ruby_nerrs = 0;
+ rb_exc_raise(ruby_errinfo);
+ }
+ if (state) jump_tag_but_local_jump(state, Qundef);
+ if (!NIL_P(ruby_errinfo)) /* exception during load */
+ rb_exc_raise(ruby_errinfo);
+}
+
+void
+rb_load_protect(fname, wrap, state)
+ VALUE fname;
+ int wrap;
+ int *state;
+{
+ int status;
+
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ rb_load(fname, wrap);
+ }
+ POP_TAG();
+ if (state) *state = status;
+}
+
+/*
+ * call-seq:
+ * load(filename, wrap=false) => true
+ *
+ * Loads and executes the Ruby
+ * program in the file _filename_. If the filename does not
+ * resolve to an absolute path, the file is searched for in the library
+ * directories listed in <code>$:</code>. If the optional _wrap_
+ * parameter is +true+, the loaded script will be executed
+ * under an anonymous module, protecting the calling program's global
+ * namespace. In no circumstance will any local variables in the loaded
+ * file be propagated to the loading environment.
+ */
+
+
+static VALUE
+rb_f_load(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, wrap;
+
+ rb_scan_args(argc, argv, "11", &fname, &wrap);
+ rb_load(fname, RTEST(wrap));
+ return Qtrue;
+}
+
+VALUE ruby_dln_librefs;
+static VALUE rb_features;
+static st_table *loading_tbl;
+
+#define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0)
+#ifdef DLEXT2
+#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0 || strcmp(e, DLEXT2) == 0)
+#else
+#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0)
+#endif
+
+
+static const char *const loadable_ext[] = {
+ ".rb", DLEXT,
+#ifdef DLEXT2
+ DLEXT2,
+#endif
+ 0
+};
+
+static int rb_feature_p _((const char **, const char *, int));
+static int search_required _((VALUE, VALUE *, VALUE *));
+
+static int
+rb_feature_p(ftptr, ext, rb)
+ const char **ftptr, *ext;
+ int rb;
+{
+ VALUE v;
+ const char *f, *e, *feature = *ftptr;
+ long i, len, elen;
+
+ if (ext) {
+ len = ext - feature;
+ elen = strlen(ext);
+ }
+ else {
+ len = strlen(feature);
+ elen = 0;
+ }
+ for (i = 0; i < RARRAY_LEN(rb_features); ++i) {
+ v = RARRAY_PTR(rb_features)[i];
+ f = StringValuePtr(v);
+ if (RSTRING_LEN(v) < len || strncmp(f, feature, len) != 0)
+ continue;
+ if (!*(e = f + len)) {
+ if (ext) continue;
+ *ftptr = 0;
+ return 'u';
+ }
+ if (*e != '.') continue;
+ if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
+ *ftptr = 0;
+ return 's';
+ }
+ if ((rb || !ext) && (strcmp(e, ".rb") == 0)) {
+ *ftptr = 0;
+ return 'r';
+ }
+ }
+ if (loading_tbl) {
+ if (st_lookup(loading_tbl, (st_data_t)feature, (st_data_t *)ftptr)) {
+ if (!ext) return 'u';
+ return strcmp(ext, ".rb") ? 's' : 'r';
+ }
+ else {
+ char *buf;
+
+ if (ext && *ext) return 0;
+ buf = ALLOCA_N(char, len + DLEXT_MAXLEN + 1);
+ MEMCPY(buf, feature, char, len);
+ for (i = 0; (e = loadable_ext[i]) != 0; i++) {
+ strncpy(buf + len, e, DLEXT_MAXLEN + 1);
+ if (st_lookup(loading_tbl, (st_data_t)buf, (st_data_t *)ftptr)) {
+ return i ? 's' : 'r';
+ }
+ }
+ }
+ }
+ return 0;
+}
+#define rb_feature_p(feature, ext, rb) rb_feature_p(&feature, ext, rb)
+
+int
+rb_provided(feature)
+ const char *feature;
+{
+ const char *ext = strrchr(feature, '.');
+
+ if (ext && !strchr(ext, '/')) {
+ if (strcmp(".rb", ext) == 0) {
+ if (rb_feature_p(feature, ext, Qtrue)) return Qtrue;
+ return Qfalse;
+ }
+ else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
+ if (rb_feature_p(feature, ext, Qfalse)) return Qtrue;
+ return Qfalse;
+ }
+ }
+ if (rb_feature_p(feature, feature + strlen(feature), Qtrue))
+ return Qtrue;
+
+ return Qfalse;
+}
+
+static void
+rb_provide_feature(feature)
+ VALUE feature;
+{
+ rb_ary_push(rb_features, feature);
+}
+
+void
+rb_provide(feature)
+ const char *feature;
+{
+ rb_provide_feature(rb_str_new2(feature));
+}
+
+static char *
+load_lock(ftptr)
+ const char *ftptr;
+{
+ st_data_t th;
+
+ if (!loading_tbl ||
+ !st_lookup(loading_tbl, (st_data_t)ftptr, &th))
+ {
+ /* loading ruby library should be serialized. */
+ if (!loading_tbl) {
+ loading_tbl = st_init_strtable();
+ }
+ /* partial state */
+ ftptr = ruby_strdup(ftptr);
+ st_insert(loading_tbl, (st_data_t)ftptr, (st_data_t)curr_thread);
+ return (char *)ftptr;
+ }
+ do {
+ rb_thread_t owner = (rb_thread_t)th;
+ if (owner == curr_thread) return 0;
+ rb_thread_join(owner->thread, -1.0);
+ } while (st_lookup(loading_tbl, (st_data_t)ftptr, &th));
+ return 0;
+}
+
+static void
+load_unlock(const char *ftptr)
+{
+ if (ftptr) {
+ st_data_t key = (st_data_t)ftptr;
+
+ if (st_delete(loading_tbl, &key, 0)) {
+ free((char *)key);
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * require(string) => true or false
+ *
+ * Ruby tries to load the library named _string_, returning
+ * +true+ if successful. If the filename does not resolve to
+ * an absolute path, it will be searched for in the directories listed
+ * in <code>$:</code>. If the file has the extension ``.rb'', it is
+ * loaded as a source file; if the extension is ``.so'', ``.o'', or
+ * ``.dll'', or whatever the default shared library extension is on
+ * the current platform, Ruby loads the shared library as a Ruby
+ * extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
+ * to the name. The name of the loaded feature is added to the array in
+ * <code>$"</code>. A feature will not be loaded if it's name already
+ * appears in <code>$"</code>. However, the file name is not converted
+ * to an absolute path, so that ``<code>require 'a';require
+ * './a'</code>'' will load <code>a.rb</code> twice.
+ *
+ * require "my-library.rb"
+ * require "db-driver"
+ */
+
+VALUE
+rb_f_require(obj, fname)
+ VALUE obj, fname;
+{
+ return rb_require_safe(fname, ruby_safe_level);
+}
+
+static int
+search_required(fname, featurep, path)
+ VALUE fname, *featurep, *path;
+{
+ VALUE tmp;
+ const char *ext, *ftptr;
+ int type;
+
+ *featurep = fname;
+ *path = 0;
+ ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
+ if (ext && !strchr(ext, '/')) {
+ if (strcmp(".rb", ext) == 0) {
+ if (rb_feature_p(ftptr, ext, Qtrue)) {
+ if (ftptr) *path = rb_str_new2(ftptr);
+ return 'r';
+ }
+ if ((*path = rb_find_file(fname)) != 0) return 'r';
+ return 0;
+ }
+ else if (IS_SOEXT(ext)) {
+ if (rb_feature_p(ftptr, ext, Qfalse)) {
+ if (ftptr) *path = rb_str_new2(ftptr);
+ return 's';
+ }
+ tmp = rb_str_new(RSTRING_PTR(fname), ext-RSTRING_PTR(fname));
+ *featurep = tmp;
+#ifdef DLEXT2
+ OBJ_FREEZE(tmp);
+ if (rb_find_file_ext(&tmp, loadable_ext+1)) {
+ *featurep = tmp;
+ *path = rb_find_file(tmp);
+ return 's';
+ }
+#else
+ rb_str_cat2(tmp, DLEXT);
+ OBJ_FREEZE(tmp);
+ if ((*path = rb_find_file(tmp)) != 0) {
+ return 's';
+ }
+#endif
+ }
+ else if (IS_DLEXT(ext)) {
+ if (rb_feature_p(ftptr, ext, Qfalse)) {
+ if (ftptr) *path = rb_str_new2(ftptr);
+ return 's';
+ }
+ if ((*path = rb_find_file(fname)) != 0) return 's';
+ }
+ }
+ tmp = fname;
+ type = rb_find_file_ext(&tmp, loadable_ext);
+ *featurep = tmp;
+ switch (type) {
+ case 0:
+ type = rb_feature_p(ftptr, 0, Qfalse);
+ if (type && ftptr) *path = rb_str_new2(ftptr);
+ return type;
+
+ default:
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (!rb_feature_p(ftptr, ext, !--type))
+ *path = rb_find_file(tmp);
+ else if (ftptr)
+ *path = rb_str_new2(ftptr);
+ }
+ return type ? 's' : 'r';
+}
+
+static void
+load_failed(fname)
+ VALUE fname;
+{
+ rb_raise(rb_eLoadError, "no such file to load -- %s", RSTRING(fname)->ptr);
+}
+
+VALUE
+rb_require_safe(fname, safe)
+ VALUE fname;
+ int safe;
+{
+ VALUE result = Qnil;
+ volatile VALUE errinfo = ruby_errinfo;
+ int state;
+ struct {
+ NODE *node;
+ ID func;
+ int vmode, safe;
+ } volatile saved;
+ char *volatile ftptr = 0;
+
+ if (OBJ_TAINTED(fname)) {
+ rb_check_safe_obj(fname);
+ }
+ StringValue(fname);
+ fname = rb_str_new4(fname);
+ saved.vmode = scope_vmode;
+ saved.node = ruby_current_node;
+ saved.func = ruby_frame->last_func;
+ saved.safe = ruby_safe_level;
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ VALUE feature, path;
+ long handle;
+ int found;
+
+ ruby_safe_level = safe;
+ found = search_required(fname, &feature, &path);
+ if (found) {
+ if (!path || !(ftptr = load_lock(RSTRING_PTR(feature)))) {
+ result = Qfalse;
+ }
+ else {
+ ruby_safe_level = 0;
+ switch (found) {
+ case 'r':
+ rb_load(path, 0);
+ break;
+
+ case 's':
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename(RSTRING(path)->ptr);
+ ruby_sourceline = 0;
+ ruby_frame->last_func = 0;
+ SCOPE_SET(SCOPE_PUBLIC);
+ handle = (long)dln_load(RSTRING(path)->ptr);
+ rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
+ break;
+ }
+ rb_provide_feature(feature);
+ result = Qtrue;
+ }
+ }
+ }
+ POP_TAG();
+ ruby_current_node = saved.node;
+ ruby_set_current_source();
+ ruby_frame->last_func = saved.func;
+ SCOPE_SET(saved.vmode);
+ ruby_safe_level = saved.safe;
+ load_unlock(ftptr);
+ if (state) JUMP_TAG(state);
+ if (NIL_P(result)) {
+ load_failed(fname);
+ }
+ ruby_errinfo = errinfo;
+
+ return result;
+}
+
+VALUE
+rb_require(fname)
+ const char *fname;
+{
+ VALUE fn = rb_str_new2(fname);
+ OBJ_FREEZE(fn);
+ return rb_require_safe(fn, ruby_safe_level);
+}
+
+void
+ruby_init_ext(name, init)
+ const char *name;
+ void (*init) _((void));
+{
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename(name);
+ ruby_sourceline = 0;
+ ruby_frame->last_func = 0;
+ ruby_frame->orig_func = 0;
+ SCOPE_SET(SCOPE_PUBLIC);
+ if (load_lock(name)) {
+ (*init)();
+ rb_provide(name);
+ load_unlock(name);
+ }
+}
+
+static void
+secure_visibility(self)
+ VALUE self;
+{
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(self)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't change method visibility");
+ }
+}
+
+static void
+set_method_visibility(self, argc, argv, ex)
+ VALUE self;
+ int argc;
+ VALUE *argv;
+ ID ex;
+{
+ int i;
+
+ secure_visibility(self);
+ for (i=0; i<argc; i++) {
+ rb_export_method(self, rb_to_id(argv[i]), ex);
+ }
+ rb_clear_cache_by_class(self);
+}
+
+/*
+ * call-seq:
+ * public => self
+ * public(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to public. With arguments, sets the named methods to
+ * have public visibility.
+ */
+
+static VALUE
+rb_mod_public(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PUBLIC);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PUBLIC);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * protected => self
+ * protected(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to protected. With arguments, sets the named methods
+ * to have protected visibility.
+ */
+
+static VALUE
+rb_mod_protected(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PROTECTED);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PROTECTED);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * private => self
+ * private(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to private. With arguments, sets the named methods
+ * to have private visibility.
+ *
+ * module Mod
+ * def a() end
+ * def b() end
+ * private
+ * def c() end
+ * private :a
+ * end
+ * Mod.private_instance_methods #=> ["a", "c"]
+ */
+
+static VALUE
+rb_mod_private(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PRIVATE);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * mod.public_class_method(symbol, ...) => mod
+ *
+ * Makes a list of existing class methods public.
+ */
+
+static VALUE
+rb_mod_public_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PUBLIC);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * mod.private_class_method(symbol, ...) => mod
+ *
+ * Makes existing class methods private. Often used to hide the default
+ * constructor <code>new</code>.
+ *
+ * class SimpleSingleton # Not thread safe
+ * private_class_method :new
+ * def SimpleSingleton.create(*args, &block)
+ * @me = new(*args, &block) if ! @me
+ * @me
+ * end
+ * end
+ */
+
+static VALUE
+rb_mod_private_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * public
+ * public(symbol, ...)
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to public. With arguments, sets the named methods to
+ * have public visibility.
+ */
+
+static VALUE
+top_public(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_mod_public(argc, argv, rb_cObject);
+}
+
+static VALUE
+top_private(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_mod_private(argc, argv, rb_cObject);
+}
+
+/*
+ * call-seq:
+ * module_function(symbol, ...) => self
+ *
+ * Creates module functions for the named methods. These functions may
+ * be called with the module as a receiver, and also become available
+ * as instance methods to classes that mix in the module. Module
+ * functions are copies of the original, and so may be changed
+ * independently. The instance-method versions are made private. If
+ * used with no arguments, subsequently defined methods become module
+ * functions.
+ *
+ * module Mod
+ * def one
+ * "This is one"
+ * end
+ * module_function :one
+ * end
+ * class Cls
+ * include Mod
+ * def callOne
+ * one
+ * end
+ * end
+ * Mod.one #=> "This is one"
+ * c = Cls.new
+ * c.callOne #=> "This is one"
+ * module Mod
+ * def one
+ * "This is the new one"
+ * end
+ * end
+ * Mod.one #=> "This is one"
+ * c.callOne #=> "This is the new one"
+ */
+
+static VALUE
+rb_mod_modfunc(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+ ID id;
+ NODE *body;
+
+ if (TYPE(module) != T_MODULE) {
+ rb_raise(rb_eTypeError, "module_function must be called for modules");
+ }
+
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_MODFUNC);
+ return module;
+ }
+
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ for (i=0; i<argc; i++) {
+ VALUE m = module;
+
+ id = rb_to_id(argv[i]);
+ for (;;) {
+ body = search_method(m, id, &m);
+ if (body == 0) {
+ body = search_method(rb_cObject, id, &m);
+ }
+ if (body == 0 || body->nd_body == 0) {
+ print_undef(module, id);
+ }
+ if (nd_type(body->nd_body) != NODE_ZSUPER) {
+ break; /* normal case: need not to follow 'super' link */
+ }
+ m = RCLASS(m)->super;
+ if (!m) break;
+ }
+ rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * append_features(mod) => mod
+ *
+ * When this module is included in another, Ruby calls
+ * <code>append_features</code> in this module, passing it the
+ * receiving module in _mod_. Ruby's default implementation is
+ * to add the constants, methods, and module variables of this module
+ * to _mod_ if this module has not already been added to
+ * _mod_ or one of its ancestors. See also <code>Module#include</code>.
+ */
+
+static VALUE
+rb_mod_append_features(module, include)
+ VALUE module, include;
+{
+ switch (TYPE(include)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ Check_Type(include, T_CLASS);
+ break;
+ }
+ rb_include_module(include, module);
+
+ return module;
+}
+
+/*
+ * call-seq:
+ * include(module, ...) => self
+ *
+ * Invokes <code>Module.append_features</code> on each parameter in turn.
+ */
+
+static VALUE
+rb_mod_include(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+
+ for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
+ while (argc--) {
+ rb_funcall(argv[argc], rb_intern("append_features"), 1, module);
+ rb_funcall(argv[argc], rb_intern("included"), 1, module);
+ }
+ return module;
+}
+
+void
+rb_obj_call_init(obj, argc, argv)
+ VALUE obj;
+ int argc;
+ VALUE *argv;
+{
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ rb_funcall2(obj, init, argc, argv);
+ POP_ITER();
+}
+
+void
+rb_extend_object(obj, module)
+ VALUE obj, module;
+{
+ rb_include_module(rb_singleton_class(obj), module);
+}
+
+/*
+ * call-seq:
+ * extend_object(obj) => obj
+ *
+ * Extends the specified object by adding this module's constants and
+ * methods (which are added as singleton methods). This is the callback
+ * method used by <code>Object#extend</code>.
+ *
+ * module Picky
+ * def Picky.extend_object(o)
+ * if String === o
+ * puts "Can't add Picky to a String"
+ * else
+ * puts "Picky added to #{o.class}"
+ * super
+ * end
+ * end
+ * end
+ * (s = Array.new).extend Picky # Call Object.extend
+ * (s = "quick brown fox").extend Picky
+ *
+ * <em>produces:</em>
+ *
+ * Picky added to Array
+ * Can't add Picky to a String
+ */
+
+static VALUE
+rb_mod_extend_object(mod, obj)
+ VALUE mod, obj;
+{
+ rb_extend_object(obj, mod);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * obj.extend(module, ...) => obj
+ *
+ * Adds to _obj_ the instance methods from each module given as a
+ * parameter.
+ *
+ * module Mod
+ * def hello
+ * "Hello from Mod.\n"
+ * end
+ * end
+ *
+ * class Klass
+ * def hello
+ * "Hello from Klass.\n"
+ * end
+ * end
+ *
+ * k = Klass.new
+ * k.hello #=> "Hello from Klass.\n"
+ * k.extend(Mod) #=> #<Klass:0x401b3bc8>
+ * k.hello #=> "Hello from Mod.\n"
+ */
+
+static VALUE
+rb_obj_extend(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ int i;
+
+ if (argc == 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
+ }
+ for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
+ while (argc--) {
+ rb_funcall(argv[argc], rb_intern("extend_object"), 1, obj);
+ rb_funcall(argv[argc], rb_intern("extended"), 1, obj);
+ }
+ return obj;
+}
+
+/*
+ * call-seq:
+ * include(module, ...) => self
+ *
+ * Invokes <code>Module.append_features</code>
+ * on each parameter in turn. Effectively adds the methods and constants
+ * in each module to the receiver.
+ */
+
+static VALUE
+top_include(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ rb_secure(4);
+ if (ruby_wrapper) {
+ rb_warning("main#include in the wrapped load is effective only in wrapper module");
+ return rb_mod_include(argc, argv, ruby_wrapper);
+ }
+ return rb_mod_include(argc, argv, rb_cObject);
+}
+
+VALUE rb_f_trace_var();
+VALUE rb_f_untrace_var();
+
+static void
+errinfo_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) {
+ rb_raise(rb_eTypeError, "assigning non-exception to $!");
+ }
+ *var = val;
+}
+
+static VALUE
+errat_getter(id)
+ ID id;
+{
+ return get_backtrace(ruby_errinfo);
+}
+
+static void
+errat_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (NIL_P(ruby_errinfo)) {
+ rb_raise(rb_eArgError, "$! not set");
+ }
+ set_backtrace(ruby_errinfo, val);
+}
+
+/*
+ * call-seq:
+ * local_variables => array
+ *
+ * Returns the names of the current local variables.
+ *
+ * fred = 1
+ * for i in 1..10
+ * # ...
+ * end
+ * local_variables #=> ["fred", "i"]
+ */
+
+static VALUE
+rb_f_local_variables()
+{
+ ID *tbl;
+ int n, i;
+ VALUE ary = rb_ary_new();
+ struct RVarmap *vars;
+
+ tbl = ruby_scope->local_tbl;
+ if (tbl) {
+ n = *tbl++;
+ for (i=2; i<n; i++) { /* skip first 2 ($_ and $~) */
+ if (!rb_is_local_id(tbl[i])) continue; /* skip flip states */
+ rb_ary_push(ary, rb_str_new2(rb_id2name(tbl[i])));
+ }
+ }
+
+ vars = ruby_dyna_vars;
+ while (vars) {
+ if (vars->id && rb_is_local_id(vars->id)) { /* skip $_, $~ and flip states */
+ rb_ary_push(ary, rb_str_new2(rb_id2name(vars->id)));
+ }
+ vars = vars->next;
+ }
+
+ return ary;
+}
+
+static VALUE rb_f_catch _((VALUE,VALUE));
+NORETURN(static VALUE rb_f_throw _((int,VALUE*)));
+
+struct end_proc_data {
+ void (*func)();
+ VALUE data;
+ int safe;
+ struct end_proc_data *next;
+};
+
+static struct end_proc_data *end_procs, *ephemeral_end_procs, *tmp_end_procs;
+
+void
+rb_set_end_proc(func, data)
+ void (*func) _((VALUE));
+ VALUE data;
+{
+ struct end_proc_data *link = ALLOC(struct end_proc_data);
+ struct end_proc_data **list;
+
+ if (ruby_wrapper) list = &ephemeral_end_procs;
+ else list = &end_procs;
+ link->next = *list;
+ link->func = func;
+ link->data = data;
+ link->safe = ruby_safe_level;
+ *list = link;
+}
+
+void
+rb_mark_end_proc()
+{
+ struct end_proc_data *link;
+
+ link = end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+ link = ephemeral_end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+ link = tmp_end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+}
+
+static void call_end_proc _((VALUE data));
+
+static void
+call_end_proc(data)
+ VALUE data;
+{
+ PUSH_ITER(ITER_NOT);
+ PUSH_FRAME();
+ ruby_frame->self = ruby_frame->prev->self;
+ ruby_frame->node = 0;
+ ruby_frame->last_func = 0;
+ ruby_frame->last_class = 0;
+ proc_invoke(data, rb_ary_new2(0), Qundef, 0);
+ POP_FRAME();
+ POP_ITER();
+}
+
+static void
+rb_f_END()
+{
+ PUSH_FRAME();
+ ruby_frame->argc = 0;
+ ruby_frame->iter = ITER_CUR;
+ rb_set_end_proc(call_end_proc, rb_block_proc());
+ POP_FRAME();
+}
+
+/*
+ * call-seq:
+ * at_exit { block } -> proc
+ *
+ * Converts _block_ to a +Proc+ object (and therefore
+ * binds it at the point of call) and registers it for execution when
+ * the program exits. If multiple handlers are registered, they are
+ * executed in reverse order of registration.
+ *
+ * def do_at_exit(str1)
+ * at_exit { print str1 }
+ * end
+ * at_exit { puts "cruel world" }
+ * do_at_exit("goodbye ")
+ * exit
+ *
+ * <em>produces:</em>
+ *
+ * goodbye cruel world
+ */
+
+static VALUE
+rb_f_at_exit()
+{
+ VALUE proc;
+
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eArgError, "called without a block");
+ }
+ proc = rb_block_proc();
+ rb_set_end_proc(call_end_proc, proc);
+ return proc;
+}
+
+void
+rb_exec_end_proc()
+{
+ struct end_proc_data *link, *tmp;
+ int status;
+ volatile int safe = ruby_safe_level;
+
+ while (ephemeral_end_procs) {
+ tmp_end_procs = link = ephemeral_end_procs;
+ ephemeral_end_procs = 0;
+ while (link) {
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ ruby_safe_level = link->safe;
+ (*link->func)(link->data);
+ }
+ POP_TAG();
+ if (status) {
+ error_handle(status);
+ }
+ tmp = link;
+ tmp_end_procs = link = link->next;
+ free(tmp);
+ }
+ }
+ while (end_procs) {
+ tmp_end_procs = link = end_procs;
+ end_procs = 0;
+ while (link) {
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ ruby_safe_level = link->safe;
+ (*link->func)(link->data);
+ }
+ POP_TAG();
+ if (status) {
+ error_handle(status);
+ }
+ tmp = link;
+ tmp_end_procs = link = link->next;
+ free(tmp);
+ }
+ }
+ ruby_safe_level = safe;
+}
+
+void
+Init_eval()
+{
+ init = rb_intern("initialize");
+ eqq = rb_intern("===");
+ each = rb_intern("each");
+
+ aref = rb_intern("[]");
+ aset = rb_intern("[]=");
+ match = rb_intern("=~");
+ missing = rb_intern("method_missing");
+ added = rb_intern("method_added");
+ singleton_added = rb_intern("singleton_method_added");
+ removed = rb_intern("method_removed");
+ singleton_removed = rb_intern("singleton_method_removed");
+ undefined = rb_intern("method_undefined");
+ singleton_undefined = rb_intern("singleton_method_undefined");
+
+ __id__ = rb_intern("__id__");
+ __send__ = rb_intern("__send__");
+
+ rb_global_variable((void *)&top_scope);
+ rb_global_variable((void *)&ruby_eval_tree_begin);
+
+ rb_global_variable((void *)&ruby_eval_tree);
+ rb_global_variable((void *)&ruby_dyna_vars);
+
+ rb_define_virtual_variable("$@", errat_getter, errat_setter);
+ rb_define_hooked_variable("$!", &ruby_errinfo, 0, errinfo_setter);
+
+ rb_define_global_function("eval", rb_f_eval, -1);
+ rb_define_global_function("iterator?", rb_f_block_given_p, 0);
+ rb_define_global_function("block_given?", rb_f_block_given_p, 0);
+ rb_define_global_function("method_missing", rb_method_missing, -1);
+ rb_define_global_function("loop", rb_f_loop, 0);
+
+ rb_define_method(rb_mKernel, "respond_to?", obj_respond_to, -1);
+ respond_to = rb_intern("respond_to?");
+ rb_global_variable((void *)&basic_respond_to);
+ basic_respond_to = rb_method_node(rb_cObject, respond_to);
+
+ rb_define_global_function("raise", rb_f_raise, -1);
+ rb_define_global_function("fail", rb_f_raise, -1);
+
+ rb_define_global_function("caller", rb_f_caller, -1);
+
+ rb_define_global_function("exit", rb_f_exit, -1);
+ rb_define_global_function("abort", rb_f_abort, -1);
+
+ rb_define_global_function("at_exit", rb_f_at_exit, 0);
+
+ rb_define_global_function("catch", rb_f_catch, 1);
+ rb_define_global_function("throw", rb_f_throw, -1);
+ rb_define_global_function("global_variables", rb_f_global_variables, 0); /* in variable.c */
+ rb_define_global_function("local_variables", rb_f_local_variables, 0);
+
+ rb_define_method(rb_mKernel, "send", rb_f_send, -1);
+ rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
+ rb_define_method(rb_mKernel, "instance_eval", rb_obj_instance_eval, -1);
+
+ rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
+ rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
+ rb_define_private_method(rb_cModule, "include", rb_mod_include, -1);
+ rb_define_private_method(rb_cModule, "public", rb_mod_public, -1);
+ rb_define_private_method(rb_cModule, "protected", rb_mod_protected, -1);
+ rb_define_private_method(rb_cModule, "private", rb_mod_private, -1);
+ rb_define_private_method(rb_cModule, "module_function", rb_mod_modfunc, -1);
+ rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, 1);
+ rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, 1);
+ rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, 1);
+ rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
+ rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
+ rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);
+ rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);
+ rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1);
+
+ rb_undef_method(rb_cClass, "module_function");
+
+ rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
+ rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
+ rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
+ rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
+
+ rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0);
+ rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, 0);
+
+ rb_define_singleton_method(ruby_top_self, "include", top_include, -1);
+ rb_define_singleton_method(ruby_top_self, "public", top_public, -1);
+ rb_define_singleton_method(ruby_top_self, "private", top_private, -1);
+
+ rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
+
+ rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */
+ rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
+
+ rb_define_global_function("set_trace_func", set_trace_func, 1);
+ rb_global_variable(&trace_func);
+
+ rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
+}
+
+/*
+ * call-seq:
+ * mod.autoload(name, filename) => nil
+ *
+ * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ * the first time that _name_ (which may be a <code>String</code> or
+ * a symbol) is accessed in the namespace of _mod_.
+ *
+ * module A
+ * end
+ * A.autoload(:B, "b")
+ * A::B.doit # autoloads "b"
+ */
+
+static VALUE
+rb_mod_autoload(mod, sym, file)
+ VALUE mod;
+ VALUE sym;
+ VALUE file;
+{
+ ID id = rb_to_id(sym);
+
+ Check_SafeStr(file);
+ rb_autoload(mod, id, RSTRING(file)->ptr);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * mod.autoload?(name) => String or nil
+ *
+ * Returns _filename_ to be loaded if _name_ is registered as
+ * +autoload+ in the namespace of _mod_.
+ *
+ * module A
+ * end
+ * A.autoload(:B, "b")
+ * A.autoload?(:B) # => "b"
+ */
+
+static VALUE
+rb_mod_autoload_p(mod, sym)
+ VALUE mod, sym;
+{
+ return rb_autoload_p(mod, rb_to_id(sym));
+}
+
+/*
+ * call-seq:
+ * autoload(module, filename) => nil
+ *
+ * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ * the first time that _module_ (which may be a <code>String</code> or
+ * a symbol) is accessed.
+ *
+ * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
+ */
+
+static VALUE
+rb_f_autoload(obj, sym, file)
+ VALUE obj;
+ VALUE sym;
+ VALUE file;
+{
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no class/module for autoload target");
+ }
+ return rb_mod_autoload(ruby_cbase, sym, file);
+}
+
+/*
+ * call-seq:
+ * autoload(module, filename) => nil
+ *
+ * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ * the first time that _module_ (which may be a <code>String</code> or
+ * a symbol) is accessed.
+ *
+ * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
+ */
+
+static VALUE
+rb_f_autoload_p(obj, sym)
+ VALUE obj;
+ VALUE sym;
+{
+ /* use ruby_cbase as same as rb_f_autoload. */
+ if (NIL_P(ruby_cbase)) {
+ return Qfalse;
+ }
+ return rb_mod_autoload_p(ruby_cbase, sym);
+}
+
+void
+Init_load()
+{
+ rb_define_readonly_variable("$:", &rb_load_path);
+ rb_define_readonly_variable("$-I", &rb_load_path);
+ rb_define_readonly_variable("$LOAD_PATH", &rb_load_path);
+ rb_load_path = rb_ary_new();
+
+ rb_define_readonly_variable("$\"", &rb_features);
+ rb_define_readonly_variable("$LOADED_FEATURES", &rb_features);
+ rb_features = rb_ary_new();
+
+ rb_define_global_function("load", rb_f_load, -1);
+ rb_define_global_function("require", rb_f_require, 1);
+ rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
+ rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+ rb_define_global_function("autoload", rb_f_autoload, 2);
+ rb_define_global_function("autoload?", rb_f_autoload_p, 1);
+ rb_global_variable(&ruby_wrapper);
+
+ rb_global_variable(&ruby_dln_librefs);
+ ruby_dln_librefs = rb_ary_new();
+}
+
+static void
+scope_dup(scope)
+ struct SCOPE *scope;
+{
+ ID *tbl;
+ VALUE *vars;
+
+ scope->flags |= SCOPE_DONT_RECYCLE;
+ if (scope->flags & SCOPE_MALLOC) return;
+
+ if (scope->local_tbl) {
+ tbl = scope->local_tbl;
+ vars = ALLOC_N(VALUE, tbl[0]+1);
+ *vars++ = scope->local_vars[-1];
+ MEMCPY(vars, scope->local_vars, VALUE, tbl[0]);
+ scope->local_vars = vars;
+ scope->flags |= SCOPE_MALLOC;
+ }
+}
+
+static void
+blk_mark(data)
+ struct BLOCK *data;
+{
+ while (data) {
+ rb_gc_mark_frame(&data->frame);
+ rb_gc_mark((VALUE)data->scope);
+ rb_gc_mark((VALUE)data->var);
+ rb_gc_mark((VALUE)data->body);
+ rb_gc_mark((VALUE)data->self);
+ rb_gc_mark((VALUE)data->dyna_vars);
+ rb_gc_mark((VALUE)data->cref);
+ rb_gc_mark(data->wrapper);
+ rb_gc_mark(data->block_obj);
+ data = data->prev;
+ }
+}
+
+static void
+frame_free(frame)
+ struct FRAME *frame;
+{
+ struct FRAME *tmp;
+
+ frame = frame->prev;
+ while (frame) {
+ tmp = frame;
+ frame = frame->prev;
+ free(tmp);
+ }
+}
+
+static void
+blk_free(data)
+ struct BLOCK *data;
+{
+ void *tmp;
+
+ while (data) {
+ frame_free(&data->frame);
+ tmp = data;
+ data = data->prev;
+ free(tmp);
+ }
+}
+
+static void
+frame_dup(frame)
+ struct FRAME *frame;
+{
+ struct FRAME *tmp;
+
+ for (;;) {
+ frame->tmp = 0; /* should not preserve tmp */
+ if (!frame->prev) break;
+ tmp = ALLOC(struct FRAME);
+ *tmp = *frame->prev;
+ frame->prev = tmp;
+ frame = tmp;
+ }
+}
+
+
+static void
+blk_copy_prev(block)
+ struct BLOCK *block;
+{
+ struct BLOCK *tmp;
+ struct RVarmap* vars;
+
+ while (block->prev) {
+ tmp = ALLOC_N(struct BLOCK, 1);
+ MEMCPY(tmp, block->prev, struct BLOCK, 1);
+ scope_dup(tmp->scope);
+ frame_dup(&tmp->frame);
+
+ for (vars = tmp->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+
+ block->prev = tmp;
+ block = tmp;
+ }
+}
+
+
+static void
+blk_dup(dup, orig)
+ struct BLOCK *dup, *orig;
+{
+ MEMCPY(dup, orig, struct BLOCK, 1);
+ frame_dup(&dup->frame);
+
+ if (dup->iter) {
+ blk_copy_prev(dup);
+ }
+ else {
+ dup->prev = 0;
+ }
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+proc_clone(self)
+ VALUE self;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+
+ Data_Get_Struct(self, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_obj_class(self),struct BLOCK,blk_mark,blk_free,data);
+ CLONESETUP(bind, self);
+ blk_dup(data, orig);
+
+ return bind;
+}
+
+/*
+ * MISSING: documentation
+ */
+
+#define PROC_TSHIFT (FL_USHIFT+1)
+#define PROC_TMASK (FL_USER1|FL_USER2|FL_USER3)
+#define PROC_TMAX (PROC_TMASK >> PROC_TSHIFT)
+
+static int proc_get_safe_level(VALUE);
+
+static VALUE
+proc_dup(self)
+ VALUE self;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+ int safe = proc_get_safe_level(self);
+
+ Data_Get_Struct(self, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_obj_class(self),struct BLOCK,blk_mark,blk_free,data);
+ blk_dup(data, orig);
+ if (safe > PROC_TMAX) safe = PROC_TMAX;
+ FL_SET(bind, (safe << PROC_TSHIFT) & PROC_TMASK);
+
+ return bind;
+}
+
+/*
+ * call-seq:
+ * binding -> a_binding
+ *
+ * Returns a +Binding+ object, describing the variable and
+ * method bindings at the point of call. This object can be used when
+ * calling +eval+ to execute the evaluated command in this
+ * environment. Also see the description of class +Binding+.
+ *
+ * def getBinding(param)
+ * return binding
+ * end
+ * b = getBinding("hello")
+ * eval("param", b) #=> "hello"
+ */
+
+static VALUE
+rb_f_binding(self)
+ VALUE self;
+{
+ struct BLOCK *data, *p;
+ struct RVarmap *vars;
+ VALUE bind;
+
+ PUSH_BLOCK(0,0);
+ bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data);
+ *data = *ruby_block;
+
+ data->orig_thread = rb_thread_current();
+ data->wrapper = ruby_wrapper;
+ data->iter = rb_f_block_given_p();
+ frame_dup(&data->frame);
+ if (ruby_frame->prev) {
+ data->frame.last_func = ruby_frame->prev->last_func;
+ data->frame.last_class = ruby_frame->prev->last_class;
+ data->frame.orig_func = ruby_frame->prev->orig_func;
+ }
+
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ scope_dup(data->scope);
+ POP_BLOCK();
+
+ return bind;
+}
+
+#define SAFE_LEVEL_MAX PROC_TMASK
+
+static void
+proc_save_safe_level(data)
+ VALUE data;
+{
+ int safe = ruby_safe_level;
+ if (safe > PROC_TMAX) safe = PROC_TMAX;
+ FL_SET(data, (safe << PROC_TSHIFT) & PROC_TMASK);
+}
+
+static int
+proc_get_safe_level(data)
+ VALUE data;
+{
+ return (RBASIC(data)->flags & PROC_TMASK) >> PROC_TSHIFT;
+}
+
+static void
+proc_set_safe_level(data)
+ VALUE data;
+{
+ ruby_safe_level = proc_get_safe_level(data);
+}
+
+static VALUE
+proc_alloc(klass, proc)
+ VALUE klass;
+ int proc;
+{
+ volatile VALUE block;
+ struct BLOCK *data, *p;
+ struct RVarmap *vars;
+
+ if (!rb_block_given_p() && !rb_f_block_given_p()) {
+ rb_raise(rb_eArgError, "tried to create Proc object without a block");
+ }
+ if (proc && !rb_block_given_p()) {
+ rb_warn("tried to create Proc object without a block");
+ }
+
+ if (!proc && ruby_block->block_obj && CLASS_OF(ruby_block->block_obj) == klass) {
+ return ruby_block->block_obj;
+ }
+ block = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data);
+ *data = *ruby_block;
+
+ data->orig_thread = rb_thread_current();
+ data->wrapper = ruby_wrapper;
+ data->iter = data->prev?Qtrue:Qfalse;
+ data->block_obj = block;
+ frame_dup(&data->frame);
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ scope_dup(data->scope);
+ proc_save_safe_level(block);
+ if (proc) {
+ data->flags |= BLOCK_LAMBDA;
+ }
+ else {
+ ruby_block->block_obj = block;
+ }
+
+ return block;
+}
+
+/*
+ * call-seq:
+ * Proc.new {|...| block } => a_proc
+ * Proc.new => a_proc
+ *
+ * Creates a new <code>Proc</code> object, bound to the current
+ * context. <code>Proc::new</code> may be called without a block only
+ * within a method with an attached block, in which case that block is
+ * converted to the <code>Proc</code> object.
+ *
+ * def proc_from
+ * Proc.new
+ * end
+ * proc = proc_from { "hello" }
+ * proc.call #=> "hello"
+ */
+
+static VALUE
+proc_s_new(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE block = proc_alloc(klass, Qfalse);
+
+ rb_obj_call_init(block, argc, argv);
+ return block;
+}
+
+VALUE
+rb_block_proc()
+{
+ return proc_alloc(rb_cProc, Qfalse);
+}
+
+VALUE
+rb_f_lambda()
+{
+ rb_warn("rb_f_lambda() is deprecated; use rb_block_proc() instead");
+ return proc_alloc(rb_cProc, Qtrue);
+}
+
+/*
+ * call-seq:
+ * proc { |...| block } => a_proc
+ * lambda { |...| block } => a_proc
+ *
+ * Equivalent to <code>Proc.new</code>, except the resulting Proc objects
+ * check the number of parameters passed when called.
+ */
+
+static VALUE
+proc_lambda()
+{
+ return proc_alloc(rb_cProc, Qtrue);
+}
+
+static int
+block_orphan(data)
+ struct BLOCK *data;
+{
+ if (data->scope->flags & SCOPE_NOSTACK) {
+ return 1;
+ }
+ if (data->orig_thread != rb_thread_current()) {
+ return 1;
+ }
+ return 0;
+}
+
+static VALUE
+proc_invoke(proc, args, self, klass)
+ VALUE proc, args; /* OK */
+ VALUE self, klass;
+{
+ struct BLOCK * volatile old_block;
+ struct BLOCK _block;
+ struct BLOCK *data;
+ volatile VALUE result = Qundef;
+ int state;
+ volatile int safe = ruby_safe_level;
+ volatile VALUE old_wrapper = ruby_wrapper;
+ volatile int pcall, avalue = Qtrue;
+ volatile VALUE tmp = args;
+
+ if (rb_block_given_p() && ruby_frame->last_func) {
+ if (klass != ruby_frame->last_class)
+ klass = rb_obj_class(proc);
+ rb_warning("block for %s#%s is useless",
+ rb_class2name(klass),
+ rb_id2name(ruby_frame->last_func));
+ }
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ pcall = (data->flags & BLOCK_LAMBDA) ? YIELD_LAMBDA_CALL : 0;
+ if (!pcall && RARRAY(args)->len == 1) {
+ avalue = Qfalse;
+ args = RARRAY(args)->ptr[0];
+ }
+
+ PUSH_VARS();
+ ruby_wrapper = data->wrapper;
+ ruby_dyna_vars = data->dyna_vars;
+ /* PUSH BLOCK from data */
+ old_block = ruby_block;
+ _block = *data;
+ if (self != Qundef) _block.frame.self = self;
+ if (klass) _block.frame.last_class = klass;
+ _block.frame.argc = RARRAY(tmp)->len;
+ _block.frame.flags = ruby_frame->flags;
+ if (_block.frame.argc && DMETHOD_P()) {
+ NEWOBJ(scope, struct SCOPE);
+ OBJSETUP(scope, tmp, T_SCOPE);
+ scope->local_tbl = _block.scope->local_tbl;
+ scope->local_vars = _block.scope->local_vars;
+ scope->flags |= SCOPE_CLONE | (_block.scope->flags & SCOPE_MALLOC);
+ _block.scope = scope;
+ }
+ /* modify current frame */
+ ruby_block = &_block;
+ PUSH_ITER(ITER_CUR);
+ ruby_frame->iter = ITER_CUR;
+ PUSH_TAG(pcall ? PROT_LAMBDA : PROT_NONE);
+ state = EXEC_TAG();
+ if (state == 0) {
+ proc_set_safe_level(proc);
+ result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0, pcall, avalue);
+ }
+ else if (TAG_DST()) {
+ result = prot_tag->retval;
+ }
+ POP_TAG();
+ POP_ITER();
+ ruby_block = old_block;
+ ruby_wrapper = old_wrapper;
+ POP_VARS();
+ ruby_safe_level = safe;
+
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETRY:
+ proc_jump_error(TAG_RETRY, Qnil); /* xxx */
+ JUMP_TAG(state);
+ break;
+ case TAG_NEXT:
+ case TAG_BREAK:
+ if (!pcall && result != Qundef) {
+ proc_jump_error(state, result);
+ }
+ case TAG_RETURN:
+ if (result != Qundef) {
+ if (pcall) break;
+ return_jump(result);
+ }
+ default:
+ JUMP_TAG(state);
+ }
+ return result;
+}
+
+/* CHECKME: are the argument checking semantics correct? */
+
+/*
+ * call-seq:
+ * prc.call(params,...) => obj
+ * prc[params,...] => obj
+ *
+ * Invokes the block, setting the block's parameters to the values in
+ * <i>params</i> using something close to method calling semantics.
+ * Generates a warning if multiple values are passed to a proc that
+ * expects just one (previously this silently converted the parameters
+ * to an array).
+ *
+ * For procs created using <code>Kernel.proc</code>, generates an
+ * error if the wrong number of parameters
+ * are passed to a proc with multiple parameters. For procs created using
+ * <code>Proc.new</code>, extra parameters are silently discarded.
+ *
+ * Returns the value of the last expression evaluated in the block. See
+ * also <code>Proc#yield</code>.
+ *
+ * a_proc = Proc.new {|a, *b| b.collect {|i| i*a }}
+ * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
+ * a_proc[9, 1, 2, 3] #=> [9, 18, 27]
+ * a_proc = Proc.new {|a,b| a}
+ * a_proc.call(1,2,3)
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:5: wrong number of arguments (3 for 2) (ArgumentError)
+ * from prog.rb:4:in `call'
+ * from prog.rb:5
+ */
+
+static VALUE
+proc_call(proc, args)
+ VALUE proc, args; /* OK */
+{
+ return proc_invoke(proc, args, Qundef, 0);
+}
+
+static VALUE bmcall _((VALUE, VALUE));
+static VALUE method_arity _((VALUE));
+
+/*
+ * call-seq:
+ * prc.arity -> fixnum
+ *
+ * Returns the number of arguments that would not be ignored. If the block
+ * is declared to take no arguments, returns 0. If the block is known
+ * to take exactly n arguments, returns n. If the block has optional
+ * arguments, return -n-1, where n is the number of mandatory
+ * arguments. A <code>proc</code> with no argument declarations
+ * is the same a block declaring <code>||</code> as its arguments.
+ *
+ * Proc.new {}.arity #=> 0
+ * Proc.new {||}.arity #=> 0
+ * Proc.new {|a|}.arity #=> 1
+ * Proc.new {|a,b|}.arity #=> 2
+ * Proc.new {|a,b,c|}.arity #=> 3
+ * Proc.new {|*a|}.arity #=> -1
+ * Proc.new {|a,*b|}.arity #=> -2
+ */
+
+static VALUE
+proc_arity(proc)
+ VALUE proc;
+{
+ struct BLOCK *data;
+ NODE *list;
+ int n;
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ if (data->var == 0) {
+ if (data->body && nd_type(data->body) == NODE_IFUNC &&
+ data->body->nd_cfnc == bmcall) {
+ return method_arity(data->body->nd_tval);
+ }
+ return INT2FIX(-1);
+ }
+ if (data->var == (NODE*)1) return INT2FIX(0);
+ if (data->var == (NODE*)2) return INT2FIX(0);
+ switch (nd_type(data->var)) {
+ default:
+ return INT2FIX(1);
+ case NODE_MASGN:
+ list = data->var->nd_head;
+ n = 0;
+ while (list) {
+ n++;
+ list = list->nd_next;
+ }
+ if (data->var->nd_args) return INT2FIX(-n-1);
+ return INT2FIX(n);
+ }
+}
+
+/*
+ * call-seq:
+ * prc == other_proc => true or false
+ *
+ * Return <code>true</code> if <i>prc</i> is the same object as
+ * <i>other_proc</i>, or if they are both procs with the same body.
+ */
+
+static VALUE
+proc_eq(self, other)
+ VALUE self, other;
+{
+ struct BLOCK *data, *data2;
+
+ if (self == other) return Qtrue;
+ if (TYPE(other) != T_DATA) return Qfalse;
+ if (RDATA(other)->dmark != (RUBY_DATA_FUNC)blk_mark) return Qfalse;
+ if (CLASS_OF(self) != CLASS_OF(other)) return Qfalse;
+ Data_Get_Struct(self, struct BLOCK, data);
+ Data_Get_Struct(other, struct BLOCK, data2);
+ if (data->body != data2->body) return Qfalse;
+ if (data->var != data2->var) return Qfalse;
+ if (data->scope != data2->scope) return Qfalse;
+ if (data->dyna_vars != data2->dyna_vars) return Qfalse;
+ if (data->flags != data2->flags) return Qfalse;
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * prc.to_s => string
+ *
+ * Shows the unique identifier for this proc, along with
+ * an indication of where the proc was defined.
+ */
+
+static VALUE
+proc_to_s(self)
+ VALUE self;
+{
+ struct BLOCK *data;
+ NODE *node;
+ char *cname = rb_obj_classname(self);
+ const int w = (sizeof(VALUE) * CHAR_BIT) / 4;
+ long len = strlen(cname)+6+w; /* 6:tags 16:addr */
+ VALUE str;
+
+ Data_Get_Struct(self, struct BLOCK, data);
+ if ((node = data->frame.node) || (node = data->body)) {
+ len += strlen(node->nd_file) + 2 + (SIZEOF_LONG*CHAR_BIT-NODE_LSHIFT)/3;
+ str = rb_str_new(0, len);
+ snprintf(RSTRING(str)->ptr, len+1,
+ "#<%s:0x%.*lx@%s:%d>", cname, w, (VALUE)data->body,
+ node->nd_file, nd_line(node));
+ }
+ else {
+ str = rb_str_new(0, len);
+ snprintf(RSTRING(str)->ptr, len+1,
+ "#<%s:0x%.*lx>", cname, w, (VALUE)data->body);
+ }
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ if (OBJ_TAINTED(self)) OBJ_TAINT(str);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * prc.to_proc -> prc
+ *
+ * Part of the protocol for converting objects to <code>Proc</code>
+ * objects. Instances of class <code>Proc</code> simply return
+ * themselves.
+ */
+
+static VALUE
+proc_to_self(self)
+ VALUE self;
+{
+ return self;
+}
+
+/*
+ * call-seq:
+ * prc.binding => binding
+ *
+ * Returns the binding associated with <i>prc</i>. Note that
+ * <code>Kernel#eval</code> accepts either a <code>Proc</code> or a
+ * <code>Binding</code> object as its second parameter.
+ *
+ * def fred(param)
+ * proc {}
+ * end
+ *
+ * b = fred(99)
+ * eval("param", b.binding) #=> 99
+ * eval("param", b) #=> 99
+ */
+
+static VALUE
+proc_binding(proc)
+ VALUE proc;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+
+ Data_Get_Struct(proc, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data);
+ MEMCPY(data, orig, struct BLOCK, 1);
+ frame_dup(&data->frame);
+
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ return bind;
+}
+
+static VALUE
+block_pass(self, node)
+ VALUE self;
+ NODE *node;
+{
+ VALUE proc = rb_eval(self, node->nd_body); /* OK */
+ VALUE b;
+ struct BLOCK * volatile old_block;
+ struct BLOCK _block;
+ struct BLOCK *data;
+ volatile VALUE result = Qnil;
+ int state;
+ volatile int orphan;
+ volatile int safe = ruby_safe_level;
+
+ if (NIL_P(proc)) {
+ PUSH_ITER(ITER_NOT);
+ result = rb_eval(self, node->nd_iter);
+ POP_ITER();
+ return result;
+ }
+ if (!rb_obj_is_proc(proc)) {
+ b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
+ if (!rb_obj_is_proc(b)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc)",
+ rb_obj_classname(proc));
+ }
+ proc = b;
+ }
+
+ if (ruby_safe_level >= 1 && OBJ_TAINTED(proc) &&
+ ruby_safe_level > proc_get_safe_level(proc)) {
+ rb_raise(rb_eSecurityError, "Insecure: tainted block value");
+ }
+
+ if (ruby_block && ruby_block->block_obj == proc) {
+ PUSH_ITER(ITER_PAS);
+ result = rb_eval(self, node->nd_iter);
+ POP_ITER();
+ return result;
+ }
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ orphan = block_orphan(data);
+
+ /* PUSH BLOCK from data */
+ old_block = ruby_block;
+ _block = *data;
+ _block.outer = ruby_block;
+ if (orphan) _block.uniq = block_unique++;
+ ruby_block = &_block;
+ PUSH_ITER(ITER_PRE);
+ if (ruby_frame->iter == ITER_NOT)
+ ruby_frame->iter = ITER_PRE;
+
+ PUSH_TAG(PROT_LOOP);
+ state = EXEC_TAG();
+ if (state == 0) {
+ retry:
+ proc_set_safe_level(proc);
+ if (safe > ruby_safe_level)
+ ruby_safe_level = safe;
+ result = rb_eval(self, node->nd_iter);
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY) {
+ state = 0;
+ goto retry;
+ }
+ POP_TAG();
+ POP_ITER();
+ ruby_block = old_block;
+ ruby_safe_level = safe;
+
+ switch (state) {/* escape from orphan block */
+ case 0:
+ break;
+ case TAG_RETURN:
+ if (orphan) {
+ proc_jump_error(state, prot_tag->retval);
+ }
+ default:
+ JUMP_TAG(state);
+ }
+
+ return result;
+}
+
+struct METHOD {
+ VALUE klass, rklass;
+ VALUE recv;
+ ID id, oid;
+ int safe_level;
+ NODE *body;
+};
+
+static void
+bm_mark(data)
+ struct METHOD *data;
+{
+ rb_gc_mark(data->rklass);
+ rb_gc_mark(data->klass);
+ rb_gc_mark(data->recv);
+ rb_gc_mark((VALUE)data->body);
+}
+
+static VALUE
+mnew(klass, obj, id, mklass)
+ VALUE klass, obj, mklass;
+ ID id;
+{
+ VALUE method;
+ NODE *body;
+ int noex;
+ struct METHOD *data;
+ VALUE rklass = klass;
+ ID oid = id;
+
+ again:
+ if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
+ print_undef(rklass, oid);
+ }
+
+ if (nd_type(body) == NODE_ZSUPER) {
+ klass = RCLASS(klass)->super;
+ goto again;
+ }
+
+ while (rklass != klass &&
+ (FL_TEST(rklass, FL_SINGLETON) || TYPE(rklass) == T_ICLASS)) {
+ rklass = RCLASS(rklass)->super;
+ }
+ method = Data_Make_Struct(mklass, struct METHOD, bm_mark, free, data);
+ data->klass = klass;
+ data->recv = obj;
+ data->id = id;
+ data->body = body;
+ data->rklass = rklass;
+ data->oid = oid;
+ data->safe_level = NOEX_WITH_SAFE(noex);
+ OBJ_INFECT(method, klass);
+
+ return method;
+}
+
+
+/**********************************************************************
+ *
+ * Document-class : Method
+ *
+ * Method objects are created by <code>Object#method</code>, and are
+ * associated with a particular object (not just with a class). They
+ * may be used to invoke the method within the object, and as a block
+ * associated with an iterator. They may also be unbound from one
+ * object (creating an <code>UnboundMethod</code>) and bound to
+ * another.
+ *
+ * class Thing
+ * def square(n)
+ * n*n
+ * end
+ * end
+ * thing = Thing.new
+ * meth = thing.method(:square)
+ *
+ * meth.call(9) #=> 81
+ * [ 1, 2, 3 ].collect(&meth) #=> [1, 4, 9]
+ *
+ */
+
+/*
+ * call-seq:
+ * meth == other_meth => true or false
+ *
+ * Two method objects are equal if that are bound to the same
+ * object and contain the same body.
+ */
+
+
+static VALUE
+method_eq(method, other)
+ VALUE method, other;
+{
+ struct METHOD *m1, *m2;
+
+ if (TYPE(other) != T_DATA || RDATA(other)->dmark != (RUBY_DATA_FUNC)bm_mark)
+ return Qfalse;
+ if (CLASS_OF(method) != CLASS_OF(other))
+ return Qfalse;
+
+ Data_Get_Struct(method, struct METHOD, m1);
+ Data_Get_Struct(other, struct METHOD, m2);
+
+ if (m1->klass != m2->klass || m1->rklass != m2->rklass ||
+ m1->recv != m2->recv || m1->body != m2->body)
+ return Qfalse;
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * meth.unbind => unbound_method
+ *
+ * Dissociates <i>meth</i> from it's current receiver. The resulting
+ * <code>UnboundMethod</code> can subsequently be bound to a new object
+ * of the same class (see <code>UnboundMethod</code>).
+ */
+
+static VALUE
+method_unbind(obj)
+ VALUE obj;
+{
+ VALUE method;
+ struct METHOD *orig, *data;
+
+ Data_Get_Struct(obj, struct METHOD, orig);
+ method = Data_Make_Struct(rb_cUnboundMethod, struct METHOD, bm_mark, free, data);
+ data->klass = orig->klass;
+ data->recv = Qundef;
+ data->id = orig->id;
+ data->body = orig->body;
+ data->rklass = orig->rklass;
+ data->oid = orig->oid;
+ OBJ_INFECT(method, obj);
+
+ return method;
+}
+
+/*
+ * call-seq:
+ * obj.method(sym) => method
+ *
+ * Looks up the named method as a receiver in <i>obj</i>, returning a
+ * <code>Method</code> object (or raising <code>NameError</code>). The
+ * <code>Method</code> object acts as a closure in <i>obj</i>'s object
+ * instance, so instance variables and the value of <code>self</code>
+ * remain available.
+ *
+ * class Demo
+ * def initialize(n)
+ * @iv = n
+ * end
+ * def hello()
+ * "Hello, @iv = #{@iv}"
+ * end
+ * end
+ *
+ * k = Demo.new(99)
+ * m = k.method(:hello)
+ * m.call #=> "Hello, @iv = 99"
+ *
+ * l = Demo.new('Fred')
+ * m = l.method("hello")
+ * m.call #=> "Hello, @iv = Fred"
+ */
+
+static VALUE
+rb_obj_method(obj, vid)
+ VALUE obj;
+ VALUE vid;
+{
+ return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod);
+}
+
+/*
+ * call-seq:
+ * mod.instance_method(symbol) => unbound_method
+ *
+ * Returns an +UnboundMethod+ representing the given
+ * instance method in _mod_.
+ *
+ * class Interpreter
+ * def do_a() print "there, "; end
+ * def do_d() print "Hello "; end
+ * def do_e() print "!\n"; end
+ * def do_v() print "Dave"; end
+ * Dispatcher = {
+ * ?a => instance_method(:do_a),
+ * ?d => instance_method(:do_d),
+ * ?e => instance_method(:do_e),
+ * ?v => instance_method(:do_v)
+ * }
+ * def interpret(string)
+ * string.each_byte {|b| Dispatcher[b].bind(self).call }
+ * end
+ * end
+ *
+ *
+ * interpreter = Interpreter.new
+ * interpreter.interpret('dave')
+ *
+ * <em>produces:</em>
+ *
+ * Hello there, Dave!
+ */
+
+static VALUE
+rb_mod_method(mod, vid)
+ VALUE mod;
+ VALUE vid;
+{
+ return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod);
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+method_clone(self)
+ VALUE self;
+{
+ VALUE clone;
+ struct METHOD *orig, *data;
+
+ Data_Get_Struct(self, struct METHOD, orig);
+ clone = Data_Make_Struct(CLASS_OF(self),struct METHOD, bm_mark, free, data);
+ CLONESETUP(clone, self);
+ *data = *orig;
+
+ return clone;
+}
+
+/*
+ * call-seq:
+ * meth.call(args, ...) => obj
+ * meth[args, ...] => obj
+ *
+ * Invokes the <i>meth</i> with the specified arguments, returning the
+ * method's return value.
+ *
+ * m = 12.method("+")
+ * m.call(3) #=> 15
+ * m.call(20) #=> 32
+ */
+
+static VALUE
+method_call(argc, argv, method)
+ int argc;
+ VALUE *argv;
+ VALUE method;
+{
+ VALUE result = Qnil; /* OK */
+ struct METHOD *data;
+ int safe;
+
+ Data_Get_Struct(method, struct METHOD, data);
+ if (data->recv == Qundef) {
+ rb_raise(rb_eTypeError, "can't call unbound method; bind first");
+ }
+ if (OBJ_TAINTED(method)) {
+ safe = NOEX_WITH(data->safe_level, 4)|NOEX_TAINTED;
+ }
+ else {
+ safe = data->safe_level;
+ }
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ result = rb_call0(data->klass,data->recv,data->id,data->oid,argc,argv,data->body,safe);
+ POP_ITER();
+ return result;
+}
+
+/**********************************************************************
+ *
+ * Document-class: UnboundMethod
+ *
+ * Ruby supports two forms of objectified methods. Class
+ * <code>Method</code> is used to represent methods that are associated
+ * with a particular object: these method objects are bound to that
+ * object. Bound method objects for an object can be created using
+ * <code>Object#method</code>.
+ *
+ * Ruby also supports unbound methods; methods objects that are not
+ * associated with a particular object. These can be created either by
+ * calling <code>Module#instance_method</code> or by calling
+ * <code>unbind</code> on a bound method object. The result of both of
+ * these is an <code>UnboundMethod</code> object.
+ *
+ * Unbound methods can only be called after they are bound to an
+ * object. That object must be be a kind_of? the method's original
+ * class.
+ *
+ * class Square
+ * def area
+ * @side * @side
+ * end
+ * def initialize(side)
+ * @side = side
+ * end
+ * end
+ *
+ * area_un = Square.instance_method(:area)
+ *
+ * s = Square.new(12)
+ * area = area_un.bind(s)
+ * area.call #=> 144
+ *
+ * Unbound methods are a reference to the method at the time it was
+ * objectified: subsequent changes to the underlying class will not
+ * affect the unbound method.
+ *
+ * class Test
+ * def test
+ * :original
+ * end
+ * end
+ * um = Test.instance_method(:test)
+ * class Test
+ * def test
+ * :modified
+ * end
+ * end
+ * t = Test.new
+ * t.test #=> :modified
+ * um.bind(t).call #=> :original
+ *
+ */
+
+/*
+ * call-seq:
+ * umeth.bind(obj) -> method
+ *
+ * Bind <i>umeth</i> to <i>obj</i>. If <code>Klass</code> was the class
+ * from which <i>umeth</i> was obtained,
+ * <code>obj.kind_of?(Klass)</code> must be true.
+ *
+ * class A
+ * def test
+ * puts "In test, class = #{self.class}"
+ * end
+ * end
+ * class B < A
+ * end
+ * class C < B
+ * end
+ *
+ *
+ * um = B.instance_method(:test)
+ * bm = um.bind(C.new)
+ * bm.call
+ * bm = um.bind(B.new)
+ * bm.call
+ * bm = um.bind(A.new)
+ * bm.call
+ *
+ * <em>produces:</em>
+ *
+ * In test, class = C
+ * In test, class = B
+ * prog.rb:16:in `bind': bind argument must be an instance of B (TypeError)
+ * from prog.rb:16
+ */
+
+static VALUE
+umethod_bind(method, recv)
+ VALUE method, recv;
+{
+ struct METHOD *data, *bound;
+ VALUE rklass = CLASS_OF(recv);
+
+ Data_Get_Struct(method, struct METHOD, data);
+ if (data->rklass != rklass) {
+ if (FL_TEST(data->rklass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton method bound for a different object");
+ }
+ if (TYPE(data->rklass) == T_MODULE) {
+ st_table *m_tbl = RCLASS(data->rklass)->m_tbl;
+ while (RCLASS(rklass)->m_tbl != m_tbl) {
+ rklass = RCLASS(rklass)->super;
+ if (!rklass) goto not_instace;
+ }
+ }
+ else if (!rb_obj_is_kind_of(recv, data->rklass)) {
+ not_instace:
+ rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
+ rb_class2name(data->rklass));
+ }
+ }
+
+ method = Data_Make_Struct(rb_cMethod,struct METHOD,bm_mark,free,bound);
+ *bound = *data;
+ bound->recv = recv;
+ bound->rklass = rklass;
+
+ return method;
+}
+
+/*
+ * call-seq:
+ * meth.arity => fixnum
+ *
+ * Returns an indication of the number of arguments accepted by a
+ * method. Returns a nonnegative integer for methods that take a fixed
+ * number of arguments. For Ruby methods that take a variable number of
+ * arguments, returns -n-1, where n is the number of required
+ * arguments. For methods written in C, returns -1 if the call takes a
+ * variable number of arguments.
+ *
+ * class C
+ * def one; end
+ * def two(a); end
+ * def three(*a); end
+ * def four(a, b); end
+ * def five(a, b, *c); end
+ * def six(a, b, *c, &d); end
+ * end
+ * c = C.new
+ * c.method(:one).arity #=> 0
+ * c.method(:two).arity #=> 1
+ * c.method(:three).arity #=> -1
+ * c.method(:four).arity #=> 2
+ * c.method(:five).arity #=> -3
+ * c.method(:six).arity #=> -3
+ *
+ * "cat".method(:size).arity #=> 0
+ * "cat".method(:replace).arity #=> 1
+ * "cat".method(:squeeze).arity #=> -1
+ * "cat".method(:count).arity #=> -1
+ */
+
+static VALUE
+method_arity(method)
+ VALUE method;
+{
+ struct METHOD *data;
+ NODE *body;
+ int n;
+
+ Data_Get_Struct(method, struct METHOD, data);
+
+ body = data->body;
+ switch (nd_type(body)) {
+ case NODE_CFUNC:
+ if (body->nd_argc < 0) return INT2FIX(-1);
+ return INT2FIX(body->nd_argc);
+ case NODE_ZSUPER:
+ return INT2FIX(-1);
+ case NODE_ATTRSET:
+ return INT2FIX(1);
+ case NODE_IVAR:
+ return INT2FIX(0);
+ case NODE_BMETHOD:
+ return proc_arity(body->nd_cval);
+ case NODE_DMETHOD:
+ return method_arity(body->nd_cval);
+ case NODE_SCOPE:
+ body = body->nd_next; /* skip NODE_SCOPE */
+ if (nd_type(body) == NODE_BLOCK)
+ body = body->nd_head;
+ if (!body) return INT2FIX(0);
+ n = body->nd_cnt;
+ if (body->nd_opt || body->nd_rest)
+ n = -n-1;
+ return INT2FIX(n);
+ default:
+ rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body));
+ }
+}
+
+/*
+ * call-seq:
+ * meth.to_s => string
+ * meth.inspect => string
+ *
+ * Show the name of the underlying method.
+ *
+ * "cat".method(:count).inspect #=> "#<Method: String#count>"
+ */
+
+static VALUE
+method_inspect(method)
+ VALUE method;
+{
+ struct METHOD *data;
+ VALUE str;
+ const char *s;
+ char *sharp = "#";
+
+ Data_Get_Struct(method, struct METHOD, data);
+ str = rb_str_buf_new2("#<");
+ s = rb_obj_classname(method);
+ rb_str_buf_cat2(str, s);
+ rb_str_buf_cat2(str, ": ");
+
+ if (FL_TEST(data->klass, FL_SINGLETON)) {
+ VALUE v = rb_iv_get(data->klass, "__attached__");
+
+ if (data->recv == Qundef) {
+ rb_str_buf_append(str, rb_inspect(data->klass));
+ }
+ else if (data->recv == v) {
+ rb_str_buf_append(str, rb_inspect(v));
+ sharp = ".";
+ }
+ else {
+ rb_str_buf_append(str, rb_inspect(data->recv));
+ rb_str_buf_cat2(str, "(");
+ rb_str_buf_append(str, rb_inspect(v));
+ rb_str_buf_cat2(str, ")");
+ sharp = ".";
+ }
+ }
+ else {
+ rb_str_buf_cat2(str, rb_class2name(data->rklass));
+ if (data->rklass != data->klass) {
+ VALUE klass = data -> klass;
+ if (TYPE(klass) == T_ICLASS) {
+ klass = RBASIC(klass)->klass;
+ }
+ rb_str_buf_cat2(str, "(");
+ rb_str_buf_cat2(str, rb_class2name(klass));
+ rb_str_buf_cat2(str, ")");
+ }
+ }
+ rb_str_buf_cat2(str, sharp);
+ rb_str_buf_cat2(str, rb_id2name(data->oid));
+ rb_str_buf_cat2(str, ">");
+
+ return str;
+}
+
+static VALUE
+mproc(method)
+ VALUE method;
+{
+ VALUE proc;
+
+ /* emulate ruby's method call */
+ PUSH_ITER(ITER_CUR);
+ PUSH_FRAME();
+ proc = rb_block_proc();
+ POP_FRAME();
+ POP_ITER();
+
+ return proc;
+}
+
+static VALUE
+bmcall(args, method)
+ VALUE args, method;
+{
+ volatile VALUE a;
+ VALUE ret;
+
+ a = svalue_to_avalue(args);
+ ret = method_call(RARRAY(a)->len, RARRAY(a)->ptr, method);
+ a = Qnil; /* prevent tail call */
+ return ret;
+}
+
+VALUE
+rb_proc_new(func, val)
+ VALUE (*func)(ANYARGS); /* VALUE yieldarg[, VALUE procarg] */
+ VALUE val;
+{
+ struct BLOCK *data;
+ VALUE proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, func, val);
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ data->body->nd_state = YIELD_FUNC_AVALUE;
+ return proc;
+}
+
+/*
+ * call-seq:
+ * meth.to_proc => prc
+ *
+ * Returns a <code>Proc</code> object corresponding to this method.
+ */
+
+static VALUE
+method_proc(method)
+ VALUE method;
+{
+ VALUE proc;
+ struct METHOD *mdata;
+ struct BLOCK *bdata;
+
+ proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, bmcall, method);
+ Data_Get_Struct(method, struct METHOD, mdata);
+ Data_Get_Struct(proc, struct BLOCK, bdata);
+ bdata->body->nd_file = mdata->body->nd_file;
+ nd_set_line(bdata->body, nd_line(mdata->body));
+ bdata->body->nd_state = YIELD_FUNC_SVALUE;
+
+ return proc;
+}
+
+static VALUE
+rb_obj_is_method(m)
+ VALUE m;
+{
+ if (TYPE(m) == T_DATA && RDATA(m)->dmark == (RUBY_DATA_FUNC)bm_mark) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * define_method(symbol, method) => new_method
+ * define_method(symbol) { block } => proc
+ *
+ * Defines an instance method in the receiver. The _method_
+ * parameter can be a +Proc+ or +Method+ object.
+ * If a block is specified, it is used as the method body. This block
+ * is evaluated using <code>instance_eval</code>, a point that is
+ * tricky to demonstrate because <code>define_method</code> is private.
+ * (This is why we resort to the +send+ hack in this example.)
+ *
+ * class A
+ * def fred
+ * puts "In Fred"
+ * end
+ * def create_method(name, &block)
+ * self.class.send(:define_method, name, &block)
+ * end
+ * define_method(:wilma) { puts "Charge it!" }
+ * end
+ * class B < A
+ * define_method(:barney, instance_method(:fred))
+ * end
+ * a = B.new
+ * a.barney
+ * a.wilma
+ * a.create_method(:betty) { p self }
+ * a.betty
+ *
+ * <em>produces:</em>
+ *
+ * In Fred
+ * Charge it!
+ * #<B:0x401b39e8>
+ */
+
+static VALUE
+rb_mod_define_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ ID id;
+ VALUE body;
+ NODE *node;
+ int noex;
+
+ if (argc == 1) {
+ id = rb_to_id(argv[0]);
+ body = proc_lambda();
+ }
+ else if (argc == 2) {
+ id = rb_to_id(argv[0]);
+ body = argv[1];
+ if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Method)",
+ rb_obj_classname(body));
+ }
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ }
+ if (RDATA(body)->dmark == (RUBY_DATA_FUNC)bm_mark) {
+ node = NEW_DMETHOD(method_unbind(body));
+ }
+ else if (RDATA(body)->dmark == (RUBY_DATA_FUNC)blk_mark) {
+ struct BLOCK *block;
+
+ body = proc_clone(body);
+ Data_Get_Struct(body, struct BLOCK, block);
+ block->frame.last_func = id;
+ block->frame.orig_func = id;
+ block->frame.last_class = mod;
+ node = NEW_BMETHOD(body);
+ }
+ else {
+ /* type error */
+ rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)");
+ }
+
+ noex = NOEX_PUBLIC;
+ if (ruby_cbase == mod) {
+ if (SCOPE_TEST(SCOPE_PRIVATE)) {
+ noex = NOEX_PRIVATE;
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ }
+ rb_add_method(mod, id, node, noex);
+ return body;
+}
+
+/*
+ * <code>Proc</code> objects are blocks of code that have been bound to
+ * a set of local variables. Once bound, the code may be called in
+ * different contexts and still access those variables.
+ *
+ * def gen_times(factor)
+ * return Proc.new {|n| n*factor }
+ * end
+ *
+ * times3 = gen_times(3)
+ * times5 = gen_times(5)
+ *
+ * times3.call(12) #=> 36
+ * times5.call(5) #=> 25
+ * times3.call(times5.call(4)) #=> 60
+ *
+ */
+
+void
+Init_Proc()
+{
+ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
+ rb_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0);
+ rb_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0);
+
+ rb_global_variable(&exception_error);
+ exception_error = rb_exc_new3(rb_eFatal,
+ rb_obj_freeze(rb_str_new2("exception reentered")));
+ OBJ_TAINT(exception_error);
+ OBJ_FREEZE(exception_error);
+
+ rb_eSysStackError = rb_define_class("SystemStackError", rb_eStandardError);
+ rb_global_variable(&sysstack_error);
+ sysstack_error = rb_exc_new3(rb_eSysStackError,
+ rb_obj_freeze(rb_str_new2("stack level too deep")));
+ OBJ_TAINT(sysstack_error);
+ OBJ_FREEZE(sysstack_error);
+
+ rb_cProc = rb_define_class("Proc", rb_cObject);
+ rb_undef_alloc_func(rb_cProc);
+ rb_define_singleton_method(rb_cProc, "new", proc_s_new, -1);
+
+ rb_define_method(rb_cProc, "clone", proc_clone, 0);
+ rb_define_method(rb_cProc, "dup", proc_dup, 0);
+ rb_define_method(rb_cProc, "call", proc_call, -2);
+ rb_define_method(rb_cProc, "arity", proc_arity, 0);
+ rb_define_method(rb_cProc, "[]", proc_call, -2);
+ rb_define_method(rb_cProc, "==", proc_eq, 1);
+ rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
+ rb_define_method(rb_cProc, "to_proc", proc_to_self, 0);
+ rb_define_method(rb_cProc, "binding", proc_binding, 0);
+
+ rb_define_global_function("proc", proc_lambda, 0);
+ rb_define_global_function("lambda", proc_lambda, 0);
+
+ rb_cMethod = rb_define_class("Method", rb_cObject);
+ rb_undef_alloc_func(rb_cMethod);
+ rb_undef_method(CLASS_OF(rb_cMethod), "new");
+ rb_define_method(rb_cMethod, "==", method_eq, 1);
+ rb_define_method(rb_cMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cMethod, "call", method_call, -1);
+ rb_define_method(rb_cMethod, "[]", method_call, -1);
+ rb_define_method(rb_cMethod, "arity", method_arity, 0);
+ rb_define_method(rb_cMethod, "inspect", method_inspect, 0);
+ rb_define_method(rb_cMethod, "to_s", method_inspect, 0);
+ rb_define_method(rb_cMethod, "to_proc", method_proc, 0);
+ rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
+ rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
+
+ rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
+ rb_undef_alloc_func(rb_cUnboundMethod);
+ rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
+ rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
+ 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, "bind", umethod_bind, 1);
+ rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1);
+}
+
+/*
+ * Objects of class <code>Binding</code> encapsulate the execution
+ * context at some particular place in the code and retain this context
+ * for future use. The variables, methods, value of <code>self</code>,
+ * and possibly an iterator block that can be accessed in this context
+ * are all retained. Binding objects can be created using
+ * <code>Kernel#binding</code>, and are made available to the callback
+ * of <code>Kernel#set_trace_func</code>.
+ *
+ * These binding objects can be passed as the second argument of the
+ * <code>Kernel#eval</code> method, establishing an environment for the
+ * evaluation.
+ *
+ * class Demo
+ * def initialize(n)
+ * @secret = n
+ * end
+ * def getBinding
+ * return binding()
+ * end
+ * end
+ *
+ * k1 = Demo.new(99)
+ * b1 = k1.getBinding
+ * k2 = Demo.new(-3)
+ * b2 = k2.getBinding
+ *
+ * eval("@secret", b1) #=> 99
+ * eval("@secret", b2) #=> -3
+ * eval("@secret") #=> nil
+ *
+ * Binding objects have no class-specific methods.
+ *
+ */
+
+void
+Init_Binding()
+{
+ rb_cBinding = rb_define_class("Binding", rb_cObject);
+ rb_undef_alloc_func(rb_cBinding);
+ rb_undef_method(CLASS_OF(rb_cBinding), "new");
+ rb_define_method(rb_cBinding, "clone", proc_clone, 0);
+ rb_define_method(rb_cBinding, "dup", proc_dup, 0);
+ rb_define_global_function("binding", rb_f_binding, 0);
+}
+
+/* Windows SEH refers data on the stack. */
+#undef SAVE_WIN32_EXCEPTION_LIST
+#if defined _WIN32 || defined __CYGWIN__
+#if defined __CYGWIN__
+typedef unsigned long DWORD;
+#endif
+
+static inline DWORD
+win32_get_exception_list()
+{
+ DWORD p;
+# if defined _MSC_VER
+# ifdef _M_IX86
+# define SAVE_WIN32_EXCEPTION_LIST
+# if _MSC_VER >= 1310
+ /* warning: unsafe assignment to fs:0 ... this is ok */
+# pragma warning(disable: 4733)
+# endif
+ __asm mov eax, fs:[0];
+ __asm mov p, eax;
+# endif
+# elif defined __GNUC__
+# ifdef __i386__
+# define SAVE_WIN32_EXCEPTION_LIST
+ __asm__("movl %%fs:0,%0" : "=r"(p));
+# endif
+# elif defined __BORLANDC__
+# define SAVE_WIN32_EXCEPTION_LIST
+ __emit__(0x64, 0xA1, 0, 0, 0, 0); /* mov eax, fs:[0] */
+ p = _EAX;
+# endif
+ return p;
+}
+
+static inline void
+win32_set_exception_list(p)
+ DWORD p;
+{
+# if defined _MSC_VER
+# ifdef _M_IX86
+ __asm mov eax, p;
+ __asm mov fs:[0], eax;
+# endif
+# elif defined __GNUC__
+# ifdef __i386__
+ __asm__("movl %0,%%fs:0" :: "r"(p));
+# endif
+# elif defined __BORLANDC__
+ _EAX = p;
+ __emit__(0x64, 0xA3, 0, 0, 0, 0); /* mov fs:[0], eax */
+# endif
+}
+
+#if !defined SAVE_WIN32_EXCEPTION_LIST && !defined _WIN32_WCE
+# error unsupported platform
+#endif
+#endif
+
+int rb_thread_pending = 0;
+
+VALUE rb_cThread;
+
+extern VALUE rb_last_status;
+
+#define WAIT_FD (1<<0)
+#define WAIT_SELECT (1<<1)
+#define WAIT_TIME (1<<2)
+#define WAIT_JOIN (1<<3)
+#define WAIT_PID (1<<4)
+
+/* +infty, for this purpose */
+#define DELAY_INFTY 1E30
+
+#if !defined HAVE_PAUSE
+# if defined _WIN32 && !defined __CYGWIN__
+# define pause() Sleep(INFINITE)
+# else
+# define pause() sleep(0x7fffffff)
+# endif
+#endif
+
+#define THREAD_TERMINATING 0x400 /* persistent flag */
+#define THREAD_NO_ENSURE 0x800 /* persistent flag */
+#define THREAD_FLAGS_MASK 0xfc00 /* mask for persistent flags */
+
+#define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next;
+#define END_FOREACH_FROM(f,x) } while (x != f)
+
+#define FOREACH_THREAD(x) FOREACH_THREAD_FROM(curr_thread,x)
+#define END_FOREACH(x) END_FOREACH_FROM(curr_thread,x)
+
+struct thread_status_t {
+ NODE *node;
+
+ int tracing;
+ VALUE errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ int safe;
+
+ enum rb_thread_status status;
+ int wait_for;
+ int fd;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ int select_value;
+ double delay;
+ rb_thread_t join;
+};
+
+#define THREAD_COPY_STATUS(src, dst) (void)( \
+ (dst)->node = (src)->node, \
+ \
+ (dst)->tracing = (src)->tracing, \
+ (dst)->errinfo = (src)->errinfo, \
+ (dst)->last_status = (src)->last_status, \
+ (dst)->last_line = (src)->last_line, \
+ (dst)->last_match = (src)->last_match, \
+ \
+ (dst)->safe = (src)->safe, \
+ \
+ (dst)->status = (src)->status, \
+ (dst)->wait_for = (src)->wait_for, \
+ (dst)->fd = (src)->fd, \
+ (dst)->readfds = (src)->readfds, \
+ (dst)->writefds = (src)->writefds, \
+ (dst)->exceptfds = (src)->exceptfds, \
+ (dst)->select_value = (src)->select_value, \
+ (dst)->delay = (src)->delay, \
+ (dst)->join = (src)->join, \
+ 0)
+
+int
+rb_thread_set_raised(th)
+ rb_thread_t th;
+{
+ if (th->flags & RAISED_EXCEPTION) {
+ return 1;
+ }
+ th->flags |= RAISED_EXCEPTION;
+ return 0;
+}
+
+int
+rb_thread_reset_raised(th)
+ rb_thread_t th;
+{
+ if (!(th->flags & RAISED_EXCEPTION)) {
+ return 0;
+ }
+ th->flags &= ~RAISED_EXCEPTION;
+ return 1;
+}
+
+static int
+thread_no_ensure()
+{
+ return ((curr_thread->flags & THREAD_NO_ENSURE) == THREAD_NO_ENSURE);
+}
+
+static void rb_thread_ready _((rb_thread_t));
+
+static VALUE run_trap_eval _((VALUE));
+static VALUE
+run_trap_eval(arg)
+ VALUE arg;
+{
+ VALUE *p = (VALUE *)arg;
+ return rb_eval_cmd(p[0], p[1], (int)p[2]);
+}
+
+static VALUE
+rb_trap_eval(cmd, sig, safe)
+ VALUE cmd;
+ int sig, safe;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+ volatile struct thread_status_t save;
+ VALUE arg[3];
+
+ arg[0] = cmd;
+ arg[1] = rb_ary_new3(1, INT2FIX(sig));
+ arg[2] = (VALUE)safe;
+ THREAD_COPY_STATUS(curr_thread, &save);
+ rb_thread_ready(curr_thread);
+ PUSH_ITER(ITER_NOT);
+ val = rb_protect(run_trap_eval, (VALUE)&arg, &state);
+ POP_ITER();
+ THREAD_COPY_STATUS(&save, curr_thread);
+
+ if (state) {
+ rb_trap_immediate = 0;
+ rb_thread_ready(curr_thread);
+ JUMP_TAG(state);
+ }
+
+ if (curr_thread->status == THREAD_STOPPED) {
+ rb_thread_schedule();
+ }
+ errno = EINTR;
+
+ return val;
+}
+
+static const char *
+thread_status_name(status)
+ enum rb_thread_status status;
+{
+ switch (status) {
+ case THREAD_RUNNABLE:
+ return "run";
+ case THREAD_STOPPED:
+ return "sleep";
+ case THREAD_TO_KILL:
+ return "aborting";
+ case THREAD_KILLED:
+ return "dead";
+ default:
+ return "unknown";
+ }
+}
+
+/* $SAFE accessor */
+void
+rb_set_safe_level(level)
+ int level;
+{
+ if (level > ruby_safe_level) {
+ if (level > SAFE_LEVEL_MAX) level = SAFE_LEVEL_MAX;
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+ }
+}
+
+static VALUE
+safe_getter()
+{
+ return INT2NUM(ruby_safe_level);
+}
+
+static void
+safe_setter(val)
+ VALUE val;
+{
+ int level = NUM2INT(val);
+
+ if (level < ruby_safe_level) {
+ rb_raise(rb_eSecurityError, "tried to downgrade safe level from %d to %d",
+ ruby_safe_level, level);
+ }
+ if (level > SAFE_LEVEL_MAX) level = SAFE_LEVEL_MAX;
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+}
+
+/* Return the current time as a floating-point number */
+static double
+timeofday()
+{
+ struct timeval tv;
+#ifdef CLOCK_MONOTONIC
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+ return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9;
+ }
+#endif
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+}
+
+#define STACK(addr) (th->stk_pos<(VALUE*)(addr) && (VALUE*)(addr)<th->stk_pos+th->stk_len)
+#define ADJ(addr) (void*)(STACK(addr)?(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr):(VALUE*)(addr))
+static void
+thread_mark(th)
+ rb_thread_t th;
+{
+ struct FRAME *frame;
+ struct BLOCK *block;
+
+ rb_gc_mark(th->result);
+ rb_gc_mark(th->thread);
+ if (th->join) rb_gc_mark(th->join->thread);
+
+ rb_gc_mark(th->klass);
+ rb_gc_mark(th->wrapper);
+ rb_gc_mark((VALUE)th->cref);
+
+ rb_gc_mark((VALUE)th->scope);
+ rb_gc_mark((VALUE)th->dyna_vars);
+ rb_gc_mark(th->errinfo);
+ rb_gc_mark(th->last_status);
+ rb_gc_mark(th->last_line);
+ rb_gc_mark(th->last_match);
+ rb_mark_tbl(th->locals);
+ rb_gc_mark(th->thgroup);
+ rb_gc_mark_maybe(th->sandbox);
+
+ /* mark data in copied stack */
+ if (th == curr_thread) return;
+ if (th->status == THREAD_KILLED) return;
+ if (th->stk_len == 0) return; /* stack not active, no need to mark. */
+ if (th->stk_ptr) {
+ rb_gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len);
+#if defined(THINK_C) || defined(__human68k__)
+ rb_gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2);
+#endif
+#ifdef __ia64
+ if (th->bstr_ptr) {
+ rb_gc_mark_locations(th->bstr_ptr, th->bstr_ptr+th->bstr_len);
+ }
+#endif
+ }
+ frame = th->frame;
+ while (frame && frame != top_frame) {
+ frame = ADJ(frame);
+ rb_gc_mark_frame(frame);
+ if (frame->tmp) {
+ struct FRAME *tmp = frame->tmp;
+
+ while (tmp && tmp != top_frame) {
+ tmp = ADJ(tmp);
+ rb_gc_mark_frame(tmp);
+ tmp = tmp->prev;
+ }
+ }
+ frame = frame->prev;
+ }
+ block = th->block;
+ while (block) {
+ block = ADJ(block);
+ rb_gc_mark_frame(&block->frame);
+ block = block->prev;
+ }
+}
+
+static int
+mark_loading_thread(key, value, lev)
+ ID key;
+ VALUE value;
+ int lev;
+{
+ rb_gc_mark(((rb_thread_t)value)->thread);
+ return ST_CONTINUE;
+}
+
+void
+rb_gc_mark_threads()
+{
+ rb_thread_t th;
+
+ /* static global mark */
+ rb_gc_mark((VALUE)ruby_cref);
+
+ if (!curr_thread) return;
+ rb_gc_mark(main_thread->thread);
+ rb_gc_mark(curr_thread->thread);
+ FOREACH_THREAD_FROM(main_thread, th) {
+ switch (th->status) {
+ case THREAD_TO_KILL:
+ case THREAD_RUNNABLE:
+ break;
+ case THREAD_STOPPED:
+ if (th->wait_for) break;
+ default:
+ continue;
+ }
+ rb_gc_mark(th->thread);
+ } END_FOREACH_FROM(main_thread, th);
+ if (loading_tbl) st_foreach(loading_tbl, mark_loading_thread, 0);
+}
+
+void
+rb_gc_abort_threads()
+{
+ rb_thread_t th;
+
+ if (!main_thread)
+ return;
+
+ FOREACH_THREAD_FROM(main_thread, th) {
+ if (FL_TEST(th->thread, FL_MARK)) continue;
+ if (th->status == THREAD_STOPPED) {
+ th->status = THREAD_TO_KILL;
+ rb_gc_mark(th->thread);
+ }
+ } END_FOREACH_FROM(main_thread, th);
+}
+
+static inline void
+stack_free(th)
+ rb_thread_t th;
+{
+ if (th->stk_ptr) free(th->stk_ptr);
+ th->stk_ptr = 0;
+#ifdef __ia64
+ if (th->bstr_ptr) free(th->bstr_ptr);
+ th->bstr_ptr = 0;
+#endif
+}
+
+static void
+thread_free(th)
+ rb_thread_t th;
+{
+ stack_free(th);
+ if (th->locals) st_free_table(th->locals);
+ if (th->status != THREAD_KILLED) {
+ if (th->prev) th->prev->next = th->next;
+ if (th->next) th->next->prev = th->prev;
+ }
+ if (th != main_thread) free(th);
+}
+
+static rb_thread_t
+rb_thread_check(data)
+ VALUE data;
+{
+ if (TYPE(data) != T_DATA || RDATA(data)->dmark != (RUBY_DATA_FUNC)thread_mark) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Thread)",
+ rb_obj_classname(data));
+ }
+ return (rb_thread_t)RDATA(data)->data;
+}
+
+static VALUE rb_thread_raise _((int, VALUE*, rb_thread_t));
+
+static VALUE th_raise_exception;
+static NODE *th_raise_node;
+static VALUE th_cmd;
+static int th_sig, th_safe;
+
+#define RESTORE_NORMAL 1
+#define RESTORE_FATAL 2
+#define RESTORE_INTERRUPT 3
+#define RESTORE_TRAP 4
+#define RESTORE_RAISE 5
+#define RESTORE_SIGNAL 6
+#define RESTORE_EXIT 7
+
+extern VALUE *rb_gc_stack_start;
+#ifdef __ia64
+extern VALUE *rb_gc_register_stack_start;
+#endif
+
+static void
+rb_thread_save_context(th)
+ rb_thread_t th;
+{
+ VALUE *pos;
+ size_t len;
+ static VALUE tval;
+
+ len = ruby_stack_length(&pos);
+ th->stk_len = 0;
+ th->stk_pos = pos;
+ if (len > th->stk_max) {
+ VALUE *ptr = realloc(th->stk_ptr, sizeof(VALUE) * len);
+ if (!ptr) rb_memerror();
+ th->stk_ptr = ptr;
+ th->stk_max = len;
+ }
+ th->stk_len = len;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len);
+#ifdef __ia64
+ th->bstr_pos = rb_gc_register_stack_start;
+ len = (VALUE*)rb_ia64_bsp() - th->bstr_pos;
+ th->bstr_len = 0;
+ if (len > th->bstr_max) {
+ VALUE *ptr = realloc(th->bstr_ptr, sizeof(VALUE) * len);
+ if (!ptr) rb_memerror();
+ th->bstr_ptr = ptr;
+ th->bstr_max = len;
+ }
+ th->bstr_len = len;
+ rb_ia64_flushrs();
+ MEMCPY(th->bstr_ptr, th->bstr_pos, VALUE, th->bstr_len);
+#endif
+#ifdef SAVE_WIN32_EXCEPTION_LIST
+ th->win32_exception_list = win32_get_exception_list();
+#endif
+
+ th->frame = ruby_frame;
+ th->scope = ruby_scope;
+ ruby_scope->flags |= SCOPE_DONT_RECYCLE;
+ th->klass = ruby_class;
+ th->wrapper = ruby_wrapper;
+ th->cref = ruby_cref;
+ th->dyna_vars = ruby_dyna_vars;
+ th->block = ruby_block;
+ th->flags &= THREAD_FLAGS_MASK;
+ th->flags |= (rb_trap_immediate<<8) | scope_vmode;
+ th->iter = ruby_iter;
+ th->tag = prot_tag;
+ th->tracing = tracing;
+ th->errinfo = ruby_errinfo;
+ th->last_status = rb_last_status;
+ tval = rb_lastline_get();
+ rb_lastline_set(th->last_line);
+ th->last_line = tval;
+ tval = rb_backref_get();
+ rb_backref_set(th->last_match);
+ th->last_match = tval;
+ th->safe = ruby_safe_level;
+
+ th->node = ruby_current_node;
+ if (ruby_sandbox_save != NULL) {
+ ruby_sandbox_save(th);
+ }
+}
+
+static int
+rb_thread_switch(n)
+ int n;
+{
+ rb_trap_immediate = (curr_thread->flags&0x100)?1:0;
+ switch (n) {
+ case 0:
+ return 0;
+ case RESTORE_FATAL:
+ JUMP_TAG(TAG_FATAL);
+ break;
+ case RESTORE_INTERRUPT:
+ rb_interrupt();
+ break;
+ case RESTORE_TRAP:
+ rb_trap_eval(th_cmd, th_sig, th_safe);
+ break;
+ case RESTORE_RAISE:
+ ruby_frame->last_func = 0;
+ ruby_current_node = th_raise_node;
+ rb_raise_jump(th_raise_exception);
+ break;
+ case RESTORE_SIGNAL:
+ rb_thread_signal_raise(th_sig);
+ break;
+ case RESTORE_EXIT:
+ ruby_errinfo = th_raise_exception;
+ ruby_current_node = th_raise_node;
+ if (!rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ terminate_process(EXIT_FAILURE, ruby_errinfo);
+ }
+ rb_exc_raise(th_raise_exception);
+ break;
+ case RESTORE_NORMAL:
+ default:
+ break;
+ }
+ return 1;
+}
+
+#define THREAD_SAVE_CONTEXT(th) \
+ (rb_thread_switch((FLUSH_REGISTER_WINDOWS, ruby_setjmp(rb_thread_save_context(th), (th)->context))))
+
+NORETURN(static void rb_thread_restore_context _((rb_thread_t,int)));
+NORETURN(NOINLINE(static void rb_thread_restore_context_0(rb_thread_t,int)));
+NORETURN(NOINLINE(static void stack_extend(rb_thread_t, int)));
+
+static void
+rb_thread_restore_context_0(rb_thread_t th, int exit)
+{
+ static rb_thread_t tmp;
+ static int ex;
+ static VALUE tval;
+
+ rb_trap_immediate = 0; /* inhibit interrupts from here */
+ if (ruby_sandbox_restore != NULL) {
+ ruby_sandbox_restore(th);
+ }
+ ruby_frame = th->frame;
+ ruby_scope = th->scope;
+ ruby_class = th->klass;
+ ruby_wrapper = th->wrapper;
+ ruby_cref = th->cref;
+ ruby_dyna_vars = th->dyna_vars;
+ ruby_block = th->block;
+ scope_vmode = th->flags&SCOPE_MASK;
+ ruby_iter = th->iter;
+ prot_tag = th->tag;
+ tracing = th->tracing;
+ ruby_errinfo = th->errinfo;
+ rb_last_status = th->last_status;
+ ruby_safe_level = th->safe;
+
+ ruby_current_node = th->node;
+
+#ifdef SAVE_WIN32_EXCEPTION_LIST
+ win32_set_exception_list(th->win32_exception_list);
+#endif
+ tmp = th;
+ ex = exit;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len);
+#ifdef __ia64
+ MEMCPY(tmp->bstr_pos, tmp->bstr_ptr, VALUE, tmp->bstr_len);
+#endif
+
+ tval = rb_lastline_get();
+ rb_lastline_set(tmp->last_line);
+ tmp->last_line = tval;
+ tval = rb_backref_get();
+ rb_backref_set(tmp->last_match);
+ tmp->last_match = tval;
+
+ ruby_longjmp(tmp->context, ex);
+}
+
+#ifdef __ia64
+#define C(a) rse_##a##0, rse_##a##1, rse_##a##2, rse_##a##3, rse_##a##4
+#define E(a) rse_##a##0= rse_##a##1= rse_##a##2= rse_##a##3= rse_##a##4
+static volatile int C(a), C(b), C(c), C(d), C(e);
+static volatile int C(f), C(g), C(h), C(i), C(j);
+static volatile int C(k), C(l), C(m), C(n), C(o);
+static volatile int C(p), C(q), C(r), C(s), C(t);
+int rb_dummy_false = 0;
+NORETURN(NOINLINE(static void register_stack_extend(rb_thread_t, int, VALUE *)));
+static void
+register_stack_extend(rb_thread_t th, int exit, VALUE *curr_bsp)
+{
+ if (rb_dummy_false) {
+ /* use registers as much as possible */
+ E(a) = E(b) = E(c) = E(d) = E(e) =
+ E(f) = E(g) = E(h) = E(i) = E(j) =
+ E(k) = E(l) = E(m) = E(n) = E(o) =
+ E(p) = E(q) = E(r) = E(s) = E(t) = 0;
+ E(a) = E(b) = E(c) = E(d) = E(e) =
+ E(f) = E(g) = E(h) = E(i) = E(j) =
+ E(k) = E(l) = E(m) = E(n) = E(o) =
+ E(p) = E(q) = E(r) = E(s) = E(t) = 0;
+ }
+ if (curr_bsp < th->bstr_pos+th->bstr_len) {
+ register_stack_extend(th, exit, (VALUE*)rb_ia64_bsp());
+ }
+ stack_extend(th, exit);
+}
+#undef C
+#undef E
+#endif
+
+static void
+stack_extend(rb_thread_t th, int exit)
+{
+#define STACK_PAD_SIZE 1024
+ volatile VALUE space[STACK_PAD_SIZE], *sp = space;
+
+#if !STACK_GROW_DIRECTION
+ if (space < rb_gc_stack_start) {
+ /* Stack grows downward */
+#endif
+#if STACK_GROW_DIRECTION <= 0
+ if (space > th->stk_pos) {
+# ifdef HAVE_ALLOCA
+ sp = ALLOCA_N(VALUE, &space[0] - th->stk_pos);
+# else
+ stack_extend(th, exit);
+# endif
+ }
+#endif
+#if !STACK_GROW_DIRECTION
+ }
+ else {
+ /* Stack grows upward */
+#endif
+#if STACK_GROW_DIRECTION >= 0
+ if (&space[STACK_PAD_SIZE] < th->stk_pos + th->stk_len) {
+# ifdef HAVE_ALLOCA
+ sp = ALLOCA_N(VALUE, th->stk_pos + th->stk_len - &space[STACK_PAD_SIZE]);
+# else
+ stack_extend(th, exit);
+# endif
+ }
+#endif
+#if !STACK_GROW_DIRECTION
+ }
+#endif
+ rb_thread_restore_context_0(th, exit);
+}
+#ifdef __ia64
+#define stack_extend(th, exit) register_stack_extend(th, exit, (VALUE*)rb_ia64_bsp())
+#endif
+
+static void
+rb_thread_restore_context(th, exit)
+ rb_thread_t th;
+ int exit;
+{
+ if (!th->stk_ptr) rb_bug("unsaved context");
+ stack_extend(th, exit);
+}
+
+static void
+rb_thread_ready(th)
+ rb_thread_t th;
+{
+ th->wait_for = 0;
+ if (th->status != THREAD_TO_KILL) {
+ th->status = THREAD_RUNNABLE;
+ }
+}
+
+static void
+rb_thread_die(th)
+ rb_thread_t th;
+{
+ th->thgroup = 0;
+ th->status = THREAD_KILLED;
+ stack_free(th);
+}
+
+static void
+rb_thread_remove(th)
+ rb_thread_t th;
+{
+ if (th->status == THREAD_KILLED) return;
+
+ rb_thread_ready(th);
+ rb_thread_die(th);
+ th->prev->next = th->next;
+ th->next->prev = th->prev;
+}
+
+static int
+rb_thread_dead(th)
+ rb_thread_t th;
+{
+ return th->status == THREAD_KILLED;
+}
+
+void
+rb_thread_fd_close(fd)
+ int fd;
+{
+ rb_thread_t th;
+
+ FOREACH_THREAD(th) {
+ if (((th->wait_for & WAIT_FD) && fd == th->fd) ||
+ ((th->wait_for & WAIT_SELECT) && (fd < th->fd) &&
+ (FD_ISSET(fd, &th->readfds) ||
+ FD_ISSET(fd, &th->writefds) ||
+ FD_ISSET(fd, &th->exceptfds)))) {
+ VALUE exc = rb_exc_new2(rb_eIOError, "stream closed");
+ rb_thread_raise(1, &exc, th);
+ }
+ }
+ END_FOREACH(th);
+}
+
+NORETURN(static void rb_thread_main_jump _((VALUE, int)));
+static void
+rb_thread_main_jump(err, tag)
+ VALUE err;
+ int tag;
+{
+ curr_thread = main_thread;
+ th_raise_exception = err;
+ th_raise_node = ruby_current_node;
+ rb_thread_restore_context(main_thread, tag);
+}
+
+NORETURN(static void rb_thread_deadlock _((void)));
+static void
+rb_thread_deadlock()
+{
+ char msg[21+SIZEOF_LONG*2];
+ VALUE e;
+
+ sprintf(msg, "Thread(0x%lx): deadlock", curr_thread->thread);
+ e = rb_exc_new2(rb_eFatal, msg);
+ if (curr_thread == main_thread) {
+ rb_exc_raise(e);
+ }
+ rb_thread_main_jump(e, RESTORE_RAISE);
+}
+
+static void
+copy_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int n = 0;
+ int i;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src)) {
+ n = i;
+ FD_SET(i, dst);
+ }
+ }
+}
+
+static int
+match_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int i;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src) && FD_ISSET(i, dst)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+static int
+intersect_fds(src, dst, max)
+ fd_set *src, *dst;
+ int max;
+{
+ int i, n = 0;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, dst)) {
+ if (FD_ISSET(i, src)) {
+ /* Wake up only one thread per fd. */
+ FD_CLR(i, src);
+ n++;
+ }
+ else {
+ FD_CLR(i, dst);
+ }
+ }
+ }
+ return n;
+}
+
+static int
+find_bad_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int i, test = Qfalse;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src) && !FD_ISSET(i, dst)) {
+ FD_CLR(i, src);
+ test = Qtrue;
+ }
+ }
+ return test;
+}
+
+void
+rb_thread_schedule()
+{
+ rb_thread_t next; /* OK */
+ rb_thread_t th;
+ rb_thread_t curr;
+ rb_thread_t th_found = 0;
+ int found = 0;
+
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ struct timeval delay_tv, *delay_ptr;
+ double delay, now; /* OK */
+ int n, max;
+ int need_select = 0;
+ int select_timeout = 0;
+
+#ifdef HAVE_NATIVETHREAD
+ if (!is_ruby_native_thread()) {
+ rb_bug("cross-thread violation on rb_thread_schedule()");
+ }
+#endif
+ rb_thread_pending = 0;
+ rb_gc_finalize_deferred();
+ if (curr_thread == curr_thread->next
+ && curr_thread->status == THREAD_RUNNABLE)
+ return;
+
+ next = 0;
+ curr = curr_thread; /* starting thread */
+
+ while (curr->status == THREAD_KILLED) {
+ curr = curr->prev;
+ }
+
+ again:
+ max = -1;
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ delay = DELAY_INFTY;
+ now = -1.0;
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (!found && th->status <= THREAD_RUNNABLE) {
+ found = 1;
+ }
+ if (th->status != THREAD_STOPPED) continue;
+ if (th->wait_for & WAIT_JOIN) {
+ if (rb_thread_dead(th->join)) {
+ th->status = THREAD_RUNNABLE;
+ found = 1;
+ }
+ }
+ if (th->wait_for & WAIT_FD) {
+ FD_SET(th->fd, &readfds);
+ if (max < th->fd) max = th->fd;
+ need_select = 1;
+ }
+ if (th->wait_for & WAIT_SELECT) {
+ copy_fds(&readfds, &th->readfds, th->fd);
+ copy_fds(&writefds, &th->writefds, th->fd);
+ copy_fds(&exceptfds, &th->exceptfds, th->fd);
+ if (max < th->fd) max = th->fd;
+ need_select = 1;
+ if (th->wait_for & WAIT_TIME) {
+ select_timeout = 1;
+ }
+ th->select_value = 0;
+ }
+ if (th->wait_for & WAIT_TIME) {
+ double th_delay;
+
+ if (now < 0.0) now = timeofday();
+ th_delay = th->delay - now;
+ if (th_delay <= 0.0) {
+ th->status = THREAD_RUNNABLE;
+ found = 1;
+ }
+ else if (th_delay < delay) {
+ delay = th_delay;
+ need_select = 1;
+ }
+ else if (th->delay == DELAY_INFTY) {
+ need_select = 1;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+
+ /* Do the select if needed */
+ if (need_select) {
+ /* Convert delay to a timeval */
+ /* If a thread is runnable, just poll */
+ if (found) {
+ delay_tv.tv_sec = 0;
+ delay_tv.tv_usec = 0;
+ delay_ptr = &delay_tv;
+ }
+ else if (delay == DELAY_INFTY) {
+ delay_ptr = 0;
+ }
+ else {
+ delay_tv.tv_sec = delay;
+ delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec)*1e6;
+ delay_ptr = &delay_tv;
+ }
+
+ n = select(max+1, &readfds, &writefds, &exceptfds, delay_ptr);
+ if (n < 0) {
+ int e = errno;
+
+ if (rb_trap_pending) rb_trap_exec();
+ if (e == EINTR) goto again;
+#ifdef ERESTART
+ if (e == ERESTART) goto again;
+#endif
+ if (e == EBADF) {
+ int badfd = -1;
+ int fd;
+ int dummy;
+ for (fd = 0; fd <= max; fd++) {
+ if ((FD_ISSET(fd, &readfds) ||
+ FD_ISSET(fd, &writefds) ||
+ FD_ISSET(fd, &exceptfds)) &&
+ fcntl(fd, F_GETFD, &dummy) == -1 &&
+ errno == EBADF) {
+ badfd = fd;
+ break;
+ }
+ }
+ if (badfd != -1) {
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->wait_for & WAIT_FD) {
+ if (th->fd == badfd) {
+ found = 1;
+ th->status = THREAD_RUNNABLE;
+ th->fd = 0;
+ break;
+ }
+ }
+ if (th->wait_for & WAIT_SELECT) {
+ if (FD_ISSET(badfd, &th->readfds) ||
+ FD_ISSET(badfd, &th->writefds) ||
+ FD_ISSET(badfd, &th->exceptfds)) {
+ found = 1;
+ th->status = THREAD_RUNNABLE;
+ th->select_value = -EBADF;
+ break;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ }
+ else {
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->wait_for & WAIT_SELECT) {
+ int v = 0;
+
+ v |= find_bad_fds(&readfds, &th->readfds, th->fd);
+ v |= find_bad_fds(&writefds, &th->writefds, th->fd);
+ v |= find_bad_fds(&exceptfds, &th->exceptfds, th->fd);
+ if (v) {
+ th->select_value = n;
+ n = max;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ }
+ if (select_timeout && n == 0) {
+ if (now < 0.0) now = timeofday();
+ FOREACH_THREAD_FROM(curr, th) {
+ if (((th->wait_for&(WAIT_SELECT|WAIT_TIME)) == (WAIT_SELECT|WAIT_TIME)) &&
+ th->delay <= now) {
+ th->status = THREAD_RUNNABLE;
+ th->wait_for = 0;
+ th->select_value = 0;
+ found = 1;
+ intersect_fds(&readfds, &th->readfds, max);
+ intersect_fds(&writefds, &th->writefds, max);
+ intersect_fds(&exceptfds, &th->exceptfds, max);
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ if (n > 0) {
+ now = -1.0;
+ /* Some descriptors are ready.
+ * Choose a thread which may run next.
+ * Don't change the status of threads which don't run next.
+ */
+ FOREACH_THREAD_FROM(curr, th) {
+ if ((th->wait_for&WAIT_FD) && FD_ISSET(th->fd, &readfds)) {
+ th_found = th;
+ found = 1;
+ break;
+ }
+ if ((th->wait_for&WAIT_SELECT) &&
+ (match_fds(&readfds, &th->readfds, max) ||
+ match_fds(&writefds, &th->writefds, max) ||
+ match_fds(&exceptfds, &th->exceptfds, max))) {
+ th_found = th;
+ found = 1;
+ break;
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ /* The delays for some of the threads should have expired.
+ Go through the loop once more, to check the delays. */
+ if (!found && delay != DELAY_INFTY)
+ goto again;
+ }
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->status == THREAD_TO_KILL) {
+ next = th;
+ break;
+ }
+ if ((th->status == THREAD_RUNNABLE || th == th_found) && th->stk_ptr) {
+ if (!next || next->priority < th->priority) {
+ if (th == th_found) {
+ th_found->status = THREAD_RUNNABLE;
+ th_found->wait_for = 0;
+ if (th->wait_for&WAIT_FD) {
+ th_found->fd = 0;
+ }
+ else { /* th->wait_for&WAIT_SELECT */
+ n = intersect_fds(&readfds, &th_found->readfds, max) +
+ intersect_fds(&writefds, &th_found->writefds, max) +
+ intersect_fds(&exceptfds, &th_found->exceptfds, max);
+ th_found->select_value = n;
+ }
+ }
+ next = th;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+
+ if (!next) {
+ /* raise fatal error to main thread */
+ curr_thread->node = ruby_current_node;
+ if (curr->next == curr) {
+ TRAP_BEG;
+ pause();
+ TRAP_END;
+ }
+ FOREACH_THREAD_FROM(curr, th) {
+ warn_printf("deadlock 0x%lx: %s:",
+ th->thread, thread_status_name(th->status));
+ if (th->wait_for & WAIT_FD) warn_printf("F(%d)", th->fd);
+ if (th->wait_for & WAIT_SELECT) warn_printf("S");
+ if (th->wait_for & WAIT_TIME) warn_printf("T(%f)", th->delay);
+ if (th->wait_for & WAIT_JOIN)
+ warn_printf("J(0x%lx)", th->join ? th->join->thread : 0);
+ if (th->wait_for & WAIT_PID) warn_printf("P");
+ if (!th->wait_for) warn_printf("-");
+ warn_printf(" %s - %s:%d\n",
+ th==main_thread ? "(main)" : "",
+ th->node->nd_file, nd_line(th->node));
+ }
+ END_FOREACH_FROM(curr, th);
+ next = main_thread;
+ rb_thread_ready(next);
+ next->status = THREAD_TO_KILL;
+ if (!rb_thread_dead(curr_thread)) {
+ rb_thread_save_context(curr_thread);
+ }
+ rb_thread_deadlock();
+ }
+ next->wait_for = 0;
+ if (next->status == THREAD_RUNNABLE && next == curr_thread) {
+ return;
+ }
+
+ /* context switch */
+ if (curr == curr_thread) {
+ if (THREAD_SAVE_CONTEXT(curr)) {
+ return;
+ }
+ }
+
+ curr_thread = next;
+ if (next->status == THREAD_TO_KILL) {
+ if (!(next->flags & THREAD_TERMINATING)) {
+ next->flags |= THREAD_TERMINATING;
+ /* terminate; execute ensure-clause if any */
+ rb_thread_restore_context(next, RESTORE_FATAL);
+ }
+ }
+ rb_thread_restore_context(next, RESTORE_NORMAL);
+}
+
+void
+rb_thread_wait_fd(fd)
+ int fd;
+{
+ if (rb_thread_critical) return;
+ if (ruby_in_compile) return;
+ if (curr_thread == curr_thread->next) return;
+ if (curr_thread->status == THREAD_TO_KILL) return;
+
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->fd = fd;
+ curr_thread->wait_for = WAIT_FD;
+ rb_thread_schedule();
+}
+
+int
+rb_thread_fd_writable(fd)
+ int fd;
+{
+ if (rb_thread_critical) return Qtrue;
+ if (curr_thread == curr_thread->next) return Qtrue;
+ if (curr_thread->status == THREAD_TO_KILL) return Qtrue;
+ if (curr_thread->status == THREAD_KILLED) return Qtrue;
+
+ curr_thread->status = THREAD_STOPPED;
+ FD_ZERO(&curr_thread->readfds);
+ FD_ZERO(&curr_thread->writefds);
+ FD_SET(fd, &curr_thread->writefds);
+ FD_ZERO(&curr_thread->exceptfds);
+ curr_thread->fd = fd+1;
+ curr_thread->wait_for = WAIT_SELECT;
+ rb_thread_schedule();
+ return Qfalse;
+}
+
+void
+rb_thread_wait_for(time)
+ struct timeval time;
+{
+ double date;
+
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+ int n;
+ int thr_critical = rb_thread_critical;
+#ifndef linux
+ double d, limit;
+ limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6;
+#endif
+ for (;;) {
+ rb_thread_critical = Qtrue;
+ TRAP_BEG;
+ n = select(0, 0, 0, 0, &time);
+ rb_thread_critical = thr_critical;
+ TRAP_END;
+ if (n == 0) return;
+ if (n < 0) {
+ switch (errno) {
+ case EINTR:
+#ifdef ERESTART
+ case ERESTART:
+#endif
+ break;
+ default:
+ rb_sys_fail("sleep");
+ }
+ }
+#ifndef linux
+ d = limit - timeofday();
+
+ time.tv_sec = (int)d;
+ time.tv_usec = (int)((d - (int)d)*1e6);
+ if (time.tv_usec < 0) {
+ time.tv_usec += (long)1e6;
+ time.tv_sec -= 1;
+ }
+ if (time.tv_sec < 0) return;
+#endif
+ }
+ }
+
+ date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = date;
+ curr_thread->wait_for = WAIT_TIME;
+ rb_thread_schedule();
+}
+
+void rb_thread_sleep_forever _((void));
+
+int
+rb_thread_alone()
+{
+ return curr_thread == curr_thread->next;
+}
+
+int
+rb_thread_select(max, read, write, except, timeout)
+ int max;
+ fd_set *read, *write, *except;
+ struct timeval *timeout;
+{
+#ifndef linux
+ double limit;
+#endif
+ int n;
+
+ if (!read && !write && !except) {
+ if (!timeout) {
+ rb_thread_sleep_forever();
+ return 0;
+ }
+ rb_thread_wait_for(*timeout);
+ return 0;
+ }
+
+#ifndef linux
+ if (timeout) {
+ limit = timeofday()+
+ (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
+ }
+#endif
+
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+#ifndef linux
+ struct timeval tv, *tvp = timeout;
+
+ if (timeout) {
+ tv = *timeout;
+ tvp = &tv;
+ }
+#else
+ struct timeval *const tvp = timeout;
+#endif
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, tvp);
+ TRAP_END;
+ if (n < 0) {
+ switch (errno) {
+ case EINTR:
+#ifdef ERESTART
+ case ERESTART:
+#endif
+#ifndef linux
+ if (timeout) {
+ double d = limit - timeofday();
+
+ tv.tv_sec = (unsigned int)d;
+ tv.tv_usec = (long)((d-(double)tv.tv_sec)*1e6);
+ if (tv.tv_sec < 0) tv.tv_sec = 0;
+ if (tv.tv_usec < 0) tv.tv_usec = 0;
+ }
+#endif
+ continue;
+ default:
+ break;
+ }
+ }
+ return n;
+ }
+ }
+
+ curr_thread->status = THREAD_STOPPED;
+ if (read) curr_thread->readfds = *read;
+ else FD_ZERO(&curr_thread->readfds);
+ if (write) curr_thread->writefds = *write;
+ else FD_ZERO(&curr_thread->writefds);
+ if (except) curr_thread->exceptfds = *except;
+ else FD_ZERO(&curr_thread->exceptfds);
+ curr_thread->fd = max;
+ curr_thread->wait_for = WAIT_SELECT;
+ if (timeout) {
+ curr_thread->delay = timeofday() +
+ (double)timeout->tv_sec + (double)timeout->tv_usec*1e-6;
+ curr_thread->wait_for |= WAIT_TIME;
+ }
+ rb_thread_schedule();
+ if (read) *read = curr_thread->readfds;
+ if (write) *write = curr_thread->writefds;
+ if (except) *except = curr_thread->exceptfds;
+ if (curr_thread->select_value < 0) {
+ errno = -curr_thread->select_value;
+ return -1;
+ }
+ return curr_thread->select_value;
+}
+
+static int rb_thread_join0 _((rb_thread_t, double));
+int rb_thread_join _((VALUE, double));
+
+static int
+rb_thread_join0(th, limit)
+ rb_thread_t th;
+ double limit;
+{
+ enum rb_thread_status last_status = THREAD_RUNNABLE;
+
+ if (rb_thread_critical) rb_thread_deadlock();
+ if (!rb_thread_dead(th)) {
+ if (th == curr_thread)
+ rb_raise(rb_eThreadError, "thread 0x%lx tried to join itself",
+ th->thread);
+ if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread)
+ rb_raise(rb_eThreadError, "Thread#join: deadlock 0x%lx - mutual join(0x%lx)",
+ curr_thread->thread, th->thread);
+ if (curr_thread->status == THREAD_TO_KILL)
+ last_status = THREAD_TO_KILL;
+ if (limit == 0) return Qfalse;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->join = th;
+ curr_thread->wait_for = WAIT_JOIN;
+ curr_thread->delay = timeofday() + limit;
+ if (limit < DELAY_INFTY) curr_thread->wait_for |= WAIT_TIME;
+ rb_thread_schedule();
+ curr_thread->status = last_status;
+ if (!rb_thread_dead(th)) return Qfalse;
+ }
+
+ if (!NIL_P(th->errinfo) && (th->flags & RAISED_EXCEPTION)) {
+ VALUE oldbt = get_backtrace(th->errinfo);
+ VALUE errat = make_backtrace();
+ VALUE errinfo = rb_obj_dup(th->errinfo);
+
+ if (TYPE(oldbt) == T_ARRAY && RARRAY(oldbt)->len > 0) {
+ rb_ary_unshift(errat, rb_ary_entry(oldbt, 0));
+ }
+ set_backtrace(errinfo, errat);
+ rb_exc_raise(errinfo);
+ }
+
+ return Qtrue;
+}
+
+int
+rb_thread_join(thread, limit)
+ VALUE thread;
+ double limit;
+{
+ if (limit < 0) limit = DELAY_INFTY;
+ return rb_thread_join0(rb_thread_check(thread), limit);
+}
+
+
+/*
+ * call-seq:
+ * thr.join => thr
+ * thr.join(limit) => thr
+ *
+ * The calling thread will suspend execution and run <i>thr</i>. Does not
+ * return until <i>thr</i> exits or until <i>limit</i> seconds have passed. If
+ * the time limit expires, <code>nil</code> will be returned, otherwise
+ * <i>thr</i> is returned.
+ *
+ * Any threads not joined will be killed when the main program exits. If
+ * <i>thr</i> had previously raised an exception and the
+ * <code>abort_on_exception</code> and <code>$DEBUG</code> flags are not set
+ * (so the exception has not yet been processed) it will be processed at this
+ * time.
+ *
+ * a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
+ * x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
+ * x.join # Let x thread finish, a will be killed on exit.
+ *
+ * <em>produces:</em>
+ *
+ * axyz
+ *
+ * The following example illustrates the <i>limit</i> parameter.
+ *
+ * y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
+ * puts "Waiting" until y.join(0.15)
+ *
+ * <em>produces:</em>
+ *
+ * tick...
+ * Waiting
+ * tick...
+ * Waitingtick...
+ *
+ *
+ * tick...
+ */
+
+static VALUE
+rb_thread_join_m(argc, argv, thread)
+ int argc;
+ VALUE *argv;
+ VALUE thread;
+{
+ VALUE limit;
+ double delay = DELAY_INFTY;
+
+ rb_scan_args(argc, argv, "01", &limit);
+ if (!NIL_P(limit)) delay = rb_num2dbl(limit);
+ if (!rb_thread_join0(rb_thread_check(thread), delay))
+ return Qnil;
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.current => thread
+ *
+ * Returns the currently executing thread.
+ *
+ * Thread.current #=> #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_current()
+{
+ return curr_thread->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.main => thread
+ *
+ * Returns the main thread for the process.
+ *
+ * Thread.main #=> #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_main()
+{
+ return main_thread->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.list => array
+ *
+ * Returns an array of <code>Thread</code> objects for all threads that are
+ * either runnable or stopped.
+ *
+ * Thread.new { sleep(200) }
+ * Thread.new { 1000000.times {|i| i*i } }
+ * Thread.new { Thread.stop }
+ * Thread.list.each {|t| p t}
+ *
+ * <em>produces:</em>
+ *
+ * #<Thread:0x401b3e84 sleep>
+ * #<Thread:0x401b3f38 run>
+ * #<Thread:0x401b3fb0 sleep>
+ * #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_list()
+{
+ rb_thread_t th;
+ VALUE ary = rb_ary_new();
+
+ FOREACH_THREAD(th) {
+ switch (th->status) {
+ case THREAD_RUNNABLE:
+ case THREAD_STOPPED:
+ case THREAD_TO_KILL:
+ rb_ary_push(ary, th->thread);
+ default:
+ break;
+ }
+ }
+ END_FOREACH(th);
+
+ return ary;
+}
+
+
+/*
+ * call-seq:
+ * thr.wakeup => thr
+ *
+ * Marks <i>thr</i> as eligible for scheduling (it may still remain blocked on
+ * I/O, however). Does not invoke the scheduler (see <code>Thread#run</code>).
+ *
+ * c = Thread.new { Thread.stop; puts "hey!" }
+ * c.wakeup
+ *
+ * <em>produces:</em>
+ *
+ * hey!
+ */
+
+VALUE
+rb_thread_wakeup(thread)
+ VALUE thread;
+{
+ if (!RTEST(rb_thread_wakeup_alive(thread)))
+ rb_raise(rb_eThreadError, "killed thread");
+ return thread;
+}
+
+VALUE
+rb_thread_wakeup_alive(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (th->status == THREAD_KILLED)
+ return Qnil;
+ rb_thread_ready(th);
+
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * thr.run => thr
+ *
+ * Wakes up <i>thr</i>, making it eligible for scheduling. If not in a critical
+ * section, then invokes the scheduler.
+ *
+ * a = Thread.new { puts "a"; Thread.stop; puts "c" }
+ * Thread.pass
+ * puts "Got here"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * a
+ * Got here
+ * c
+ */
+
+VALUE
+rb_thread_run(thread)
+ VALUE thread;
+{
+ rb_thread_wakeup(thread);
+ if (!rb_thread_critical) rb_thread_schedule();
+
+ return thread;
+}
+
+
+static void
+rb_kill_thread(th, flags)
+ rb_thread_t th;
+ int flags;
+{
+ if (th != curr_thread && th->safe < 4) {
+ rb_secure(4);
+ }
+ if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED)
+ return;
+ if (th == th->next || th == main_thread) rb_exit(EXIT_SUCCESS);
+
+ rb_thread_ready(th);
+ th->flags |= flags;
+ th->status = THREAD_TO_KILL;
+ if (!rb_thread_critical) rb_thread_schedule();
+}
+
+
+/*
+ * call-seq:
+ * thr.exit => thr
+ * thr.kill => thr
+ * thr.terminate => thr
+ *
+ * Terminates <i>thr</i> and schedules another thread to be run, returning
+ * the terminated <code>Thread</code>. If this is the main thread, or the
+ * last thread, exits the process.
+ */
+
+VALUE
+rb_thread_kill(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ rb_kill_thread(th, 0);
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * thr.exit! => thr
+ * thr.kill! => thr
+ * thr.terminate! => thr
+ *
+ * Terminates <i>thr</i> without calling ensure clauses and schedules
+ * another thread to be run, returning the terminated <code>Thread</code>.
+ * If this is the main thread, or the last thread, exits the process.
+ *
+ * See <code>Thread#exit</code> for the safer version.
+ */
+
+static VALUE
+rb_thread_kill_bang(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+ rb_kill_thread(th, THREAD_NO_ENSURE);
+ return thread;
+}
+
+/*
+ * call-seq:
+ * Thread.kill(thread) => thread
+ *
+ * Causes the given <em>thread</em> to exit (see <code>Thread::exit</code>).
+ *
+ * count = 0
+ * a = Thread.new { loop { count += 1 } }
+ * sleep(0.1) #=> 0
+ * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
+ * count #=> 93947
+ * a.alive? #=> false
+ */
+
+static VALUE
+rb_thread_s_kill(obj, th)
+ VALUE obj, th;
+{
+ return rb_thread_kill(th);
+}
+
+
+/*
+ * call-seq:
+ * Thread.exit => thread
+ *
+ * Terminates the currently running thread and schedules another thread to be
+ * run. If this thread is already marked to be killed, <code>exit</code>
+ * returns the <code>Thread</code>. If this is the main thread, or the last
+ * thread, exit the process.
+ */
+
+static VALUE
+rb_thread_exit()
+{
+ return rb_thread_kill(curr_thread->thread);
+}
+
+
+/*
+ * call-seq:
+ * Thread.pass => nil
+ *
+ * Invokes the thread scheduler to pass execution to another thread.
+ *
+ * a = Thread.new { print "a"; Thread.pass;
+ * print "b"; Thread.pass;
+ * print "c" }
+ * b = Thread.new { print "x"; Thread.pass;
+ * print "y"; Thread.pass;
+ * print "z" }
+ * a.join
+ * b.join
+ *
+ * <em>produces:</em>
+ *
+ * axbycz
+ */
+
+static VALUE
+rb_thread_pass()
+{
+ rb_thread_schedule();
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Thread.stop => nil
+ *
+ * Stops execution of the current thread, putting it into a ``sleep'' state,
+ * and schedules execution of another thread. Resets the ``critical'' condition
+ * to <code>false</code>.
+ *
+ * a = Thread.new { print "a"; Thread.stop; print "c" }
+ * Thread.pass
+ * print "b"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * abc
+ */
+
+VALUE
+rb_thread_stop()
+{
+ enum rb_thread_status last_status = THREAD_RUNNABLE;
+
+ rb_thread_critical = 0;
+ if (curr_thread == curr_thread->next) {
+ rb_raise(rb_eThreadError, "stopping only thread\n\tnote: use sleep to stop forever");
+ }
+ if (curr_thread->status == THREAD_TO_KILL)
+ last_status = THREAD_TO_KILL;
+ curr_thread->status = THREAD_STOPPED;
+ rb_thread_schedule();
+ curr_thread->status = last_status;
+
+ return Qnil;
+}
+
+struct timeval rb_time_timeval();
+
+void
+rb_thread_polling()
+{
+ if (curr_thread != curr_thread->next) {
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = timeofday() + (double)0.06;
+ curr_thread->wait_for = WAIT_TIME;
+ rb_thread_schedule();
+ }
+}
+
+void
+rb_thread_sleep(sec)
+ int sec;
+{
+ if (curr_thread == curr_thread->next) {
+ TRAP_BEG;
+ sleep(sec);
+ TRAP_END;
+ return;
+ }
+ rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
+}
+
+void
+rb_thread_sleep_forever()
+{
+ int thr_critical = rb_thread_critical;
+ if (curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+ rb_thread_critical = Qtrue;
+ TRAP_BEG;
+ pause();
+ rb_thread_critical = thr_critical;
+ TRAP_END;
+ return;
+ }
+
+ curr_thread->delay = DELAY_INFTY;
+ curr_thread->wait_for = WAIT_TIME;
+ curr_thread->status = THREAD_STOPPED;
+ rb_thread_schedule();
+}
+
+
+/*
+ * call-seq:
+ * thr.priority => integer
+ *
+ * Returns the priority of <i>thr</i>. Default is inherited from the
+ * current thread which creating the new thread, or zero for the
+ * initial main thread; higher-priority threads will run before
+ * lower-priority threads.
+ *
+ * Thread.current.priority #=> 0
+ */
+
+static VALUE
+rb_thread_priority(thread)
+ VALUE thread;
+{
+ return INT2NUM(rb_thread_check(thread)->priority);
+}
+
+
+/*
+ * call-seq:
+ * thr.priority= integer => thr
+ *
+ * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
+ * will run before lower-priority threads.
+ *
+ * count1 = count2 = 0
+ * a = Thread.new do
+ * loop { count1 += 1 }
+ * end
+ * a.priority = -1
+ *
+ * b = Thread.new do
+ * loop { count2 += 1 }
+ * end
+ * b.priority = -2
+ * sleep 1 #=> 1
+ * Thread.critical = 1
+ * count1 #=> 622504
+ * count2 #=> 5832
+ */
+
+static VALUE
+rb_thread_priority_set(thread, prio)
+ VALUE thread, prio;
+{
+ rb_thread_t th;
+
+ rb_secure(4);
+ th = rb_thread_check(thread);
+
+ th->priority = NUM2INT(prio);
+ rb_thread_schedule();
+ return prio;
+}
+
+
+/*
+ * call-seq:
+ * thr.safe_level => integer
+ *
+ * Returns the safe level in effect for <i>thr</i>. Setting thread-local safe
+ * levels can help when implementing sandboxes which run insecure code.
+ *
+ * thr = Thread.new { $SAFE = 3; sleep }
+ * Thread.current.safe_level #=> 0
+ * thr.safe_level #=> 3
+ */
+
+static VALUE
+rb_thread_safe_level(thread)
+ VALUE thread;
+{
+ rb_thread_t th;
+
+ th = rb_thread_check(thread);
+ if (th == curr_thread) {
+ return INT2NUM(ruby_safe_level);
+ }
+ return INT2NUM(th->safe);
+}
+
+static int ruby_thread_abort;
+static VALUE thgroup_default;
+
+
+/*
+ * call-seq:
+ * Thread.abort_on_exception => true or false
+ *
+ * Returns the status of the global ``abort on exception'' condition. The
+ * default is <code>false</code>. When set to <code>true</code>, or if the
+ * global <code>$DEBUG</code> flag is <code>true</code> (perhaps because the
+ * command line option <code>-d</code> was specified) all threads will abort
+ * (the process will <code>exit(0)</code>) if an exception is raised in any
+ * thread. See also <code>Thread::abort_on_exception=</code>.
+ */
+
+static VALUE
+rb_thread_s_abort_exc()
+{
+ return ruby_thread_abort?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * Thread.abort_on_exception= boolean => true or false
+ *
+ * When set to <code>true</code>, all threads will abort if an exception is
+ * raised. Returns the new state.
+ *
+ * Thread.abort_on_exception = true
+ * t1 = Thread.new do
+ * puts "In new thread"
+ * raise "Exception from thread"
+ * end
+ * sleep(1)
+ * puts "not reached"
+ *
+ * <em>produces:</em>
+ *
+ * In new thread
+ * prog.rb:4: Exception from thread (RuntimeError)
+ * from prog.rb:2:in `initialize'
+ * from prog.rb:2:in `new'
+ * from prog.rb:2
+ */
+
+static VALUE
+rb_thread_s_abort_exc_set(self, val)
+ VALUE self, val;
+{
+ rb_secure(4);
+ ruby_thread_abort = RTEST(val);
+ return val;
+}
+
+
+/*
+ * call-seq:
+ * thr.abort_on_exception => true or false
+ *
+ * Returns the status of the thread-local ``abort on exception'' condition for
+ * <i>thr</i>. The default is <code>false</code>. See also
+ * <code>Thread::abort_on_exception=</code>.
+ */
+
+static VALUE
+rb_thread_abort_exc(thread)
+ VALUE thread;
+{
+ return rb_thread_check(thread)->abort?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * thr.abort_on_exception= boolean => true or false
+ *
+ * When set to <code>true</code>, causes all threads (including the main
+ * program) to abort if an exception is raised in <i>thr</i>. The process will
+ * effectively <code>exit(0)</code>.
+ */
+
+static VALUE
+rb_thread_abort_exc_set(thread, val)
+ VALUE thread, val;
+{
+ rb_secure(4);
+ rb_thread_check(thread)->abort = RTEST(val);
+ return val;
+}
+
+
+enum rb_thread_status
+rb_thread_status(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+ return th->status;
+}
+
+
+/*
+ * call-seq:
+ * thr.group => thgrp or nil
+ *
+ * Returns the <code>ThreadGroup</code> which contains <i>thr</i>, or nil if
+ * the thread is not a member of any group.
+ *
+ * Thread.main.group #=> #<ThreadGroup:0x4029d914>
+ */
+
+VALUE
+rb_thread_group(thread)
+ VALUE thread;
+{
+ VALUE group = rb_thread_check(thread)->thgroup;
+ if (!group) {
+ group = Qnil;
+ }
+ return group;
+}
+
+#ifdef __ia64
+# define IA64_INIT(x) x
+#else
+# define IA64_INIT(x)
+#endif
+
+#define THREAD_ALLOC(th) do {\
+ th = ALLOC(struct rb_thread);\
+\
+ th->next = 0;\
+ th->prev = 0;\
+\
+ th->status = THREAD_RUNNABLE;\
+ th->result = 0;\
+ th->flags = 0;\
+\
+ th->stk_ptr = 0;\
+ th->stk_len = 0;\
+ th->stk_max = 0;\
+ th->wait_for = 0;\
+ IA64_INIT(th->bstr_ptr = 0);\
+ IA64_INIT(th->bstr_len = 0);\
+ IA64_INIT(th->bstr_max = 0);\
+ FD_ZERO(&th->readfds);\
+ FD_ZERO(&th->writefds);\
+ FD_ZERO(&th->exceptfds);\
+ th->delay = 0.0;\
+ th->join = 0;\
+\
+ th->frame = 0;\
+ th->scope = 0;\
+ th->klass = 0;\
+ th->wrapper = 0;\
+ th->cref = ruby_cref;\
+ th->dyna_vars = ruby_dyna_vars;\
+ th->block = 0;\
+ th->iter = 0;\
+ th->tag = 0;\
+ th->tracing = 0;\
+ th->errinfo = Qnil;\
+ th->last_status = 0;\
+ th->last_line = 0;\
+ th->last_match = Qnil;\
+ th->abort = 0;\
+ th->priority = 0;\
+ th->thgroup = thgroup_default;\
+ th->locals = 0;\
+ th->thread = 0;\
+ if (curr_thread == 0) {\
+ th->sandbox = Qnil;\
+ } else {\
+ th->sandbox = curr_thread->sandbox;\
+ }\
+} while (0)
+
+static rb_thread_t
+rb_thread_alloc(klass)
+ VALUE klass;
+{
+ rb_thread_t th;
+ struct RVarmap *vars;
+
+ THREAD_ALLOC(th);
+ th->thread = Data_Wrap_Struct(klass, thread_mark, thread_free, th);
+
+ for (vars = th->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ return th;
+}
+
+static int thread_init;
+
+#if defined(_THREAD_SAFE)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ /* cause EINTR */
+}
+
+static int time_thread_alive_p = 0;
+static pthread_t time_thread;
+
+static void*
+thread_timer(dummy)
+ void *dummy;
+{
+#ifdef _THREAD_SAFE
+#define test_cancel() pthread_testcancel()
+#else
+#define test_cancel() /* void */
+#endif
+
+ sigset_t all_signals;
+
+ sigfillset(&all_signals);
+ pthread_sigmask(SIG_BLOCK, &all_signals, 0);
+
+ for (;;) {
+#ifdef HAVE_NANOSLEEP
+ struct timespec req, rem;
+
+ test_cancel();
+ req.tv_sec = 0;
+ req.tv_nsec = 10000000;
+ nanosleep(&req, &rem);
+#else
+ struct timeval tv;
+
+ test_cancel();
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ select(0, NULL, NULL, NULL, &tv);
+#endif
+ if (!rb_thread_critical) {
+ rb_thread_pending = 1;
+ if (rb_trap_immediate) {
+ pthread_kill(ruby_thid, SIGVTALRM);
+ }
+ }
+ }
+#undef test_cancel
+}
+
+void
+rb_thread_start_timer()
+{
+}
+
+void
+rb_thread_stop_timer()
+{
+}
+
+void
+rb_child_atfork()
+{
+ time_thread_alive_p = 0;
+}
+
+void
+rb_thread_cancel_timer()
+{
+#ifdef _THREAD_SAFE
+ if( time_thread_alive_p )
+ {
+ pthread_cancel( time_thread );
+ pthread_join( time_thread, NULL );
+ time_thread_alive_p = 0;
+ }
+ thread_init = 0;
+#endif
+}
+#elif defined(HAVE_SETITIMER)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ if (!rb_thread_critical) {
+ rb_thread_pending = 1;
+ }
+ /* cause EINTR */
+}
+
+void
+rb_thread_start_timer()
+{
+ struct itimerval tval;
+
+ if (!thread_init) return;
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 10000;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+}
+
+void
+rb_thread_stop_timer()
+{
+ struct itimerval tval;
+
+ if (!thread_init) return;
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+}
+
+void
+rb_thread_cancel_timer()
+{
+}
+
+#else /* !(_THREAD_SAFE || HAVE_SETITIMER) */
+int rb_thread_tick = THREAD_TICK;
+
+void
+rb_thread_cancel_timer()
+{
+}
+#endif
+
+static VALUE
+rb_thread_start_0(fn, arg, th)
+ VALUE (*fn)();
+ void *arg;
+ rb_thread_t th;
+{
+ volatile rb_thread_t th_save = th;
+ volatile VALUE thread = th->thread;
+ struct BLOCK *volatile saved_block = 0;
+ enum rb_thread_status status;
+ int state;
+
+ if (OBJ_FROZEN(curr_thread->thgroup)) {
+ rb_raise(rb_eThreadError,
+ "can't start a new thread (frozen ThreadGroup)");
+ }
+
+ if (!thread_init) {
+ thread_init = 1;
+#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
+#if defined(POSIX_SIGNAL)
+ posix_signal(SIGVTALRM, catch_timer);
+#else
+ signal(SIGVTALRM, catch_timer);
+#endif
+
+#ifdef _THREAD_SAFE
+ pthread_create(&time_thread, 0, thread_timer, 0);
+ time_thread_alive_p = 1;
+ pthread_atfork(0, 0, rb_child_atfork);
+#else
+ rb_thread_start_timer();
+#endif
+#endif
+ }
+
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return thread;
+ }
+
+ if (ruby_block) { /* should nail down higher blocks */
+ struct BLOCK dummy;
+
+ dummy.prev = ruby_block;
+ blk_copy_prev(&dummy);
+ saved_block = ruby_block = dummy.prev;
+ }
+ scope_dup(ruby_scope);
+
+ if (!th->next) {
+ /* merge in thread list */
+ th->prev = curr_thread;
+ curr_thread->next->prev = th;
+ th->next = curr_thread->next;
+ curr_thread->next = th;
+ th->priority = curr_thread->priority;
+ th->thgroup = curr_thread->thgroup;
+ }
+
+ PUSH_TAG(PROT_THREAD);
+ if ((state = EXEC_TAG()) == 0) {
+ if (THREAD_SAVE_CONTEXT(th) == 0) {
+ curr_thread = th;
+ th->result = (*fn)(arg, th);
+ }
+ th = th_save;
+ }
+ else if (TAG_DST()) {
+ th = th_save;
+ th->result = prot_tag->retval;
+ }
+ POP_TAG();
+ status = th->status;
+
+ if (th == main_thread) ruby_stop(state);
+ rb_thread_remove(th);
+
+ if (saved_block) {
+ blk_free(saved_block);
+ }
+
+ if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) {
+ th->flags |= RAISED_EXCEPTION;
+ if (state == TAG_FATAL) {
+ /* fatal error within this thread, need to stop whole script */
+ main_thread->errinfo = ruby_errinfo;
+ rb_thread_cleanup();
+ }
+ else if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ if (th->safe >= 4) {
+ char buf[32];
+
+ sprintf(buf, "Insecure exit at level %d", th->safe);
+ th->errinfo = rb_exc_new2(rb_eSecurityError, buf);
+ }
+ else {
+ /* delegate exception to main_thread */
+ rb_thread_main_jump(ruby_errinfo, RESTORE_RAISE);
+ }
+ }
+ else if (th->safe < 4 && (ruby_thread_abort || th->abort || RTEST(ruby_debug))) {
+ /* exit on main_thread */
+ error_print();
+ rb_thread_main_jump(ruby_errinfo, RESTORE_EXIT);
+ }
+ else {
+ th->errinfo = ruby_errinfo;
+ }
+ }
+ rb_thread_schedule();
+ ruby_stop(0); /* last thread termination */
+ return 0; /* not reached */
+}
+
+VALUE
+rb_thread_create(fn, arg)
+ VALUE (*fn)();
+ void *arg;
+{
+ Init_stack((void *)&arg);
+ return rb_thread_start_0(fn, arg, rb_thread_alloc(rb_cThread));
+}
+
+static VALUE
+rb_thread_yield(arg, th)
+ VALUE arg;
+ rb_thread_t th;
+{
+ const ID *tbl;
+
+ scope_dup(ruby_block->scope);
+
+ tbl = ruby_scope->local_tbl;
+ if (tbl) {
+ int n = *tbl++;
+ for (tbl += 2, n -= 2; n > 0; --n) { /* skip first 2 ($_ and $~) */
+ ID id = *tbl++;
+ if (id != 0 && !rb_is_local_id(id)) /* push flip states */
+ rb_dvar_push(id, Qfalse);
+ }
+ }
+ rb_dvar_push('_', Qnil);
+ rb_dvar_push('~', Qnil);
+ ruby_block->dyna_vars = ruby_dyna_vars;
+
+ return rb_yield_0(arg, 0, 0, YIELD_LAMBDA_CALL, Qtrue);
+}
+
+/*
+ * call-seq:
+ * Thread.new([arg]*) {|args| block } => thread
+ *
+ * Creates and runs a new thread to execute the instructions given in
+ * <i>block</i>. Any arguments passed to <code>Thread::new</code> are passed
+ * into the block.
+ *
+ * x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
+ * a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
+ * x.join # Let the threads finish before
+ * a.join # main thread exits...
+ *
+ * <em>produces:</em>
+ *
+ * abxyzc
+ */
+
+static VALUE
+rb_thread_s_new(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ rb_thread_t th = rb_thread_alloc(klass);
+ volatile VALUE *pos;
+
+ pos = th->stk_pos;
+ rb_obj_call_init(th->thread, argc, argv);
+ if (th->stk_pos == 0) {
+ rb_raise(rb_eThreadError, "uninitialized thread - check `%s#initialize'",
+ rb_class2name(klass));
+ }
+
+ return th->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.new([arg]*) {|args| block } => thread
+ *
+ * Creates and runs a new thread to execute the instructions given in
+ * <i>block</i>. Any arguments passed to <code>Thread::new</code> are passed
+ * into the block.
+ *
+ * x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
+ * a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
+ * x.join # Let the threads finish before
+ * a.join # main thread exits...
+ *
+ * <em>produces:</em>
+ *
+ * abxyzc
+ */
+
+static VALUE
+rb_thread_initialize(thread, args)
+ VALUE thread, args;
+{
+ rb_thread_t th;
+
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eThreadError, "must be called with a block");
+ }
+ th = rb_thread_check(thread);
+ if (th->stk_max) {
+ NODE *node = th->node;
+ if (!node) {
+ rb_raise(rb_eThreadError, "already initialized thread");
+ }
+ rb_raise(rb_eThreadError, "already initialized thread - %s:%d",
+ node->nd_file, nd_line(node));
+ }
+ return rb_thread_start_0(rb_thread_yield, args, th);
+}
+
+
+/*
+ * call-seq:
+ * Thread.start([args]*) {|args| block } => thread
+ * Thread.fork([args]*) {|args| block } => thread
+ *
+ * Basically the same as <code>Thread::new</code>. However, if class
+ * <code>Thread</code> is subclassed, then calling <code>start</code> in that
+ * subclass will not invoke the subclass's <code>initialize</code> method.
+ */
+
+static VALUE
+rb_thread_start(klass, args)
+ VALUE klass, args;
+{
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eThreadError, "must be called with a block");
+ }
+ return rb_thread_start_0(rb_thread_yield, args, rb_thread_alloc(klass));
+}
+
+
+/*
+ * call-seq:
+ * thr.value => obj
+ *
+ * Waits for <i>thr</i> to complete (via <code>Thread#join</code>) and returns
+ * its value.
+ *
+ * a = Thread.new { 2 + 2 }
+ * a.value #=> 4
+ */
+
+static VALUE
+rb_thread_value(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ while (!rb_thread_join0(th, DELAY_INFTY));
+
+ return th->result;
+}
+
+
+/*
+ * call-seq:
+ * thr.status => string, false or nil
+ *
+ * Returns the status of <i>thr</i>: ``<code>sleep</code>'' if <i>thr</i> is
+ * sleeping or waiting on I/O, ``<code>run</code>'' if <i>thr</i> is executing,
+ * ``<code>aborting</code>'' if <i>thr</i> is aborting, <code>false</code> if
+ * <i>thr</i> terminated normally, and <code>nil</code> if <i>thr</i>
+ * terminated with an exception.
+ *
+ * a = Thread.new { raise("die now") }
+ * b = Thread.new { Thread.stop }
+ * c = Thread.new { Thread.exit }
+ * d = Thread.new { sleep }
+ * Thread.critical = true
+ * d.kill #=> #<Thread:0x401b3678 aborting>
+ * a.status #=> nil
+ * b.status #=> "sleep"
+ * c.status #=> false
+ * d.status #=> "aborting"
+ * Thread.current.status #=> "run"
+ */
+
+static VALUE
+rb_thread_status_name(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) {
+ if (!NIL_P(th->errinfo) && (th->flags & RAISED_EXCEPTION))
+ return Qnil;
+ return Qfalse;
+ }
+
+ return rb_str_new2(thread_status_name(th->status));
+}
+
+
+/*
+ * call-seq:
+ * thr.alive? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is running or sleeping.
+ *
+ * thr = Thread.new { }
+ * thr.join #=> #<Thread:0x401b3fb0 dead>
+ * Thread.current.alive? #=> true
+ * thr.alive? #=> false
+ */
+
+VALUE
+rb_thread_alive_p(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) return Qfalse;
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * thr.stop? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is dead or sleeping.
+ *
+ * a = Thread.new { Thread.stop }
+ * b = Thread.current
+ * a.stop? #=> true
+ * b.stop? #=> false
+ */
+
+static VALUE
+rb_thread_stop_p(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) return Qtrue;
+ if (th->status == THREAD_STOPPED) return Qtrue;
+ return Qfalse;
+}
+
+static void
+rb_thread_wait_other_threads()
+{
+ rb_thread_t th;
+ int found;
+
+ /* wait other threads to terminate */
+ while (curr_thread != curr_thread->next) {
+ found = 0;
+ FOREACH_THREAD(th) {
+ if (th != curr_thread && th->status != THREAD_STOPPED) {
+ found = 1;
+ break;
+ }
+ }
+ END_FOREACH(th);
+ if (!found) return;
+ rb_thread_schedule();
+ }
+}
+
+static void
+rb_thread_cleanup()
+{
+ rb_thread_t curr, th;
+
+ curr = curr_thread;
+ while (curr->status == THREAD_KILLED) {
+ curr = curr->prev;
+ }
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->status != THREAD_KILLED) {
+ rb_thread_ready(th);
+ if (th != main_thread) {
+ th->thgroup = 0;
+ th->priority = 0;
+ th->status = THREAD_TO_KILL;
+ RDATA(th->thread)->dfree = NULL;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+}
+
+int rb_thread_critical;
+
+
+/*
+ * call-seq:
+ * Thread.critical => true or false
+ *
+ * Returns the status of the global ``thread critical'' condition.
+ */
+
+static VALUE
+rb_thread_critical_get()
+{
+ return rb_thread_critical?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * Thread.critical= boolean => true or false
+ *
+ * Sets the status of the global ``thread critical'' condition and returns
+ * it. When set to <code>true</code>, prohibits scheduling of any existing
+ * thread. Does not block new threads from being created and run. Certain
+ * thread operations (such as stopping or killing a thread, sleeping in the
+ * current thread, and raising an exception) may cause a thread to be scheduled
+ * even when in a critical section. <code>Thread::critical</code> is not
+ * intended for daily use: it is primarily there to support folks writing
+ * threading libraries.
+ */
+
+static VALUE
+rb_thread_critical_set(obj, val)
+ VALUE obj, val;
+{
+ rb_thread_critical = RTEST(val);
+ return val;
+}
+
+void
+rb_thread_interrupt()
+{
+ rb_thread_critical = 0;
+ rb_thread_ready(main_thread);
+ if (curr_thread == main_thread) {
+ rb_interrupt();
+ }
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_INTERRUPT);
+}
+
+void
+rb_thread_signal_raise(sig)
+ int sig;
+{
+ rb_thread_critical = 0;
+ if (curr_thread == main_thread) {
+ VALUE argv[1];
+
+ rb_thread_ready(curr_thread);
+ argv[0] = INT2FIX(sig);
+ rb_exc_raise(rb_class_new_instance(1, argv, rb_eSignal));
+ }
+ rb_thread_ready(main_thread);
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ th_sig = sig;
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_SIGNAL);
+}
+
+void
+rb_thread_trap_eval(cmd, sig, safe)
+ VALUE cmd;
+ int sig, safe;
+{
+ rb_thread_critical = 0;
+ if (curr_thread == main_thread) {
+ rb_trap_eval(cmd, sig, safe);
+ return;
+ }
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ th_cmd = cmd;
+ th_sig = sig;
+ th_safe = safe;
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_TRAP);
+}
+
+void
+rb_thread_signal_exit()
+{
+ VALUE args[2];
+
+ rb_thread_critical = 0;
+ if (curr_thread == main_thread) {
+ rb_thread_ready(curr_thread);
+ rb_exit(EXIT_SUCCESS);
+ }
+ args[0] = INT2NUM(EXIT_SUCCESS);
+ args[1] = rb_str_new2("exit");
+ rb_thread_ready(main_thread);
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ rb_thread_main_jump(rb_class_new_instance(2, args, rb_eSystemExit),
+ RESTORE_EXIT);
+}
+
+static VALUE
+rb_thread_raise(argc, argv, th)
+ int argc;
+ VALUE *argv;
+ rb_thread_t th;
+{
+ volatile rb_thread_t th_save = th;
+ VALUE exc;
+
+ if (!th->next) {
+ rb_raise(rb_eArgError, "unstarted thread");
+ }
+ if (rb_thread_dead(th)) return Qnil;
+ exc = rb_make_exception(argc, argv);
+ if (curr_thread == th) {
+ rb_raise_jump(exc);
+ }
+
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return th_save->thread;
+ }
+ }
+
+ rb_thread_ready(th);
+ curr_thread = th;
+
+ th_raise_exception = exc;
+ th_raise_node = ruby_current_node;
+ rb_thread_restore_context(curr_thread, RESTORE_RAISE);
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * call-seq:
+ * thr.raise(exception)
+ *
+ * Raises an exception (see <code>Kernel::raise</code>) from <i>thr</i>. The
+ * caller does not have to be <i>thr</i>.
+ *
+ * Thread.abort_on_exception = true
+ * a = Thread.new { sleep(200) }
+ * a.raise("Gotcha")
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:3: Gotcha (RuntimeError)
+ * from prog.rb:2:in `initialize'
+ * from prog.rb:2:in `new'
+ * from prog.rb:2
+ */
+
+static VALUE
+rb_thread_raise_m(argc, argv, thread)
+ int argc;
+ VALUE *argv;
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (ruby_safe_level > th->safe) {
+ rb_secure(4);
+ }
+ rb_thread_raise(argc, argv, th);
+ return Qnil; /* not reached */
+}
+
+VALUE
+rb_thread_local_aref(thread, id)
+ VALUE thread;
+ ID id;
+{
+ rb_thread_t th;
+ VALUE val;
+
+ th = rb_thread_check(thread);
+ if (ruby_safe_level >= 4 && th != curr_thread) {
+ rb_raise(rb_eSecurityError, "Insecure: thread locals");
+ }
+ if (!th->locals) return Qnil;
+ if (st_lookup(th->locals, id, &val)) {
+ return val;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * thr[sym] => obj or nil
+ *
+ * Attribute Reference---Returns the value of a thread-local variable, using
+ * either a symbol or a string name. If the specified variable does not exist,
+ * returns <code>nil</code>.
+ *
+ * a = Thread.new { Thread.current["name"] = "A"; Thread.stop }
+ * b = Thread.new { Thread.current[:name] = "B"; Thread.stop }
+ * c = Thread.new { Thread.current["name"] = "C"; Thread.stop }
+ * Thread.list.each {|x| puts "#{x.inspect}: #{x[:name]}" }
+ *
+ * <em>produces:</em>
+ *
+ * #<Thread:0x401b3b3c sleep>: C
+ * #<Thread:0x401b3bc8 sleep>: B
+ * #<Thread:0x401b3c68 sleep>: A
+ * #<Thread:0x401bdf4c run>:
+ */
+
+static VALUE
+rb_thread_aref(thread, id)
+ VALUE thread, id;
+{
+ return rb_thread_local_aref(thread, rb_to_id(id));
+}
+
+VALUE
+rb_thread_local_aset(thread, id, val)
+ VALUE thread;
+ ID id;
+ VALUE val;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (ruby_safe_level >= 4 && th != curr_thread) {
+ rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
+ }
+ if (OBJ_FROZEN(thread)) rb_error_frozen("thread locals");
+
+ if (!th->locals) {
+ th->locals = st_init_numtable();
+ }
+ if (NIL_P(val)) {
+ st_delete(th->locals, (st_data_t*)&id, 0);
+ return Qnil;
+ }
+ st_insert(th->locals, id, val);
+
+ return val;
+}
+
+
+/*
+ * call-seq:
+ * thr[sym] = obj => obj
+ *
+ * Attribute Assignment---Sets or creates the value of a thread-local variable,
+ * using either a symbol or a string. See also <code>Thread#[]</code>.
+ */
+
+static VALUE
+rb_thread_aset(thread, id, val)
+ VALUE thread, id, val;
+{
+ return rb_thread_local_aset(thread, rb_to_id(id), val);
+}
+
+
+/*
+ * call-seq:
+ * thr.key?(sym) => true or false
+ *
+ * Returns <code>true</code> if the given string (or symbol) exists as a
+ * thread-local variable.
+ *
+ * me = Thread.current
+ * me[:oliver] = "a"
+ * me.key?(:oliver) #=> true
+ * me.key?(:stanley) #=> false
+ */
+
+static VALUE
+rb_thread_key_p(thread, id)
+ VALUE thread, id;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (!th->locals) return Qfalse;
+ if (st_lookup(th->locals, rb_to_id(id), 0))
+ return Qtrue;
+ return Qfalse;
+}
+
+static int
+thread_keys_i(key, value, ary)
+ ID key;
+ VALUE value, ary;
+{
+ rb_ary_push(ary, ID2SYM(key));
+ return ST_CONTINUE;
+}
+
+
+/*
+ * call-seq:
+ * thr.keys => array
+ *
+ * Returns an an array of the names of the thread-local variables (as Symbols).
+ *
+ * thr = Thread.new do
+ * Thread.current[:cat] = 'meow'
+ * Thread.current["dog"] = 'woof'
+ * end
+ * thr.join #=> #<Thread:0x401b3f10 dead>
+ * thr.keys #=> [:dog, :cat]
+ */
+
+static VALUE
+rb_thread_keys(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+ VALUE ary = rb_ary_new();
+
+ if (th->locals) {
+ st_foreach(th->locals, thread_keys_i, ary);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * thr.inspect => string
+ *
+ * Dump the name, id, and status of _thr_ to a string.
+ */
+
+static VALUE
+rb_thread_inspect(thread)
+ VALUE thread;
+{
+ char *cname = rb_obj_classname(thread);
+ rb_thread_t th = rb_thread_check(thread);
+ const char *status = thread_status_name(th->status);
+ VALUE str;
+ size_t len = strlen(cname)+7+16+9+1;
+
+ str = rb_str_new(0, len); /* 7:tags 16:addr 9:status 1:nul */
+ snprintf(RSTRING(str)->ptr, len, "#<%s:0x%lx %s>", cname, thread, status);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ OBJ_INFECT(str, thread);
+
+ return str;
+}
+
+void
+rb_thread_atfork()
+{
+ rb_thread_t th;
+
+ if (rb_thread_alone()) return;
+ FOREACH_THREAD(th) {
+ if (th != curr_thread) {
+ rb_thread_die(th);
+ }
+ }
+ END_FOREACH(th);
+ main_thread = curr_thread;
+ curr_thread->next = curr_thread;
+ curr_thread->prev = curr_thread;
+}
+
+
+static void
+cc_purge(cc)
+ rb_thread_t cc;
+{
+ /* free continuation's stack if it has just died */
+ if (NIL_P(cc->thread)) return;
+ if (rb_thread_check(cc->thread)->status == THREAD_KILLED) {
+ cc->thread = Qnil;
+ rb_thread_die(cc); /* can't possibly activate this stack */
+ }
+}
+
+static void
+cc_mark(cc)
+ rb_thread_t cc;
+{
+ /* mark this continuation's stack only if its parent thread is still alive */
+ cc_purge(cc);
+ thread_mark(cc);
+}
+
+static rb_thread_t
+rb_cont_check(data)
+ VALUE data;
+{
+ if (TYPE(data) != T_DATA || RDATA(data)->dmark != (RUBY_DATA_FUNC)cc_mark) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Continuation)",
+ rb_obj_classname(data));
+ }
+ return (rb_thread_t)RDATA(data)->data;
+}
+
+/*
+ * Document-class: Continuation
+ *
+ * Continuation objects are generated by
+ * <code>Kernel#callcc</code>. They hold a return address and execution
+ * context, allowing a nonlocal return to the end of the
+ * <code>callcc</code> block from anywhere within a program.
+ * Continuations are somewhat analogous to a structured version of C's
+ * <code>setjmp/longjmp</code> (although they contain more state, so
+ * you might consider them closer to threads).
+ *
+ * For instance:
+ *
+ * arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
+ * callcc{|$cc|}
+ * puts(message = arr.shift)
+ * $cc.call unless message =~ /Max/
+ *
+ * <em>produces:</em>
+ *
+ * Freddie
+ * Herbie
+ * Ron
+ * Max
+ *
+ * This (somewhat contrived) example allows the inner loop to abandon
+ * processing early:
+ *
+ * callcc {|cont|
+ * for i in 0..4
+ * print "\n#{i}: "
+ * for j in i*5...(i+1)*5
+ * cont.call() if j == 17
+ * printf "%3d", j
+ * end
+ * end
+ * }
+ * print "\n"
+ *
+ * <em>produces:</em>
+ *
+ * 0: 0 1 2 3 4
+ * 1: 5 6 7 8 9
+ * 2: 10 11 12 13 14
+ * 3: 15 16
+ */
+
+VALUE rb_cCont;
+
+/*
+ * call-seq:
+ * callcc {|cont| block } => obj
+ *
+ * Generates a <code>Continuation</code> object, which it passes to the
+ * associated block. Performing a <em>cont</em><code>.call</code> will
+ * cause the <code>callcc</code> to return (as will falling through the
+ * end of the block). The value returned by the <code>callcc</code> is
+ * the value of the block, or the value passed to
+ * <em>cont</em><code>.call</code>. See class <code>Continuation</code>
+ * for more details. Also see <code>Kernel::throw</code> for
+ * an alternative mechanism for unwinding a call stack.
+ */
+
+static VALUE
+rb_callcc(self)
+ VALUE self;
+{
+ volatile VALUE cont;
+ rb_thread_t th;
+ volatile rb_thread_t th_save;
+ struct tag *tag;
+ struct RVarmap *vars;
+
+ THREAD_ALLOC(th);
+ /* must finish th initialization before any possible gc.
+ * brent@mbari.org */
+ th->thread = curr_thread->thread;
+ th->thgroup = cont_protect;
+ cont = Data_Wrap_Struct(rb_cCont, cc_mark, thread_free, th);
+
+ scope_dup(ruby_scope);
+ for (tag=prot_tag; tag; tag=tag->prev) {
+ scope_dup(tag->scope);
+ }
+
+ for (vars = ruby_dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ th_save = th;
+ if (THREAD_SAVE_CONTEXT(th)) {
+ return th_save->result;
+ }
+ else {
+ return rb_yield(cont);
+ }
+}
+
+/*
+ * call-seq:
+ * cont.call(args, ...)
+ * cont[args, ...]
+ *
+ * Invokes the continuation. The program continues from the end of the
+ * <code>callcc</code> block. If no arguments are given, the original
+ * <code>callcc</code> returns <code>nil</code>. If one argument is
+ * given, <code>callcc</code> returns it. Otherwise, an array
+ * containing <i>args</i> is returned.
+ *
+ * callcc {|cont| cont.call } #=> nil
+ * callcc {|cont| cont.call 1 } #=> 1
+ * callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3]
+ */
+
+static VALUE
+rb_cont_call(argc, argv, cont)
+ int argc;
+ VALUE *argv;
+ VALUE cont;
+{
+ rb_thread_t th = rb_cont_check(cont);
+
+ if (th->thread != curr_thread->thread) {
+ rb_raise(rb_eRuntimeError, "continuation called across threads");
+ }
+ if (th->thgroup != cont_protect) {
+ rb_raise(rb_eRuntimeError, "continuation called across trap");
+ }
+ switch (argc) {
+ case 0:
+ th->result = Qnil;
+ break;
+ case 1:
+ th->result = argv[0];
+ break;
+ default:
+ th->result = rb_ary_new4(argc, argv);
+ break;
+ }
+
+ rb_thread_restore_context(th, RESTORE_NORMAL);
+ return Qnil;
+}
+
+struct thgroup {
+ int enclosed;
+ VALUE group;
+};
+
+
+/*
+ * Document-class: ThreadGroup
+ *
+ * <code>ThreadGroup</code> provides a means of keeping track of a number of
+ * threads as a group. A <code>Thread</code> can belong to only one
+ * <code>ThreadGroup</code> at a time; adding a thread to a new group will
+ * remove it from any previous group.
+ *
+ * Newly created threads belong to the same group as the thread from which they
+ * were created.
+ */
+
+static VALUE thgroup_s_alloc _((VALUE));
+static VALUE
+thgroup_s_alloc(klass)
+ VALUE klass;
+{
+ VALUE group;
+ struct thgroup *data;
+
+ group = Data_Make_Struct(klass, struct thgroup, 0, free, data);
+ data->enclosed = 0;
+ data->group = group;
+
+ return group;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.list => array
+ *
+ * Returns an array of all existing <code>Thread</code> objects that belong to
+ * this group.
+ *
+ * ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
+ */
+
+static VALUE
+thgroup_list(group)
+ VALUE group;
+{
+ struct thgroup *data;
+ rb_thread_t th;
+ VALUE ary;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ ary = rb_ary_new();
+
+ FOREACH_THREAD(th) {
+ if (th->thgroup == data->group) {
+ rb_ary_push(ary, th->thread);
+ }
+ }
+ END_FOREACH(th);
+
+ return ary;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.enclose => thgrp
+ *
+ * Prevents threads from being added to or removed from the receiving
+ * <code>ThreadGroup</code>. New threads can still be started in an enclosed
+ * <code>ThreadGroup</code>.
+ *
+ * ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
+ * thr = Thread::new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
+ * tg = ThreadGroup::new #=> #<ThreadGroup:0x402752d4>
+ * tg.add thr
+ *
+ * <em>produces:</em>
+ *
+ * ThreadError: can't move from the enclosed thread group
+ */
+
+static VALUE
+thgroup_enclose(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ data->enclosed = 1;
+
+ return group;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.enclosed? => true or false
+ *
+ * Returns <code>true</code> if <em>thgrp</em> is enclosed. See also
+ * ThreadGroup#enclose.
+ */
+
+static VALUE
+thgroup_enclosed_p(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.add(thread) => thgrp
+ *
+ * Adds the given <em>thread</em> to this group, removing it from any other
+ * group to which it may have previously belonged.
+ *
+ * puts "Initial group is #{ThreadGroup::Default.list}"
+ * tg = ThreadGroup.new
+ * t1 = Thread.new { sleep }
+ * t2 = Thread.new { sleep }
+ * puts "t1 is #{t1}"
+ * puts "t2 is #{t2}"
+ * tg.add(t1)
+ * puts "Initial group now #{ThreadGroup::Default.list}"
+ * puts "tg group now #{tg.list}"
+ *
+ * <em>produces:</em>
+ *
+ * Initial group is #<Thread:0x401bdf4c>
+ * t1 is #<Thread:0x401b3c90>
+ * t2 is #<Thread:0x401b3c18>
+ * Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
+ * tg group now #<Thread:0x401b3c90>
+ */
+
+static VALUE
+thgroup_add(group, thread)
+ VALUE group, thread;
+{
+ rb_thread_t th;
+ struct thgroup *data;
+
+ rb_secure(4);
+ th = rb_thread_check(thread);
+
+ if (OBJ_FROZEN(group)) {
+ rb_raise(rb_eThreadError, "can't move to the frozen thread group");
+ }
+ Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
+ }
+
+ if (!th->thgroup) {
+ return Qnil;
+ }
+ if (OBJ_FROZEN(th->thgroup)) {
+ rb_raise(rb_eThreadError, "can't move from the frozen thread group");
+ }
+ Data_Get_Struct(th->thgroup, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move from the enclosed thread group");
+ }
+
+ th->thgroup = group;
+ return group;
+}
+
+
+/*
+ * +Thread+ encapsulates the behavior of a thread of
+ * execution, including the main thread of the Ruby script.
+ *
+ * In the descriptions of the methods in this class, the parameter _sym_
+ * refers to a symbol, which is either a quoted string or a
+ * +Symbol+ (such as <code>:name</code>).
+ */
+
+void
+Init_Thread()
+{
+ VALUE cThGroup;
+
+ rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
+ rb_cThread = rb_define_class("Thread", rb_cObject);
+ rb_undef_alloc_func(rb_cThread);
+
+ rb_define_singleton_method(rb_cThread, "new", rb_thread_s_new, -1);
+ rb_define_method(rb_cThread, "initialize", rb_thread_initialize, -2);
+ rb_define_singleton_method(rb_cThread, "start", rb_thread_start, -2);
+ rb_define_singleton_method(rb_cThread, "fork", rb_thread_start, -2);
+
+ rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0);
+ rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
+ rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
+ rb_define_singleton_method(rb_cThread, "pass", rb_thread_pass, 0);
+ rb_define_singleton_method(rb_cThread, "current", rb_thread_current, 0);
+ rb_define_singleton_method(rb_cThread, "main", rb_thread_main, 0);
+ rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
+
+ rb_define_singleton_method(rb_cThread, "critical", rb_thread_critical_get, 0);
+ rb_define_singleton_method(rb_cThread, "critical=", rb_thread_critical_set, 1);
+
+ rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
+ rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
+
+ rb_define_method(rb_cThread, "run", rb_thread_run, 0);
+ rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
+ rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "kill!", rb_thread_kill_bang, 0);
+ rb_define_method(rb_cThread, "terminate!", rb_thread_kill_bang, 0);
+ rb_define_method(rb_cThread, "exit!", rb_thread_kill_bang, 0);
+ rb_define_method(rb_cThread, "value", rb_thread_value, 0);
+ rb_define_method(rb_cThread, "status", rb_thread_status_name, 0);
+ rb_define_method(rb_cThread, "join", rb_thread_join_m, -1);
+ rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
+ rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
+ rb_define_method(rb_cThread, "raise", rb_thread_raise_m, -1);
+
+ rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
+ rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
+
+ rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
+ rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
+ rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
+ rb_define_method(rb_cThread, "group", rb_thread_group, 0);
+
+ rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
+ rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
+ rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
+ rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
+
+ rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);
+
+ rb_cCont = rb_define_class("Continuation", rb_cObject);
+ rb_undef_alloc_func(rb_cCont);
+ rb_undef_method(CLASS_OF(rb_cCont), "new");
+ rb_define_method(rb_cCont, "call", rb_cont_call, -1);
+ rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
+ rb_define_global_function("callcc", rb_callcc, 0);
+ rb_global_variable(&cont_protect);
+
+ cThGroup = rb_define_class("ThreadGroup", rb_cObject);
+ rb_define_alloc_func(cThGroup, thgroup_s_alloc);
+ rb_define_method(cThGroup, "list", thgroup_list, 0);
+ rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
+ rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
+ rb_define_method(cThGroup, "add", thgroup_add, 1);
+ rb_global_variable(&thgroup_default);
+ thgroup_default = rb_obj_alloc(cThGroup);
+ rb_define_const(cThGroup, "Default", thgroup_default);
+
+ /* allocate main thread */
+ main_thread = rb_thread_alloc(rb_cThread);
+ curr_thread = main_thread->prev = main_thread->next = main_thread;
+}
+
+/*
+ * call-seq:
+ * catch(symbol) {| | block } > obj
+ *
+ * +catch+ executes its block. If a +throw+ is
+ * executed, Ruby searches up its stack for a +catch+ block
+ * with a tag corresponding to the +throw+'s
+ * _symbol_. If found, that block is terminated, and
+ * +catch+ returns the value given to +throw+. If
+ * +throw+ is not called, the block terminates normally, and
+ * the value of +catch+ is the value of the last expression
+ * evaluated. +catch+ expressions may be nested, and the
+ * +throw+ call need not be in lexical scope.
+ *
+ * def routine(n)
+ * puts n
+ * throw :done if n <= 0
+ * routine(n-1)
+ * end
+ *
+ *
+ * catch(:done) { routine(3) }
+ *
+ * <em>produces:</em>
+ *
+ * 3
+ * 2
+ * 1
+ * 0
+ */
+
+static VALUE
+rb_f_catch(dmy, tag)
+ VALUE dmy, tag;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+
+ tag = ID2SYM(rb_to_id(tag));
+ PUSH_TAG(tag);
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_yield_0(tag, 0, 0, 0, Qfalse);
+ }
+ else if (state == TAG_THROW && tag == prot_tag->dst) {
+ val = prot_tag->retval;
+ state = 0;
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+
+ return val;
+}
+
+static VALUE
+catch_i(tag)
+ VALUE tag;
+{
+ return rb_funcall(Qnil, rb_intern("catch"), 1, tag);
+}
+
+VALUE
+rb_catch(tag, func, data)
+ const char *tag;
+ VALUE (*func)();
+ VALUE data;
+{
+ return rb_iterate((VALUE(*)_((VALUE)))catch_i, ID2SYM(rb_intern(tag)), func, data);
+}
+
+/*
+ * call-seq:
+ * throw(symbol [, obj])
+ *
+ * Transfers control to the end of the active +catch+ block
+ * waiting for _symbol_. Raises +NameError+ if there
+ * is no +catch+ block for the symbol. The optional second
+ * parameter supplies a return value for the +catch+ block,
+ * which otherwise defaults to +nil+. For examples, see
+ * <code>Kernel::catch</code>.
+ */
+
+static VALUE
+rb_f_throw(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tag, value;
+ struct tag *tt = prot_tag;
+
+ rb_scan_args(argc, argv, "11", &tag, &value);
+ tag = ID2SYM(rb_to_id(tag));
+
+ while (tt) {
+ if (tt->tag == tag) {
+ tt->dst = tag;
+ tt->retval = value;
+ break;
+ }
+ if (tt->tag == PROT_THREAD) {
+ rb_raise(rb_eThreadError, "uncaught throw `%s' in thread 0x%lx",
+ rb_id2name(SYM2ID(tag)),
+ curr_thread);
+ }
+ tt = tt->prev;
+ }
+ if (!tt) {
+ rb_name_error(SYM2ID(tag), "uncaught throw `%s'", rb_id2name(SYM2ID(tag)));
+ }
+ rb_trap_restore_mask();
+ JUMP_TAG(TAG_THROW);
+#ifndef __GNUC__
+ return Qnil; /* not reached */
+#endif
+}
+
+void
+rb_throw(tag, val)
+ const char *tag;
+ VALUE val;
+{
+ VALUE argv[2];
+
+ argv[0] = ID2SYM(rb_intern(tag));
+ argv[1] = val;
+ rb_f_throw(2, argv);
+}
+/**********************************************************************
+
+ file.c -
+
+ $Author: wyhaines $
+ $Date: 2009-08-25 20:37:21 +0200 (Tue, 25 Aug 2009) $
+ created at: Mon Nov 15 12:24:34 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#ifdef _WIN32
+#include "missing/file.h"
+#endif
+#ifdef __CYGWIN__
+#define OpenFile WINAPI_OpenFile
+#include <windows.h>
+#include <sys/cygwin.h>
+#undef OpenFile
+#endif
+
+#include "ruby.h"
+#include "rubyio.h"
+#include "rubysig.h"
+#include "util.h"
+#include "dln.h"
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+#else
+int flock _((int, int));
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 1024
+#endif
+
+#include <time.h>
+
+VALUE rb_time_new _((time_t, time_t));
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#elif defined HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifndef HAVE_STRING_H
+char *strrchr _((const char*,const char));
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#if !defined HAVE_LSTAT && !defined lstat
+#define lstat stat
+#endif
+#if !HAVE_FSEEKO && !defined(fseeko)
+# define fseeko fseek
+#endif
+
+#ifdef __BEOS__ /* should not change ID if -1 */
+static int
+be_chown(const char *path, uid_t owner, gid_t group)
+{
+ if (owner == -1 || group == -1) {
+ struct stat st;
+ if (stat(path, &st) < 0) return -1;
+ if (owner == -1) owner = st.st_uid;
+ if (group == -1) group = st.st_gid;
+ }
+ return chown(path, owner, group);
+}
+#define chown be_chown
+static int
+be_fchown(int fd, uid_t owner, gid_t group)
+{
+ if (owner == -1 || group == -1) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) return -1;
+ if (owner == -1) owner = st.st_uid;
+ if (group == -1) group = st.st_gid;
+ }
+ return fchown(fd, owner, group);
+}
+#define fchown be_fchown
+#endif /* __BEOS__ */
+
+VALUE rb_cFile;
+VALUE rb_mFileTest;
+VALUE rb_cStat;
+
+static long apply2files _((void (*)(const char *, void *), VALUE, void *));
+static long
+apply2files(func, vargs, arg)
+ void (*func)_((const char *, void *));
+ VALUE vargs;
+ void *arg;
+{
+ long i;
+ VALUE path;
+ struct RArray *args = RARRAY(vargs);
+
+ for (i=0; i<args->len; i++) {
+ path = args->ptr[i];
+ SafeStringValue(path);
+ (*func)(StringValueCStr(path), arg);
+ }
+
+ return args->len;
+}
+
+/*
+ * call-seq:
+ * file.path -> filename
+ *
+ * Returns the pathname used to create <i>file</i> as a string. Does
+ * not normalize the name.
+ *
+ * File.new("testfile").path #=> "testfile"
+ * File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
+ *
+ */
+
+static VALUE
+rb_file_path(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ fptr = RFILE(rb_io_taint_check(obj))->fptr;
+ rb_io_check_initialized(fptr);
+ if (!fptr->path) return Qnil;
+ return rb_tainted_str_new2(fptr->path);
+}
+
+static VALUE
+stat_new_0(klass, st)
+ VALUE klass;
+ struct stat *st;
+{
+ struct stat *nst = 0;
+
+ if (st) {
+ nst = ALLOC(struct stat);
+ *nst = *st;
+ }
+ return Data_Wrap_Struct(klass, NULL, free, nst);
+}
+
+static VALUE
+stat_new(st)
+ struct stat *st;
+{
+ return stat_new_0(rb_cStat, st);
+}
+
+static struct stat*
+get_stat(self)
+ VALUE self;
+{
+ struct stat* st;
+ Data_Get_Struct(self, struct stat, st);
+ if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
+ return st;
+}
+
+/*
+ * call-seq:
+ * stat <=> other_stat => -1, 0, 1
+ *
+ * Compares <code>File::Stat</code> objects by comparing their
+ * respective modification times.
+ *
+ * f1 = File.new("f1", "w")
+ * sleep 1
+ * f2 = File.new("f2", "w")
+ * f1.stat <=> f2.stat #=> -1
+ */
+
+static VALUE
+rb_stat_cmp(self, other)
+ VALUE self, other;
+{
+ if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
+ time_t t1 = get_stat(self)->st_mtime;
+ time_t t2 = get_stat(other)->st_mtime;
+ if (t1 == t2)
+ return INT2FIX(0);
+ else if (t1 < t2)
+ return INT2FIX(-1);
+ else
+ return INT2FIX(1);
+ }
+ return Qnil;
+}
+
+static VALUE rb_stat_dev _((VALUE));
+static VALUE rb_stat_ino _((VALUE));
+static VALUE rb_stat_mode _((VALUE));
+static VALUE rb_stat_nlink _((VALUE));
+static VALUE rb_stat_uid _((VALUE));
+static VALUE rb_stat_gid _((VALUE));
+static VALUE rb_stat_rdev _((VALUE));
+static VALUE rb_stat_size _((VALUE));
+static VALUE rb_stat_blksize _((VALUE));
+static VALUE rb_stat_blocks _((VALUE));
+static VALUE rb_stat_atime _((VALUE));
+static VALUE rb_stat_mtime _((VALUE));
+static VALUE rb_stat_ctime _((VALUE));
+
+/*
+ * call-seq:
+ * stat.dev => fixnum
+ *
+ * Returns an integer representing the device on which <i>stat</i>
+ * resides.
+ *
+ * File.stat("testfile").dev #=> 774
+ */
+
+static VALUE
+rb_stat_dev(self)
+ VALUE self;
+{
+ return INT2NUM(get_stat(self)->st_dev);
+}
+
+/*
+ * call-seq:
+ * stat.dev_major => fixnum
+ *
+ * Returns the major part of <code>File_Stat#dev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").dev_major #=> 2
+ * File.stat("/dev/tty").dev_major #=> 5
+ */
+
+static VALUE
+rb_stat_dev_major(self)
+ VALUE self;
+{
+#if defined(major)
+ long dev = get_stat(self)->st_dev;
+ return ULONG2NUM(major(dev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.dev_minor => fixnum
+ *
+ * Returns the minor part of <code>File_Stat#dev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").dev_minor #=> 1
+ * File.stat("/dev/tty").dev_minor #=> 0
+ */
+
+static VALUE
+rb_stat_dev_minor(self)
+ VALUE self;
+{
+#if defined(minor)
+ long dev = get_stat(self)->st_dev;
+ return ULONG2NUM(minor(dev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.ino => fixnum
+ *
+ * Returns the inode number for <i>stat</i>.
+ *
+ * File.stat("testfile").ino #=> 1083669
+ *
+ */
+
+static VALUE
+rb_stat_ino(self)
+ VALUE self;
+{
+#ifdef HUGE_ST_INO
+ return ULL2NUM(get_stat(self)->st_ino);
+#else
+ return ULONG2NUM(get_stat(self)->st_ino);
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.mode => fixnum
+ *
+ * Returns an integer representing the permission bits of
+ * <i>stat</i>. The meaning of the bits is platform dependent; on
+ * Unix systems, see <code>stat(2)</code>.
+ *
+ * File.chmod(0644, "testfile") #=> 1
+ * s = File.stat("testfile")
+ * sprintf("%o", s.mode) #=> "100644"
+ */
+
+static VALUE
+rb_stat_mode(self)
+ VALUE self;
+{
+#ifdef __BORLANDC__
+ return UINT2NUM((unsigned short)(get_stat(self)->st_mode));
+#else
+ return UINT2NUM(get_stat(self)->st_mode);
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.nlink => fixnum
+ *
+ * Returns the number of hard links to <i>stat</i>.
+ *
+ * File.stat("testfile").nlink #=> 1
+ * File.link("testfile", "testfile.bak") #=> 0
+ * File.stat("testfile").nlink #=> 2
+ *
+ */
+
+static VALUE
+rb_stat_nlink(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_nlink);
+}
+
+/*
+ * call-seq:
+ * stat.uid => fixnum
+ *
+ * Returns the numeric user id of the owner of <i>stat</i>.
+ *
+ * File.stat("testfile").uid #=> 501
+ *
+ */
+
+static VALUE
+rb_stat_uid(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_uid);
+}
+
+/*
+ * call-seq:
+ * stat.gid => fixnum
+ *
+ * Returns the numeric group id of the owner of <i>stat</i>.
+ *
+ * File.stat("testfile").gid #=> 500
+ *
+ */
+
+static VALUE
+rb_stat_gid(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_gid);
+}
+
+/*
+ * call-seq:
+ * stat.rdev => fixnum or nil
+ *
+ * Returns an integer representing the device type on which
+ * <i>stat</i> resides. Returns <code>nil</code> if the operating
+ * system doesn't support this feature.
+ *
+ * File.stat("/dev/fd1").rdev #=> 513
+ * File.stat("/dev/tty").rdev #=> 1280
+ */
+
+static VALUE
+rb_stat_rdev(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_RDEV
+ return ULONG2NUM(get_stat(self)->st_rdev);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.rdev_major => fixnum
+ *
+ * Returns the major part of <code>File_Stat#rdev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").rdev_major #=> 2
+ * File.stat("/dev/tty").rdev_major #=> 5
+ */
+
+static VALUE
+rb_stat_rdev_major(self)
+ VALUE self;
+{
+#if defined(HAVE_ST_RDEV) && defined(major)
+ long rdev = get_stat(self)->st_rdev;
+ return ULONG2NUM(major(rdev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.rdev_minor => fixnum
+ *
+ * Returns the minor part of <code>File_Stat#rdev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").rdev_minor #=> 1
+ * File.stat("/dev/tty").rdev_minor #=> 0
+ */
+
+static VALUE
+rb_stat_rdev_minor(self)
+ VALUE self;
+{
+#if defined(HAVE_ST_RDEV) && defined(minor)
+ long rdev = get_stat(self)->st_rdev;
+ return ULONG2NUM(minor(rdev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.size => fixnum
+ *
+ * Returns the size of <i>stat</i> in bytes.
+ *
+ * File.stat("testfile").size #=> 66
+ */
+
+static VALUE
+rb_stat_size(self)
+ VALUE self;
+{
+ return OFFT2NUM(get_stat(self)->st_size);
+}
+
+/*
+ * call-seq:
+ * stat.blksize => integer or nil
+ *
+ * Returns the native file system's block size. Will return <code>nil</code>
+ * on platforms that don't support this information.
+ *
+ * File.stat("testfile").blksize #=> 4096
+ *
+ */
+
+static VALUE
+rb_stat_blksize(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_BLKSIZE
+ return ULONG2NUM(get_stat(self)->st_blksize);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.blocks => integer or nil
+ *
+ * Returns the number of native file system blocks allocated for this
+ * file, or <code>nil</code> if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").blocks #=> 2
+ */
+
+static VALUE
+rb_stat_blocks(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_BLOCKS
+ return ULONG2NUM(get_stat(self)->st_blocks);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.atime => time
+ *
+ * Returns the last access time for this file as an object of class
+ * <code>Time</code>.
+ *
+ * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
+ *
+ */
+
+static VALUE
+rb_stat_atime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.mtime -> aTime
+ *
+ * Returns the modification time of <i>stat</i>.
+ *
+ * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_stat_mtime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.ctime -> aTime
+ *
+ * Returns the change time for <i>stat</i> (that is, the time
+ * directory information about the file was changed, not the file
+ * itself).
+ *
+ * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_stat_ctime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_ctime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.inspect => string
+ *
+ * Produce a nicely formatted description of <i>stat</i>.
+ *
+ * File.stat("/etc/passwd").inspect
+ * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
+ * nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
+ * blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
+ * mtime=Fri Sep 12 15:41:41 CDT 2003,
+ * ctime=Mon Oct 27 11:20:27 CST 2003>"
+ */
+
+static VALUE
+rb_stat_inspect(self)
+ VALUE self;
+{
+ VALUE str;
+ int i;
+ static const struct {
+ const char *name;
+ VALUE (*func)_((VALUE));
+ } member[] = {
+ {"dev", rb_stat_dev},
+ {"ino", rb_stat_ino},
+ {"mode", rb_stat_mode},
+ {"nlink", rb_stat_nlink},
+ {"uid", rb_stat_uid},
+ {"gid", rb_stat_gid},
+ {"rdev", rb_stat_rdev},
+ {"size", rb_stat_size},
+ {"blksize", rb_stat_blksize},
+ {"blocks", rb_stat_blocks},
+ {"atime", rb_stat_atime},
+ {"mtime", rb_stat_mtime},
+ {"ctime", rb_stat_ctime},
+ };
+
+ str = rb_str_buf_new2("#<");
+ rb_str_buf_cat2(str, rb_obj_classname(self));
+ rb_str_buf_cat2(str, " ");
+
+ for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
+ VALUE v;
+
+ if (i > 0) {
+ rb_str_buf_cat2(str, ", ");
+ }
+ rb_str_buf_cat2(str, member[i].name);
+ rb_str_buf_cat2(str, "=");
+ v = (*member[i].func)(self);
+ if (i == 2) { /* mode */
+ char buf[32];
+
+ sprintf(buf, "0%lo", NUM2ULONG(v));
+ rb_str_buf_cat2(str, buf);
+ }
+ else if (i == 0 || i == 6) { /* dev/rdev */
+ char buf[32];
+
+ sprintf(buf, "0x%lx", NUM2ULONG(v));
+ rb_str_buf_cat2(str, buf);
+ }
+ else {
+ rb_str_append(str, rb_inspect(v));
+ }
+ }
+ rb_str_buf_cat2(str, ">");
+ OBJ_INFECT(str, self);
+
+ return str;
+}
+
+static int
+rb_stat(file, st)
+ VALUE file;
+ struct stat *st;
+{
+ VALUE tmp;
+
+ tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
+ if (!NIL_P(tmp)) {
+ OpenFile *fptr;
+
+ rb_secure(2);
+ GetOpenFile(tmp, fptr);
+ return fstat(fileno(fptr->f), st);
+ }
+ SafeStringValue(file);
+ return stat(StringValueCStr(file), st);
+}
+
+#ifdef _WIN32
+static HANDLE
+w32_io_info(file, st)
+ VALUE *file;
+ BY_HANDLE_FILE_INFORMATION *st;
+{
+ VALUE tmp;
+ HANDLE f, ret = 0;
+
+ tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
+ if (!NIL_P(tmp)) {
+ OpenFile *fptr;
+
+ GetOpenFile(tmp, fptr);
+ f = (HANDLE)rb_w32_get_osfhandle(fileno(fptr->f));
+ if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
+ }
+ else {
+ SafeStringValue(*file);
+ f = CreateFile(StringValueCStr(*file), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (f == INVALID_HANDLE_VALUE) return f;
+ ret = f;
+ }
+ if (GetFileType(f) == FILE_TYPE_DISK) {
+ ZeroMemory(st, sizeof(*st));
+ if (GetFileInformationByHandle(f, st)) return ret;
+ }
+ if (ret) CloseHandle(ret);
+ return INVALID_HANDLE_VALUE;
+}
+#endif
+
+/*
+ * call-seq:
+ * File.stat(file_name) => stat
+ *
+ * Returns a <code>File::Stat</code> object for the named file (see
+ * <code>File::Stat</code>).
+ *
+ * File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_stat(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ SafeStringValue(fname);
+ if (rb_stat(fname, &st) < 0) {
+ rb_sys_fail(StringValueCStr(fname));
+ }
+ return stat_new(&st);
+}
+
+/*
+ * call-seq:
+ * ios.stat => stat
+ *
+ * Returns status information for <em>ios</em> as an object of type
+ * <code>File::Stat</code>.
+ *
+ * f = File.new("testfile")
+ * s = f.stat
+ * "%o" % s.mode #=> "100644"
+ * s.blksize #=> 4096
+ * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
+ *
+ */
+
+static VALUE
+rb_io_stat(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+}
+
+/*
+ * call-seq:
+ * File.lstat(file_name) => stat
+ *
+ * Same as <code>File::stat</code>, but does not follow the last symbolic
+ * link. Instead, reports on the link itself.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.stat("testfile").size #=> 66
+ * File.lstat("link2test").size #=> 8
+ * File.stat("link2test").size #=> 66
+ *
+ */
+
+static VALUE
+rb_file_s_lstat(klass, fname)
+ VALUE klass, fname;
+{
+#ifdef HAVE_LSTAT
+ struct stat st;
+
+ SafeStringValue(fname);
+ if (lstat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+ return stat_new(&st);
+#else
+ return rb_file_s_stat(klass, fname);
+#endif
+}
+
+/*
+ * call-seq:
+ * file.lstat => stat
+ *
+ * Same as <code>IO#stat</code>, but does not follow the last symbolic
+ * link. Instead, reports on the link itself.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.stat("testfile").size #=> 66
+ * f = File.new("link2test")
+ * f.lstat.size #=> 8
+ * f.stat.size #=> 66
+ */
+
+static VALUE
+rb_file_lstat(obj)
+ VALUE obj;
+{
+#ifdef HAVE_LSTAT
+ OpenFile *fptr;
+ struct stat st;
+
+ rb_secure(2);
+ GetOpenFile(obj, fptr);
+ if (!fptr->path) return Qnil;
+ if (lstat(fptr->path, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+#else
+ return rb_io_stat(obj);
+#endif
+}
+
+#ifndef HAVE_GROUP_MEMBER
+static int
+group_member(gid)
+ GETGROUPS_T gid;
+{
+#ifndef _WIN32
+ if (getgid() == gid || getegid() == gid)
+ return Qtrue;
+
+# ifdef HAVE_GETGROUPS
+# ifndef NGROUPS
+# ifdef NGROUPS_MAX
+# define NGROUPS NGROUPS_MAX
+# else
+# define NGROUPS 32
+# endif
+# endif
+ {
+ GETGROUPS_T gary[NGROUPS];
+ int anum;
+
+ anum = getgroups(NGROUPS, gary);
+ while (--anum >= 0)
+ if (gary[anum] == gid)
+ return Qtrue;
+ }
+# endif
+#endif
+ return Qfalse;
+}
+#endif
+
+#ifndef S_IXUGO
+# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif
+
+#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
+#define USE_GETEUID 1
+#endif
+
+#ifndef HAVE_EACCESS
+int
+eaccess(path, mode)
+ const char *path;
+ int mode;
+{
+#ifdef USE_GETEUID
+ struct stat st;
+ int euid;
+
+ if (stat(path, &st) < 0) return -1;
+
+ euid = geteuid();
+
+ if (euid == 0) {
+ /* Root can read or write any file. */
+ if (!(mode & X_OK))
+ return 0;
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return 0;
+
+ return -1;
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (group_member(st.st_gid))
+ mode <<= 3;
+
+ if ((st.st_mode & mode) == mode) return 0;
+
+ return -1;
+#else
+# if _MSC_VER >= 1400
+ mode &= 6;
+# endif
+ return access(path, mode);
+#endif
+}
+#endif
+
+
+/*
+ * Document-class: FileTest
+ *
+ * <code>FileTest</code> implements file test operations similar to
+ * those used in <code>File::Stat</code>. It exists as a standalone
+ * module, and its methods are also insinuated into the <code>File</code>
+ * class. (Note that this is not done by inclusion: the interpreter cheats).
+ *
+ */
+
+/*
+ * call-seq:
+ * File.directory?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a directory,
+ * <code>false</code> otherwise.
+ *
+ * File.directory?(".")
+ */
+
+static VALUE
+test_d(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISDIR(st.st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.pipe?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a pipe.
+ */
+
+static VALUE
+test_p(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_IFIFO
+# ifndef S_ISFIFO
+# define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
+# endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISFIFO(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.symlink?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a symbolic link.
+ */
+
+static VALUE
+test_l(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISLNK
+# ifdef _S_ISLNK
+# define S_ISLNK(m) _S_ISLNK(m)
+# elif defined __BORLANDC__
+# ifdef _S_IFLNK
+# define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == _S_IFLNK)
+# else
+# ifdef S_IFLNK
+# define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == S_IFLNK)
+# endif
+# endif
+# else
+# ifdef _S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
+# else
+# ifdef S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISLNK
+ struct stat st;
+
+ SafeStringValue(fname);
+ if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (S_ISLNK(st.st_mode)) return Qtrue;
+#endif
+
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.socket?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a socket.
+ */
+
+static VALUE
+test_S(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISSOCK
+# ifdef _S_ISSOCK
+# define S_ISSOCK(m) _S_ISSOCK(m)
+# elif defined __BORLANDC__
+# ifdef _S_IFSOCK
+# define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == _S_IFSOCK)
+# else
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == S_IFSOCK)
+# endif
+# endif
+# else
+# ifdef _S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
+# else
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISSOCK
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISSOCK(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.blockdev?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a block device.
+ */
+
+static VALUE
+test_b(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISBLK
+# ifdef S_IFBLK
+# define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
+# else
+# define S_ISBLK(m) (0) /* anytime false */
+# endif
+#endif
+
+#ifdef S_ISBLK
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISBLK(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.chardev?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a character device.
+ */
+static VALUE
+test_c(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISCHR
+# define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
+#endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISCHR(st.st_mode)) return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.exist?(file_name) => true or false
+ * File.exists?(file_name) => true or false (obsolete)
+ *
+ * Return <code>true</code> if the named file exists.
+ */
+
+static VALUE
+test_e(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.readable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is readable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_r(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.readable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is readable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_R(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.writable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is writable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_w(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.writable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is writable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_W(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.executable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is executable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_x(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.executable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is executable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_X(obj, fname)
+ VALUE obj, fname;
+{
+ SafeStringValue(fname);
+ if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+#ifndef S_ISREG
+# define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
+#endif
+
+/*
+ * call-seq:
+ * File.file?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and is a
+ * regular file.
+ */
+
+static VALUE
+test_f(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISREG(st.st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.zero?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and has
+ * a zero size.
+ */
+
+static VALUE
+test_z(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_size == 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.size?(file_name) => Integer or nil
+ *
+ * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
+ * file otherwise.
+ */
+
+static VALUE
+test_s(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qnil;
+ if (st.st_size == 0) return Qnil;
+ return OFFT2NUM(st.st_size);
+}
+
+/*
+ * call-seq:
+ * File.owned?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and the
+ * effective used id of the calling process is the owner of
+ * the file.
+ */
+
+static VALUE
+test_owned(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_uid == geteuid()) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+test_rowned(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_uid == getuid()) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.grpowned?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and the
+ * effective group id of the calling process is the owner of
+ * the file. Returns <code>false</code> on Windows.
+ */
+
+static VALUE
+test_grpowned(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef _WIN32
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (group_member(st.st_gid)) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
+static VALUE
+check3rdbyte(fname, mode)
+ VALUE fname;
+ int mode;
+{
+ struct stat st;
+
+ SafeStringValue(fname);
+ if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (st.st_mode & mode) return Qtrue;
+ return Qfalse;
+}
+#endif
+
+/*
+ * call-seq:
+ * File.setuid?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file has the setuid bit set.
+ */
+
+static VALUE
+test_suid(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISUID
+ return check3rdbyte(fname, S_ISUID);
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.setgid?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file has the setgid bit set.
+ */
+
+static VALUE
+test_sgid(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISGID
+ return check3rdbyte(fname, S_ISGID);
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.sticky?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file has the sticky bit set.
+ */
+
+static VALUE
+test_sticky(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISVTX
+ return check3rdbyte(fname, S_ISVTX);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.identical?(file_1, file_2) => true or false
+ *
+ * Returns <code>true</code> if the named files are identical.
+ *
+ * open("a", "w") {}
+ * p File.identical?("a", "a") #=> true
+ * p File.identical?("a", "./a") #=> true
+ * File.link("a", "b")
+ * p File.identical?("a", "b") #=> true
+ * File.symlink("a", "c")
+ * p File.identical?("a", "c") #=> true
+ * open("d", "w") {}
+ * p File.identical?("a", "d") #=> false
+ */
+
+static VALUE
+test_identical(obj, fname1, fname2)
+ VALUE obj, fname1, fname2;
+{
+#ifndef DOSISH
+ struct stat st1, st2;
+
+ if (rb_stat(fname1, &st1) < 0) return Qfalse;
+ if (rb_stat(fname2, &st2) < 0) return Qfalse;
+ if (st1.st_dev != st2.st_dev) return Qfalse;
+ if (st1.st_ino != st2.st_ino) return Qfalse;
+#else
+#ifdef _WIN32
+ BY_HANDLE_FILE_INFORMATION st1, st2;
+ HANDLE f1 = 0, f2 = 0;
+#endif
+
+ rb_secure(2);
+#ifdef _WIN32
+ f1 = w32_io_info(&fname1, &st1);
+ if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
+ f2 = w32_io_info(&fname2, &st2);
+ if (f1) CloseHandle(f1);
+ if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
+ if (f2) CloseHandle(f2);
+
+ if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
+ st1.nFileIndexHigh == st2.nFileIndexHigh &&
+ st1.nFileIndexLow == st2.nFileIndexLow)
+ return Qtrue;
+ if (!f1 || !f2) return Qfalse;
+ if (rb_w32_iswin95()) return Qfalse;
+#else
+ SafeStringValue(fname1);
+ fname1 = rb_str_new4(fname1);
+ SafeStringValue(fname2);
+ if (access(RSTRING(fname1)->ptr, 0)) return Qfalse;
+ if (access(RSTRING(fname2)->ptr, 0)) return Qfalse;
+#endif
+ fname1 = rb_file_expand_path(fname1, Qnil);
+ fname2 = rb_file_expand_path(fname2, Qnil);
+ if (RSTRING(fname1)->len != RSTRING(fname2)->len) return Qfalse;
+ if (rb_memcicmp(RSTRING(fname1)->ptr, RSTRING(fname2)->ptr, RSTRING(fname1)->len))
+ return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.size(file_name) => integer
+ *
+ * Returns the size of <code>file_name</code>.
+ */
+
+static VALUE
+rb_file_s_size(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(StringValueCStr(fname));
+ return OFFT2NUM(st.st_size);
+}
+
+static VALUE
+rb_file_ftype(st)
+ struct stat *st;
+{
+ char *t;
+
+ if (S_ISREG(st->st_mode)) {
+ t = "file";
+ }
+ else if (S_ISDIR(st->st_mode)) {
+ t = "directory";
+ }
+ else if (S_ISCHR(st->st_mode)) {
+ t = "characterSpecial";
+ }
+#ifdef S_ISBLK
+ else if (S_ISBLK(st->st_mode)) {
+ t = "blockSpecial";
+ }
+#endif
+#ifdef S_ISFIFO
+ else if (S_ISFIFO(st->st_mode)) {
+ t = "fifo";
+ }
+#endif
+#ifdef S_ISLNK
+ else if (S_ISLNK(st->st_mode)) {
+ t = "link";
+ }
+#endif
+#ifdef S_ISSOCK
+ else if (S_ISSOCK(st->st_mode)) {
+ t = "socket";
+ }
+#endif
+ else {
+ t = "unknown";
+ }
+
+ return rb_str_new2(t);
+}
+
+/*
+ * call-seq:
+ * File.ftype(file_name) => string
+ *
+ * Identifies the type of the named file; the return string is one of
+ * ``<code>file</code>'', ``<code>directory</code>'',
+ * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
+ * ``<code>fifo</code>'', ``<code>link</code>'',
+ * ``<code>socket</code>'', or ``<code>unknown</code>''.
+ *
+ * File.ftype("testfile") #=> "file"
+ * File.ftype("/dev/tty") #=> "characterSpecial"
+ * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
+ */
+
+static VALUE
+rb_file_s_ftype(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ SafeStringValue(fname);
+ if (lstat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+
+ return rb_file_ftype(&st);
+}
+
+/*
+ * call-seq:
+ * File.atime(file_name) => time
+ *
+ * Returns the last access time for the named file as a Time object).
+ *
+ * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_atime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(StringValueCStr(fname));
+ return rb_time_new(st.st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * file.atime => time
+ *
+ * Returns the last access time (a <code>Time</code> object)
+ * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
+ *
+ * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
+ *
+ */
+
+static VALUE
+rb_file_atime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * File.mtime(file_name) => time
+ *
+ * Returns the modification time for the named file as a Time object.
+ *
+ * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_mtime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(RSTRING(fname)->ptr);
+ return rb_time_new(st.st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * file.mtime -> time
+ *
+ * Returns the modification time for <i>file</i>.
+ *
+ * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_mtime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * File.ctime(file_name) => time
+ *
+ * Returns the change time for the named file (the time at which
+ * directory information about the file was changed, not the file
+ * itself).
+ *
+ * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_ctime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(RSTRING(fname)->ptr);
+ return rb_time_new(st.st_ctime, 0);
+}
+
+/*
+ * call-seq:
+ * file.ctime -> time
+ *
+ * Returns the change time for <i>file</i> (that is, the time directory
+ * information about the file was changed, not the file itself).
+ *
+ * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_ctime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fileno(fptr->f), &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_ctime, 0);
+}
+
+static void chmod_internal _((const char *, void *));
+static void
+chmod_internal(path, mode)
+ const char *path;
+ void *mode;
+{
+ if (chmod(path, *(int *)mode) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.chmod(mode_int, file_name, ... ) -> integer
+ *
+ * Changes permission bits on the named file(s) to the bit pattern
+ * represented by <i>mode_int</i>. Actual effects are operating system
+ * dependent (see the beginning of this section). On Unix systems, see
+ * <code>chmod(2)</code> for details. Returns the number of files
+ * processed.
+ *
+ * File.chmod(0644, "testfile", "out") #=> 2
+ */
+
+static VALUE
+rb_file_s_chmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vmode;
+ VALUE rest;
+ int mode;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "1*", &vmode, &rest);
+ mode = NUM2INT(vmode);
+
+ n = apply2files(chmod_internal, rest, &mode);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * file.chmod(mode_int) => 0
+ *
+ * Changes permission bits on <i>file</i> to the bit pattern
+ * represented by <i>mode_int</i>. Actual effects are platform
+ * dependent; on Unix systems, see <code>chmod(2)</code> for details.
+ * Follows symbolic links. Also see <code>File#lchmod</code>.
+ *
+ * f = File.new("out", "w");
+ * f.chmod(0644) #=> 0
+ */
+
+static VALUE
+rb_file_chmod(obj, vmode)
+ VALUE obj, vmode;
+{
+ OpenFile *fptr;
+ int mode;
+
+ rb_secure(2);
+ mode = NUM2INT(vmode);
+
+ GetOpenFile(obj, fptr);
+#ifdef HAVE_FCHMOD
+ if (fchmod(fileno(fptr->f), mode) == -1)
+ rb_sys_fail(fptr->path);
+#else
+ if (!fptr->path) return Qnil;
+ if (chmod(fptr->path, mode) == -1)
+ rb_sys_fail(fptr->path);
+#endif
+
+ return INT2FIX(0);
+}
+
+#if defined(HAVE_LCHMOD)
+static void lchmod_internal _((const char *, void *));
+static void
+lchmod_internal(path, mode)
+ const char *path;
+ void *mode;
+{
+ if (lchmod(path, (int)mode) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.lchmod(mode_int, file_name, ...) => integer
+ *
+ * Equivalent to <code>File::chmod</code>, but does not follow symbolic
+ * links (so it will change the permissions associated with the link,
+ * not the file referenced by the link). Often not available.
+ *
+ */
+
+static VALUE
+rb_file_s_lchmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vmode;
+ VALUE rest;
+ long mode, n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "1*", &vmode, &rest);
+ mode = NUM2INT(vmode);
+
+ n = apply2files(lchmod_internal, rest, (void *)(long)mode);
+ return LONG2FIX(n);
+}
+#else
+static VALUE
+rb_file_s_lchmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_notimplement();
+ return Qnil; /* not reached */
+}
+#endif
+
+struct chown_args {
+ int owner, group;
+};
+
+static void chown_internal _((const char *, void *));
+static void
+chown_internal(path, argp)
+ const char *path;
+ void *argp;
+{
+ struct chown_args *args = (struct chown_args *)argp;
+ if (chown(path, args->owner, args->group) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.chown(owner_int, group_int, file_name,... ) -> integer
+ *
+ * Changes the owner and group of the named file(s) to the given
+ * numeric owner and group id's. Only a process with superuser
+ * privileges may change the owner of a file. The current owner of a
+ * file may change the file's group to any group to which the owner
+ * belongs. A <code>nil</code> or -1 owner or group id is ignored.
+ * Returns the number of files processed.
+ *
+ * File.chown(nil, 100, "testfile")
+ *
+ */
+
+static VALUE
+rb_file_s_chown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE o, g, rest;
+ struct chown_args arg;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "2*", &o, &g, &rest);
+ if (NIL_P(o)) {
+ arg.owner = -1;
+ }
+ else {
+ arg.owner = NUM2INT(o);
+ }
+ if (NIL_P(g)) {
+ arg.group = -1;
+ }
+ else {
+ arg.group = NUM2INT(g);
+ }
+
+ n = apply2files(chown_internal, rest, &arg);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * file.chown(owner_int, group_int ) => 0
+ *
+ * Changes the owner and group of <i>file</i> to the given numeric
+ * owner and group id's. Only a process with superuser privileges may
+ * change the owner of a file. The current owner of a file may change
+ * the file's group to any group to which the owner belongs. A
+ * <code>nil</code> or -1 owner or group id is ignored. Follows
+ * symbolic links. See also <code>File#lchown</code>.
+ *
+ * File.new("testfile").chown(502, 1000)
+ *
+ */
+
+static VALUE
+rb_file_chown(obj, owner, group)
+ VALUE obj, owner, group;
+{
+ OpenFile *fptr;
+ int o, g;
+
+ rb_secure(2);
+ o = NIL_P(owner) ? -1 : NUM2INT(owner);
+ g = NIL_P(group) ? -1 : NUM2INT(group);
+ GetOpenFile(obj, fptr);
+#if defined(DJGPP) || defined(__CYGWIN32__) || defined(_WIN32) || defined(__EMX__)
+ if (!fptr->path) return Qnil;
+ if (chown(fptr->path, o, g) == -1)
+ rb_sys_fail(fptr->path);
+#else
+ if (fchown(fileno(fptr->f), o, g) == -1)
+ rb_sys_fail(fptr->path);
+#endif
+
+ return INT2FIX(0);
+}
+
+#if defined(HAVE_LCHOWN) && !defined(__CHECKER__)
+static void lchown_internal _((const char *, void *));
+static void
+lchown_internal(path, argp)
+ const char *path;
+ void *argp;
+{
+ struct chown_args *args = (struct chown_args *)argp;
+ if (lchown(path, args->owner, args->group) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * file.lchown(owner_int, group_int, file_name,..) => integer
+ *
+ * Equivalent to <code>File::chown</code>, but does not follow symbolic
+ * links (so it will change the owner associated with the link, not the
+ * file referenced by the link). Often not available. Returns number
+ * of files in the argument list.
+ *
+ */
+
+static VALUE
+rb_file_s_lchown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE o, g, rest;
+ struct chown_args arg;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "2*", &o, &g, &rest);
+ if (NIL_P(o)) {
+ arg.owner = -1;
+ }
+ else {
+ arg.owner = NUM2INT(o);
+ }
+ if (NIL_P(g)) {
+ arg.group = -1;
+ }
+ else {
+ arg.group = NUM2INT(g);
+ }
+
+ n = apply2files(lchown_internal, rest, &arg);
+ return LONG2FIX(n);
+}
+#else
+static VALUE
+rb_file_s_lchown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_notimplement();
+}
+#endif
+
+struct timeval rb_time_timeval();
+
+static void utime_internal _((const char *, void *));
+
+#if defined(HAVE_UTIMES) && !defined(__CHECKER__)
+
+static void
+utime_internal(path, arg)
+ const char *path;
+ void *arg;
+{
+ struct timeval *tvp = arg;
+ if (utimes(path, tvp) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.utime(atime, mtime, file_name,...) => integer
+ *
+ * Sets the access and modification times of each
+ * named file to the first two arguments. Returns
+ * the number of file names in the argument list.
+ */
+
+static VALUE
+rb_file_s_utime(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE atime, mtime, rest;
+ struct timeval tvs[2], *tvp = NULL;
+ long n;
+
+ rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
+
+ if (!NIL_P(atime) || !NIL_P(mtime)) {
+ tvp = tvs;
+ tvp[0] = rb_time_timeval(atime);
+ tvp[1] = rb_time_timeval(mtime);
+ }
+
+ n = apply2files(utime_internal, rest, tvp);
+ return LONG2FIX(n);
+}
+
+#else
+
+#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
+struct utimbuf {
+ long actime;
+ long modtime;
+};
+#endif
+
+static void
+utime_internal(path, arg)
+ const char *path;
+ void *arg;
+{
+ struct utimbuf *utp = arg;
+ if (utime(path, utp) < 0)
+ rb_sys_fail(path);
+}
+
+static VALUE
+rb_file_s_utime(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE atime, mtime, rest;
+ long n;
+ struct timeval tv;
+ struct utimbuf utbuf, *utp = NULL;
+
+ rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
+
+ if (!NIL_P(atime) || !NIL_P(mtime)) {
+ utp = &utbuf;
+ tv = rb_time_timeval(atime);
+ utp->actime = tv.tv_sec;
+ tv = rb_time_timeval(mtime);
+ utp->modtime = tv.tv_sec;
+ }
+
+ n = apply2files(utime_internal, rest, utp);
+ return LONG2FIX(n);
+}
+
+#endif
+
+NORETURN(static void sys_fail2 _((VALUE,VALUE)));
+static void
+sys_fail2(s1, s2)
+ VALUE s1, s2;
+{
+ char *buf;
+ int len;
+
+ len = RSTRING(s1)->len + RSTRING(s2)->len + 5;
+ buf = ALLOCA_N(char, len);
+ snprintf(buf, len, "%s or %s", RSTRING(s1)->ptr, RSTRING(s2)->ptr);
+ rb_sys_fail(buf);
+}
+
+/*
+ * call-seq:
+ * File.link(old_name, new_name) => 0
+ *
+ * Creates a new name for an existing file using a hard link. Will not
+ * overwrite <i>new_name</i> if it already exists (raising a subclass
+ * of <code>SystemCallError</code>). Not available on all platforms.
+ *
+ * File.link("testfile", ".testfile") #=> 0
+ * IO.readlines(".testfile")[0] #=> "This is line one\n"
+ */
+
+static VALUE
+rb_file_s_link(klass, from, to)
+ VALUE klass, from, to;
+{
+#ifdef HAVE_LINK
+ SafeStringValue(from);
+ SafeStringValue(to);
+
+ if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * File.symlink(old_name, new_name) => 0
+ *
+ * Creates a symbolic link called <i>new_name</i> for the existing file
+ * <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
+ * platforms that do not support symbolic links.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ *
+ */
+
+static VALUE
+rb_file_s_symlink(klass, from, to)
+ VALUE klass, from, to;
+{
+#ifdef HAVE_SYMLINK
+ SafeStringValue(from);
+ SafeStringValue(to);
+
+ if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * File.readlink(link_name) -> file_name
+ *
+ * Returns the name of the file referenced by the given link.
+ * Not available on all platforms.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.readlink("link2test") #=> "testfile"
+ */
+
+static VALUE
+rb_file_s_readlink(klass, path)
+ VALUE klass, path;
+{
+#ifdef HAVE_READLINK
+ char *buf;
+ int size = 100;
+ int rv;
+ VALUE v;
+
+ SafeStringValue(path);
+ buf = xmalloc(size);
+ while ((rv = readlink(RSTRING(path)->ptr, buf, size)) == size
+#ifdef _AIX
+ || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
+#endif
+ ) {
+ size *= 2;
+ buf = xrealloc(buf, size);
+ }
+ if (rv < 0) {
+ free(buf);
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+ v = rb_tainted_str_new(buf, rv);
+ free(buf);
+
+ return v;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static void unlink_internal _((const char *, void *));
+static void
+unlink_internal(path, arg)
+ const char *path;
+ void *arg;
+{
+ if (unlink(path) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.delete(file_name, ...) => integer
+ * File.unlink(file_name, ...) => integer
+ *
+ * Deletes the named files, returning the number of names
+ * passed as arguments. Raises an exception on any error.
+ * See also <code>Dir::rmdir</code>.
+ */
+
+static VALUE
+rb_file_s_unlink(klass, args)
+ VALUE klass, args;
+{
+ long n;
+
+ rb_secure(2);
+ n = apply2files(unlink_internal, args, 0);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * File.rename(old_name, new_name) => 0
+ *
+ * Renames the given file to the new name. Raises a
+ * <code>SystemCallError</code> if the file cannot be renamed.
+ *
+ * File.rename("afile", "afile.bak") #=> 0
+ */
+
+static VALUE
+rb_file_s_rename(klass, from, to)
+ VALUE klass, from, to;
+{
+ const char *src, *dst;
+ SafeStringValue(from);
+ SafeStringValue(to);
+
+ src = StringValueCStr(from);
+ dst = StringValueCStr(to);
+#if defined __CYGWIN__
+ errno = 0;
+#endif
+ if (rename(src, dst) < 0) {
+#if defined DOSISH && !defined _WIN32
+ switch (errno) {
+ case EEXIST:
+#if defined (__EMX__)
+ case EACCES:
+#endif
+ if (chmod(dst, 0666) == 0 &&
+ unlink(dst) == 0 &&
+ rename(src, dst) == 0)
+ return INT2FIX(0);
+ }
+#endif
+ sys_fail2(from, to);
+ }
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * File.umask() => integer
+ * File.umask(integer) => integer
+ *
+ * Returns the current umask value for this process. If the optional
+ * argument is given, set the umask to that value and return the
+ * previous value. Umask values are <em>subtracted</em> from the
+ * default permissions, so a umask of <code>0222</code> would make a
+ * file read-only for everyone.
+ *
+ * File.umask(0006) #=> 18
+ * File.umask #=> 6
+ */
+
+static VALUE
+rb_file_s_umask(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int omask = 0;
+
+ rb_secure(2);
+ if (argc == 0) {
+ omask = umask(0);
+ umask(omask);
+ }
+ else if (argc == 1) {
+ omask = umask(NUM2INT(argv[0]));
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ return INT2FIX(omask);
+}
+
+#ifdef __CYGWIN__
+#undef DOSISH
+#endif
+#if defined __CYGWIN__ || defined DOSISH
+#define DOSISH_UNC
+#define DOSISH_DRIVE_LETTER
+#define isdirsep(x) ((x) == '/' || (x) == '\\')
+#else
+#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))
+# else
+# define CharNext(p) ((p) + 1)
+# endif
+#endif
+
+#ifdef DOSISH_DRIVE_LETTER
+static inline int
+has_drive_letter(buf)
+ const char *buf;
+{
+ if (ISALPHA(buf[0]) && buf[1] == ':') {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static char*
+getcwdofdrv(drv)
+ int drv;
+{
+ char drive[4];
+ char *drvcwd, *oldcwd;
+
+ drive[0] = drv;
+ drive[1] = ':';
+ drive[2] = '\0';
+
+ /* the only way that I know to get the current directory
+ of a particular drive is to change chdir() to that drive,
+ so save the old cwd before chdir()
+ */
+ oldcwd = my_getcwd();
+ if (chdir(drive) == 0) {
+ drvcwd = my_getcwd();
+ chdir(oldcwd);
+ free(oldcwd);
+ }
+ else {
+ /* perhaps the drive is not exist. we return only drive letter */
+ drvcwd = strdup(drive);
+ }
+ return drvcwd;
+}
+#endif
+
+static inline char *
+skiproot(path)
+ const char *path;
+{
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path)) path += 2;
+#endif
+ while (isdirsep(*path)) path++;
+ return (char *)path;
+}
+
+#define nextdirsep rb_path_next
+char *
+rb_path_next(s)
+ const char *s;
+{
+ while (*s && !isdirsep(*s)) {
+ s = CharNext(s);
+ }
+ return (char *)s;
+}
+
+#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
+#define skipprefix rb_path_skip_prefix
+#else
+#define skipprefix(path) (path)
+#endif
+char *
+rb_path_skip_prefix(path)
+ const char *path;
+{
+#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
+#ifdef DOSISH_UNC
+ if (isdirsep(path[0]) && isdirsep(path[1])) {
+ path += 2;
+ while (isdirsep(*path)) path++;
+ if (*(path = nextdirsep(path)) && path[1] && !isdirsep(path[1]))
+ path = nextdirsep(path + 1);
+ return (char *)path;
+ }
+#endif
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path))
+ return (char *)(path + 2);
+#endif
+#endif
+ return (char *)path;
+}
+
+#define strrdirsep rb_path_last_separator
+char *
+rb_path_last_separator(path)
+ const char *path;
+{
+ char *last = NULL;
+ while (*path) {
+ if (isdirsep(*path)) {
+ const char *tmp = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) break;
+ last = (char *)tmp;
+ }
+ else {
+ path = CharNext(path);
+ }
+ }
+ return last;
+}
+
+static char *
+chompdirsep(path)
+ const char *path;
+{
+ while (*path) {
+ if (isdirsep(*path)) {
+ const char *last = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) return (char *)last;
+ }
+ else {
+ path = CharNext(path);
+ }
+ }
+ return (char *)path;
+}
+
+char *
+rb_path_end(path)
+ const char *path;
+{
+ if (isdirsep(*path)) path++;
+ return chompdirsep(path);
+}
+
+#if USE_NTFS
+static char *
+ntfs_tail(const char *path)
+{
+ while (*path == '.') 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;\
+ if (cond) {\
+ do {buflen *= 2;} while (cond);\
+ rb_str_resize(result, buflen);\
+ buf = RSTRING(result)->ptr;\
+ p = buf + bdiff;\
+ pend = buf + buflen;\
+ }\
+} while (0)
+
+#define BUFINIT() (\
+ p = buf = RSTRING(result)->ptr,\
+ buflen = RSTRING(result)->len,\
+ pend = p + buflen)
+
+#if !defined(TOLOWER)
+#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c))
+#endif
+
+static int is_absolute_path _((const char*));
+
+static VALUE
+file_expand_path(fname, dname, result)
+ VALUE fname, dname, result;
+{
+ const char *s, *b;
+ char *buf, *p, *pend, *root;
+ long buflen, dirlen;
+ int tainted;
+
+ s = StringValuePtr(fname);
+ BUFINIT();
+ tainted = OBJ_TAINTED(fname);
+
+ if (s[0] == '~') {
+ if (isdirsep(s[1]) || s[1] == '\0') {
+ char *dir = getenv("HOME");
+
+ if (!dir) {
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `%s'", s);
+ }
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+#if defined DOSISH || defined __CYGWIN__
+ for (p = buf; *p; p = CharNext(p)) {
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+#else
+ p = buf + strlen(dir);
+#endif
+ s++;
+ tainted = 1;
+ }
+ else {
+#ifdef HAVE_PWD_H
+ struct passwd *pwPtr;
+ s++;
+#endif
+ s = nextdirsep(b = s);
+ BUFCHECK(bdiff + (s-b) >= buflen);
+ memcpy(p, b, s-b);
+ p += s-b;
+ *p = '\0';
+#ifdef HAVE_PWD_H
+ pwPtr = getpwnam(buf);
+ if (!pwPtr) {
+ endpwent();
+ rb_raise(rb_eArgError, "user %s doesn't exist", buf);
+ }
+ dirlen = strlen(pwPtr->pw_dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, pwPtr->pw_dir);
+ p = buf + strlen(pwPtr->pw_dir);
+ endpwent();
+#endif
+ }
+ }
+#ifdef DOSISH_DRIVE_LETTER
+ /* skip drive letter */
+ else if (has_drive_letter(s)) {
+ if (isdirsep(s[2])) {
+ /* specified drive letter, and full path */
+ /* skip drive letter */
+ BUFCHECK(bdiff + 2 >= buflen);
+ memcpy(p, s, 2);
+ p += 2;
+ s += 2;
+ }
+ else {
+ /* specified drive, but not full path */
+ int same = 0;
+ if (!NIL_P(dname)) {
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
+ if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
+ /* ok, same drive */
+ same = 1;
+ }
+ }
+ if (!same) {
+ char *dir = getcwdofdrv(*s);
+
+ tainted = 1;
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+ free(dir);
+ }
+ p = chompdirsep(skiproot(buf));
+ s += 2;
+ }
+ }
+#endif
+ else if (!is_absolute_path(s)) {
+ if (!NIL_P(dname)) {
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
+ }
+ else {
+ char *dir = my_getcwd();
+
+ tainted = 1;
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+ free(dir);
+ }
+#if defined DOSISH || defined __CYGWIN__
+ if (isdirsep(*s)) {
+ /* specified full path, but not drive letter nor UNC */
+ /* we need to get the drive letter or UNC share name */
+ p = skipprefix(buf);
+ }
+ else
+#endif
+ p = chompdirsep(skiproot(buf));
+ }
+ else {
+ b = s;
+ do s++; while (isdirsep(*s));
+ p = buf + (s - b);
+ BUFCHECK(bdiff >= buflen);
+ memset(buf, '/', p - buf);
+ }
+ if (p > buf && p[-1] == '/')
+ --p;
+ else
+ *p = '/';
+
+ p[1] = 0;
+ root = skipprefix(buf);
+
+ b = s;
+ while (*s) {
+ switch (*s) {
+ case '.':
+ if (b == s++) { /* beginning of path element */
+ switch (*s) {
+ case '\0':
+ b = s;
+ break;
+ case '.':
+ if (*(s+1) == '\0' || isdirsep(*(s+1))) {
+ /* We must go back to the parent */
+ char *n;
+ *p = '\0';
+ if (!(n = strrdirsep(root))) {
+ *p = '/';
+ }
+ else {
+ p = n;
+ }
+ b = ++s;
+ }
+#if USE_NTFS
+ else {
+ do *++s; while (istrailinggabage(*s));
+ }
+#endif
+ break;
+ case '/':
+#if defined DOSISH || defined __CYGWIN__
+ case '\\':
+#endif
+ b = ++s;
+ break;
+ default:
+ /* ordinary path element, beginning don't move */
+ 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__
+ case '\\':
+#endif
+ if (s > b) {
+ long rootdiff = root - buf;
+ BUFCHECK(bdiff + (s-b+1) >= buflen);
+ root = buf + rootdiff;
+ memcpy(++p, b, s-b);
+ p += s-b;
+ *p = '/';
+ }
+ b = ++s;
+ break;
+ default:
+ s = CharNext(s);
+ break;
+ }
+ }
+
+ 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++;
+
+#if USE_NTFS
+ *p = '\0';
+ if ((s = strrdirsep(b = buf)) != 0 && !strpbrk(s, "*?")) {
+ size_t len;
+ WIN32_FIND_DATA wfd;
+#ifdef __CYGWIN__
+ int lnk_added = 0, is_symlink = 0;
+ struct stat st;
+ char w32buf[MAXPATHLEN];
+ p = (char *)s;
+ if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
+ is_symlink = 1;
+ *p = '\0';
+ }
+ if (cygwin_conv_to_win32_path((*buf ? buf : "/"), w32buf) == 0) {
+ b = w32buf;
+ }
+ if (is_symlink && b == w32buf) {
+ *p = '\\';
+ strlcat(w32buf, p, sizeof(w32buf));
+ len = strlen(p);
+ if (len > 4 && strcasecmp(p + len - 4, ".lnk") != 0) {
+ lnk_added = 1;
+ strlcat(w32buf, ".lnk", sizeof(w32buf));
+ }
+ }
+ *p = '/';
+#endif
+ HANDLE h = FindFirstFile(b, &wfd);
+ if (h != INVALID_HANDLE_VALUE) {
+ FindClose(h);
+ len = strlen(wfd.cFileName);
+#ifdef __CYGWIN__
+ if (lnk_added && len > 4 &&
+ strcasecmp(wfd.cFileName + len - 4, ".lnk") == 0) {
+ wfd.cFileName[len -= 4] = '\0';
+ }
+#else
+ p = (char *)s;
+#endif
+ ++p;
+ BUFCHECK(bdiff + len >= buflen);
+ memcpy(p, wfd.cFileName, len + 1);
+ p += len;
+ }
+#ifdef __CYGWIN__
+ else {
+ p += strlen(p);
+ }
+#endif
+ }
+#endif
+
+ if (tainted) OBJ_TAINT(result);
+ RSTRING(result)->len = p - buf;
+ RSTRING(result)->ptr[p - buf] = '\0';
+ return result;
+}
+
+VALUE
+rb_file_expand_path(fname, dname)
+ VALUE fname, dname;
+{
+ return file_expand_path(fname, dname, rb_str_new(0, MAXPATHLEN + 2));
+}
+
+/*
+ * call-seq:
+ * File.expand_path(file_name [, dir_string] ) -> abs_file_name
+ *
+ * Converts a pathname to an absolute pathname. Relative paths are
+ * referenced from the current working directory of the process unless
+ * <i>dir_string</i> is given, in which case it will be used as the
+ * starting point. The given pathname may start with a
+ * ``<code>~</code>'', which expands to the process owner's home
+ * directory (the environment variable <code>HOME</code> must be set
+ * correctly). ``<code>~</code><i>user</i>'' expands to the named
+ * user's home directory.
+ *
+ * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
+ * File.expand_path("../../bin", "/tmp/x") #=> "/bin"
+ */
+
+VALUE
+rb_file_s_expand_path(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, dname;
+
+ if (argc == 1) {
+ return rb_file_expand_path(argv[0], Qnil);
+ }
+ rb_scan_args(argc, argv, "11", &fname, &dname);
+
+ return rb_file_expand_path(fname, dname);
+}
+
+static int
+rmext(p, l1, e)
+ const char *p, *e;
+ int l1;
+{
+ int l2;
+
+ if (!e) return 0;
+
+ l2 = strlen(e);
+ if (l2 == 2 && e[1] == '*') {
+ 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 CASEFOLD_FILESYSTEM
+#define fncomp strncasecmp
+#else
+#define fncomp strncmp
+#endif
+ if (fncomp(p+l1-l2, e, l2) == 0) {
+ return l1-l2;
+ }
+ return 0;
+}
+
+/*
+ * call-seq:
+ * File.basename(file_name [, suffix] ) -> base_name
+ *
+ * Returns the last component of the filename given in <i>file_name</i>,
+ * which must be formed using forward slashes (``<code>/</code>'')
+ * regardless of the separator used on the local file system. If
+ * <i>suffix</i> is given and present at the end of <i>file_name</i>,
+ * it is removed.
+ *
+ * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
+ * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
+ */
+
+static VALUE
+rb_file_s_basename(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, fext, basename;
+ char *name, *p;
+#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
+ char *root;
+#endif
+ int f, n;
+
+ if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
+ StringValue(fext);
+ }
+ StringValue(fname);
+ if (RSTRING(fname)->len == 0 || !*(name = RSTRING(fname)->ptr))
+ return fname;
+ name = skipprefix(name);
+#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
+ root = name;
+#endif
+ while (isdirsep(*name))
+ name++;
+ if (!*name) {
+ p = name - 1;
+ f = 1;
+#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
+ if (name != root) {
+ /* has slashes */
+ }
+#ifdef DOSISH_DRIVE_LETTER
+ else if (*p == ':') {
+ p++;
+ f = 0;
+ }
+#endif
+#ifdef DOSISH_UNC
+ else {
+ p = "/";
+ }
+#endif
+#endif
+ }
+ else {
+ 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);
+ return basename;
+}
+
+/*
+ * call-seq:
+ * File.dirname(file_name ) -> dir_name
+ *
+ * Returns all components of the filename given in <i>file_name</i>
+ * except the last one. The filename must be formed using forward
+ * slashes (``<code>/</code>'') regardless of the separator used on the
+ * local file system.
+ *
+ * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
+ */
+
+static VALUE
+rb_file_s_dirname(klass, fname)
+ VALUE klass, fname;
+{
+ const char *name, *root, *p;
+ VALUE dirname;
+
+ name = StringValueCStr(fname);
+ root = skiproot(name);
+#ifdef DOSISH_UNC
+ if (root > name + 1 && isdirsep(*name))
+ root = skipprefix(name = root - 2);
+#else
+ if (root > name + 1)
+ name = root - 1;
+#endif
+ p = strrdirsep(root);
+ if (!p) {
+ p = root;
+ }
+ if (p == name)
+ return rb_str_new2(".");
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(name) && isdirsep(*(name + 2))) {
+ const char *top = skiproot(name + 2);
+ dirname = rb_str_new(name, 3);
+ rb_str_cat(dirname, top, p - top);
+ }
+ else
+#endif
+ dirname = rb_str_new(name, p - name);
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(name) && root == name + 2 && p - name == 2)
+ rb_str_cat(dirname, ".", 1);
+#endif
+ OBJ_INFECT(dirname, fname);
+ return dirname;
+}
+
+/*
+ * call-seq:
+ * File.extname(path) -> string
+ *
+ * Returns the extension (the portion of file name in <i>path</i>
+ * after the period).
+ *
+ * File.extname("test.rb") #=> ".rb"
+ * File.extname("a/b/d/test.rb") #=> ".rb"
+ * File.extname("test") #=> ""
+ * File.extname(".profile") #=> ""
+ *
+ */
+
+static VALUE
+rb_file_s_extname(klass, fname)
+ VALUE klass, fname;
+{
+ const char *name, *p, *e;
+ VALUE extname;
+
+ name = StringValueCStr(fname);
+ p = strrdirsep(name); /* get the last path component */
+ if (!p)
+ p = name;
+ else
+ 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;
+ }
+ if (*last == '.' || dot > last) 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;
+}
+
+/*
+ * call-seq:
+ * File.split(file_name) => array
+ *
+ * Splits the given string into a directory and a file component and
+ * returns them in a two-element array. See also
+ * <code>File::dirname</code> and <code>File::basename</code>.
+ *
+ * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
+ */
+
+static VALUE
+rb_file_s_split(klass, path)
+ VALUE klass, path;
+{
+ StringValue(path); /* get rid of converting twice */
+ return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
+}
+
+static VALUE separator;
+
+static VALUE rb_file_join _((VALUE ary, VALUE sep));
+
+static VALUE
+file_inspect_join(ary, arg)
+ VALUE ary;
+ VALUE *arg;
+{
+ return rb_file_join(arg[0], arg[1]);
+}
+
+static VALUE
+rb_file_join(ary, sep)
+ VALUE ary, sep;
+{
+ long len, i;
+ int taint = 0;
+ VALUE result, tmp;
+ char *name, *tail;
+
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+ if (OBJ_TAINTED(ary)) taint = 1;
+ if (OBJ_TAINTED(sep)) taint = 1;
+
+ len = 1;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (TYPE(RARRAY(ary)->ptr[i]) == T_STRING) {
+ len += RSTRING(RARRAY(ary)->ptr[i])->len;
+ }
+ else {
+ len += 10;
+ }
+ }
+ if (!NIL_P(sep) && TYPE(sep) == T_STRING) {
+ len += RSTRING(sep)->len * RARRAY(ary)->len - 1;
+ }
+ result = rb_str_buf_new(len);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ break;
+ case T_ARRAY:
+ if (rb_inspecting_p(tmp)) {
+ tmp = rb_str_new2("[...]");
+ }
+ else {
+ VALUE args[2];
+
+ args[0] = tmp;
+ args[1] = sep;
+ tmp = rb_protect_inspect(file_inspect_join, ary, (VALUE)args);
+ }
+ break;
+ default:
+ StringValueCStr(tmp);
+ }
+ name = StringValueCStr(result);
+ if (i > 0 && !NIL_P(sep)) {
+ tail = chompdirsep(name);
+ if (RSTRING(tmp)->ptr && isdirsep(RSTRING(tmp)->ptr[0])) {
+ RSTRING(result)->len = tail - name;
+ }
+ else if (!*tail) {
+ rb_str_buf_append(result, sep);
+ }
+ }
+ rb_str_buf_append(result, tmp);
+ if (OBJ_TAINTED(tmp)) taint = 1;
+ }
+
+ if (taint) OBJ_TAINT(result);
+ return result;
+}
+
+/*
+ * call-seq:
+ * File.join(string, ...) -> path
+ *
+ * Returns a new string formed by joining the strings using
+ * <code>File::SEPARATOR</code>.
+ *
+ * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
+ *
+ */
+
+static VALUE
+rb_file_s_join(klass, args)
+ VALUE klass, args;
+{
+ return rb_file_join(args, separator);
+}
+
+/*
+ * call-seq:
+ * File.truncate(file_name, integer) => 0
+ *
+ * Truncates the file <i>file_name</i> to be at most <i>integer</i>
+ * bytes long. Not available on all platforms.
+ *
+ * f = File.new("out", "w")
+ * f.write("1234567890") #=> 10
+ * f.close #=> nil
+ * File.truncate("out", 5) #=> 0
+ * File.size("out") #=> 5
+ *
+ */
+
+static VALUE
+rb_file_s_truncate(klass, path, len)
+ VALUE klass, path, len;
+{
+ off_t pos;
+
+ rb_secure(2);
+ pos = NUM2OFFT(len);
+ SafeStringValue(path);
+
+#ifdef HAVE_TRUNCATE
+ if (truncate(StringValueCStr(path), pos) < 0)
+ rb_sys_fail(RSTRING(path)->ptr);
+#else
+# ifdef HAVE_CHSIZE
+ {
+ int tmpfd;
+
+# ifdef _WIN32
+ if ((tmpfd = open(StringValueCStr(path), O_RDWR)) < 0) {
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+# else
+ if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+# endif
+ if (chsize(tmpfd, pos) < 0) {
+ close(tmpfd);
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+ close(tmpfd);
+ }
+# else
+ rb_notimplement();
+# endif
+#endif
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * file.truncate(integer) => 0
+ *
+ * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
+ * must be opened for writing. Not available on all platforms.
+ *
+ * f = File.new("out", "w")
+ * f.syswrite("1234567890") #=> 10
+ * f.truncate(5) #=> 0
+ * f.close() #=> nil
+ * File.size("out") #=> 5
+ */
+
+static VALUE
+rb_file_truncate(obj, len)
+ VALUE obj, len;
+{
+ OpenFile *fptr;
+ FILE *f;
+ off_t pos;
+
+ rb_secure(2);
+ pos = NUM2OFFT(len);
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ rb_raise(rb_eIOError, "not opened for writing");
+ }
+ f = GetWriteFile(fptr);
+ fflush(f);
+ fseeko(f, (off_t)0, SEEK_CUR);
+#ifdef HAVE_FTRUNCATE
+ if (ftruncate(fileno(f), pos) < 0)
+ rb_sys_fail(fptr->path);
+#else
+# ifdef HAVE_CHSIZE
+ if (chsize(fileno(f), pos) < 0)
+ rb_sys_fail(fptr->path);
+# else
+ rb_notimplement();
+# endif
+#endif
+ return INT2FIX(0);
+}
+
+# ifndef LOCK_SH
+# define LOCK_SH 1
+# endif
+# ifndef LOCK_EX
+# define LOCK_EX 2
+# endif
+# ifndef LOCK_NB
+# define LOCK_NB 4
+# endif
+# ifndef LOCK_UN
+# define LOCK_UN 8
+# endif
+
+#ifdef __CYGWIN__
+#include <winerror.h>
+extern unsigned long __attribute__((stdcall)) GetLastError(void);
+
+static int
+cygwin_flock(int fd, int op)
+{
+ int old_errno = errno;
+ int ret = flock(fd, op);
+ if (GetLastError() == ERROR_NOT_LOCKED) {
+ ret = 0;
+ errno = old_errno;
+ }
+ return ret;
+}
+# define flock(fd, op) cygwin_flock(fd, op)
+#endif
+
+static int
+rb_thread_flock(fd, op, fptr)
+ int fd, op;
+ OpenFile *fptr;
+{
+ if (rb_thread_alone() || (op & LOCK_NB)) {
+ int ret;
+ TRAP_BEG;
+ ret = flock(fd, op);
+ TRAP_END;
+ return ret;
+ }
+ op |= LOCK_NB;
+ while (flock(fd, op) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EACCES:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ rb_thread_polling(); /* busy wait */
+ rb_io_check_closed(fptr);
+ continue;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+}
+#ifdef __CYGWIN__
+# undef flock
+#endif
+#define flock(fd, op) rb_thread_flock(fd, op, fptr)
+
+/*
+ * call-seq:
+ * file.flock (locking_constant ) => 0 or false
+ *
+ * Locks or unlocks a file according to <i>locking_constant</i> (a
+ * logical <em>or</em> of the values in the table below).
+ * Returns <code>false</code> if <code>File::LOCK_NB</code> is
+ * specified and the operation would otherwise have blocked. Not
+ * available on all platforms.
+ *
+ * Locking constants (in class File):
+ *
+ * LOCK_EX | Exclusive lock. Only one process may hold an
+ * | exclusive lock for a given file at a time.
+ * ----------+------------------------------------------------
+ * LOCK_NB | Don't block when locking. May be combined
+ * | with other lock options using logical or.
+ * ----------+------------------------------------------------
+ * LOCK_SH | Shared lock. Multiple processes may each hold a
+ * | shared lock for a given file at the same time.
+ * ----------+------------------------------------------------
+ * LOCK_UN | Unlock.
+ *
+ * Example:
+ *
+ * File.new("testfile").flock(File::LOCK_UN) #=> 0
+ *
+ */
+
+static VALUE
+rb_file_flock(obj, operation)
+ VALUE obj;
+ VALUE operation;
+{
+#ifndef __CHECKER__
+ OpenFile *fptr;
+ int op;
+
+ rb_secure(2);
+ op = NUM2INT(operation);
+ GetOpenFile(obj, fptr);
+
+ if (fptr->mode & FMODE_WRITABLE) {
+ fflush(GetWriteFile(fptr));
+ }
+ retry:
+ if (flock(fileno(fptr->f), op) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EACCES:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return Qfalse;
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ goto retry;
+ }
+ rb_sys_fail(fptr->path);
+ }
+#endif
+ return INT2FIX(0);
+}
+#undef flock
+
+static void
+test_check(n, argc, argv)
+ int n, argc;
+ VALUE *argv;
+{
+ int i;
+
+ n+=1;
+ if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
+ for (i=1; i<n; i++) {
+ switch (TYPE(argv[i])) {
+ case T_STRING:
+ default:
+ SafeStringValue(argv[i]);
+ break;
+ case T_FILE:
+ break;
+ }
+ }
+}
+
+#define CHECK(n) test_check((n), argc, argv)
+
+/*
+ * call-seq:
+ * test(int_cmd, file1 [, file2] ) => obj
+ *
+ * Uses the integer <i>aCmd</i> to perform various tests on
+ * <i>file1</i> (first table below) or on <i>file1</i> and
+ * <i>file2</i> (second table).
+ *
+ * File tests on a single file:
+ *
+ * Test Returns Meaning
+ * ?A | Time | Last access time for file1
+ * ?b | boolean | True if file1 is a block device
+ * ?c | boolean | True if file1 is a character device
+ * ?C | Time | Last change time for file1
+ * ?d | boolean | True if file1 exists and is a directory
+ * ?e | boolean | True if file1 exists
+ * ?f | boolean | True if file1 exists and is a regular file
+ * ?g | boolean | True if file1 has the \CF{setgid} bit
+ * | | set (false under NT)
+ * ?G | boolean | True if file1 exists and has a group
+ * | | ownership equal to the caller's group
+ * ?k | boolean | True if file1 exists and has the sticky bit set
+ * ?l | boolean | True if file1 exists and is a symbolic link
+ * ?M | Time | Last modification time for file1
+ * ?o | boolean | True if file1 exists and is owned by
+ * | | the caller's effective uid
+ * ?O | boolean | True if file1 exists and is owned by
+ * | | the caller's real uid
+ * ?p | boolean | True if file1 exists and is a fifo
+ * ?r | boolean | True if file1 is readable by the effective
+ * | | uid/gid of the caller
+ * ?R | boolean | True if file is readable by the real
+ * | | uid/gid of the caller
+ * ?s | int/nil | If file1 has nonzero size, return the size,
+ * | | otherwise return nil
+ * ?S | boolean | True if file1 exists and is a socket
+ * ?u | boolean | True if file1 has the setuid bit set
+ * ?w | boolean | True if file1 exists and is writable by
+ * | | the effective uid/gid
+ * ?W | boolean | True if file1 exists and is writable by
+ * | | the real uid/gid
+ * ?x | boolean | True if file1 exists and is executable by
+ * | | the effective uid/gid
+ * ?X | boolean | True if file1 exists and is executable by
+ * | | the real uid/gid
+ * ?z | boolean | True if file1 exists and has a zero length
+ *
+ * Tests that take two files:
+ *
+ * ?- | boolean | True if file1 and file2 are identical
+ * ?= | boolean | True if the modification times of file1
+ * | | and file2 are equal
+ * ?< | boolean | True if the modification time of file1
+ * | | is prior to that of file2
+ * ?> | boolean | True if the modification time of file1
+ * | | is after that of file2
+ */
+
+static VALUE
+rb_f_test(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int cmd;
+
+ if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments");
+#if 0 /* 1.7 behavior? */
+ if (argc == 1) {
+ return RTEST(argv[0]) ? Qtrue : Qfalse;
+ }
+#endif
+ cmd = NUM2CHR(argv[0]);
+ if (cmd == 0) return Qfalse;
+ if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
+ CHECK(1);
+ switch (cmd) {
+ case 'b':
+ return test_b(0, argv[1]);
+
+ case 'c':
+ return test_c(0, argv[1]);
+
+ case 'd':
+ return test_d(0, argv[1]);
+
+ case 'a':
+ case 'e':
+ return test_e(0, argv[1]);
+
+ case 'f':
+ return test_f(0, argv[1]);
+
+ case 'g':
+ return test_sgid(0, argv[1]);
+
+ case 'G':
+ return test_grpowned(0, argv[1]);
+
+ case 'k':
+ return test_sticky(0, argv[1]);
+
+ case 'l':
+ return test_l(0, argv[1]);
+
+ case 'o':
+ return test_owned(0, argv[1]);
+
+ case 'O':
+ return test_rowned(0, argv[1]);
+
+ case 'p':
+ return test_p(0, argv[1]);
+
+ case 'r':
+ return test_r(0, argv[1]);
+
+ case 'R':
+ return test_R(0, argv[1]);
+
+ case 's':
+ return test_s(0, argv[1]);
+
+ case 'S':
+ return test_S(0, argv[1]);
+
+ case 'u':
+ return test_suid(0, argv[1]);
+
+ case 'w':
+ return test_w(0, argv[1]);
+
+ case 'W':
+ return test_W(0, argv[1]);
+
+ case 'x':
+ return test_x(0, argv[1]);
+
+ case 'X':
+ return test_X(0, argv[1]);
+
+ case 'z':
+ return test_z(0, argv[1]);
+ }
+ }
+
+ if (strchr("MAC", cmd)) {
+ struct stat st;
+
+ CHECK(1);
+ if (rb_stat(argv[1], &st) == -1) {
+ rb_sys_fail(RSTRING(argv[1])->ptr);
+ }
+
+ switch (cmd) {
+ case 'A':
+ return rb_time_new(st.st_atime, 0);
+ case 'M':
+ return rb_time_new(st.st_mtime, 0);
+ case 'C':
+ return rb_time_new(st.st_ctime, 0);
+ }
+ }
+
+ if (cmd == '-') {
+ CHECK(2);
+ return test_identical(0, argv[1], argv[2]);
+ }
+
+ if (strchr("=<>", cmd)) {
+ struct stat st1, st2;
+
+ CHECK(2);
+ if (rb_stat(argv[1], &st1) < 0) return Qfalse;
+ if (rb_stat(argv[2], &st2) < 0) return Qfalse;
+
+ switch (cmd) {
+ case '=':
+ if (st1.st_mtime == st2.st_mtime) return Qtrue;
+ return Qfalse;
+
+ case '>':
+ if (st1.st_mtime > st2.st_mtime) return Qtrue;
+ return Qfalse;
+
+ case '<':
+ if (st1.st_mtime < st2.st_mtime) return Qtrue;
+ return Qfalse;
+ }
+ }
+ /* unknown command */
+ rb_raise(rb_eArgError, "unknown command ?%c", cmd);
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * Document-class: File::Stat
+ *
+ * Objects of class <code>File::Stat</code> encapsulate common status
+ * information for <code>File</code> objects. The information is
+ * recorded at the moment the <code>File::Stat</code> object is
+ * created; changes made to the file after that point will not be
+ * reflected. <code>File::Stat</code> objects are returned by
+ * <code>IO#stat</code>, <code>File::stat</code>,
+ * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
+ * methods return platform-specific values, and not all values are
+ * meaningful on all systems. See also <code>Kernel#test</code>.
+ */
+
+static VALUE rb_stat_s_alloc _((VALUE));
+static VALUE
+rb_stat_s_alloc(klass)
+ VALUE klass;
+{
+ return stat_new_0(klass, 0);
+}
+
+/*
+ * call-seq:
+ *
+ * File::Stat.new(file_name) => stat
+ *
+ * Create a File::Stat object for the given file name (raising an
+ * exception if the file doesn't exist).
+ */
+
+static VALUE
+rb_stat_init(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st, *nst;
+
+ SafeStringValue(fname);
+
+ if (stat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+ if (DATA_PTR(obj)) {
+ free(DATA_PTR(obj));
+ DATA_PTR(obj) = NULL;
+ }
+ nst = ALLOC(struct stat);
+ *nst = st;
+ DATA_PTR(obj) = nst;
+
+ return Qnil;
+}
+
+/* :nodoc: */
+static VALUE
+rb_stat_init_copy(copy, orig)
+ VALUE copy, orig;
+{
+ struct stat *nst;
+
+ if (copy == orig) return orig;
+ rb_check_frozen(copy);
+ /* need better argument type check */
+ if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
+ rb_raise(rb_eTypeError, "wrong argument class");
+ }
+ if (DATA_PTR(copy)) {
+ free(DATA_PTR(copy));
+ DATA_PTR(copy) = 0;
+ }
+ if (DATA_PTR(orig)) {
+ nst = ALLOC(struct stat);
+ *nst = *(struct stat*)DATA_PTR(orig);
+ DATA_PTR(copy) = nst;
+ }
+
+ return copy;
+}
+
+/*
+ * call-seq:
+ * stat.ftype => string
+ *
+ * Identifies the type of <i>stat</i>. The return string is one of:
+ * ``<code>file</code>'', ``<code>directory</code>'',
+ * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
+ * ``<code>fifo</code>'', ``<code>link</code>'',
+ * ``<code>socket</code>'', or ``<code>unknown</code>''.
+ *
+ * File.stat("/dev/tty").ftype #=> "characterSpecial"
+ *
+ */
+
+static VALUE
+rb_stat_ftype(obj)
+ VALUE obj;
+{
+ return rb_file_ftype(get_stat(obj));
+}
+
+/*
+ * call-seq:
+ * stat.directory? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a directory,
+ * <code>false</code> otherwise.
+ *
+ * File.stat("testfile").directory? #=> false
+ * File.stat(".").directory? #=> true
+ */
+
+static VALUE
+rb_stat_d(obj)
+ VALUE obj;
+{
+ if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.pipe? => true or false
+ *
+ * Returns <code>true</code> if the operating system supports pipes and
+ * <i>stat</i> is a pipe; <code>false</code> otherwise.
+ */
+
+static VALUE
+rb_stat_p(obj)
+ VALUE obj;
+{
+#ifdef S_IFIFO
+ if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.symlink? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a symbolic link,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature. As <code>File::stat</code> automatically
+ * follows symbolic links, <code>symlink?</code> will always be
+ * <code>false</code> for an object returned by
+ * <code>File::stat</code>.
+ *
+ * File.symlink("testfile", "alink") #=> 0
+ * File.stat("alink").symlink? #=> false
+ * File.lstat("alink").symlink? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_l(obj)
+ VALUE obj;
+{
+#ifdef S_ISLNK
+ if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.socket? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a socket,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").socket? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_S(obj)
+ VALUE obj;
+{
+#ifdef S_ISSOCK
+ if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.blockdev? => true or false
+ *
+ * Returns <code>true</code> if the file is a block device,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").blockdev? #=> false
+ * File.stat("/dev/hda1").blockdev? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_b(obj)
+ VALUE obj;
+{
+#ifdef S_ISBLK
+ if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.chardev? => true or false
+ *
+ * Returns <code>true</code> if the file is a character device,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("/dev/tty").chardev? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_c(obj)
+ VALUE obj;
+{
+ if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.owned? => true or false
+ *
+ * Returns <code>true</code> if the effective user id of the process is
+ * the same as the owner of <i>stat</i>.
+ *
+ * File.stat("testfile").owned? #=> true
+ * File.stat("/etc/passwd").owned? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_owned(obj)
+ VALUE obj;
+{
+ if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+rb_stat_rowned(obj)
+ VALUE obj;
+{
+ if (get_stat(obj)->st_uid == getuid()) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.grpowned? => true or false
+ *
+ * Returns true if the effective group id of the process is the same as
+ * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
+ *
+ * File.stat("testfile").grpowned? #=> true
+ * File.stat("/etc/passwd").grpowned? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_grpowned(obj)
+ VALUE obj;
+{
+#ifndef _WIN32
+ if (group_member(get_stat(obj)->st_gid)) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.readable? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is readable by the
+ * effective user id of this process.
+ *
+ * File.stat("testfile").readable? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_r(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (geteuid() == 0) return Qtrue;
+#endif
+#ifdef S_IRUSR
+ if (rb_stat_owned(obj))
+ return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IRGRP
+ if (rb_stat_grpowned(obj))
+ return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IROTH
+ if (!(st->st_mode & S_IROTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.readable_real? -> true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is readable by the real
+ * user id of this process.
+ *
+ * File.stat("testfile").readable_real? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_R(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (getuid() == 0) return Qtrue;
+#endif
+#ifdef S_IRUSR
+ if (rb_stat_rowned(obj))
+ return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IRGRP
+ if (group_member(get_stat(obj)->st_gid))
+ return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IROTH
+ if (!(st->st_mode & S_IROTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.writable? -> true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is writable by the
+ * effective user id of this process.
+ *
+ * File.stat("testfile").writable? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_w(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (geteuid() == 0) return Qtrue;
+#endif
+#ifdef S_IWUSR
+ if (rb_stat_owned(obj))
+ return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IWGRP
+ if (rb_stat_grpowned(obj))
+ return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IWOTH
+ if (!(st->st_mode & S_IWOTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.writable_real? -> true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is writable by the real
+ * user id of this process.
+ *
+ * File.stat("testfile").writable_real? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_W(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (getuid() == 0) return Qtrue;
+#endif
+#ifdef S_IWUSR
+ if (rb_stat_rowned(obj))
+ return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IWGRP
+ if (group_member(get_stat(obj)->st_gid))
+ return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IWOTH
+ if (!(st->st_mode & S_IWOTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.executable? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is executable or if the
+ * operating system doesn't distinguish executable files from
+ * nonexecutable files. The tests are made using the effective owner of
+ * the process.
+ *
+ * File.stat("testfile").executable? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_x(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (geteuid() == 0) {
+ return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
+ }
+#endif
+#ifdef S_IXUSR
+ if (rb_stat_owned(obj))
+ return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IXGRP
+ if (rb_stat_grpowned(obj))
+ return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IXOTH
+ if (!(st->st_mode & S_IXOTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.executable_real? => true or false
+ *
+ * Same as <code>executable?</code>, but tests using the real owner of
+ * the process.
+ */
+
+static VALUE
+rb_stat_X(obj)
+ VALUE obj;
+{
+ struct stat *st = get_stat(obj);
+
+#ifdef USE_GETEUID
+ if (getuid() == 0) {
+ return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
+ }
+#endif
+#ifdef S_IXUSR
+ if (rb_stat_rowned(obj))
+ return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
+#endif
+#ifdef S_IXGRP
+ if (group_member(get_stat(obj)->st_gid))
+ return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
+#endif
+#ifdef S_IXOTH
+ if (!(st->st_mode & S_IXOTH)) return Qfalse;
+#endif
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * stat.file? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a regular file (not
+ * a device file, pipe, socket, etc.).
+ *
+ * File.stat("testfile").file? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_f(obj)
+ VALUE obj;
+{
+ if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.zero? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a zero-length file;
+ * <code>false</code> otherwise.
+ *
+ * File.stat("testfile").zero? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_z(obj)
+ VALUE obj;
+{
+ if (get_stat(obj)->st_size == 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * state.size => integer
+ *
+ * Returns the size of <i>stat</i> in bytes.
+ *
+ * File.stat("testfile").size #=> 66
+ *
+ */
+
+static VALUE
+rb_stat_s(obj)
+ VALUE obj;
+{
+ off_t size = get_stat(obj)->st_size;
+
+ if (size == 0) return Qnil;
+ return OFFT2NUM(size);
+}
+
+/*
+ * call-seq:
+ * stat.setuid? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> has the set-user-id
+ * permission bit set, <code>false</code> if it doesn't or if the
+ * operating system doesn't support this feature.
+ *
+ * File.stat("/bin/su").setuid? #=> true
+ */
+
+static VALUE
+rb_stat_suid(obj)
+ VALUE obj;
+{
+#ifdef S_ISUID
+ if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.setgid? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> has the set-group-id
+ * permission bit set, <code>false</code> if it doesn't or if the
+ * operating system doesn't support this feature.
+ *
+ * File.stat("/usr/sbin/lpc").setgid? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_sgid(obj)
+ VALUE obj;
+{
+#ifdef S_ISGID
+ if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.sticky? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> has its sticky bit set,
+ * <code>false</code> if it doesn't or if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").sticky? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_sticky(obj)
+ VALUE obj;
+{
+#ifdef S_ISVTX
+ if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+VALUE rb_mFConst;
+
+void
+rb_file_const(name, value)
+ const char *name;
+ VALUE value;
+{
+ rb_define_const(rb_mFConst, name, value);
+}
+
+static int
+is_absolute_path(path)
+ const char *path;
+{
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path) && isdirsep(path[2])) return 1;
+#endif
+#ifdef DOSISH_UNC
+ if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
+#endif
+#ifndef DOSISH
+ if (path[0] == '/') return 1;
+#endif
+ return 0;
+}
+
+#ifndef ENABLE_PATH_CHECK
+# if defined DOSISH || defined __CYGWIN__
+# define ENABLE_PATH_CHECK 0
+# else
+# define ENABLE_PATH_CHECK 1
+# endif
+#endif
+
+#if ENABLE_PATH_CHECK
+static int
+path_check_0(fpath, execpath)
+ VALUE fpath;
+ int execpath;
+{
+ struct stat st;
+ char *p0 = StringValueCStr(fpath);
+ char *p = 0, *s;
+
+ if (!is_absolute_path(p0)) {
+ char *buf = my_getcwd();
+ VALUE newpath;
+
+ newpath = rb_str_new2(buf);
+ free(buf);
+
+ rb_str_cat2(newpath, "/");
+ rb_str_cat2(newpath, p0);
+ p0 = RSTRING(fpath = newpath)->ptr;
+ }
+ for (;;) {
+#ifndef S_IWOTH
+# define S_IWOTH 002
+#endif
+ if (stat(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
+#ifdef S_ISVTX
+ && !(p && execpath && (st.st_mode & S_ISVTX))
+#endif
+ ) {
+ rb_warn("Insecure world writable dir %s in %sPATH, mode 0%o",
+ p0, (execpath ? "" : "LOAD_"), st.st_mode);
+ if (p) *p = '/';
+ return 0;
+ }
+ s = strrdirsep(p0);
+ if (p) *p = '/';
+ if (!s || s == p0) return 1;
+ p = s;
+ *p = '\0';
+ }
+}
+#endif
+
+static int
+fpath_check(path)
+ char *path;
+{
+#if ENABLE_PATH_CHECK
+ return path_check_0(rb_str_new2(path), Qfalse);
+#else
+ return 1;
+#endif
+}
+
+int
+rb_path_check(path)
+ char *path;
+{
+#if ENABLE_PATH_CHECK
+ char *p0, *p, *pend;
+ const char sep = PATH_SEP_CHAR;
+
+ if (!path) return 1;
+
+ pend = path + strlen(path);
+ p0 = path;
+ p = strchr(path, sep);
+ if (!p) p = pend;
+
+ for (;;) {
+ if (!path_check_0(rb_str_new(p0, p - p0), Qtrue)) {
+ return 0; /* not safe */
+ }
+ p0 = p + 1;
+ if (p0 > pend) break;
+ p = strchr(p0, sep);
+ if (!p) p = pend;
+ }
+#endif
+ return 1;
+}
+
+#if defined(__MACOS__) || defined(riscos)
+static int
+is_macos_native_path(path)
+ const char *path;
+{
+ if (strchr(path, ':')) return 1;
+ return 0;
+}
+#endif
+
+static int
+file_load_ok(file)
+ char *file;
+{
+ int ret = 1;
+ int fd = open(file, O_RDONLY);
+ if (fd == -1) return 0;
+#if !defined DOSISH
+ {
+ struct stat st;
+ if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
+ ret = 0;
+ }
+ }
+#endif
+ (void)close(fd);
+ return ret;
+}
+
+extern VALUE rb_load_path;
+
+int
+rb_find_file_ext(filep, ext)
+ VALUE *filep;
+ const char * const *ext;
+{
+ char *path, *found;
+ char *f = RSTRING(*filep)->ptr;
+ VALUE fname;
+ long i, j;
+
+ if (f[0] == '~') {
+ fname = rb_file_expand_path(*filep, Qnil);
+ if (rb_safe_level() >= 2 && OBJ_TAINTED(fname)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ }
+ OBJ_FREEZE(fname);
+ f = StringValueCStr(fname);
+ *filep = fname;
+ }
+
+ if (is_absolute_path(f)) {
+ for (i=0; ext[i]; i++) {
+ fname = rb_str_dup(*filep);
+ rb_str_cat2(fname, ext[i]);
+ OBJ_FREEZE(fname);
+ if (file_load_ok(StringValueCStr(fname))) {
+ *filep = fname;
+ return i+1;
+ }
+ }
+ return 0;
+ }
+
+ if (!rb_load_path) return 0;
+
+ Check_Type(rb_load_path, T_ARRAY);
+ for (i=0;i<RARRAY(rb_load_path)->len;i++) {
+ VALUE str = RARRAY(rb_load_path)->ptr[i];
+
+ SafeStringValue(str);
+ if (RSTRING(str)->len == 0) continue;
+ path = RSTRING(str)->ptr;
+ for (j=0; ext[j]; j++) {
+ fname = rb_str_dup(*filep);
+ rb_str_cat2(fname, ext[j]);
+ OBJ_FREEZE(fname);
+ found = dln_find_file(StringValueCStr(fname), path);
+ if (found && file_load_ok(found)) {
+ *filep = fname;
+ return j+1;
+ }
+ }
+ }
+ return 0;
+}
+
+VALUE
+rb_find_file(path)
+ VALUE path;
+{
+ VALUE tmp;
+ char *f = StringValueCStr(path);
+ char *lpath;
+
+ if (f[0] == '~') {
+ path = rb_file_expand_path(path, Qnil);
+ if (rb_safe_level() >= 1 && OBJ_TAINTED(path)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
+ }
+ OBJ_FREEZE(path);
+ f = StringValueCStr(path);
+ }
+
+#if defined(__MACOS__) || defined(riscos)
+ if (is_macos_native_path(f)) {
+ if (rb_safe_level() >= 1 && !fpath_check(f)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ }
+ if (file_load_ok(f)) return path;
+ }
+#endif
+
+ if (is_absolute_path(f)) {
+ if (rb_safe_level() >= 1 && !fpath_check(f)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ }
+ if (file_load_ok(f)) return path;
+ }
+
+ if (rb_safe_level() >= 4) {
+ rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
+ }
+
+ if (rb_load_path) {
+ long i;
+
+ Check_Type(rb_load_path, T_ARRAY);
+ tmp = rb_ary_new();
+ for (i=0;i<RARRAY(rb_load_path)->len;i++) {
+ VALUE str = RARRAY(rb_load_path)->ptr[i];
+ SafeStringValue(str);
+ if (RSTRING(str)->len > 0) {
+ rb_ary_push(tmp, str);
+ }
+ }
+ tmp = rb_ary_join(tmp, rb_str_new2(PATH_SEP));
+ if (RSTRING(tmp)->len == 0) {
+ lpath = 0;
+ }
+ else {
+ lpath = RSTRING(tmp)->ptr;
+ }
+ }
+ else {
+ lpath = 0;
+ }
+
+ if (!lpath) {
+ return 0; /* no path, no load */
+ }
+ if (!(f = dln_find_file(f, lpath))) {
+ return 0;
+ }
+ if (rb_safe_level() >= 1 && !fpath_check(f)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ }
+ if (file_load_ok(f)) {
+ tmp = rb_str_new2(f);
+ OBJ_FREEZE(tmp);
+ return tmp;
+ }
+ return 0;
+}
+
+static void
+define_filetest_function(name, func, argc)
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_module_function(rb_mFileTest, name, func, argc);
+ rb_define_singleton_method(rb_cFile, name, func, argc);
+}
+
+
+/*
+ * A <code>File</code> is an abstraction of any file object accessible
+ * by the program and is closely associated with class <code>IO</code>
+ * <code>File</code> includes the methods of module
+ * <code>FileTest</code> as class methods, allowing you to write (for
+ * example) <code>File.exist?("foo")</code>.
+ *
+ * In the description of File methods,
+ * <em>permission bits</em> are a platform-specific
+ * set of bits that indicate permissions of a file. On Unix-based
+ * systems, permissions are viewed as a set of three octets, for the
+ * owner, the group, and the rest of the world. For each of these
+ * entities, permissions may be set to read, write, or execute the
+ * file:
+ *
+ * The permission bits <code>0644</code> (in octal) would thus be
+ * interpreted as read/write for owner, and read-only for group and
+ * other. Higher-order bits may also be used to indicate the type of
+ * file (plain, directory, pipe, socket, and so on) and various other
+ * special features. If the permissions are for a directory, the
+ * meaning of the execute bit changes; when set the directory can be
+ * searched.
+ *
+ * On non-Posix operating systems, there may be only the ability to
+ * make a file read-only or read-write. In this case, the remaining
+ * permission bits will be synthesized to resemble typical values. For
+ * instance, on Windows NT the default permission bits are
+ * <code>0644</code>, which means read/write for owner, read-only for
+ * all others. The only change that can be made is to make the file
+ * read-only, which is reported as <code>0444</code>.
+ */
+
+void
+Init_File()
+{
+ rb_mFileTest = rb_define_module("FileTest");
+ rb_cFile = rb_define_class("File", rb_cIO);
+
+ define_filetest_function("directory?", test_d, 1);
+ define_filetest_function("exist?", test_e, 1);
+ define_filetest_function("exists?", test_e, 1); /* temporary */
+ define_filetest_function("readable?", test_r, 1);
+ define_filetest_function("readable_real?", test_R, 1);
+ define_filetest_function("writable?", test_w, 1);
+ define_filetest_function("writable_real?", test_W, 1);
+ define_filetest_function("executable?", test_x, 1);
+ define_filetest_function("executable_real?", test_X, 1);
+ define_filetest_function("file?", test_f, 1);
+ define_filetest_function("zero?", test_z, 1);
+ define_filetest_function("size?", test_s, 1);
+ define_filetest_function("size", rb_file_s_size, 1);
+ define_filetest_function("owned?", test_owned, 1);
+ define_filetest_function("grpowned?", test_grpowned, 1);
+
+ define_filetest_function("pipe?", test_p, 1);
+ define_filetest_function("symlink?", test_l, 1);
+ define_filetest_function("socket?", test_S, 1);
+
+ define_filetest_function("blockdev?", test_b, 1);
+ define_filetest_function("chardev?", test_c, 1);
+
+ define_filetest_function("setuid?", test_suid, 1);
+ define_filetest_function("setgid?", test_sgid, 1);
+ define_filetest_function("sticky?", test_sticky, 1);
+
+ define_filetest_function("identical?", test_identical, 2);
+
+ rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
+ rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
+ rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
+
+ rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
+ rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
+ rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
+
+ rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
+ rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
+ rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
+ rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
+ rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
+
+ rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
+ rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
+ rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
+
+ rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
+ rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
+ rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
+ rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
+ rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
+ rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
+ rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
+ rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
+ rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
+
+ separator = rb_obj_freeze(rb_str_new2("/"));
+ rb_define_const(rb_cFile, "Separator", separator);
+ rb_define_const(rb_cFile, "SEPARATOR", separator);
+ rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
+ rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
+
+#ifdef DOSISH
+ rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_str_new2("\\")));
+#else
+ rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
+#endif
+ rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
+
+ rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
+ rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
+
+ rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
+ rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
+ rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
+
+ rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
+ rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
+ rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
+
+ rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
+
+ rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
+ rb_include_module(rb_cIO, rb_mFConst);
+ rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
+ rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
+ rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
+ rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
+
+ rb_define_method(rb_cFile, "path", rb_file_path, 0);
+ rb_define_global_function("test", rb_f_test, -1);
+
+ rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
+ rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
+ rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
+ rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
+
+ rb_include_module(rb_cStat, rb_mComparable);
+
+ rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
+
+ rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
+ rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
+ rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
+ rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
+ rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
+ rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
+ rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
+ rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
+ rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
+ rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
+ rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
+ rb_define_method(rb_cStat, "size", rb_stat_size, 0);
+ rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
+ rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
+ rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
+ rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
+ rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
+
+ rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
+
+ rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
+
+ rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
+ rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
+ rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
+ rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
+ rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
+ rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
+ rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
+ rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
+ rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
+ rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
+ rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
+ rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
+
+ rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
+ rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
+ rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
+
+ rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
+ rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
+
+ rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
+ rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
+ rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);
+}
+/**********************************************************************
+
+ gc.c -
+
+ $Author: wyhaines $
+ $Date: 2009-07-10 20:31:58 +0200 (Fri, 10 Jul 2009) $
+ created at: Tue Oct 5 09:44:46 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+#include "st.h"
+#include "node.h"
+#include "env.h"
+#include "re.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+#include <windows.h>
+#endif
+
+void re_free_registers _((struct re_registers*));
+void rb_io_fptr_finalize _((struct OpenFile*));
+
+#if !defined(setjmp) && defined(HAVE__SETJMP)
+#define setjmp(env) _setjmp(env)
+#endif
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+# ifndef atarist
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# endif /* atarist */
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifndef _AIX
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+void *alloca ();
+# endif
+# endif /* AIX */
+# endif /* HAVE_ALLOCA_H */
+#endif /* __GNUC__ */
+
+#ifndef GC_MALLOC_LIMIT
+#if defined(MSDOS) || defined(__human68k__)
+#define GC_MALLOC_LIMIT 200000
+#else
+#define GC_MALLOC_LIMIT 8000000
+#endif
+#endif
+
+static unsigned long malloc_increase = 0;
+static unsigned long malloc_limit = GC_MALLOC_LIMIT;
+static void run_final();
+static VALUE nomem_error;
+static void garbage_collect();
+
+NORETURN(void rb_exc_jump _((VALUE)));
+
+void
+rb_memerror()
+{
+ rb_thread_t th = rb_curr_thread;
+
+ if (!nomem_error ||
+ (rb_thread_raised_p(th, RAISED_NOMEMORY) && rb_safe_level() < 4)) {
+ fprintf(stderr, "[FATAL] failed to allocate memory\n");
+ exit(1);
+ }
+ if (rb_thread_raised_p(th, RAISED_NOMEMORY)) {
+ rb_exc_jump(nomem_error);
+ }
+ rb_thread_raised_set(th, RAISED_NOMEMORY);
+ rb_exc_raise(nomem_error);
+}
+
+void *
+ruby_xmalloc(size)
+ long size;
+{
+ void *mem;
+
+ if (size < 0) {
+ rb_raise(rb_eNoMemError, "negative allocation size (or too big)");
+ }
+ if (size == 0) size = 1;
+
+ if ((malloc_increase+size) > malloc_limit) {
+ garbage_collect();
+ }
+ RUBY_CRITICAL(mem = malloc(size));
+ if (!mem) {
+ garbage_collect();
+ RUBY_CRITICAL(mem = malloc(size));
+ if (!mem) {
+ rb_memerror();
+ }
+ }
+ malloc_increase += size;
+
+ return mem;
+}
+
+void *
+ruby_xcalloc(n, size)
+ long n, size;
+{
+ void *mem;
+
+ mem = xmalloc(n * size);
+ memset(mem, 0, n * size);
+
+ return mem;
+}
+
+void *
+ruby_xrealloc(ptr, size)
+ void *ptr;
+ long size;
+{
+ void *mem;
+
+ if (size < 0) {
+ rb_raise(rb_eArgError, "negative re-allocation size");
+ }
+ if (!ptr) return xmalloc(size);
+ if (size == 0) size = 1;
+ RUBY_CRITICAL(mem = realloc(ptr, size));
+ if (!mem) {
+ garbage_collect();
+ RUBY_CRITICAL(mem = realloc(ptr, size));
+ if (!mem) {
+ rb_memerror();
+ }
+ }
+ malloc_increase += size;
+
+ return mem;
+}
+
+void
+ruby_xfree(x)
+ void *x;
+{
+ if (x)
+ RUBY_CRITICAL(free(x));
+}
+
+extern int ruby_in_compile;
+static int dont_gc;
+static int during_gc;
+static int need_call_final = 0;
+static st_table *finalizer_table = 0;
+
+
+/*
+ * call-seq:
+ * GC.enable => true or false
+ *
+ * Enables garbage collection, returning <code>true</code> if garbage
+ * collection was previously disabled.
+ *
+ * GC.disable #=> false
+ * GC.enable #=> true
+ * GC.enable #=> false
+ *
+ */
+
+VALUE
+rb_gc_enable()
+{
+ int old = dont_gc;
+
+ dont_gc = Qfalse;
+ return old;
+}
+
+/*
+ * call-seq:
+ * GC.disable => true or false
+ *
+ * Disables garbage collection, returning <code>true</code> if garbage
+ * collection was already disabled.
+ *
+ * GC.disable #=> false
+ * GC.disable #=> true
+ *
+ */
+
+VALUE
+rb_gc_disable()
+{
+ int old = dont_gc;
+
+ dont_gc = Qtrue;
+ return old;
+}
+
+VALUE rb_mGC;
+
+static struct gc_list {
+ VALUE *varptr;
+ struct gc_list *next;
+} *global_List = 0;
+
+void
+rb_gc_register_address(addr)
+ VALUE *addr;
+{
+ struct gc_list *tmp;
+
+ tmp = ALLOC(struct gc_list);
+ tmp->next = global_List;
+ tmp->varptr = addr;
+ global_List = tmp;
+}
+
+void
+rb_gc_unregister_address(addr)
+ VALUE *addr;
+{
+ struct gc_list *tmp = global_List;
+
+ if (tmp->varptr == addr) {
+ global_List = tmp->next;
+ RUBY_CRITICAL(free(tmp));
+ return;
+ }
+ while (tmp->next) {
+ if (tmp->next->varptr == addr) {
+ struct gc_list *t = tmp->next;
+
+ tmp->next = tmp->next->next;
+ RUBY_CRITICAL(free(t));
+ break;
+ }
+ tmp = tmp->next;
+ }
+}
+
+#undef GC_DEBUG
+
+void
+rb_global_variable(var)
+ VALUE *var;
+{
+ rb_gc_register_address(var);
+}
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
+#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
+#endif
+
+typedef struct RVALUE {
+ union {
+ struct {
+ unsigned long flags; /* always 0 for freed obj */
+ struct RVALUE *next;
+ } free;
+ struct RBasic basic;
+ struct RObject object;
+ struct RClass klass;
+ struct RFloat flonum;
+ struct RString string;
+ struct RArray array;
+ struct RRegexp regexp;
+ struct RHash hash;
+ struct RData data;
+ struct RStruct rstruct;
+ struct RBignum bignum;
+ struct RFile file;
+ struct RNode node;
+ struct RMatch match;
+ struct RVarmap varmap;
+ struct SCOPE scope;
+ } as;
+#ifdef GC_DEBUG
+ char *file;
+ int line;
+#endif
+} RVALUE;
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
+#pragma pack(pop)
+#endif
+
+static RVALUE *freelist = 0;
+static RVALUE *deferred_final_list = 0;
+
+#define HEAPS_INCREMENT 10
+static struct heaps_slot {
+ void *membase;
+ RVALUE *slot;
+ int limit;
+} *heaps;
+static int heaps_length = 0;
+static int heaps_used = 0;
+
+#define HEAP_MIN_SLOTS 10000
+static int heap_slots = HEAP_MIN_SLOTS;
+
+#define FREE_MIN 4096
+
+static RVALUE *himem, *lomem;
+
+static void
+add_heap()
+{
+ RVALUE *p, *pend;
+
+ if (heaps_used == heaps_length) {
+ /* Realloc heaps */
+ struct heaps_slot *p;
+ int length;
+
+ heaps_length += HEAPS_INCREMENT;
+ length = heaps_length*sizeof(struct heaps_slot);
+ RUBY_CRITICAL(
+ if (heaps_used > 0) {
+ p = (struct heaps_slot *)realloc(heaps, length);
+ if (p) heaps = p;
+ }
+ else {
+ p = heaps = (struct heaps_slot *)malloc(length);
+ });
+ if (p == 0) rb_memerror();
+ }
+
+ for (;;) {
+ RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
+ if (p == 0) {
+ if (heap_slots == HEAP_MIN_SLOTS) {
+ rb_memerror();
+ }
+ heap_slots = HEAP_MIN_SLOTS;
+ continue;
+ }
+ heaps[heaps_used].membase = p;
+ if ((VALUE)p % sizeof(RVALUE) == 0)
+ heap_slots += 1;
+ else
+ p = (RVALUE*)((VALUE)p + sizeof(RVALUE) - ((VALUE)p % sizeof(RVALUE)));
+ heaps[heaps_used].slot = p;
+ heaps[heaps_used].limit = heap_slots;
+ break;
+ }
+ pend = p + heap_slots;
+ if (lomem == 0 || lomem > p) lomem = p;
+ if (himem < pend) himem = pend;
+ heaps_used++;
+ heap_slots *= 1.8;
+ if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
+
+ while (p < pend) {
+ p->as.free.flags = 0;
+ p->as.free.next = freelist;
+ freelist = p;
+ p++;
+ }
+}
+#define RANY(o) ((RVALUE*)(o))
+
+int
+rb_during_gc()
+{
+ return during_gc;
+}
+
+VALUE
+rb_newobj()
+{
+ VALUE obj;
+
+ if (during_gc)
+ rb_bug("object allocation during garbage collection phase");
+
+ if (!freelist) garbage_collect();
+
+ obj = (VALUE)freelist;
+ freelist = freelist->as.free.next;
+ MEMZERO((void*)obj, RVALUE, 1);
+#ifdef GC_DEBUG
+ RANY(obj)->file = ruby_sourcefile;
+ RANY(obj)->line = ruby_sourceline;
+#endif
+ return obj;
+}
+
+VALUE
+rb_data_object_alloc(klass, datap, dmark, dfree)
+ VALUE klass;
+ void *datap;
+ RUBY_DATA_FUNC dmark;
+ RUBY_DATA_FUNC dfree;
+{
+ NEWOBJ(data, struct RData);
+ if (klass) Check_Type(klass, T_CLASS);
+ OBJSETUP(data, klass, T_DATA);
+ data->data = datap;
+ data->dfree = dfree;
+ data->dmark = dmark;
+
+ return (VALUE)data;
+}
+
+extern st_table *rb_class_tbl;
+VALUE *rb_gc_stack_start = 0;
+#ifdef __ia64
+VALUE *rb_gc_register_stack_start = 0;
+#endif
+
+#ifdef DJGPP
+/* set stack size (http://www.delorie.com/djgpp/v2faq/faq15_9.html) */
+unsigned int _stklen = 0x180000; /* 1.5 kB */
+#endif
+
+#if defined(DJGPP) || defined(_WIN32_WCE)
+static unsigned int STACK_LEVEL_MAX = 65535;
+#elif defined(__human68k__)
+unsigned int _stacksize = 262144;
+# define STACK_LEVEL_MAX (_stacksize - 4096)
+# undef HAVE_GETRLIMIT
+#elif defined(HAVE_GETRLIMIT) || defined(_WIN32)
+static size_t STACK_LEVEL_MAX = 655300;
+#else
+# define STACK_LEVEL_MAX 655300
+#endif
+
+#ifdef C_ALLOCA
+# define SET_STACK_END VALUE stack_end; alloca(0);
+# define STACK_END (&stack_end)
+#else
+# if defined(__GNUC__) && defined(USE_BUILTIN_FRAME_ADDRESS) && !defined(__ia64)
+# if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
+__attribute__ ((noinline))
+# endif
+static void
+stack_end_address(VALUE **stack_end_p)
+{
+ VALUE stack_end;
+ *stack_end_p = &stack_end;
+}
+# define SET_STACK_END VALUE *stack_end; stack_end_address(&stack_end)
+# else
+# define SET_STACK_END VALUE *stack_end = alloca(1)
+# endif
+# define STACK_END (stack_end)
+#endif
+#if STACK_GROW_DIRECTION < 0
+# define STACK_LENGTH (rb_gc_stack_start - STACK_END)
+#elif STACK_GROW_DIRECTION > 0
+# define STACK_LENGTH (STACK_END - rb_gc_stack_start + 1)
+#else
+# define STACK_LENGTH ((STACK_END < rb_gc_stack_start) ? rb_gc_stack_start - STACK_END\
+ : STACK_END - rb_gc_stack_start + 1)
+#endif
+#if STACK_GROW_DIRECTION > 0
+# define STACK_UPPER(x, a, b) a
+#elif STACK_GROW_DIRECTION < 0
+# define STACK_UPPER(x, a, b) b
+#else
+static int grow_direction;
+static int
+stack_grow_direction(addr)
+ VALUE *addr;
+{
+ SET_STACK_END;
+
+ if (STACK_END > addr) return grow_direction = 1;
+ return grow_direction = -1;
+}
+# define stack_growup_p(x) ((grow_direction ? grow_direction : stack_grow_direction(x)) > 0)
+# define STACK_UPPER(x, a, b) (stack_growup_p(x) ? a : b)
+#endif
+
+#define GC_WATER_MARK 512
+
+#define CHECK_STACK(ret) do {\
+ SET_STACK_END;\
+ (ret) = (STACK_LENGTH > STACK_LEVEL_MAX + GC_WATER_MARK);\
+} while (0)
+
+size_t
+ruby_stack_length(p)
+ VALUE **p;
+{
+ SET_STACK_END;
+ if (p) *p = STACK_UPPER(STACK_END, rb_gc_stack_start, STACK_END);
+ return STACK_LENGTH;
+}
+
+int
+ruby_stack_check()
+{
+ int ret;
+
+ CHECK_STACK(ret);
+ return ret;
+}
+
+#define MARK_STACK_MAX 1024
+static VALUE mark_stack[MARK_STACK_MAX];
+static VALUE *mark_stack_ptr;
+static int mark_stack_overflow;
+
+static void
+init_mark_stack()
+{
+ mark_stack_overflow = 0;
+ mark_stack_ptr = mark_stack;
+}
+
+#define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack)
+
+static st_table *source_filenames;
+
+char *
+rb_source_filename(f)
+ const char *f;
+{
+ st_data_t name;
+
+ if (!st_lookup(source_filenames, (st_data_t)f, &name)) {
+ long len = strlen(f) + 1;
+ char *ptr = ALLOC_N(char, len + 1);
+ name = (st_data_t)ptr;
+ *ptr++ = 0;
+ MEMCPY(ptr, f, char, len);
+ st_add_direct(source_filenames, (st_data_t)ptr, name);
+ return ptr;
+ }
+ return (char *)name + 1;
+}
+
+static void
+mark_source_filename(f)
+ char *f;
+{
+ if (f) {
+ f[-1] = 1;
+ }
+}
+
+static int
+sweep_source_filename(key, value)
+ char *key, *value;
+{
+ if (*value) {
+ *value = 0;
+ return ST_CONTINUE;
+ }
+ else {
+ free(value);
+ return ST_DELETE;
+ }
+}
+
+static void gc_mark _((VALUE ptr, int lev));
+static void gc_mark_children _((VALUE ptr, int lev));
+
+static void
+gc_mark_all()
+{
+ RVALUE *p, *pend;
+ int i;
+
+ init_mark_stack();
+ for (i = 0; i < heaps_used; i++) {
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ while (p < pend) {
+ if ((p->as.basic.flags & FL_MARK) &&
+ (p->as.basic.flags != FL_MARK)) {
+ gc_mark_children((VALUE)p, 0);
+ }
+ p++;
+ }
+ }
+}
+
+static void
+gc_mark_rest()
+{
+ VALUE tmp_arry[MARK_STACK_MAX];
+ VALUE *p;
+
+ p = (mark_stack_ptr - mark_stack) + tmp_arry;
+ MEMCPY(tmp_arry, mark_stack, VALUE, MARK_STACK_MAX);
+
+ init_mark_stack();
+ while(p != tmp_arry){
+ p--;
+ gc_mark_children(*p, 0);
+ }
+}
+
+static inline int
+is_pointer_to_heap(ptr)
+ void *ptr;
+{
+ register RVALUE *p = RANY(ptr);
+ register RVALUE *heap_org;
+ register long i;
+
+ if (p < lomem || p > himem) return Qfalse;
+ if ((VALUE)p % sizeof(RVALUE) != 0) return Qfalse;
+
+ /* check if p looks like a pointer */
+ for (i=0; i < heaps_used; i++) {
+ heap_org = heaps[i].slot;
+ if (heap_org <= p && p < heap_org + heaps[i].limit)
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static void
+mark_locations_array(x, n)
+ register VALUE *x;
+ register long n;
+{
+ VALUE v;
+ while (n--) {
+ v = *x;
+ if (is_pointer_to_heap((void *)v)) {
+ gc_mark(v, 0);
+ }
+ x++;
+ }
+}
+
+void
+rb_gc_mark_locations(start, end)
+ VALUE *start, *end;
+{
+ long n;
+
+ n = end - start;
+ mark_locations_array(start,n);
+}
+
+static int
+mark_entry(key, value, lev)
+ ID key;
+ VALUE value;
+ int lev;
+{
+ gc_mark(value, lev);
+ return ST_CONTINUE;
+}
+
+static void
+mark_tbl(tbl, lev)
+ st_table *tbl;
+ int lev;
+{
+ if (!tbl) return;
+ st_foreach(tbl, mark_entry, lev);
+}
+
+void
+rb_mark_tbl(tbl)
+ st_table *tbl;
+{
+ mark_tbl(tbl, 0);
+}
+
+static int
+mark_key(key, value, lev)
+ VALUE key, value;
+ int lev;
+{
+ gc_mark(key, lev);
+ return ST_CONTINUE;
+}
+
+static void
+mark_set(tbl, lev)
+ st_table *tbl;
+ int lev;
+{
+ if (!tbl) return;
+ st_foreach(tbl, mark_key, lev);
+}
+
+void
+rb_mark_set(tbl)
+ st_table *tbl;
+{
+ mark_set(tbl, 0);
+}
+
+static int
+mark_keyvalue(key, value, lev)
+ VALUE key;
+ VALUE value;
+ int lev;
+{
+ gc_mark(key, lev);
+ gc_mark(value, lev);
+ return ST_CONTINUE;
+}
+
+static void
+mark_hash(tbl, lev)
+ st_table *tbl;
+ int lev;
+{
+ if (!tbl) return;
+ st_foreach(tbl, mark_keyvalue, lev);
+}
+
+void
+rb_mark_hash(tbl)
+ st_table *tbl;
+{
+ mark_hash(tbl, 0);
+}
+
+void
+rb_gc_mark_maybe(obj)
+ VALUE obj;
+{
+ if (is_pointer_to_heap((void *)obj)) {
+ gc_mark(obj, 0);
+ }
+}
+
+#define GC_LEVEL_MAX 250
+
+static void
+gc_mark(ptr, lev)
+ VALUE ptr;
+ int lev;
+{
+ register RVALUE *obj;
+
+ obj = RANY(ptr);
+ if (rb_special_const_p(ptr)) return; /* special const not marked */
+ if (obj->as.basic.flags == 0) return; /* free cell */
+ if (obj->as.basic.flags & FL_MARK) return; /* already marked */
+ obj->as.basic.flags |= FL_MARK;
+
+ if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) {
+ if (!mark_stack_overflow) {
+ if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) {
+ *mark_stack_ptr = ptr;
+ mark_stack_ptr++;
+ }
+ else {
+ mark_stack_overflow = 1;
+ }
+ }
+ return;
+ }
+ gc_mark_children(ptr, lev+1);
+}
+
+void
+rb_gc_mark(ptr)
+ VALUE ptr;
+{
+ gc_mark(ptr, 0);
+}
+
+static void
+gc_mark_children(ptr, lev)
+ VALUE ptr;
+ int lev;
+{
+ register RVALUE *obj = RANY(ptr);
+
+ goto marking; /* skip */
+
+ again:
+ obj = RANY(ptr);
+ if (rb_special_const_p(ptr)) return; /* special const not marked */
+ if (obj->as.basic.flags == 0) return; /* free cell */
+ if (obj->as.basic.flags & FL_MARK) return; /* already marked */
+ obj->as.basic.flags |= FL_MARK;
+
+ marking:
+ if (FL_TEST(obj, FL_EXIVAR)) {
+ rb_mark_generic_ivar(ptr);
+ }
+
+ switch (obj->as.basic.flags & T_MASK) {
+ case T_NIL:
+ case T_FIXNUM:
+ rb_bug("rb_gc_mark() called for broken object");
+ break;
+
+ case T_NODE:
+ mark_source_filename(obj->as.node.nd_file);
+ switch (nd_type(obj)) {
+ case NODE_IF: /* 1,2,3 */
+ case NODE_FOR:
+ case NODE_ITER:
+ case NODE_CREF:
+ case NODE_WHEN:
+ case NODE_MASGN:
+ case NODE_RESCUE:
+ case NODE_RESBODY:
+ case NODE_CLASS:
+ gc_mark((VALUE)obj->as.node.u2.node, lev);
+ /* fall through */
+ case NODE_BLOCK: /* 1,3 */
+ case NODE_ARRAY:
+ case NODE_DSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ case NODE_FBODY:
+ case NODE_ENSURE:
+ case NODE_CALL:
+ case NODE_DEFS:
+ case NODE_OP_ASGN1:
+ gc_mark((VALUE)obj->as.node.u1.node, lev);
+ /* fall through */
+ case NODE_SUPER: /* 3 */
+ case NODE_FCALL:
+ case NODE_DEFN:
+ case NODE_NEWLINE:
+ ptr = (VALUE)obj->as.node.u3.node;
+ goto again;
+
+ case NODE_WHILE: /* 1,2 */
+ case NODE_UNTIL:
+ case NODE_AND:
+ case NODE_OR:
+ case NODE_CASE:
+ case NODE_SCLASS:
+ case NODE_DOT2:
+ case NODE_DOT3:
+ case NODE_FLIP2:
+ case NODE_FLIP3:
+ case NODE_MATCH2:
+ case NODE_MATCH3:
+ case NODE_OP_ASGN_OR:
+ case NODE_OP_ASGN_AND:
+ case NODE_MODULE:
+ case NODE_ALIAS:
+ case NODE_VALIAS:
+ case NODE_ARGS:
+ gc_mark((VALUE)obj->as.node.u1.node, lev);
+ /* fall through */
+ case NODE_METHOD: /* 2 */
+ case NODE_NOT:
+ case NODE_GASGN:
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:
+ case NODE_IASGN:
+ case NODE_CVDECL:
+ case NODE_CVASGN:
+ case NODE_COLON3:
+ case NODE_OPT_N:
+ case NODE_EVSTR:
+ case NODE_UNDEF:
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
+
+ case NODE_HASH: /* 1 */
+ case NODE_LIT:
+ case NODE_STR:
+ case NODE_XSTR:
+ case NODE_DEFINED:
+ case NODE_MATCH:
+ case NODE_RETURN:
+ case NODE_BREAK:
+ case NODE_NEXT:
+ case NODE_YIELD:
+ case NODE_COLON2:
+ case NODE_SPLAT:
+ case NODE_TO_ARY:
+ case NODE_SVALUE:
+ ptr = (VALUE)obj->as.node.u1.node;
+ goto again;
+
+ case NODE_SCOPE: /* 2,3 */
+ case NODE_BLOCK_PASS:
+ case NODE_CDECL:
+ gc_mark((VALUE)obj->as.node.u3.node, lev);
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
+
+ case NODE_ZARRAY: /* - */
+ case NODE_ZSUPER:
+ case NODE_CFUNC:
+ case NODE_VCALL:
+ case NODE_GVAR:
+ case NODE_LVAR:
+ case NODE_DVAR:
+ case NODE_IVAR:
+ case NODE_CVAR:
+ case NODE_NTH_REF:
+ case NODE_BACK_REF:
+ case NODE_REDO:
+ case NODE_RETRY:
+ case NODE_SELF:
+ case NODE_NIL:
+ case NODE_TRUE:
+ case NODE_FALSE:
+ case NODE_ATTRSET:
+ case NODE_BLOCK_ARG:
+ case NODE_POSTEXE:
+ break;
+ case NODE_ALLOCA:
+ mark_locations_array((VALUE*)obj->as.node.u1.value,
+ obj->as.node.u3.cnt);
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
+
+ default: /* unlisted NODE */
+ if (is_pointer_to_heap(obj->as.node.u1.node)) {
+ gc_mark((VALUE)obj->as.node.u1.node, lev);
+ }
+ if (is_pointer_to_heap(obj->as.node.u2.node)) {
+ gc_mark((VALUE)obj->as.node.u2.node, lev);
+ }
+ if (is_pointer_to_heap(obj->as.node.u3.node)) {
+ gc_mark((VALUE)obj->as.node.u3.node, lev);
+ }
+ }
+ return; /* no need to mark class. */
+ }
+
+ gc_mark(obj->as.basic.klass, lev);
+ switch (obj->as.basic.flags & T_MASK) {
+ case T_ICLASS:
+ case T_CLASS:
+ case T_MODULE:
+ mark_tbl(obj->as.klass.m_tbl, lev);
+ mark_tbl(obj->as.klass.iv_tbl, lev);
+ ptr = obj->as.klass.super;
+ goto again;
+
+ case T_ARRAY:
+ if (FL_TEST(obj, ELTS_SHARED)) {
+ ptr = obj->as.array.aux.shared;
+ goto again;
+ }
+ else {
+ long i, len = obj->as.array.len;
+ VALUE *ptr = obj->as.array.ptr;
+
+ for (i=0; i < len; i++) {
+ gc_mark(*ptr++, lev);
+ }
+ }
+ break;
+
+ case T_HASH:
+ mark_hash(obj->as.hash.tbl, lev);
+ ptr = obj->as.hash.ifnone;
+ goto again;
+
+ case T_STRING:
+#define STR_ASSOC FL_USER3 /* copied from string.c */
+ if (FL_TEST(obj, ELTS_SHARED|STR_ASSOC)) {
+ ptr = obj->as.string.aux.shared;
+ goto again;
+ }
+ break;
+
+ case T_DATA:
+ if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj));
+ break;
+
+ case T_OBJECT:
+ mark_tbl(obj->as.object.iv_tbl, lev);
+ break;
+
+ case T_FILE:
+ case T_REGEXP:
+ case T_FLOAT:
+ case T_BIGNUM:
+ case T_BLKTAG:
+ break;
+
+ case T_MATCH:
+ if (obj->as.match.str) {
+ ptr = obj->as.match.str;
+ goto again;
+ }
+ break;
+
+ case T_VARMAP:
+ gc_mark(obj->as.varmap.val, lev);
+ ptr = (VALUE)obj->as.varmap.next;
+ goto again;
+
+ case T_SCOPE:
+ if (obj->as.scope.local_vars && (obj->as.scope.flags & SCOPE_MALLOC)) {
+ int n = obj->as.scope.local_tbl[0]+1;
+ VALUE *vars = &obj->as.scope.local_vars[-1];
+
+ while (n--) {
+ gc_mark(*vars++, lev);
+ }
+ }
+ break;
+
+ case T_STRUCT:
+ {
+ long len = obj->as.rstruct.len;
+ VALUE *ptr = obj->as.rstruct.ptr;
+
+ while (len--) {
+ gc_mark(*ptr++, lev);
+ }
+ }
+ break;
+
+ default:
+ rb_bug("rb_gc_mark(): unknown data type 0x%lx(0x%lx) %s",
+ obj->as.basic.flags & T_MASK, obj,
+ is_pointer_to_heap(obj) ? "corrupted object" : "non object");
+ }
+}
+
+static int obj_free _((VALUE));
+
+static inline void
+add_freelist(p)
+ RVALUE *p;
+{
+ p->as.free.flags = 0;
+ p->as.free.next = freelist;
+ freelist = p;
+}
+
+static void
+finalize_list(p)
+ RVALUE *p;
+{
+ while (p) {
+ RVALUE *tmp = p->as.free.next;
+ run_final((VALUE)p);
+ if (!FL_TEST(p, FL_SINGLETON)) { /* not freeing page */
+ add_freelist(p);
+ }
+ p = tmp;
+ }
+}
+
+static void
+free_unused_heaps()
+{
+ int i, j;
+
+ for (i = j = 1; j < heaps_used; i++) {
+ if (heaps[i].limit == 0) {
+ free(heaps[i].membase);
+ heaps_used--;
+ }
+ else {
+ if (i != j) {
+ heaps[j] = heaps[i];
+ }
+ j++;
+ }
+ }
+}
+
+#define T_DEFERRED 0x3a
+
+void rb_gc_abort_threads(void);
+
+static void
+gc_sweep()
+{
+ RVALUE *p, *pend, *final_list;
+ int freed = 0;
+ int i;
+ unsigned long live = 0;
+ unsigned long free_min = 0;
+
+ for (i = 0; i < heaps_used; i++) {
+ free_min += heaps[i].limit;
+ }
+ free_min = free_min * 0.2;
+ if (free_min < FREE_MIN)
+ free_min = FREE_MIN;
+
+ if (ruby_in_compile && ruby_parser_stack_on_heap()) {
+ /* should not reclaim nodes during compilation
+ if yacc's semantic stack is not allocated on machine stack */
+ for (i = 0; i < heaps_used; i++) {
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ while (p < pend) {
+ if (!(p->as.basic.flags&FL_MARK) && BUILTIN_TYPE(p) == T_NODE)
+ gc_mark((VALUE)p, 0);
+ p++;
+ }
+ }
+ }
+
+ mark_source_filename(ruby_sourcefile);
+ if (source_filenames) {
+ st_foreach(source_filenames, sweep_source_filename, 0);
+ }
+
+ freelist = 0;
+ final_list = deferred_final_list;
+ deferred_final_list = 0;
+ for (i = 0; i < heaps_used; i++) {
+ int n = 0;
+ RVALUE *free = freelist;
+ RVALUE *final = final_list;
+ int deferred;
+
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ while (p < pend) {
+ if (!(p->as.basic.flags & FL_MARK)) {
+ if (p->as.basic.flags &&
+ ((deferred = obj_free((VALUE)p)) ||
+ ((FL_TEST(p, FL_FINALIZE)) && need_call_final))) {
+ if (!deferred) {
+ p->as.free.flags = T_DEFERRED;
+ RDATA(p)->dfree = 0;
+ }
+ p->as.free.flags |= FL_MARK;
+ p->as.free.next = final_list;
+ final_list = p;
+ }
+ else {
+ add_freelist(p);
+ }
+ n++;
+ }
+ else if (BUILTIN_TYPE(p) == T_DEFERRED) {
+ /* objects to be finalized */
+ /* do nothing remain marked */
+ }
+ else {
+ RBASIC(p)->flags &= ~FL_MARK;
+ live++;
+ }
+ p++;
+ }
+ if (n == heaps[i].limit && freed > free_min) {
+ RVALUE *pp;
+
+ heaps[i].limit = 0;
+ for (pp = final_list; pp != final; pp = pp->as.free.next) {
+ pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */
+ }
+ freelist = free; /* cancel this page from freelist */
+ }
+ else {
+ freed += n;
+ }
+ }
+ if (malloc_increase > malloc_limit) {
+ malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
+ if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
+ }
+ malloc_increase = 0;
+ if (freed < free_min) {
+ add_heap();
+ }
+ during_gc = 0;
+
+ /* clear finalization list */
+ if (final_list) {
+ deferred_final_list = final_list;
+ rb_thread_pending = 1;
+ return;
+ }
+ free_unused_heaps();
+}
+
+void
+rb_gc_force_recycle(p)
+ VALUE p;
+{
+ add_freelist((RVALUE*)p);
+}
+
+static inline void
+make_deferred(p)
+ RVALUE *p;
+{
+ p->as.basic.flags = (p->as.basic.flags & ~T_MASK) | T_DEFERRED;
+}
+
+static int
+obj_free(obj)
+ VALUE obj;
+{
+ switch (BUILTIN_TYPE(obj)) {
+ case T_NIL:
+ case T_FIXNUM:
+ case T_TRUE:
+ case T_FALSE:
+ rb_bug("obj_free() called for broken object");
+ break;
+ }
+
+ if (FL_TEST(obj, FL_EXIVAR)) {
+ rb_free_generic_ivar((VALUE)obj);
+ }
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ if (RANY(obj)->as.object.iv_tbl) {
+ st_free_table(RANY(obj)->as.object.iv_tbl);
+ }
+ break;
+ case T_MODULE:
+ case T_CLASS:
+ rb_clear_cache_by_class((VALUE)obj);
+ st_free_table(RANY(obj)->as.klass.m_tbl);
+ if (RANY(obj)->as.object.iv_tbl) {
+ st_free_table(RANY(obj)->as.object.iv_tbl);
+ }
+ break;
+ case T_STRING:
+ if (RANY(obj)->as.string.ptr && !FL_TEST(obj, ELTS_SHARED)) {
+ RUBY_CRITICAL(free(RANY(obj)->as.string.ptr));
+ }
+ break;
+ case T_ARRAY:
+ if (RANY(obj)->as.array.ptr && !FL_TEST(obj, ELTS_SHARED)) {
+ RUBY_CRITICAL(free(RANY(obj)->as.array.ptr));
+ }
+ break;
+ case T_HASH:
+ if (RANY(obj)->as.hash.tbl) {
+ st_free_table(RANY(obj)->as.hash.tbl);
+ }
+ break;
+ case T_REGEXP:
+ if (RANY(obj)->as.regexp.ptr) {
+ re_free_pattern(RANY(obj)->as.regexp.ptr);
+ }
+ if (RANY(obj)->as.regexp.str) {
+ RUBY_CRITICAL(free(RANY(obj)->as.regexp.str));
+ }
+ break;
+ case T_DATA:
+ if (DATA_PTR(obj)) {
+ if ((long)RANY(obj)->as.data.dfree == -1) {
+ RUBY_CRITICAL(free(DATA_PTR(obj)));
+ }
+ else if (RANY(obj)->as.data.dfree) {
+ make_deferred(RANY(obj));
+ return 1;
+ }
+ }
+ break;
+ case T_MATCH:
+ if (RANY(obj)->as.match.regs) {
+ re_free_registers(RANY(obj)->as.match.regs);
+ RUBY_CRITICAL(free(RANY(obj)->as.match.regs));
+ }
+ break;
+ case T_FILE:
+ if (RANY(obj)->as.file.fptr) {
+ struct rb_io_t *fptr = RANY(obj)->as.file.fptr;
+ make_deferred(RANY(obj));
+ RDATA(obj)->dfree = (void (*)(void*))rb_io_fptr_finalize;
+ RDATA(obj)->data = fptr;
+ return 1;
+ }
+ break;
+ case T_ICLASS:
+ /* iClass shares table with the module */
+ break;
+
+ case T_FLOAT:
+ case T_VARMAP:
+ case T_BLKTAG:
+ break;
+
+ case T_BIGNUM:
+ if (RANY(obj)->as.bignum.digits) {
+ RUBY_CRITICAL(free(RANY(obj)->as.bignum.digits));
+ }
+ break;
+ case T_NODE:
+ switch (nd_type(obj)) {
+ case NODE_SCOPE:
+ if (RANY(obj)->as.node.u1.tbl) {
+ RUBY_CRITICAL(free(RANY(obj)->as.node.u1.tbl));
+ }
+ break;
+ case NODE_ALLOCA:
+ RUBY_CRITICAL(free(RANY(obj)->as.node.u1.node));
+ break;
+ }
+ break; /* no need to free iv_tbl */
+
+ case T_SCOPE:
+ if (RANY(obj)->as.scope.local_vars &&
+ RANY(obj)->as.scope.flags != SCOPE_ALLOCA) {
+ VALUE *vars = RANY(obj)->as.scope.local_vars-1;
+ if (!(RANY(obj)->as.scope.flags & SCOPE_CLONE) && vars[0] == 0)
+ RUBY_CRITICAL(free(RANY(obj)->as.scope.local_tbl));
+ if ((RANY(obj)->as.scope.flags & (SCOPE_MALLOC|SCOPE_CLONE)) == SCOPE_MALLOC)
+ RUBY_CRITICAL(free(vars));
+ }
+ break;
+
+ case T_STRUCT:
+ if (RANY(obj)->as.rstruct.ptr) {
+ RUBY_CRITICAL(free(RANY(obj)->as.rstruct.ptr));
+ }
+ break;
+
+ default:
+ rb_bug("gc_sweep(): unknown data type 0x%lx(0x%lx)",
+ RANY(obj)->as.basic.flags & T_MASK, obj);
+ }
+
+ return 0;
+}
+
+void
+rb_gc_mark_frame(frame)
+ struct FRAME *frame;
+{
+ gc_mark((VALUE)frame->node, 0);
+}
+
+#ifdef __GNUC__
+#if defined(__human68k__) || defined(DJGPP)
+#if defined(__human68k__)
+typedef unsigned long rb_jmp_buf[8];
+__asm__ (".even\n\
+_rb_setjmp:\n\
+ move.l 4(sp),a0\n\
+ movem.l d3-d7/a3-a5,(a0)\n\
+ moveq.l #0,d0\n\
+ rts");
+#ifdef setjmp
+#undef setjmp
+#endif
+#else
+#if defined(DJGPP)
+typedef unsigned long rb_jmp_buf[6];
+__asm__ (".align 4\n\
+_rb_setjmp:\n\
+ pushl %ebp\n\
+ movl %esp,%ebp\n\
+ movl 8(%ebp),%ebp\n\
+ movl %eax,(%ebp)\n\
+ movl %ebx,4(%ebp)\n\
+ movl %ecx,8(%ebp)\n\
+ movl %edx,12(%ebp)\n\
+ movl %esi,16(%ebp)\n\
+ movl %edi,20(%ebp)\n\
+ popl %ebp\n\
+ xorl %eax,%eax\n\
+ ret");
+#endif
+#endif
+int rb_setjmp (rb_jmp_buf);
+#define jmp_buf rb_jmp_buf
+#define setjmp rb_setjmp
+#endif /* __human68k__ or DJGPP */
+#endif /* __GNUC__ */
+
+static void
+garbage_collect()
+{
+ struct gc_list *list;
+ struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
+ jmp_buf save_regs_gc_mark;
+ SET_STACK_END;
+
+#ifdef HAVE_NATIVETHREAD
+ if (!is_ruby_native_thread()) {
+ rb_bug("cross-thread violation on rb_gc()");
+ }
+#endif
+ if (dont_gc || during_gc) {
+ if (!freelist) {
+ add_heap();
+ }
+ return;
+ }
+ if (during_gc) return;
+ during_gc++;
+
+ init_mark_stack();
+
+ gc_mark((VALUE)ruby_current_node, 0);
+
+ /* mark frame stack */
+ for (frame = ruby_frame; frame; frame = frame->prev) {
+ rb_gc_mark_frame(frame);
+ if (frame->tmp) {
+ struct FRAME *tmp = frame->tmp;
+ while (tmp) {
+ rb_gc_mark_frame(tmp);
+ tmp = tmp->prev;
+ }
+ }
+ }
+ gc_mark((VALUE)ruby_scope, 0);
+ gc_mark((VALUE)ruby_dyna_vars, 0);
+ if (finalizer_table) {
+ mark_tbl(finalizer_table, 0);
+ }
+
+ FLUSH_REGISTER_WINDOWS;
+ /* This assumes that all registers are saved into the jmp_buf (and stack) */
+ setjmp(save_regs_gc_mark);
+ mark_locations_array((VALUE*)save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *));
+#if STACK_GROW_DIRECTION < 0
+ rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
+#elif STACK_GROW_DIRECTION > 0
+ rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END + 1);
+#else
+ if ((VALUE*)STACK_END < rb_gc_stack_start)
+ rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
+ else
+ rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END + 1);
+#endif
+#ifdef __ia64
+ /* mark backing store (flushed register window on the stack) */
+ /* the basic idea from guile GC code */
+ rb_gc_mark_locations(rb_gc_register_stack_start, (VALUE*)rb_ia64_bsp());
+#endif
+#if defined(__human68k__) || defined(__mc68000__)
+ rb_gc_mark_locations((VALUE*)((char*)STACK_END + 2),
+ (VALUE*)((char*)rb_gc_stack_start + 2));
+#endif
+ rb_gc_mark_threads();
+
+ /* mark protected global variables */
+ for (list = global_List; list; list = list->next) {
+ rb_gc_mark_maybe(*list->varptr);
+ }
+ rb_mark_end_proc();
+ rb_gc_mark_global_tbl();
+
+ rb_mark_tbl(rb_class_tbl);
+ rb_gc_mark_trap_list();
+
+ /* mark generic instance variables for special constants */
+ rb_mark_generic_ivar_tbl();
+
+ rb_gc_mark_parser();
+
+ /* gc_mark objects whose marking are not completed*/
+ do {
+ while (!MARK_STACK_EMPTY) {
+ if (mark_stack_overflow){
+ gc_mark_all();
+ }
+ else {
+ gc_mark_rest();
+ }
+ }
+ rb_gc_abort_threads();
+ } while (!MARK_STACK_EMPTY);
+
+ gc_sweep();
+}
+
+void
+rb_gc()
+{
+ garbage_collect();
+ rb_gc_finalize_deferred();
+}
+
+/*
+ * call-seq:
+ * GC.start => nil
+ * gc.garbage_collect => nil
+ * ObjectSpace.garbage_collect => nil
+ *
+ * Initiates garbage collection, unless manually disabled.
+ *
+ */
+
+VALUE
+rb_gc_start()
+{
+ rb_gc();
+ return Qnil;
+}
+
+void
+ruby_set_stack_size(size)
+ size_t size;
+{
+#ifndef STACK_LEVEL_MAX
+ STACK_LEVEL_MAX = size / sizeof(VALUE);
+#endif
+}
+
+void
+Init_stack(addr)
+ VALUE *addr;
+{
+#ifdef __ia64
+ if (rb_gc_register_stack_start == 0) {
+# if defined(__FreeBSD__)
+ /*
+ * FreeBSD/ia64 currently does not have a way for a process to get the
+ * base address for the RSE backing store, so hardcode it.
+ */
+ rb_gc_register_stack_start = (4ULL<<61);
+# elif defined(HAVE___LIBC_IA64_REGISTER_BACKING_STORE_BASE)
+# pragma weak __libc_ia64_register_backing_store_base
+ extern unsigned long __libc_ia64_register_backing_store_base;
+ rb_gc_register_stack_start = (VALUE*)__libc_ia64_register_backing_store_base;
+# endif
+ }
+ {
+ VALUE *bsp = (VALUE*)rb_ia64_bsp();
+ if (rb_gc_register_stack_start == 0 ||
+ bsp < rb_gc_register_stack_start) {
+ rb_gc_register_stack_start = bsp;
+ }
+ }
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+ MEMORY_BASIC_INFORMATION m;
+ memset(&m, 0, sizeof(m));
+ VirtualQuery(&m, &m, sizeof(m));
+ rb_gc_stack_start =
+ STACK_UPPER((VALUE *)&m, (VALUE *)m.BaseAddress,
+ (VALUE *)((char *)m.BaseAddress + m.RegionSize) - 1);
+#elif defined(STACK_END_ADDRESS)
+ {
+ extern void *STACK_END_ADDRESS;
+ rb_gc_stack_start = STACK_END_ADDRESS;
+ }
+#else
+ if (!addr) addr = (void *)&addr;
+ STACK_UPPER(&addr, addr, ++addr);
+ if (rb_gc_stack_start) {
+ if (STACK_UPPER(&addr,
+ rb_gc_stack_start > addr,
+ rb_gc_stack_start < addr))
+ rb_gc_stack_start = addr;
+ return;
+ }
+ rb_gc_stack_start = addr;
+#endif
+#ifdef HAVE_GETRLIMIT
+ {
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
+ unsigned int space = rlim.rlim_cur/5;
+
+ if (space > 1024*1024) space = 1024*1024;
+ STACK_LEVEL_MAX = (rlim.rlim_cur - space) / sizeof(VALUE);
+ }
+ }
+#endif
+}
+
+void ruby_init_stack(VALUE *addr
+#ifdef __ia64
+ , void *bsp
+#endif
+ )
+{
+ if (!rb_gc_stack_start ||
+ STACK_UPPER(&addr,
+ rb_gc_stack_start > addr,
+ rb_gc_stack_start < addr)) {
+ rb_gc_stack_start = addr;
+ }
+#ifdef __ia64
+ if (!rb_gc_register_stack_start ||
+ (VALUE*)bsp < rb_gc_register_stack_start) {
+ rb_gc_register_stack_start = (VALUE*)bsp;
+ }
+#endif
+#ifdef HAVE_GETRLIMIT
+ {
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
+ unsigned int space = rlim.rlim_cur/5;
+
+ if (space > 1024*1024) space = 1024*1024;
+ STACK_LEVEL_MAX = (rlim.rlim_cur - space) / sizeof(VALUE);
+ }
+ }
+#elif defined _WIN32
+ {
+ MEMORY_BASIC_INFORMATION mi;
+ DWORD size;
+ DWORD space;
+
+ if (VirtualQuery(&mi, &mi, sizeof(mi))) {
+ size = (char *)mi.BaseAddress - (char *)mi.AllocationBase;
+ space = size / 5;
+ if (space > 1024*1024) space = 1024*1024;
+ STACK_LEVEL_MAX = (size - space) / sizeof(VALUE);
+ }
+ }
+#endif
+}
+
+/*
+ * Document-class: ObjectSpace
+ *
+ * The <code>ObjectSpace</code> module contains a number of routines
+ * that interact with the garbage collection facility and allow you to
+ * traverse all living objects with an iterator.
+ *
+ * <code>ObjectSpace</code> also provides support for object
+ * finalizers, procs that will be called when a specific object is
+ * about to be destroyed by garbage collection.
+ *
+ * include ObjectSpace
+ *
+ *
+ * a = "A"
+ * b = "B"
+ * c = "C"
+ *
+ *
+ * define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
+ * define_finalizer(a, proc {|id| puts "Finalizer two on #{id}" })
+ * define_finalizer(b, proc {|id| puts "Finalizer three on #{id}" })
+ *
+ * <em>produces:</em>
+ *
+ * Finalizer three on 537763470
+ * Finalizer one on 537763480
+ * Finalizer two on 537763480
+ *
+ */
+
+void
+Init_heap()
+{
+ if (!rb_gc_stack_start) {
+ Init_stack(0);
+ }
+ add_heap();
+}
+
+static VALUE
+os_obj_of(of)
+ VALUE of;
+{
+ int i;
+ int n = 0;
+
+ for (i = 0; i < heaps_used; i++) {
+ RVALUE *p, *pend;
+
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ for (;p < pend; p++) {
+ if (p->as.basic.flags) {
+ switch (BUILTIN_TYPE(p)) {
+ case T_NONE:
+ case T_ICLASS:
+ case T_VARMAP:
+ case T_SCOPE:
+ case T_NODE:
+ case T_DEFERRED:
+ continue;
+ case T_CLASS:
+ if (FL_TEST(p, FL_SINGLETON)) continue;
+ default:
+ if (!p->as.basic.klass) continue;
+ if (!of || rb_obj_is_kind_of((VALUE)p, of)) {
+ rb_yield((VALUE)p);
+ n++;
+ }
+ }
+ }
+ }
+ }
+
+ return INT2FIX(n);
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.each_object([module]) {|obj| ... } => fixnum
+ *
+ * Calls the block once for each living, nonimmediate object in this
+ * Ruby process. If <i>module</i> is specified, calls the block
+ * for only those classes or modules that match (or are a subclass of)
+ * <i>module</i>. Returns the number of objects found. Immediate
+ * objects (<code>Fixnum</code>s, <code>Symbol</code>s
+ * <code>true</code>, <code>false</code>, and <code>nil</code>) are
+ * never returned. In the example below, <code>each_object</code>
+ * returns both the numbers we defined and several constants defined in
+ * the <code>Math</code> module.
+ *
+ * a = 102.7
+ * b = 95 # Won't be returned
+ * c = 12345678987654321
+ * count = ObjectSpace.each_object(Numeric) {|x| p x }
+ * puts "Total count: #{count}"
+ *
+ * <em>produces:</em>
+ *
+ * 12345678987654321
+ * 102.7
+ * 2.71828182845905
+ * 3.14159265358979
+ * 2.22044604925031e-16
+ * 1.7976931348623157e+308
+ * 2.2250738585072e-308
+ * Total count: 7
+ *
+ */
+
+static VALUE
+os_each_obj(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE of;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "01", &of) == 0) {
+ of = 0;
+ }
+ return os_obj_of(of);
+}
+
+static VALUE finalizers;
+
+/* deprecated
+ */
+
+static VALUE
+add_final(os, block)
+ VALUE os, block;
+{
+ rb_warn("ObjectSpace::add_finalizer is deprecated; use define_finalizer");
+ if (!rb_respond_to(block, rb_intern("call"))) {
+ rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
+ rb_obj_classname(block));
+ }
+ rb_ary_push(finalizers, block);
+ return block;
+}
+
+/*
+ * deprecated
+ */
+static VALUE
+rm_final(os, block)
+ VALUE os, block;
+{
+ rb_warn("ObjectSpace::remove_finalizer is deprecated; use undefine_finalizer");
+ rb_ary_delete(finalizers, block);
+ return block;
+}
+
+/*
+ * deprecated
+ */
+static VALUE
+finals()
+{
+ rb_warn("ObjectSpace::finalizers is deprecated");
+ return finalizers;
+}
+
+/*
+ * deprecated
+ */
+
+static VALUE
+call_final(os, obj)
+ VALUE os, obj;
+{
+ rb_warn("ObjectSpace::call_finalizer is deprecated; use define_finalizer");
+ need_call_final = 1;
+ FL_SET(obj, FL_FINALIZE);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.undefine_finalizer(obj)
+ *
+ * Removes all finalizers for <i>obj</i>.
+ *
+ */
+
+static VALUE
+undefine_final(os, obj)
+ VALUE os, obj;
+{
+ if (finalizer_table) {
+ st_delete(finalizer_table, (st_data_t*)&obj, 0);
+ }
+ return obj;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.define_finalizer(obj, aProc=proc())
+ *
+ * Adds <i>aProc</i> as a finalizer, to be called after <i>obj</i>
+ * was destroyed.
+ *
+ */
+
+static VALUE
+define_final(argc, argv, os)
+ int argc;
+ VALUE *argv;
+ VALUE os;
+{
+ VALUE obj, block, table;
+
+ rb_scan_args(argc, argv, "11", &obj, &block);
+ if (argc == 1) {
+ block = rb_block_proc();
+ }
+ else if (!rb_respond_to(block, rb_intern("call"))) {
+ rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
+ rb_obj_classname(block));
+ }
+ need_call_final = 1;
+ if (!FL_ABLE(obj)) {
+ rb_raise(rb_eArgError, "cannot define finalizer for %s",
+ rb_obj_classname(obj));
+ }
+ RBASIC(obj)->flags |= FL_FINALIZE;
+
+ block = rb_ary_new3(2, INT2FIX(ruby_safe_level), block);
+ OBJ_FREEZE(block);
+
+ if (!finalizer_table) {
+ finalizer_table = st_init_numtable();
+ }
+ if (st_lookup(finalizer_table, obj, &table)) {
+ rb_ary_push(table, block);
+ }
+ else {
+ table = rb_ary_new3(1, block);
+ RBASIC(table)->klass = 0;
+ st_add_direct(finalizer_table, obj, table);
+ }
+ return block;
+}
+
+void
+rb_gc_copy_finalizer(dest, obj)
+ VALUE dest, obj;
+{
+ VALUE table;
+
+ if (!finalizer_table) return;
+ if (!FL_TEST(obj, FL_FINALIZE)) return;
+ if (st_lookup(finalizer_table, obj, &table)) {
+ st_insert(finalizer_table, dest, table);
+ }
+ RBASIC(dest)->flags |= FL_FINALIZE;
+}
+
+static VALUE
+run_single_final(args)
+ VALUE *args;
+{
+ rb_eval_cmd(args[0], args[1], (int)args[2]);
+ return Qnil;
+}
+
+static void
+run_final(obj)
+ VALUE obj;
+{
+ long i;
+ int status, critical_save = rb_thread_critical;
+ VALUE args[3], table, objid;
+
+ objid = rb_obj_id(obj); /* make obj into id */
+ rb_thread_critical = Qtrue;
+ /* NOTE: This change below, adding DATA_PTR(obj) to the if line, is a stopgap fix for segfaults; the reason for DATA_PTR(obj) == 0 needs to be found and fixed. */
+ if (BUILTIN_TYPE(obj) == T_DEFERRED && RDATA(obj)->dfree && DATA_PTR(obj)) {
+ (*RDATA(obj)->dfree)(DATA_PTR(obj));
+ }
+ args[1] = 0;
+ args[2] = (VALUE)ruby_safe_level;
+ for (i=0; i<RARRAY(finalizers)->len; i++) {
+ args[0] = RARRAY(finalizers)->ptr[i];
+ if (!args[1]) args[1] = rb_ary_new3(1, objid);
+ rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
+ }
+ if (finalizer_table && st_delete(finalizer_table, (st_data_t*)&obj, &table)) {
+ for (i=0; i<RARRAY(table)->len; i++) {
+ VALUE final = RARRAY(table)->ptr[i];
+ args[0] = RARRAY(final)->ptr[1];
+ if (!args[1]) args[1] = rb_ary_new3(1, objid);
+ args[2] = FIX2INT(RARRAY(final)->ptr[0]);
+ rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
+ }
+ }
+ rb_thread_critical = critical_save;
+}
+
+void
+rb_gc_finalize_deferred()
+{
+ RVALUE *p = deferred_final_list;
+
+ deferred_final_list = 0;
+ if (p) {
+ finalize_list(p);
+ free_unused_heaps();
+ }
+}
+
+static int
+chain_finalized_object(st_data_t key, st_data_t val, st_data_t arg)
+{
+ RVALUE *p = (RVALUE *)key, **final_list = (RVALUE **)arg;
+ if ((p->as.basic.flags & (FL_FINALIZE|FL_MARK)) == FL_FINALIZE) {
+ if (BUILTIN_TYPE(p) != T_DEFERRED) {
+ p->as.free.flags = FL_MARK | T_DEFERRED; /* remain marked */
+ RDATA(p)->dfree = 0;
+ }
+ p->as.free.next = *final_list;
+ *final_list = p;
+ }
+ return ST_CONTINUE;
+}
+
+void
+rb_gc_call_finalizer_at_exit()
+{
+ RVALUE *p, *pend;
+ int i;
+
+ /* run finalizers */
+ if (need_call_final) {
+ do {
+ p = deferred_final_list;
+ deferred_final_list = 0;
+ finalize_list(p);
+ mark_tbl(finalizer_table, 0);
+ st_foreach(finalizer_table, chain_finalized_object,
+ (st_data_t)&deferred_final_list);
+ } while (deferred_final_list);
+ }
+ /* run data object's finalizers */
+ for (i = 0; i < heaps_used; i++) {
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ while (p < pend) {
+ if (BUILTIN_TYPE(p) == T_DATA &&
+ DATA_PTR(p) && RANY(p)->as.data.dfree &&
+ RANY(p)->as.basic.klass != rb_cThread) {
+ p->as.free.flags = 0;
+ if ((long)RANY(p)->as.data.dfree == -1) {
+ RUBY_CRITICAL(free(DATA_PTR(p)));
+ }
+ else if (RANY(p)->as.data.dfree) {
+ (*RANY(p)->as.data.dfree)(DATA_PTR(p));
+ }
+ }
+ else if (BUILTIN_TYPE(p) == T_FILE) {
+ p->as.free.flags = 0;
+ rb_io_fptr_finalize(RANY(p)->as.file.fptr);
+ }
+ p++;
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * ObjectSpace._id2ref(object_id) -> an_object
+ *
+ * Converts an object id to a reference to the object. May not be
+ * called on an object id passed as a parameter to a finalizer.
+ *
+ * s = "I am a string" #=> "I am a string"
+ * r = ObjectSpace._id2ref(s.object_id) #=> "I am a string"
+ * r == s #=> true
+ *
+ */
+
+static VALUE
+id2ref(obj, objid)
+ VALUE obj, objid;
+{
+ unsigned long ptr, p0;
+ int type;
+
+ rb_secure(4);
+ p0 = ptr = NUM2ULONG(objid);
+ if (ptr == Qtrue) return Qtrue;
+ if (ptr == Qfalse) return Qfalse;
+ if (ptr == Qnil) return Qnil;
+ if (FIXNUM_P(ptr)) return (VALUE)ptr;
+ ptr = objid ^ FIXNUM_FLAG; /* unset FIXNUM_FLAG */
+
+ if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
+ ID symid = ptr / sizeof(RVALUE);
+ if (rb_id2name(symid) == 0)
+ rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
+ return ID2SYM(symid);
+ }
+
+ if (!is_pointer_to_heap((void *)ptr)||
+ (type = BUILTIN_TYPE(ptr)) > T_SYMBOL || type == T_ICLASS) {
+ rb_raise(rb_eRangeError, "0x%lx is not id value", p0);
+ }
+ if (BUILTIN_TYPE(ptr) == 0 || RBASIC(ptr)->klass == 0) {
+ rb_raise(rb_eRangeError, "0x%lx is recycled object", p0);
+ }
+ return (VALUE)ptr;
+}
+
+/*
+ * Document-method: __id__
+ * Document-method: object_id
+ *
+ * call-seq:
+ * obj.__id__ => fixnum
+ * obj.object_id => fixnum
+ *
+ * Returns an integer identifier for <i>obj</i>. The same number will
+ * be returned on all calls to <code>id</code> for a given object, and
+ * no two active objects will share an id.
+ * <code>Object#object_id</code> is a different concept from the
+ * <code>:name</code> notation, which returns the symbol id of
+ * <code>name</code>. Replaces the deprecated <code>Object#id</code>.
+ */
+
+/*
+ * call-seq:
+ * obj.hash => fixnum
+ *
+ * Generates a <code>Fixnum</code> hash value for this object. This
+ * function must have the property that <code>a.eql?(b)</code> implies
+ * <code>a.hash == b.hash</code>. The hash value is used by class
+ * <code>Hash</code>. Any hash value that exceeds the capacity of a
+ * <code>Fixnum</code> will be truncated before being used.
+ */
+
+VALUE
+rb_obj_id(VALUE obj)
+{
+ /*
+ * 32-bit VALUE space
+ * MSB ------------------------ LSB
+ * false 00000000000000000000000000000000
+ * true 00000000000000000000000000000010
+ * nil 00000000000000000000000000000100
+ * undef 00000000000000000000000000000110
+ * symbol ssssssssssssssssssssssss00001110
+ * object oooooooooooooooooooooooooooooo00 = 0 (mod sizeof(RVALUE))
+ * fixnum fffffffffffffffffffffffffffffff1
+ *
+ * object_id space
+ * LSB
+ * false 00000000000000000000000000000000
+ * true 00000000000000000000000000000010
+ * nil 00000000000000000000000000000100
+ * undef 00000000000000000000000000000110
+ * symbol 000SSSSSSSSSSSSSSSSSSSSSSSSSSS0 S...S % A = 4 (S...S = s...s * A + 4)
+ * object oooooooooooooooooooooooooooooo0 o...o % A = 0
+ * fixnum fffffffffffffffffffffffffffffff1 bignum if required
+ *
+ * where A = sizeof(RVALUE)/4
+ *
+ * sizeof(RVALUE) is
+ * 20 if 32-bit, double is 4-byte aligned
+ * 24 if 32-bit, double is 8-byte aligned
+ * 40 if 64-bit
+ */
+ if (TYPE(obj) == T_SYMBOL) {
+ return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
+ }
+ if (SPECIAL_CONST_P(obj)) {
+ return LONG2NUM((long)obj);
+ }
+ return (VALUE)((long)obj|FIXNUM_FLAG);
+}
+
+/*
+ * The <code>GC</code> module provides an interface to Ruby's mark and
+ * sweep garbage collection mechanism. Some of the underlying methods
+ * are also available via the <code>ObjectSpace</code> module.
+ */
+
+void
+Init_GC()
+{
+ VALUE rb_mObSpace;
+
+ rb_mGC = rb_define_module("GC");
+ rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
+ rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0);
+ rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
+ rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
+
+ rb_mObSpace = rb_define_module("ObjectSpace");
+ rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
+ rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
+ rb_define_module_function(rb_mObSpace, "add_finalizer", add_final, 1);
+ rb_define_module_function(rb_mObSpace, "remove_finalizer", rm_final, 1);
+ rb_define_module_function(rb_mObSpace, "finalizers", finals, 0);
+ rb_define_module_function(rb_mObSpace, "call_finalizer", call_final, 1);
+
+ rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
+ rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);
+
+ rb_define_module_function(rb_mObSpace, "_id2ref", id2ref, 1);
+
+ rb_gc_register_address(&rb_mObSpace);
+ rb_global_variable(&finalizers);
+ rb_gc_unregister_address(&rb_mObSpace);
+ finalizers = rb_ary_new();
+
+ source_filenames = st_init_strtable();
+
+ rb_global_variable(&nomem_error);
+ nomem_error = rb_exc_new3(rb_eNoMemError,
+ rb_obj_freeze(rb_str_new2("failed to allocate memory")));
+ OBJ_TAINT(nomem_error);
+ OBJ_FREEZE(nomem_error);
+
+ rb_define_method(rb_mKernel, "hash", rb_obj_id, 0);
+ rb_define_method(rb_mKernel, "__id__", rb_obj_id, 0);
+ rb_define_method(rb_mKernel, "object_id", rb_obj_id, 0);
+}
+/**********************************************************************
+
+ hash.c -
+
+ $Author: shyouhei $
+ $Date: 2007-08-22 05:42:36 +0200 (Wed, 22 Aug 2007) $
+ created at: Mon Nov 22 18:51:18 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "st.h"
+#include "util.h"
+#include "rubysig.h"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+#endif
+
+#define HASH_DELETED FL_USER1
+#define HASH_PROC_DEFAULT FL_USER2
+
+static void
+rb_hash_modify(hash)
+ VALUE hash;
+{
+ if (!RHASH(hash)->tbl) rb_raise(rb_eTypeError, "uninitialized Hash");
+ if (OBJ_FROZEN(hash)) rb_error_frozen("hash");
+ if (!OBJ_TAINTED(hash) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify hash");
+}
+
+VALUE
+rb_hash_freeze(hash)
+ VALUE hash;
+{
+ return rb_obj_freeze(hash);
+}
+
+VALUE rb_cHash;
+
+static VALUE envtbl;
+static ID id_hash, id_call, id_default;
+
+static VALUE
+eql(args)
+ VALUE *args;
+{
+ return (VALUE)rb_eql(args[0], args[1]);
+}
+
+static int
+rb_any_cmp(a, b)
+ VALUE a, b;
+{
+ VALUE args[2];
+
+ if (a == b) return 0;
+ if (FIXNUM_P(a) && FIXNUM_P(b)) {
+ return a != b;
+ }
+ if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString &&
+ TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
+ return rb_str_cmp(a, b);
+ }
+ if (a == Qundef || b == Qundef) return -1;
+ if (SYMBOL_P(a) && SYMBOL_P(b)) {
+ return a != b;
+ }
+
+ args[0] = a;
+ args[1] = b;
+ return !rb_with_disable_interrupt(eql, (VALUE)args);
+}
+
+VALUE
+rb_hash(obj)
+ VALUE obj;
+{
+ return rb_funcall(obj, id_hash, 0);
+}
+
+static int
+rb_any_hash(a)
+ VALUE a;
+{
+ VALUE hval;
+
+ switch (TYPE(a)) {
+ case T_FIXNUM:
+ case T_SYMBOL:
+ return (int)a;
+ break;
+
+ case T_STRING:
+ return rb_str_hash(a);
+ break;
+
+ default:
+ hval = rb_funcall(a, id_hash, 0);
+ if (!FIXNUM_P(hval)) {
+ hval = rb_funcall(hval, '%', 1, INT2FIX(536870923));
+ }
+ return (int)FIX2LONG(hval);
+ }
+}
+
+static struct st_hash_type objhash = {
+ rb_any_cmp,
+ rb_any_hash,
+};
+
+struct foreach_safe_arg {
+ st_table *tbl;
+ int (*func)();
+ st_data_t arg;
+};
+
+static int
+foreach_safe_i(key, value, arg)
+ st_data_t key, value;
+ struct foreach_safe_arg *arg;
+{
+ int status;
+
+ if (key == Qundef) return ST_CONTINUE;
+ status = (*arg->func)(key, value, arg->arg);
+ if (status == ST_CONTINUE) {
+ return ST_CHECK;
+ }
+ return status;
+}
+
+void
+st_foreach_safe(table, func, a)
+ st_table *table;
+ int (*func)();
+ st_data_t a;
+{
+ struct foreach_safe_arg arg;
+
+ arg.tbl = table;
+ arg.func = func;
+ arg.arg = a;
+ if (st_foreach(table, foreach_safe_i, (st_data_t)&arg)) {
+ rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ }
+}
+
+struct hash_foreach_arg {
+ VALUE hash;
+ int (*func)();
+ VALUE arg;
+};
+
+static int
+hash_foreach_iter(key, value, arg)
+ VALUE key, value;
+ struct hash_foreach_arg *arg;
+{
+ int status;
+ st_table *tbl;
+
+ tbl = RHASH(arg->hash)->tbl;
+ if (key == Qundef) return ST_CONTINUE;
+ status = (*arg->func)(key, value, arg->arg);
+ if (RHASH(arg->hash)->tbl != tbl) {
+ rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
+ }
+ switch (status) {
+ case ST_DELETE:
+ st_delete_safe(tbl, (st_data_t*)&key, 0, Qundef);
+ FL_SET(arg->hash, HASH_DELETED);
+ case ST_CONTINUE:
+ break;
+ case ST_STOP:
+ return ST_STOP;
+ }
+ return ST_CHECK;
+}
+
+static VALUE
+hash_foreach_ensure(hash)
+ VALUE hash;
+{
+ RHASH(hash)->iter_lev--;
+
+ if (RHASH(hash)->iter_lev == 0) {
+ if (FL_TEST(hash, HASH_DELETED)) {
+ st_cleanup_safe(RHASH(hash)->tbl, Qundef);
+ FL_UNSET(hash, HASH_DELETED);
+ }
+ }
+ return 0;
+}
+
+static VALUE
+hash_foreach_call(arg)
+ struct hash_foreach_arg *arg;
+{
+ if (st_foreach(RHASH(arg->hash)->tbl, hash_foreach_iter, (st_data_t)arg)) {
+ rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ }
+ return Qnil;
+}
+
+void
+rb_hash_foreach(hash, func, farg)
+ VALUE hash;
+ int (*func)();
+ VALUE farg;
+{
+ struct hash_foreach_arg arg;
+
+ RHASH(hash)->iter_lev++;
+ arg.hash = hash;
+ arg.func = func;
+ arg.arg = farg;
+ rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
+}
+
+static VALUE hash_alloc0 _((VALUE));
+static VALUE hash_alloc _((VALUE));
+static VALUE
+hash_alloc0(klass)
+ VALUE klass;
+{
+ NEWOBJ(hash, struct RHash);
+ OBJSETUP(hash, klass, T_HASH);
+
+ hash->ifnone = Qnil;
+
+ return (VALUE)hash;
+}
+
+static VALUE
+hash_alloc(klass)
+ VALUE klass;
+{
+ VALUE hash = hash_alloc0(klass);
+
+ RHASH(hash)->tbl = st_init_table(&objhash);
+
+ return hash;
+}
+
+VALUE
+rb_hash_new()
+{
+ return hash_alloc(rb_cHash);
+}
+
+/*
+ * call-seq:
+ * Hash.new => hash
+ * Hash.new(obj) => aHash
+ * Hash.new {|hash, key| block } => aHash
+ *
+ * Returns a new, empty hash. If this hash is subsequently accessed by
+ * a key that doesn't correspond to a hash entry, the value returned
+ * depends on the style of <code>new</code> used to create the hash. In
+ * the first form, the access returns <code>nil</code>. If
+ * <i>obj</i> is specified, this single object will be used for
+ * all <em>default values</em>. If a block is specified, it will be
+ * called with the hash object and the key, and should return the
+ * default value. It is the block's responsibility to store the value
+ * in the hash if required.
+ *
+ * h = Hash.new("Go Fish")
+ * h["a"] = 100
+ * h["b"] = 200
+ * h["a"] #=> 100
+ * h["c"] #=> "Go Fish"
+ * # The following alters the single default object
+ * h["c"].upcase! #=> "GO FISH"
+ * h["d"] #=> "GO FISH"
+ * h.keys #=> ["a", "b"]
+ *
+ * # While this creates a new default object each time
+ * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
+ * h["c"] #=> "Go Fish: c"
+ * h["c"].upcase! #=> "GO FISH: C"
+ * h["d"] #=> "Go Fish: d"
+ * h.keys #=> ["c", "d"]
+ *
+ */
+
+static VALUE
+rb_hash_initialize(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE ifnone;
+
+ rb_hash_modify(hash);
+ if (rb_block_given_p()) {
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ RHASH(hash)->ifnone = rb_block_proc();
+ FL_SET(hash, HASH_PROC_DEFAULT);
+ }
+ else {
+ rb_scan_args(argc, argv, "01", &ifnone);
+ RHASH(hash)->ifnone = ifnone;
+ }
+
+ return hash;
+}
+
+/*
+ * call-seq:
+ * Hash[ [key =>|, value]* ] => hash
+ *
+ * Creates a new hash populated with the given objects. Equivalent to
+ * the literal <code>{ <i>key</i>, <i>value</i>, ... }</code>. Keys and
+ * values occur in pairs, so there must be an even number of arguments.
+ *
+ * Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
+ * Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
+ * { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
+ */
+
+static VALUE
+rb_hash_s_create(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE hash;
+ int i;
+
+ if (argc == 1 && TYPE(argv[0]) == T_HASH) {
+ hash = hash_alloc0(klass);
+ RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
+
+ return hash;
+ }
+
+ if (argc % 2 != 0) {
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
+ }
+
+ hash = hash_alloc(klass);
+ for (i=0; i<argc; i+=2) {
+ rb_hash_aset(hash, argv[i], argv[i + 1]);
+ }
+
+ return hash;
+}
+
+static VALUE
+to_hash(hash)
+ VALUE hash;
+{
+ return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
+}
+
+static int
+rb_hash_rehash_i(key, value, tbl)
+ VALUE key, value;
+ st_table *tbl;
+{
+ if (key != Qundef) st_insert(tbl, key, value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.rehash -> hsh
+ *
+ * Rebuilds the hash based on the current hash values for each key. If
+ * values of key objects have changed since they were inserted, this
+ * method will reindex <i>hsh</i>. If <code>Hash#rehash</code> is
+ * called while an iterator is traversing the hash, an
+ * <code>IndexError</code> will be raised in the iterator.
+ *
+ * a = [ "a", "b" ]
+ * c = [ "c", "d" ]
+ * h = { a => 100, c => 300 }
+ * h[a] #=> 100
+ * a[0] = "z"
+ * h[a] #=> nil
+ * h.rehash #=> {["z", "b"]=>100, ["c", "d"]=>300}
+ * h[a] #=> 100
+ */
+
+static VALUE
+rb_hash_rehash(hash)
+ VALUE hash;
+{
+ st_table *tbl;
+
+ rb_hash_modify(hash);
+ tbl = st_init_table_with_size(&objhash, RHASH(hash)->tbl->num_entries);
+ rb_hash_foreach(hash, rb_hash_rehash_i, (st_data_t)tbl);
+ st_free_table(RHASH(hash)->tbl);
+ RHASH(hash)->tbl = tbl;
+
+ return hash;
+}
+
+/*
+ * call-seq:
+ * hsh[key] => value
+ *
+ * Element Reference---Retrieves the <i>value</i> object corresponding
+ * to the <i>key</i> object. If not found, returns the a default value (see
+ * <code>Hash::new</code> for details).
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h["a"] #=> 100
+ * h["c"] #=> nil
+ *
+ */
+
+VALUE
+rb_hash_aref(hash, key)
+ VALUE hash, key;
+{
+ VALUE val;
+
+ if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
+ return rb_funcall(hash, id_default, 1, key);
+ }
+ return val;
+}
+
+/*
+ * call-seq:
+ * hsh.fetch(key [, default] ) => obj
+ * hsh.fetch(key) {| key | block } => obj
+ *
+ * Returns a value from the hash for the given key. If the key can't be
+ * found, there are several options: With no other arguments, it will
+ * raise an <code>IndexError</code> exception; if <i>default</i> is
+ * given, then that will be returned; if the optional code block is
+ * specified, then that will be run and its result returned.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.fetch("a") #=> 100
+ * h.fetch("z", "go fish") #=> "go fish"
+ * h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z"
+ *
+ * The following example shows that an exception is raised if the key
+ * is not found and a default value is not supplied.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.fetch("z")
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:2:in `fetch': key not found (IndexError)
+ * from prog.rb:2
+ *
+ */
+
+static VALUE
+rb_hash_fetch(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE key, if_none;
+ VALUE val;
+ long block_given;
+
+ rb_scan_args(argc, argv, "11", &key, &if_none);
+
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
+ if (block_given) return rb_yield(key);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "key not found");
+ }
+ return if_none;
+ }
+ return val;
+}
+
+/*
+ * call-seq:
+ * hsh.default(key=nil) => obj
+ *
+ * Returns the default value, the value that would be returned by
+ * <i>hsh</i>[<i>key</i>] if <i>key</i> did not exist in <i>hsh</i>.
+ * See also <code>Hash::new</code> and <code>Hash#default=</code>.
+ *
+ * h = Hash.new #=> {}
+ * h.default #=> nil
+ * h.default(2) #=> nil
+ *
+ * h = Hash.new("cat") #=> {}
+ * h.default #=> "cat"
+ * h.default(2) #=> "cat"
+ *
+ * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {}
+ * h.default #=> 0
+ * h.default(2) #=> 20
+ */
+
+static VALUE
+rb_hash_default(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE key;
+
+ rb_scan_args(argc, argv, "01", &key);
+ if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
+ if (argc == 0) return Qnil;
+ return rb_funcall(RHASH(hash)->ifnone, id_call, 2, hash, key);
+ }
+ return RHASH(hash)->ifnone;
+}
+
+/*
+ * call-seq:
+ * hsh.default = obj => hsh
+ *
+ * Sets the default value, the value returned for a key that does not
+ * exist in the hash. It is not possible to set the a default to a
+ * <code>Proc</code> that will be executed on each key lookup.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.default = "Go fish"
+ * h["a"] #=> 100
+ * h["z"] #=> "Go fish"
+ * # This doesn't do what you might hope...
+ * h.default = proc do |hash, key|
+ * hash[key] = key + key
+ * end
+ * h[2] #=> #<Proc:0x401b3948@-:6>
+ * h["cat"] #=> #<Proc:0x401b3948@-:6>
+ */
+
+static VALUE
+rb_hash_set_default(hash, ifnone)
+ VALUE hash, ifnone;
+{
+ rb_hash_modify(hash);
+ RHASH(hash)->ifnone = ifnone;
+ FL_UNSET(hash, HASH_PROC_DEFAULT);
+ return ifnone;
+}
+
+/*
+ * call-seq:
+ * hsh.default_proc -> anObject
+ *
+ * If <code>Hash::new</code> was invoked with a block, return that
+ * block, otherwise return <code>nil</code>.
+ *
+ * h = Hash.new {|h,k| h[k] = k*k } #=> {}
+ * p = h.default_proc #=> #<Proc:0x401b3d08@-:1>
+ * a = [] #=> []
+ * p.call(a, 2)
+ * a #=> [nil, nil, 4]
+ */
+
+
+static VALUE
+rb_hash_default_proc(hash)
+ VALUE hash;
+{
+ if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
+ return RHASH(hash)->ifnone;
+ }
+ return Qnil;
+}
+
+static int
+index_i(key, value, args)
+ VALUE key, value;
+ VALUE *args;
+{
+ if (rb_equal(value, args[0])) {
+ args[1] = key;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+rb_hash_delete_key(hash, key)
+ VALUE hash, key;
+{
+ st_data_t ktmp = (st_data_t)key, val;
+
+ if (RHASH(hash)->iter_lev > 0) {
+ if (st_delete_safe(RHASH(hash)->tbl, &ktmp, &val, Qundef)) {
+ FL_SET(hash, HASH_DELETED);
+ return (VALUE)val;
+ }
+ }
+ else if (st_delete(RHASH(hash)->tbl, &ktmp, &val))
+ return (VALUE)val;
+ return Qundef;
+}
+
+/*
+ * call-seq:
+ * hsh.index(value) => key
+ *
+ * Returns the key for a given value. If not found, returns <code>nil</code>.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.index(200) #=> "b"
+ * h.index(999) #=> nil
+ *
+ */
+
+static VALUE
+rb_hash_index(hash, value)
+ VALUE hash, value;
+{
+ VALUE args[2];
+
+ args[0] = value;
+ args[1] = Qnil;
+
+ rb_hash_foreach(hash, index_i, (st_data_t)args);
+
+ return args[1];
+}
+
+/*
+ * call-seq:
+ * hsh.indexes(key, ...) => array
+ * hsh.indices(key, ...) => array
+ *
+ * Deprecated in favor of <code>Hash#select</code>.
+ *
+ */
+
+static VALUE
+rb_hash_indexes(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE indexes;
+ int i;
+
+ rb_warn("Hash#%s is deprecated; use Hash#values_at",
+ rb_id2name(rb_frame_last_func()));
+ indexes = rb_ary_new2(argc);
+ for (i=0; i<argc; i++) {
+ RARRAY(indexes)->ptr[i] = rb_hash_aref(hash, argv[i]);
+ RARRAY(indexes)->len++;
+ }
+ return indexes;
+}
+
+/*
+ * call-seq:
+ * hsh.delete(key) => value
+ * hsh.delete(key) {| key | block } => value
+ *
+ * Deletes and returns a key-value pair from <i>hsh</i> whose key is
+ * equal to <i>key</i>. If the key is not found, returns the
+ * <em>default value</em>. If the optional code block is given and the
+ * key is not found, pass in the key and return the result of
+ * <i>block</i>.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.delete("a") #=> 100
+ * h.delete("z") #=> nil
+ * h.delete("z") { |el| "#{el} not found" } #=> "z not found"
+ *
+ */
+
+VALUE
+rb_hash_delete(hash, key)
+ VALUE hash, key;
+{
+ VALUE val;
+
+ rb_hash_modify(hash);
+ val = rb_hash_delete_key(hash, key);
+ if (val != Qundef) return val;
+ if (rb_block_given_p()) {
+ return rb_yield(key);
+ }
+ return Qnil;
+}
+
+struct shift_var {
+ VALUE key;
+ VALUE val;
+};
+
+static int
+shift_i(key, value, var)
+ VALUE key, value;
+ struct shift_var *var;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ if (var->key != Qundef) return ST_STOP;
+ var->key = key;
+ var->val = value;
+ return ST_DELETE;
+}
+
+static int
+shift_i_safe(key, value, var)
+ VALUE key, value;
+ struct shift_var *var;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ var->key = key;
+ var->val = value;
+ return ST_STOP;
+}
+
+/*
+ * call-seq:
+ * hsh.shift -> anArray or obj
+ *
+ * Removes a key-value pair from <i>hsh</i> and returns it as the
+ * two-item array <code>[</code> <i>key, value</i> <code>]</code>, or
+ * the hash's default value if the hash is empty.
+ *
+ * h = { 1 => "a", 2 => "b", 3 => "c" }
+ * h.shift #=> [1, "a"]
+ * h #=> {2=>"b", 3=>"c"}
+ */
+
+static VALUE
+rb_hash_shift(hash)
+ VALUE hash;
+{
+ struct shift_var var;
+
+ rb_hash_modify(hash);
+ var.key = Qundef;
+ if (RHASH(hash)->iter_lev > 0) {
+ rb_hash_foreach(hash, shift_i_safe, (st_data_t)&var);
+ if (var.key != Qundef) {
+ st_data_t key = var.key;
+ if (st_delete_safe(RHASH(hash)->tbl, &key, 0, Qundef)) {
+ FL_SET(hash, HASH_DELETED);
+ }
+ }
+ }
+ else {
+ rb_hash_foreach(hash, shift_i, (st_data_t)&var);
+ }
+
+ if (var.key != Qundef) {
+ return rb_assoc_new(var.key, var.val);
+ }
+ else if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
+ return rb_funcall(RHASH(hash)->ifnone, id_call, 2, hash, Qnil);
+ }
+ else {
+ return RHASH(hash)->ifnone;
+ }
+}
+
+static int
+delete_if_i(key, value, hash)
+ VALUE key, value, hash;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ if (RTEST(rb_yield_values(2, key, value))) {
+ rb_hash_delete_key(hash, key);
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.delete_if {| key, value | block } -> hsh
+ *
+ * Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
+ * evaluates to <code>true</code>.
+ *
+ * h = { "a" => 100, "b" => 200, "c" => 300 }
+ * h.delete_if {|key, value| key >= "b" } #=> {"a"=>100}
+ *
+ */
+
+VALUE
+rb_hash_delete_if(hash)
+ VALUE hash;
+{
+ rb_hash_modify(hash);
+ rb_hash_foreach(hash, delete_if_i, hash);
+ return hash;
+}
+
+/*
+ * call-seq:
+ * hsh.reject! {| key, value | block } -> hsh or nil
+ *
+ * Equivalent to <code>Hash#delete_if</code>, but returns
+ * <code>nil</code> if no changes were made.
+ */
+
+VALUE
+rb_hash_reject_bang(hash)
+ VALUE hash;
+{
+ int n = RHASH(hash)->tbl->num_entries;
+ rb_hash_delete_if(hash);
+ if (n == RHASH(hash)->tbl->num_entries) return Qnil;
+ return hash;
+}
+
+/*
+ * call-seq:
+ * hsh.reject {| key, value | block } -> a_hash
+ *
+ * Same as <code>Hash#delete_if</code>, but works on (and returns) a
+ * copy of the <i>hsh</i>. Equivalent to
+ * <code><i>hsh</i>.dup.delete_if</code>.
+ *
+ */
+
+static VALUE
+rb_hash_reject(hash)
+ VALUE hash;
+{
+ return rb_hash_delete_if(rb_obj_dup(hash));
+}
+
+static int
+select_i(key, value, result)
+ VALUE key, value, result;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ if (RTEST(rb_yield_values(2, key, value)))
+ rb_ary_push(result, rb_assoc_new(key, value));
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.values_at(key, ...) => array
+ *
+ * Return an array containing the values associated with the given keys.
+ * Also see <code>Hash.select</code>.
+ *
+ * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
+ * h.values_at("cow", "cat") #=> ["bovine", "feline"]
+*/
+
+VALUE
+rb_hash_values_at(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE result = rb_ary_new();
+ long i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(result, rb_hash_aref(hash, argv[i]));
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * hsh.select {|key, value| block} => array
+ *
+ * Returns a new array consisting of <code>[key,value]</code>
+ * pairs for which the block returns true.
+ * Also see <code>Hash.values_at</code>.
+ *
+ * h = { "a" => 100, "b" => 200, "c" => 300 }
+ * h.select {|k,v| k > "a"} #=> [["b", 200], ["c", 300]]
+ * h.select {|k,v| v < 200} #=> [["a", 100]]
+ */
+
+VALUE
+rb_hash_select(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE result;
+
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ result = rb_ary_new();
+ rb_hash_foreach(hash, select_i, result);
+ return result;
+}
+
+static int
+clear_i(key, value, dummy)
+ VALUE key, value, dummy;
+{
+ return ST_DELETE;
+}
+
+/*
+ * call-seq:
+ * hsh.clear -> hsh
+ *
+ * Removes all key-value pairs from <i>hsh</i>.
+ *
+ * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
+ * h.clear #=> {}
+ *
+ */
+
+static VALUE
+rb_hash_clear(hash)
+ VALUE hash;
+{
+ rb_hash_modify(hash);
+ if (RHASH(hash)->tbl->num_entries > 0) {
+ rb_hash_foreach(hash, clear_i, 0);
+ }
+
+ return hash;
+}
+
+/*
+ * call-seq:
+ * hsh[key] = value => value
+ * hsh.store(key, value) => value
+ *
+ * Element Assignment---Associates the value given by
+ * <i>value</i> with the key given by <i>key</i>.
+ * <i>key</i> should not have its value changed while it is in
+ * use as a key (a <code>String</code> passed as a key will be
+ * duplicated and frozen).
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h["a"] = 9
+ * h["c"] = 4
+ * h #=> {"a"=>9, "b"=>200, "c"=>4}
+ *
+ */
+
+VALUE
+rb_hash_aset(hash, key, val)
+ VALUE hash, key, val;
+{
+ rb_hash_modify(hash);
+ if (TYPE(key) != T_STRING || st_lookup(RHASH(hash)->tbl, key, 0)) {
+ st_insert(RHASH(hash)->tbl, key, val);
+ }
+ else {
+ st_add_direct(RHASH(hash)->tbl, rb_str_new4(key), val);
+ }
+ return val;
+}
+
+static int
+replace_i(key, val, hash)
+ VALUE key, val, hash;
+{
+ if (key != Qundef) {
+ rb_hash_aset(hash, key, val);
+ }
+
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.replace(other_hash) -> hsh
+ *
+ * Replaces the contents of <i>hsh</i> with the contents of
+ * <i>other_hash</i>.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
+ *
+ */
+
+static VALUE
+rb_hash_replace(hash, hash2)
+ VALUE hash, hash2;
+{
+ hash2 = to_hash(hash2);
+ if (hash == hash2) return hash;
+ rb_hash_clear(hash);
+ rb_hash_foreach(hash2, replace_i, hash);
+ RHASH(hash)->ifnone = RHASH(hash2)->ifnone;
+ if (FL_TEST(hash2, HASH_PROC_DEFAULT)) {
+ FL_SET(hash, HASH_PROC_DEFAULT);
+ }
+ else {
+ FL_UNSET(hash, HASH_PROC_DEFAULT);
+ }
+
+ return hash;
+}
+
+/*
+ * call-seq:
+ * hsh.length => fixnum
+ * hsh.size => fixnum
+ *
+ * Returns the number of key-value pairs in the hash.
+ *
+ * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
+ * h.length #=> 4
+ * h.delete("a") #=> 200
+ * h.length #=> 3
+ */
+
+static VALUE
+rb_hash_size(hash)
+ VALUE hash;
+{
+ return INT2FIX(RHASH(hash)->tbl->num_entries);
+}
+
+
+/*
+ * call-seq:
+ * hsh.empty? => true or false
+ *
+ * Returns <code>true</code> if <i>hsh</i> contains no key-value pairs.
+ *
+ * {}.empty? #=> true
+ *
+ */
+
+static VALUE
+rb_hash_empty_p(hash)
+ VALUE hash;
+{
+ if (RHASH(hash)->tbl->num_entries == 0)
+ return Qtrue;
+ return Qfalse;
+}
+
+static int
+each_value_i(key, value)
+ VALUE key, value;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_yield(value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.each_value {| value | block } -> hsh
+ *
+ * Calls <i>block</i> once for each key in <i>hsh</i>, passing the
+ * value as a parameter.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.each_value {|value| puts value }
+ *
+ * <em>produces:</em>
+ *
+ * 100
+ * 200
+ */
+
+static VALUE
+rb_hash_each_value(hash)
+ VALUE hash;
+{
+ rb_hash_foreach(hash, each_value_i, 0);
+ return hash;
+}
+
+static int
+each_key_i(key, value)
+ VALUE key, value;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_yield(key);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.each_key {| key | block } -> hsh
+ *
+ * Calls <i>block</i> once for each key in <i>hsh</i>, passing the key
+ * as a parameter.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.each_key {|key| puts key }
+ *
+ * <em>produces:</em>
+ *
+ * a
+ * b
+ */
+static VALUE
+rb_hash_each_key(hash)
+ VALUE hash;
+{
+ rb_hash_foreach(hash, each_key_i, 0);
+ return hash;
+}
+
+static int
+each_pair_i(key, value)
+ VALUE key, value;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_yield_values(2, key, value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.each_pair {| key_value_array | block } -> hsh
+ *
+ * Calls <i>block</i> once for each key in <i>hsh</i>, passing the key
+ * and value as parameters.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.each_pair {|key, value| puts "#{key} is #{value}" }
+ *
+ * <em>produces:</em>
+ *
+ * a is 100
+ * b is 200
+ *
+ */
+
+static VALUE
+rb_hash_each_pair(hash)
+ VALUE hash;
+{
+ rb_hash_foreach(hash, each_pair_i, 0);
+ return hash;
+}
+
+static int
+each_i(key, value)
+ VALUE key, value;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_yield(rb_assoc_new(key, value));
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.each {| key, value | block } -> hsh
+ *
+ * Calls <i>block</i> once for each key in <i>hsh</i>, passing the key
+ * and value to the block as a two-element array. Because of the assignment
+ * semantics of block parameters, these elements will be split out if the
+ * block has two formal parameters. Also see <code>Hash.each_pair</code>, which
+ * will be marginally more efficient for blocks with two parameters.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.each {|key, value| puts "#{key} is #{value}" }
+ *
+ * <em>produces:</em>
+ *
+ * a is 100
+ * b is 200
+ *
+ */
+
+static VALUE
+rb_hash_each(hash)
+ VALUE hash;
+{
+ rb_hash_foreach(hash, each_i, 0);
+ return hash;
+}
+
+static int
+to_a_i(key, value, ary)
+ VALUE key, value, ary;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_ary_push(ary, rb_assoc_new(key, value));
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.to_a -> array
+ *
+ * Converts <i>hsh</i> to a nested array of <code>[</code> <i>key,
+ * value</i> <code>]</code> arrays.
+ *
+ * h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
+ * h.to_a #=> [["a", 100], ["c", 300], ["d", 400]]
+ */
+
+static VALUE
+rb_hash_to_a(hash)
+ VALUE hash;
+{
+ VALUE ary;
+
+ ary = rb_ary_new();
+ rb_hash_foreach(hash, to_a_i, ary);
+ if (OBJ_TAINTED(hash)) OBJ_TAINT(ary);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * hsh.sort => array
+ * hsh.sort {| a, b | block } => array
+ *
+ * Converts <i>hsh</i> to a nested array of <code>[</code> <i>key,
+ * value</i> <code>]</code> arrays and sorts it, using
+ * <code>Array#sort</code>.
+ *
+ * h = { "a" => 20, "b" => 30, "c" => 10 }
+ * h.sort #=> [["a", 20], ["b", 30], ["c", 10]]
+ * h.sort {|a,b| a[1]<=>b[1]} #=> [["c", 10], ["a", 20], ["b", 30]]
+ *
+ */
+
+static VALUE
+rb_hash_sort(hash)
+ VALUE hash;
+{
+ VALUE entries = rb_hash_to_a(hash);
+ rb_ary_sort_bang(entries);
+ return entries;
+}
+
+static int
+inspect_i(key, value, str)
+ VALUE key, value, str;
+{
+ VALUE str2;
+
+ if (key == Qundef) return ST_CONTINUE;
+ if (RSTRING(str)->len > 1) {
+ rb_str_cat2(str, ", ");
+ }
+ str2 = rb_inspect(key);
+ rb_str_buf_append(str, str2);
+ OBJ_INFECT(str, str2);
+ rb_str_buf_cat2(str, "=>");
+ str2 = rb_inspect(value);
+ rb_str_buf_append(str, str2);
+ OBJ_INFECT(str, str2);
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+inspect_hash(hash)
+ VALUE hash;
+{
+ VALUE str;
+
+ str = rb_str_buf_new2("{");
+ rb_hash_foreach(hash, inspect_i, str);
+ rb_str_buf_cat2(str, "}");
+ OBJ_INFECT(str, hash);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * hsh.inspect => string
+ *
+ * Return the contents of this hash as a string.
+ */
+
+static VALUE
+rb_hash_inspect(hash)
+ VALUE hash;
+{
+ if (RHASH(hash)->tbl == 0 || RHASH(hash)->tbl->num_entries == 0)
+ return rb_str_new2("{}");
+ if (rb_inspecting_p(hash)) return rb_str_new2("{...}");
+ return rb_protect_inspect(inspect_hash, hash, 0);
+}
+
+static VALUE
+to_s_hash(hash)
+ VALUE hash;
+{
+ return rb_ary_to_s(rb_hash_to_a(hash));
+}
+
+/*
+ * call-seq:
+ * hsh.to_s => string
+ *
+ * Converts <i>hsh</i> to a string by converting the hash to an array
+ * of <code>[</code> <i>key, value</i> <code>]</code> pairs and then
+ * converting that array to a string using <code>Array#join</code> with
+ * the default separator.
+ *
+ * h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
+ * h.to_s #=> "a100c300d400"
+ */
+
+static VALUE
+rb_hash_to_s(hash)
+ VALUE hash;
+{
+ if (rb_inspecting_p(hash)) return rb_str_new2("{...}");
+ return rb_protect_inspect(to_s_hash, hash, 0);
+}
+
+/*
+ * call-seq:
+ * hsh.to_hash => hsh
+ *
+ * Returns <i>self</i>.
+ */
+
+static VALUE
+rb_hash_to_hash(hash)
+ VALUE hash;
+{
+ return hash;
+}
+
+static int
+keys_i(key, value, ary)
+ VALUE key, value, ary;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_ary_push(ary, key);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.keys => array
+ *
+ * Returns a new array populated with the keys from this hash. See also
+ * <code>Hash#values</code>.
+ *
+ * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
+ * h.keys #=> ["a", "b", "c", "d"]
+ *
+ */
+
+static VALUE
+rb_hash_keys(hash)
+ VALUE hash;
+{
+ VALUE ary;
+
+ ary = rb_ary_new();
+ rb_hash_foreach(hash, keys_i, ary);
+
+ return ary;
+}
+
+static int
+values_i(key, value, ary)
+ VALUE key, value, ary;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_ary_push(ary, value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.values => array
+ *
+ * Returns a new array populated with the values from <i>hsh</i>. See
+ * also <code>Hash#keys</code>.
+ *
+ * h = { "a" => 100, "b" => 200, "c" => 300 }
+ * h.values #=> [100, 200, 300]
+ *
+ */
+
+static VALUE
+rb_hash_values(hash)
+ VALUE hash;
+{
+ VALUE ary;
+
+ ary = rb_ary_new();
+ rb_hash_foreach(hash, values_i, ary);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * hsh.has_key?(key) => true or false
+ * hsh.include?(key) => true or false
+ * hsh.key?(key) => true or false
+ * hsh.member?(key) => true or false
+ *
+ * Returns <code>true</code> if the given key is present in <i>hsh</i>.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.has_key?("a") #=> true
+ * h.has_key?("z") #=> false
+ *
+ */
+
+static VALUE
+rb_hash_has_key(hash, key)
+ VALUE hash;
+ VALUE key;
+{
+ if (st_lookup(RHASH(hash)->tbl, key, 0)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static int
+rb_hash_search_value(key, value, data)
+ VALUE key, value, *data;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ if (rb_equal(value, data[1])) {
+ data[0] = Qtrue;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.has_value?(value) => true or false
+ * hsh.value?(value) => true or false
+ *
+ * Returns <code>true</code> if the given value is present for some key
+ * in <i>hsh</i>.
+ *
+ * h = { "a" => 100, "b" => 200 }
+ * h.has_value?(100) #=> true
+ * h.has_value?(999) #=> false
+ */
+
+static VALUE
+rb_hash_has_value(hash, val)
+ VALUE hash;
+ VALUE val;
+{
+ VALUE data[2];
+
+ data[0] = Qfalse;
+ data[1] = val;
+ rb_hash_foreach(hash, rb_hash_search_value, (st_data_t)data);
+ return data[0];
+}
+
+struct equal_data {
+ int result;
+ st_table *tbl;
+};
+
+static int
+equal_i(key, val1, data)
+ VALUE key, val1;
+ struct equal_data *data;
+{
+ VALUE val2;
+
+ if (key == Qundef) return ST_CONTINUE;
+ if (!st_lookup(data->tbl, key, &val2)) {
+ data->result = Qfalse;
+ return ST_STOP;
+ }
+ if (!rb_equal(val1, val2)) {
+ data->result = Qfalse;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+hash_equal(hash1, hash2, eql)
+ VALUE hash1, hash2;
+ int eql; /* compare default value if true */
+{
+ struct equal_data data;
+
+ if (hash1 == hash2) return Qtrue;
+ if (TYPE(hash2) != T_HASH) {
+ if (!rb_respond_to(hash2, rb_intern("to_hash"))) {
+ return Qfalse;
+ }
+ return rb_equal(hash2, hash1);
+ }
+ if (RHASH(hash1)->tbl->num_entries != RHASH(hash2)->tbl->num_entries)
+ return Qfalse;
+ if (eql) {
+ if (!(rb_equal(RHASH(hash1)->ifnone, RHASH(hash2)->ifnone) &&
+ FL_TEST(hash1, HASH_PROC_DEFAULT) == FL_TEST(hash2, HASH_PROC_DEFAULT)))
+ return Qfalse;
+ }
+
+ data.tbl = RHASH(hash2)->tbl;
+ data.result = Qtrue;
+ rb_hash_foreach(hash1, equal_i, (st_data_t)&data);
+
+ return data.result;
+}
+
+/*
+ * call-seq:
+ * hsh == other_hash => true or false
+ *
+ * Equality---Two hashes are equal if they each contain the same number
+ * of keys and if each key-value pair is equal to (according to
+ * <code>Object#==</code>) the corresponding elements in the other
+ * hash.
+ *
+ * h1 = { "a" => 1, "c" => 2 }
+ * h2 = { 7 => 35, "c" => 2, "a" => 1 }
+ * h3 = { "a" => 1, "c" => 2, 7 => 35 }
+ * h4 = { "a" => 1, "d" => 2, "f" => 35 }
+ * h1 == h2 #=> false
+ * h2 == h3 #=> true
+ * h3 == h4 #=> false
+ *
+ */
+
+static VALUE
+rb_hash_equal(hash1, hash2)
+ VALUE hash1, hash2;
+{
+ return hash_equal(hash1, hash2, Qfalse);
+}
+
+static int
+rb_hash_invert_i(key, value, hash)
+ VALUE key, value;
+ VALUE hash;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_hash_aset(hash, value, key);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.invert -> aHash
+ *
+ * Returns a new hash created by using <i>hsh</i>'s values as keys, and
+ * the keys as values.
+ *
+ * h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
+ * h.invert #=> {0=>"a", 100=>"n", 200=>"d", 300=>"y"}
+ *
+ */
+
+static VALUE
+rb_hash_invert(hash)
+ VALUE hash;
+{
+ VALUE h = rb_hash_new();
+
+ rb_hash_foreach(hash, rb_hash_invert_i, h);
+ return h;
+}
+
+static int
+rb_hash_update_i(key, value, hash)
+ VALUE key, value;
+ VALUE hash;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ rb_hash_aset(hash, key, value);
+ return ST_CONTINUE;
+}
+
+static int
+rb_hash_update_block_i(key, value, hash)
+ VALUE key, value;
+ VALUE hash;
+{
+ if (key == Qundef) return ST_CONTINUE;
+ if (rb_hash_has_key(hash, key)) {
+ value = rb_yield_values(3, key, rb_hash_aref(hash, key), value);
+ }
+ rb_hash_aset(hash, key, value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.merge!(other_hash) => hsh
+ * hsh.update(other_hash) => hsh
+ * hsh.merge!(other_hash){|key, oldval, newval| block} => hsh
+ * hsh.update(other_hash){|key, oldval, newval| block} => hsh
+ *
+ * Adds the contents of <i>other_hash</i> to <i>hsh</i>, overwriting
+ * entries with duplicate keys with those from <i>other_hash</i>.
+ *
+ * h1 = { "a" => 100, "b" => 200 }
+ * h2 = { "b" => 254, "c" => 300 }
+ * h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
+ */
+
+static VALUE
+rb_hash_update(hash1, hash2)
+ VALUE hash1, hash2;
+{
+ hash2 = to_hash(hash2);
+ if (rb_block_given_p()) {
+ rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
+ }
+ else {
+ rb_hash_foreach(hash2, rb_hash_update_i, hash1);
+ }
+ return hash1;
+}
+
+/*
+ * call-seq:
+ * hsh.merge(other_hash) -> a_hash
+ * hsh.merge(other_hash){|key, oldval, newval| block} -> a_hash
+ *
+ * Returns a new hash containing the contents of <i>other_hash</i> and
+ * the contents of <i>hsh</i>, overwriting entries in <i>hsh</i> with
+ * duplicate keys with those from <i>other_hash</i>.
+ *
+ * h1 = { "a" => 100, "b" => 200 }
+ * h2 = { "b" => 254, "c" => 300 }
+ * h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
+ * h1 #=> {"a"=>100, "b"=>200}
+ *
+ */
+
+static VALUE
+rb_hash_merge(hash1, hash2)
+ VALUE hash1, hash2;
+{
+ return rb_hash_update(rb_obj_dup(hash1), hash2);
+}
+
+static int path_tainted = -1;
+
+static char **origenviron;
+#ifdef _WIN32
+#define GET_ENVIRON(e) (e = rb_w32_get_environ())
+#define FREE_ENVIRON(e) rb_w32_free_environ(e)
+static char **my_environ;
+#undef environ
+#define environ my_environ
+#elif defined(__APPLE__)
+#undef environ
+#define environ (*_NSGetEnviron())
+#define GET_ENVIRON(e) (e)
+#define FREE_ENVIRON(e)
+#else
+extern char **environ;
+#define GET_ENVIRON(e) (e)
+#define FREE_ENVIRON(e)
+#endif
+
+static VALUE
+env_str_new(ptr, len)
+ const char *ptr;
+ long len;
+{
+ VALUE str = rb_tainted_str_new(ptr, len);
+
+ rb_obj_freeze(str);
+ return str;
+}
+
+static VALUE
+env_str_new2(ptr)
+ const char *ptr;
+{
+ if (!ptr) return Qnil;
+ return env_str_new(ptr, strlen(ptr));
+}
+
+static VALUE
+env_delete(obj, name)
+ VALUE obj, name;
+{
+ char *nam, *val;
+
+ rb_secure(4);
+ SafeStringValue(name);
+ nam = RSTRING(name)->ptr;
+ if (strlen(nam) != RSTRING(name)->len) {
+ rb_raise(rb_eArgError, "bad environment variable name");
+ }
+ val = getenv(nam);
+ if (val) {
+ VALUE value = env_str_new2(val);
+
+ ruby_setenv(nam, 0);
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0)
+#else
+ if (strcmp(nam, PATH_ENV) == 0)
+#endif
+ {
+ path_tainted = 0;
+ }
+ return value;
+ }
+ return Qnil;
+}
+
+static VALUE
+env_delete_m(obj, name)
+ VALUE obj, name;
+{
+ VALUE val;
+
+ val = env_delete(obj, name);
+ if (NIL_P(val) && rb_block_given_p()) rb_yield(name);
+ return val;
+}
+
+static VALUE
+rb_f_getenv(obj, name)
+ VALUE obj, name;
+{
+ char *nam, *env;
+
+ StringValue(name);
+ nam = RSTRING(name)->ptr;
+ if (strlen(nam) != RSTRING(name)->len) {
+ rb_raise(rb_eArgError, "bad environment variable name");
+ }
+ env = getenv(nam);
+ if (env) {
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
+#else
+ if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
+#endif
+ {
+ VALUE str = rb_str_new2(env);
+
+ rb_obj_freeze(str);
+ return str;
+ }
+ return env_str_new2(env);
+ }
+ return Qnil;
+}
+
+static VALUE
+env_fetch(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE key, if_none;
+ long block_given;
+ char *nam, *env;
+
+ rb_scan_args(argc, argv, "11", &key, &if_none);
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ StringValue(key);
+ nam = RSTRING(key)->ptr;
+ if (strlen(nam) != RSTRING(key)->len) {
+ rb_raise(rb_eArgError, "bad environment variable name");
+ }
+ env = getenv(nam);
+ if (!env) {
+ if (block_given) return rb_yield(key);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "key not found");
+ }
+ return if_none;
+ }
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
+#else
+ if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
+#endif
+ return rb_str_new2(env);
+ return env_str_new2(env);
+}
+
+static void
+path_tainted_p(path)
+ char *path;
+{
+ path_tainted = rb_path_check(path)?0:1;
+}
+
+int
+rb_env_path_tainted()
+{
+ if (path_tainted < 0) {
+ path_tainted_p(getenv(PATH_ENV));
+ }
+ return path_tainted;
+}
+
+static int
+envix(nam)
+ const char *nam;
+{
+ register int i, len = strlen(nam);
+ char **env;
+
+ env = GET_ENVIRON(environ);
+ for (i = 0; env[i]; i++) {
+ if (
+#ifdef ENV_IGNORECASE
+ strncasecmp(env[i],nam,len) == 0
+#else
+ memcmp(env[i],nam,len) == 0
+#endif
+ && env[i][len] == '=')
+ break; /* memcmp must come first to avoid */
+ } /* potential SEGV's */
+ FREE_ENVIRON(environ);
+ return i;
+}
+
+void
+ruby_setenv(name, value)
+ const char *name;
+ const char *value;
+{
+#if defined(_WIN32)
+ /* The sane way to deal with the environment.
+ * Has these advantages over putenv() & co.:
+ * * enables us to store a truly empty value in the
+ * environment (like in UNIX).
+ * * we don't have to deal with RTL globals, bugs and leaks.
+ * * Much faster.
+ * Why you may want to enable USE_WIN32_RTL_ENV:
+ * * environ[] and RTL functions will not reflect changes,
+ * which might be an issue if extensions want to access
+ * the env. via RTL. This cuts both ways, since RTL will
+ * not see changes made by extensions that call the Win32
+ * functions directly, either.
+ * GSAR 97-06-07
+ *
+ * REMARK: USE_WIN32_RTL_ENV is already obsoleted since we don't use
+ * RTL's environ global variable directly yet.
+ */
+ SetEnvironmentVariable(name,value);
+#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+#undef setenv
+#undef unsetenv
+ if (value)
+ setenv(name,value,1);
+ else
+ unsetenv(name);
+#else /* WIN32 */
+ size_t len;
+ int i=envix(name); /* where does it go? */
+
+ if (environ == origenviron) { /* need we copy environment? */
+ int j;
+ int max;
+ char **tmpenv;
+
+ for (max = i; environ[max]; max++) ;
+ tmpenv = ALLOC_N(char*, max+2);
+ for (j=0; j<max; j++) /* copy environment */
+ tmpenv[j] = strdup(environ[j]);
+ tmpenv[max] = 0;
+ environ = tmpenv; /* tell exec where it is now */
+ }
+ if (environ[i]) {
+ char **envp = origenviron;
+ while (*envp && *envp != environ[i]) envp++;
+ if (!*envp)
+ free(environ[i]);
+ if (!value) {
+ while (environ[i]) {
+ environ[i] = environ[i+1];
+ i++;
+ }
+ return;
+ }
+ }
+ else { /* does not exist yet */
+ if (!value) return;
+ REALLOC_N(environ, char*, i+2); /* just expand it a bit */
+ environ[i+1] = 0; /* make sure it's null terminated */
+ }
+ len = strlen(name) + strlen(value) + 2;
+ environ[i] = ALLOC_N(char, len);
+#ifndef MSDOS
+ snprintf(environ[i],len,"%s=%s",name,value); /* all that work just for this */
+#else
+ /* MS-DOS requires environment variable names to be in uppercase */
+ /* [Tom Dinger, 27 August 1990: Well, it doesn't _require_ it, but
+ * some utilities and applications may break because they only look
+ * for upper case strings. (Fixed strupr() bug here.)]
+ */
+ strcpy(environ[i],name); strupr(environ[i]);
+ sprintf(environ[i] + strlen(name),"=%s", value);
+#endif /* MSDOS */
+
+#endif /* WIN32 */
+}
+
+void
+ruby_unsetenv(name)
+ const char *name;
+{
+ ruby_setenv(name, 0);
+}
+
+static VALUE
+env_aset(obj, nm, val)
+ VALUE obj, nm, val;
+{
+ char *name, *value;
+
+ if (rb_safe_level() >= 4) {
+ rb_raise(rb_eSecurityError, "can't change environment variable");
+ }
+
+ if (NIL_P(val)) {
+ env_delete(obj, nm);
+ return Qnil;
+ }
+
+ StringValue(nm);
+ StringValue(val);
+ name = RSTRING(nm)->ptr;
+ value = RSTRING(val)->ptr;
+ if (strlen(name) != RSTRING(nm)->len)
+ rb_raise(rb_eArgError, "bad environment variable name");
+ if (strlen(value) != RSTRING(val)->len)
+ rb_raise(rb_eArgError, "bad environment variable value");
+
+ ruby_setenv(name, value);
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(name, PATH_ENV) == 0) {
+#else
+ if (strcmp(name, PATH_ENV) == 0) {
+#endif
+ if (OBJ_TAINTED(val)) {
+ /* already tainted, no check */
+ path_tainted = 1;
+ return val;
+ }
+ else {
+ path_tainted_p(value);
+ }
+ }
+ return val;
+}
+
+static VALUE
+env_keys()
+{
+ char **env;
+ VALUE ary = rb_ary_new();
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ rb_ary_push(ary, env_str_new(*env, s-*env));
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return ary;
+}
+
+static VALUE
+env_each_key(ehash)
+ VALUE ehash;
+{
+ VALUE keys = env_keys();
+ long i;
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ rb_yield(RARRAY(keys)->ptr[i]);
+ }
+ return ehash;
+}
+
+static VALUE
+env_values()
+{
+ char **env;
+ VALUE ary = rb_ary_new();
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ rb_ary_push(ary, env_str_new2(s+1));
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return ary;
+}
+
+static VALUE
+env_each_value(ehash)
+ VALUE ehash;
+{
+ VALUE values = env_values();
+ long i;
+
+ for (i=0; i<RARRAY(values)->len; i++) {
+ rb_yield(RARRAY(values)->ptr[i]);
+ }
+ return ehash;
+}
+
+static VALUE
+env_each_i(ehash, values)
+ VALUE ehash;
+ int values;
+{
+ char **env;
+ VALUE ary = rb_ary_new();
+ long i;
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ rb_ary_push(ary, env_str_new(*env, s-*env));
+ rb_ary_push(ary, env_str_new2(s+1));
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+
+ for (i=0; i<RARRAY(ary)->len; i+=2) {
+ if (values) {
+ rb_yield_values(2, RARRAY(ary)->ptr[i], RARRAY(ary)->ptr[i+1]);
+ }
+ else {
+ rb_yield(rb_assoc_new(RARRAY(ary)->ptr[i], RARRAY(ary)->ptr[i+1]));
+ }
+ }
+ return ehash;
+}
+
+static VALUE
+env_each(ehash)
+ VALUE ehash;
+{
+ return env_each_i(ehash, Qfalse);
+}
+
+static VALUE
+env_each_pair(ehash)
+ VALUE ehash;
+{
+ return env_each_i(ehash, Qtrue);
+}
+
+static VALUE
+env_reject_bang()
+{
+ volatile VALUE keys;
+ long i;
+ int del = 0;
+
+ rb_secure(4);
+ keys = env_keys();
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY(keys)->ptr[i]);
+ if (!NIL_P(val)) {
+ if (RTEST(rb_yield_values(2, RARRAY(keys)->ptr[i], val))) {
+ FL_UNSET(RARRAY(keys)->ptr[i], FL_TAINT);
+ env_delete(Qnil, RARRAY(keys)->ptr[i]);
+ del++;
+ }
+ }
+ }
+ if (del == 0) return Qnil;
+ return envtbl;
+}
+
+static VALUE
+env_delete_if()
+{
+ env_reject_bang();
+ return envtbl;
+}
+
+static VALUE
+env_values_at(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE result = rb_ary_new();
+ long i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(result, rb_f_getenv(Qnil, argv[i]));
+ }
+ return result;
+}
+
+static VALUE
+env_select(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE result;
+ char **env;
+
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ result = rb_ary_new();
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ VALUE k = env_str_new(*env, s-*env);
+ VALUE v = env_str_new2(s+1);
+ if (RTEST(rb_yield_values(2, k, v))) {
+ rb_ary_push(result, rb_assoc_new(k, v));
+ }
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+
+ return result;
+}
+
+static VALUE
+env_clear()
+{
+ volatile VALUE keys;
+ long i;
+
+ rb_secure(4);
+ keys = env_keys();
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY(keys)->ptr[i]);
+ if (!NIL_P(val)) {
+ env_delete(Qnil, RARRAY(keys)->ptr[i]);
+ }
+ }
+ return envtbl;
+}
+
+static VALUE
+env_to_s()
+{
+ return rb_str_new2("ENV");
+}
+
+static VALUE
+env_inspect()
+{
+ char **env;
+ VALUE str = rb_str_buf_new2("{");
+ VALUE i;
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+
+ if (env != environ) {
+ rb_str_buf_cat2(str, ", ");
+ }
+ if (s) {
+ rb_str_buf_cat2(str, "\"");
+ rb_str_buf_cat(str, *env, s-*env);
+ rb_str_buf_cat2(str, "\"=>");
+ i = rb_inspect(rb_str_new2(s+1));
+ rb_str_buf_append(str, i);
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ rb_str_buf_cat2(str, "}");
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+static VALUE
+env_to_a()
+{
+ char **env;
+ VALUE ary = rb_ary_new();
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env),
+ env_str_new2(s+1)));
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return ary;
+}
+
+static VALUE
+env_none()
+{
+ return Qnil;
+}
+
+static VALUE
+env_size()
+{
+ int i;
+ char **env;
+
+ env = GET_ENVIRON(environ);
+ for(i=0; env[i]; i++)
+ ;
+ FREE_ENVIRON(environ);
+ return INT2FIX(i);
+}
+
+static VALUE
+env_empty_p()
+{
+ char **env;
+
+ env = GET_ENVIRON(environ);
+ if (env[0] == 0) {
+ FREE_ENVIRON(environ);
+ return Qtrue;
+ }
+ FREE_ENVIRON(environ);
+ return Qfalse;
+}
+
+static VALUE
+env_has_key(env, key)
+ VALUE env, key;
+{
+ char *s;
+
+ s = StringValuePtr(key);
+ if (strlen(s) != RSTRING(key)->len)
+ rb_raise(rb_eArgError, "bad environment variable name");
+ if (getenv(s)) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+env_has_value(dmy, value)
+ VALUE dmy, value;
+{
+ char **env;
+
+ if (TYPE(value) != T_STRING) return Qfalse;
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s++) {
+ long len = strlen(s);
+ if (RSTRING(value)->len == len && strncmp(s, RSTRING(value)->ptr, len) == 0) {
+ FREE_ENVIRON(environ);
+ return Qtrue;
+ }
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return Qfalse;
+}
+
+static VALUE
+env_index(dmy, value)
+ VALUE dmy, value;
+{
+ char **env;
+ VALUE str;
+
+ StringValue(value);
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s++) {
+ long len = strlen(s);
+ if (RSTRING(value)->len == len && strncmp(s, RSTRING(value)->ptr, len) == 0) {
+ str = env_str_new(*env, s-*env-1);
+ FREE_ENVIRON(environ);
+ return str;
+ }
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return Qnil;
+}
+
+static VALUE
+env_indexes(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int i;
+ VALUE indexes = rb_ary_new2(argc);
+
+ rb_warn("ENV.%s is deprecated; use ENV.values_at",
+ rb_id2name(rb_frame_last_func()));
+ for (i=0;i<argc;i++) {
+ VALUE tmp = rb_check_string_type(argv[i]);
+ if (NIL_P(tmp)) {
+ RARRAY(indexes)->ptr[i] = Qnil;
+ }
+ else {
+ RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
+ }
+ RARRAY(indexes)->len = i+1;
+ }
+
+ return indexes;
+}
+
+static VALUE
+env_to_hash()
+{
+ char **env;
+ VALUE hash = rb_hash_new();
+
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ rb_hash_aset(hash, env_str_new(*env, s-*env),
+ env_str_new2(s+1));
+ }
+ env++;
+ }
+ FREE_ENVIRON(environ);
+ return hash;
+}
+
+static VALUE
+env_reject()
+{
+ return rb_hash_delete_if(env_to_hash());
+}
+
+static VALUE
+env_shift()
+{
+ char **env;
+
+ env = GET_ENVIRON(environ);
+ if (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ VALUE key = env_str_new(*env, s-*env);
+ VALUE val = env_str_new2(getenv(RSTRING(key)->ptr));
+ env_delete(Qnil, key);
+ return rb_assoc_new(key, val);
+ }
+ }
+ FREE_ENVIRON(environ);
+ return Qnil;
+}
+
+static VALUE
+env_invert()
+{
+ return rb_hash_invert(env_to_hash());
+}
+
+static int
+env_replace_i(key, val, keys)
+ VALUE key, val, keys;
+{
+ if (key != Qundef) {
+ env_aset(Qnil, key, val);
+ if (rb_ary_includes(keys, key)) {
+ rb_ary_delete(keys, key);
+ }
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+env_replace(env, hash)
+ VALUE env, hash;
+{
+ volatile VALUE keys = env_keys();
+ long i;
+
+ if (env == hash) return env;
+ hash = to_hash(hash);
+ rb_hash_foreach(hash, env_replace_i, keys);
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ env_delete(env, RARRAY(keys)->ptr[i]);
+ }
+ return env;
+}
+
+static int
+env_update_i(key, val)
+ VALUE key, val;
+{
+ if (key != Qundef) {
+ if (rb_block_given_p()) {
+ val = rb_yield_values(3, key, rb_f_getenv(Qnil, key), val);
+ }
+ env_aset(Qnil, key, val);
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+env_update(env, hash)
+ VALUE env, hash;
+{
+ if (env == hash) return env;
+ hash = to_hash(hash);
+ rb_hash_foreach(hash, env_update_i, 0);
+ return env;
+}
+
+/*
+ * A <code>Hash</code> is a collection of key-value pairs. It is
+ * similar to an <code>Array</code>, except that indexing is done via
+ * arbitrary keys of any object type, not an integer index. The order
+ * in which you traverse a hash by either key or value may seem
+ * arbitrary, and will generally not be in the insertion order.
+ *
+ * Hashes have a <em>default value</em> that is returned when accessing
+ * keys that do not exist in the hash. By default, that value is
+ * <code>nil</code>.
+ *
+ * <code>Hash</code> uses <code>key.eql?</code> to test keys for equality.
+ * If you need to use instances of your own classes as keys in a <code>Hash</code>,
+ * it is recommended that you define both the <code>eql?</code> and <code>hash</code>
+ * methods. The <code>hash</code> method must have the property that
+ * <code>a.eql?(b)</code> implies <code>a.hash == b.hash</code>.
+ *
+ * class MyClass
+ * attr_reader :str
+ * def initialize(str)
+ * @str = str
+ * end
+ * def eql?(o)
+ * o.is_a?(MyClass) && str == o.str
+ * end
+ * def hash
+ * @str.hash
+ * end
+ * end
+ *
+ * a = MyClass.new("some string")
+ * b = MyClass.new("some string")
+ * a.eql? b #=> true
+ *
+ * h = {}
+ *
+ * h[a] = 1
+ * h[a] #=> 1
+ * h[b] #=> 1
+ *
+ * h[b] = 2
+ * h[a] #=> 2
+ * h[b] #=> 2
+ */
+
+void
+Init_Hash()
+{
+ id_hash = rb_intern("hash");
+ id_call = rb_intern("call");
+ id_default = rb_intern("default");
+
+ rb_cHash = rb_define_class("Hash", rb_cObject);
+
+ rb_include_module(rb_cHash, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cHash, hash_alloc);
+ rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
+ rb_define_method(rb_cHash,"initialize", rb_hash_initialize, -1);
+ rb_define_method(rb_cHash,"initialize_copy", rb_hash_replace, 1);
+ rb_define_method(rb_cHash,"rehash", rb_hash_rehash, 0);
+
+ rb_define_method(rb_cHash,"to_hash", rb_hash_to_hash, 0);
+ rb_define_method(rb_cHash,"to_a", rb_hash_to_a, 0);
+ rb_define_method(rb_cHash,"to_s", rb_hash_to_s, 0);
+ rb_define_method(rb_cHash,"inspect", rb_hash_inspect, 0);
+
+ rb_define_method(rb_cHash,"==", rb_hash_equal, 1);
+ rb_define_method(rb_cHash,"[]", rb_hash_aref, 1);
+ rb_define_method(rb_cHash,"fetch", rb_hash_fetch, -1);
+ rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2);
+ rb_define_method(rb_cHash,"store", rb_hash_aset, 2);
+ rb_define_method(rb_cHash,"default", rb_hash_default, -1);
+ rb_define_method(rb_cHash,"default=", rb_hash_set_default, 1);
+ rb_define_method(rb_cHash,"default_proc", rb_hash_default_proc, 0);
+ rb_define_method(rb_cHash,"index", rb_hash_index, 1);
+ rb_define_method(rb_cHash,"indexes", rb_hash_indexes, -1);
+ rb_define_method(rb_cHash,"indices", rb_hash_indexes, -1);
+ rb_define_method(rb_cHash,"size", rb_hash_size, 0);
+ rb_define_method(rb_cHash,"length", rb_hash_size, 0);
+ rb_define_method(rb_cHash,"empty?", rb_hash_empty_p, 0);
+
+ rb_define_method(rb_cHash,"each", rb_hash_each, 0);
+ rb_define_method(rb_cHash,"each_value", rb_hash_each_value, 0);
+ rb_define_method(rb_cHash,"each_key", rb_hash_each_key, 0);
+ rb_define_method(rb_cHash,"each_pair", rb_hash_each_pair, 0);
+ rb_define_method(rb_cHash,"sort", rb_hash_sort, 0);
+
+ rb_define_method(rb_cHash,"keys", rb_hash_keys, 0);
+ rb_define_method(rb_cHash,"values", rb_hash_values, 0);
+ rb_define_method(rb_cHash,"values_at", rb_hash_values_at, -1);
+
+ rb_define_method(rb_cHash,"shift", rb_hash_shift, 0);
+ rb_define_method(rb_cHash,"delete", rb_hash_delete, 1);
+ rb_define_method(rb_cHash,"delete_if", rb_hash_delete_if, 0);
+ rb_define_method(rb_cHash,"select", rb_hash_select, -1);
+ rb_define_method(rb_cHash,"reject", rb_hash_reject, 0);
+ rb_define_method(rb_cHash,"reject!", rb_hash_reject_bang, 0);
+ rb_define_method(rb_cHash,"clear", rb_hash_clear, 0);
+ rb_define_method(rb_cHash,"invert", rb_hash_invert, 0);
+ rb_define_method(rb_cHash,"update", rb_hash_update, 1);
+ rb_define_method(rb_cHash,"replace", rb_hash_replace, 1);
+ rb_define_method(rb_cHash,"merge!", rb_hash_update, 1);
+ rb_define_method(rb_cHash,"merge", rb_hash_merge, 1);
+
+ rb_define_method(rb_cHash,"include?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash,"member?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash,"has_key?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash,"has_value?", rb_hash_has_value, 1);
+ rb_define_method(rb_cHash,"key?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash,"value?", rb_hash_has_value, 1);
+
+#ifndef __MACOS__ /* environment variables nothing on MacOS. */
+ origenviron = environ;
+ envtbl = rb_obj_alloc(rb_cObject);
+ rb_extend_object(envtbl, rb_mEnumerable);
+
+ rb_define_singleton_method(envtbl,"[]", rb_f_getenv, 1);
+ rb_define_singleton_method(envtbl,"fetch", env_fetch, -1);
+ rb_define_singleton_method(envtbl,"[]=", env_aset, 2);
+ rb_define_singleton_method(envtbl,"store", env_aset, 2);
+ rb_define_singleton_method(envtbl,"each", env_each, 0);
+ rb_define_singleton_method(envtbl,"each_pair", env_each_pair, 0);
+ rb_define_singleton_method(envtbl,"each_key", env_each_key, 0);
+ rb_define_singleton_method(envtbl,"each_value", env_each_value, 0);
+ rb_define_singleton_method(envtbl,"delete", env_delete_m, 1);
+ rb_define_singleton_method(envtbl,"delete_if", env_delete_if, 0);
+ rb_define_singleton_method(envtbl,"clear", env_clear, 0);
+ rb_define_singleton_method(envtbl,"reject", env_reject, 0);
+ rb_define_singleton_method(envtbl,"reject!", env_reject_bang, 0);
+ rb_define_singleton_method(envtbl,"select", env_select, -1);
+ rb_define_singleton_method(envtbl,"shift", env_shift, 0);
+ rb_define_singleton_method(envtbl,"invert", env_invert, 0);
+ rb_define_singleton_method(envtbl,"replace", env_replace, 1);
+ rb_define_singleton_method(envtbl,"update", env_update, 1);
+ rb_define_singleton_method(envtbl,"inspect", env_inspect, 0);
+ rb_define_singleton_method(envtbl,"rehash", env_none, 0);
+ rb_define_singleton_method(envtbl,"to_a", env_to_a, 0);
+ rb_define_singleton_method(envtbl,"to_s", env_to_s, 0);
+ rb_define_singleton_method(envtbl,"index", env_index, 1);
+ rb_define_singleton_method(envtbl,"indexes", env_indexes, -1);
+ rb_define_singleton_method(envtbl,"indices", env_indexes, -1);
+ rb_define_singleton_method(envtbl,"size", env_size, 0);
+ rb_define_singleton_method(envtbl,"length", env_size, 0);
+ rb_define_singleton_method(envtbl,"empty?", env_empty_p, 0);
+ rb_define_singleton_method(envtbl,"keys", env_keys, 0);
+ rb_define_singleton_method(envtbl,"values", env_values, 0);
+ rb_define_singleton_method(envtbl,"values_at", env_values_at, -1);
+ rb_define_singleton_method(envtbl,"include?", env_has_key, 1);
+ rb_define_singleton_method(envtbl,"member?", env_has_key, 1);
+ rb_define_singleton_method(envtbl,"has_key?", env_has_key, 1);
+ rb_define_singleton_method(envtbl,"has_value?", env_has_value, 1);
+ rb_define_singleton_method(envtbl,"key?", env_has_key, 1);
+ rb_define_singleton_method(envtbl,"value?", env_has_value, 1);
+ rb_define_singleton_method(envtbl,"to_hash", env_to_hash, 0);
+
+ rb_define_global_const("ENV", envtbl);
+#else /* __MACOS__ */
+ envtbl = rb_hash_s_new(0, NULL, rb_cHash);
+ rb_define_global_const("ENV", envtbl);
+#endif /* ifndef __MACOS__ environment variables nothing on MacOS. */
+}
+/**********************************************************************
+
+ inits.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Tue Dec 28 16:01:58 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+void Init_Array _((void));
+void Init_Bignum _((void));
+void Init_Binding _((void));
+void Init_Comparable _((void));
+void Init_Dir _((void));
+void Init_Enumerable _((void));
+void Init_Exception _((void));
+void Init_syserr _((void));
+void Init_eval _((void));
+void Init_load _((void));
+void Init_Proc _((void));
+void Init_Thread _((void));
+void Init_File _((void));
+void Init_GC _((void));
+void Init_Hash _((void));
+void Init_IO _((void));
+void Init_Math _((void));
+void Init_marshal _((void));
+void Init_Numeric _((void));
+void Init_Object _((void));
+void Init_pack _((void));
+void Init_Precision _((void));
+void Init_sym _((void));
+void Init_process _((void));
+void Init_Random _((void));
+void Init_Range _((void));
+void Init_Regexp _((void));
+void Init_signal _((void));
+void Init_String _((void));
+void Init_Struct _((void));
+void Init_Time _((void));
+void Init_var_tables _((void));
+void Init_version _((void));
+
+void
+rb_call_inits()
+{
+ Init_sym();
+ Init_var_tables();
+ Init_Object();
+ Init_Comparable();
+ Init_Enumerable();
+ Init_Precision();
+ Init_eval();
+ Init_String();
+ Init_Exception();
+ Init_Thread();
+ Init_Numeric();
+ Init_Bignum();
+ Init_syserr();
+ Init_Array();
+ Init_Hash();
+ Init_Struct();
+ Init_Regexp();
+ Init_pack();
+ Init_Range();
+ Init_IO();
+ Init_Dir();
+ Init_Time();
+ Init_Random();
+ Init_signal();
+ Init_process();
+ Init_load();
+ Init_Proc();
+ Init_Binding();
+ Init_Math();
+ Init_GC();
+ Init_marshal();
+ Init_version();
+}
+/**********************************************************************
+
+ io.c -
+
+ $Author: shyouhei $
+ $Date: 2009-03-09 01:52:15 +0100 (Mon, 09 Mar 2009) $
+ created at: Fri Oct 15 18:08:59 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#if defined(__VMS)
+#define _XOPEN_SOURCE
+#define _POSIX_C_SOURCE 2
+#endif
+
+#include "ruby.h"
+#include "rubyio.h"
+#include "rubysig.h"
+#include "env.h"
+#include <ctype.h>
+#include <errno.h>
+
+#if defined(MSDOS) || defined(__BOW__) || defined(__CYGWIN__) || defined(_WIN32) || defined(__human68k__) || defined(__EMX__) || defined(__BEOS__)
+# define NO_SAFE_RENAME
+#endif
+
+#if defined(MSDOS) || defined(__CYGWIN__) || defined(_WIN32)
+# define NO_LONG_FNAME
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(sun) || defined(_nec_ews)
+# define USE_SETVBUF
+#endif
+
+#ifdef __QNXNTO__
+#include "unix.h"
+#endif
+
+#include <sys/types.h>
+#if defined(HAVE_SYS_IOCTL_H) && !defined(DJGPP) && !defined(_WIN32) && !defined(__human68k__)
+#include <sys/ioctl.h>
+#endif
+#if defined(HAVE_FCNTL_H) || defined(_WIN32)
+#include <fcntl.h>
+#elif defined(HAVE_SYS_FCNTL_H)
+#include <sys/fcntl.h>
+#endif
+
+#if !HAVE_OFF_T && !defined(off_t)
+# define off_t long
+#endif
+#if !HAVE_FSEEKO && !defined(fseeko)
+# define fseeko fseek
+#endif
+#if !HAVE_FTELLO && !defined(ftello)
+# define ftello ftell
+#endif
+
+#include <sys/stat.h>
+
+/* EMX has sys/param.h, but.. */
+#if defined(HAVE_SYS_PARAM_H) && !(defined(__EMX__) || defined(__HIUX_MPP__))
+# include <sys/param.h>
+#endif
+
+#if !defined NOFILE
+# define NOFILE 64
+#endif
+
+#ifdef HAVE_UNISTD_H
+#ifdef HAVE_SYSCALL_H
+#include <syscall.h>
+#elif defined HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#include <unistd.h>
+#endif
+
+extern void Init_File _((void));
+
+#ifdef __BEOS__
+# ifndef NOFILE
+# define NOFILE (OPEN_MAX)
+# endif
+#include <net/socket.h>
+#endif
+
+#include "util.h"
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#if SIZEOF_OFF_T > SIZEOF_LONG && !defined(HAVE_LONG_LONG)
+# error off_t is bigger than long, but you have no long long...
+#endif
+
+#ifndef PIPE_BUF
+# ifdef _POSIX_PIPE_BUF
+# define PIPE_BUF _POSIX_PIPE_BUF
+# else
+# define PIPE_BUF 512 /* is this ok? */
+# endif
+#endif
+
+VALUE rb_cIO;
+VALUE rb_eEOFError;
+VALUE rb_eIOError;
+
+VALUE rb_stdin, rb_stdout, rb_stderr;
+VALUE rb_deferr; /* rescue VIM plugin */
+static VALUE orig_stdout, orig_stderr;
+
+VALUE rb_output_fs;
+VALUE rb_rs;
+VALUE rb_output_rs;
+VALUE rb_default_rs;
+
+static VALUE argf;
+
+static ID id_write, id_read, id_getc;
+
+extern char *ruby_inplace_mode;
+
+struct timeval rb_time_interval _((VALUE));
+
+static VALUE filename, current_file;
+static int gets_lineno;
+static int init_p = 0, next_p = 0;
+static VALUE lineno = INT2FIX(0);
+
+#ifdef _STDIO_USES_IOSTREAM /* GNU libc */
+# ifdef _IO_fpos_t
+# define READ_DATA_PENDING(fp) ((fp)->_IO_read_ptr != (fp)->_IO_read_end)
+# define READ_DATA_PENDING_COUNT(fp) ((fp)->_IO_read_end - (fp)->_IO_read_ptr)
+# define READ_DATA_PENDING_PTR(fp) ((fp)->_IO_read_ptr)
+# else
+# define READ_DATA_PENDING(fp) ((fp)->_gptr < (fp)->_egptr)
+# define READ_DATA_PENDING_COUNT(fp) ((fp)->_egptr - (fp)->_gptr)
+# define READ_DATA_PENDING_PTR(fp) ((fp)->_gptr)
+# endif
+#elif defined(_LP64) && (defined(__sun__) || defined(__sun))
+typedef struct _FILE64 {
+ unsigned char *_ptr; /* next character from/to here in buffer */
+ unsigned char *_base; /* the buffer */
+ unsigned char *_end; /* the end of the buffer */
+ ssize_t _cnt; /* number of available characters in buffer */
+ int _file; /* UNIX System file descriptor */
+ unsigned int _flag; /* the state of the stream */
+ char __fill[80]; /* filler to bring size to 128 bytes */
+} FILE64;
+# define READ_DATA_PENDING(fp) (((FILE64*)(fp))->_cnt > 0)
+# define READ_DATA_PENDING_COUNT(fp) (((FILE64*)(fp))->_cnt)
+# define READ_DATA_PENDING_PTR(fp) ((char *)((FILE64*)(fp))->_ptr)
+#elif defined(FILE_COUNT)
+# define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0)
+# define READ_DATA_PENDING_COUNT(fp) ((fp)->FILE_COUNT)
+#elif defined(FILE_READEND)
+# define READ_DATA_PENDING(fp) ((fp)->FILE_READPTR < (fp)->FILE_READEND)
+# define READ_DATA_PENDING_COUNT(fp) ((fp)->FILE_READEND - (fp)->FILE_READPTR)
+#elif defined(__BEOS__)
+# define READ_DATA_PENDING(fp) (fp->_state._eof == 0)
+#elif defined(__VMS)
+# define READ_DATA_PENDING_COUNT(fp) ((unsigned int)(*(fp))->_cnt)
+# define READ_DATA_PENDING(fp) (((unsigned int)(*(fp))->_cnt) > 0)
+# define READ_DATA_BUFFERED(fp) 0
+#elif defined(__DragonFly__)
+/* FILE is an incomplete struct type since DragonFly BSD 1.4.0 */
+# define READ_DATA_PENDING(fp) (((struct __FILE_public *)(fp))->_r > 0)
+# define READ_DATA_PENDING_COUNT(fp) (((struct __FILE_public *)(fp))->_r)
+#else
+/* requires systems own version of the ReadDataPending() */
+extern int ReadDataPending();
+# define READ_DATA_PENDING(fp) (!feof(fp))
+# define READ_DATA_BUFFERED(fp) 0
+#endif
+#ifndef READ_DATA_BUFFERED
+# define READ_DATA_BUFFERED(fp) READ_DATA_PENDING(fp)
+#endif
+
+#ifndef READ_DATA_PENDING_PTR
+# ifdef FILE_READPTR
+# define READ_DATA_PENDING_PTR(fp) ((char *)(fp)->FILE_READPTR)
+# endif
+#endif
+
+#if defined __DJGPP__
+# undef READ_DATA_PENDING_COUNT
+# undef READ_DATA_PENDING_PTR
+#endif
+
+#define READ_CHECK(fp) do {\
+ if (!READ_DATA_PENDING(fp)) {\
+ rb_thread_wait_fd(fileno(fp));\
+ rb_io_check_closed(fptr);\
+ }\
+} while(0)
+
+void
+rb_eof_error()
+{
+ rb_raise(rb_eEOFError, "end of file reached");
+}
+
+VALUE
+rb_io_taint_check(io)
+ VALUE io;
+{
+ if (!OBJ_TAINTED(io) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+ rb_check_frozen(io);
+ return io;
+}
+
+void
+rb_io_check_initialized(fptr)
+ OpenFile *fptr;
+{
+ if (!fptr) {
+ rb_raise(rb_eIOError, "uninitialized stream");
+ }
+}
+
+void
+rb_io_check_closed(fptr)
+ OpenFile *fptr;
+{
+ rb_io_check_initialized(fptr);
+ if (!fptr->f && !fptr->f2) {
+ rb_raise(rb_eIOError, "closed stream");
+ }
+}
+
+static void io_fflush _((FILE *, OpenFile *));
+
+static OpenFile *
+flush_before_seek(fptr)
+ OpenFile *fptr;
+{
+ if (fptr->mode & FMODE_WBUF) {
+ io_fflush(GetWriteFile(fptr), fptr);
+ }
+ errno = 0;
+ return fptr;
+}
+
+#define io_seek(fptr, ofs, whence) fseeko(flush_before_seek(fptr)->f, ofs, whence)
+#define io_tell(fptr) ftello(flush_before_seek(fptr)->f)
+
+#ifndef SEEK_CUR
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#define FMODE_SYNCWRITE (FMODE_SYNC|FMODE_WRITABLE)
+
+void
+rb_io_check_readable(fptr)
+ OpenFile *fptr;
+{
+ rb_io_check_closed(fptr);
+ if (!(fptr->mode & FMODE_READABLE)) {
+ rb_raise(rb_eIOError, "not opened for reading");
+ }
+#ifdef NEED_IO_SEEK_BETWEEN_RW
+ if (((fptr->mode & FMODE_WBUF) ||
+ (fptr->mode & (FMODE_SYNCWRITE|FMODE_RBUF)) == FMODE_SYNCWRITE) &&
+ !feof(fptr->f) &&
+ !fptr->f2) {
+ io_seek(fptr, 0, SEEK_CUR);
+ }
+#endif
+ fptr->mode |= FMODE_RBUF;
+}
+
+void
+rb_io_check_writable(fptr)
+ OpenFile *fptr;
+{
+ rb_io_check_closed(fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ rb_raise(rb_eIOError, "not opened for writing");
+ }
+ if ((fptr->mode & FMODE_RBUF) && !feof(fptr->f) && !fptr->f2) {
+ io_seek(fptr, 0, SEEK_CUR);
+ }
+ if (!fptr->f2) {
+ fptr->mode &= ~FMODE_RBUF;
+ }
+}
+
+int
+rb_read_pending(fp)
+ FILE *fp;
+{
+ return READ_DATA_PENDING(fp);
+}
+
+void
+rb_read_check(fp)
+ FILE *fp;
+{
+ if (!READ_DATA_PENDING(fp)) {
+ rb_thread_wait_fd(fileno(fp));
+ }
+}
+
+static int
+ruby_dup(orig)
+ int orig;
+{
+ int fd;
+
+ fd = dup(orig);
+ if (fd < 0) {
+ if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
+ rb_gc();
+ fd = dup(orig);
+ }
+ if (fd < 0) {
+ rb_sys_fail(0);
+ }
+ }
+ return fd;
+}
+
+static VALUE io_alloc _((VALUE));
+static VALUE
+io_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(io, struct RFile);
+ OBJSETUP(io, klass, T_FILE);
+
+ io->fptr = 0;
+
+ return (VALUE)io;
+}
+
+static void
+io_fflush(f, fptr)
+ FILE *f;
+ OpenFile *fptr;
+{
+ int n;
+
+ if (!rb_thread_fd_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ }
+ for (;;) {
+ TRAP_BEG;
+ n = fflush(f);
+ TRAP_END;
+ if (n != EOF) break;
+ if (!rb_io_wait_writable(fileno(f)))
+ rb_sys_fail(fptr->path);
+ }
+ fptr->mode &= ~FMODE_WBUF;
+}
+
+int
+rb_io_wait_readable(f)
+ int f;
+{
+ fd_set rfds;
+
+ switch (errno) {
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ rb_thread_wait_fd(f);
+ return Qtrue;
+
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ FD_ZERO(&rfds);
+ FD_SET(f, &rfds);
+ rb_thread_select(f + 1, &rfds, NULL, NULL, NULL);
+ return Qtrue;
+
+ default:
+ return Qfalse;
+ }
+}
+
+int
+rb_io_wait_writable(f)
+ int f;
+{
+ fd_set wfds;
+
+ switch (errno) {
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ rb_thread_fd_writable(f);
+ return Qtrue;
+
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ FD_ZERO(&wfds);
+ FD_SET(f, &wfds);
+ rb_thread_select(f + 1, NULL, &wfds, NULL, NULL);
+ return Qtrue;
+
+ default:
+ return Qfalse;
+ }
+}
+
+#ifndef S_ISREG
+# define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
+#endif
+
+static int
+wsplit_p(OpenFile *fptr)
+{
+ FILE *f = GetWriteFile(fptr);
+ int r;
+ if (!(fptr->mode & FMODE_WSPLIT_INITIALIZED)) {
+ struct stat buf;
+ if (fstat(fileno(f), &buf) == 0 &&
+ !S_ISREG(buf.st_mode)
+#if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(O_NONBLOCK)
+ && (r = fcntl(fileno(f), F_GETFL)) != -1 &&
+ !(r & O_NONBLOCK)
+#endif
+ ) {
+ fptr->mode |= FMODE_WSPLIT;
+ }
+ fptr->mode |= FMODE_WSPLIT_INITIALIZED;
+ }
+ return fptr->mode & FMODE_WSPLIT;
+}
+
+/* writing functions */
+static long
+io_fwrite(str, fptr)
+ VALUE str;
+ OpenFile *fptr;
+{
+ long len, n, r, l, offset = 0;
+ FILE *f = GetWriteFile(fptr);
+
+ len = RSTRING(str)->len;
+ if ((n = len) <= 0) return n;
+ if (fptr->mode & FMODE_SYNC) {
+ io_fflush(f, fptr);
+ if (!rb_thread_fd_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ }
+ retry:
+ l = n;
+ if (PIPE_BUF < l &&
+ !rb_thread_critical &&
+ !rb_thread_alone() &&
+ wsplit_p(fptr)) {
+ l = PIPE_BUF;
+ }
+ TRAP_BEG;
+ r = write(fileno(f), RSTRING(str)->ptr+offset, l);
+ TRAP_END;
+ if (r == n) return len;
+ if (0 <= r) {
+ offset += r;
+ n -= r;
+ errno = EAGAIN;
+ }
+ if (rb_io_wait_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ if (offset < RSTRING(str)->len)
+ goto retry;
+ }
+ return -1L;
+ }
+#if defined(__human68k__) || defined(__vms)
+ do {
+ if (fputc(RSTRING(str)->ptr[offset++], f) == EOF) {
+ if (ferror(f)) return -1L;
+ break;
+ }
+ } while (--n > 0);
+#else
+ while (errno = 0, offset += (r = fwrite(RSTRING(str)->ptr+offset, 1, n, f)), (n -= r) > 0) {
+ if (ferror(f)
+#if defined __BORLANDC__
+ || errno
+#endif
+ ) {
+#ifdef __hpux
+ if (!errno) errno = EAGAIN;
+#elif defined(_WIN32) && !defined(__BORLANDC__)
+ /* workaround for MSVCRT's bug */
+ if (!errno) {
+ if (GetLastError() == ERROR_NO_DATA)
+ errno = EPIPE;
+ else
+ errno = EBADF;
+ }
+#endif
+ if (rb_io_wait_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ clearerr(f);
+ if (offset < RSTRING(str)->len)
+ continue;
+ }
+ return -1L;
+ }
+ }
+#endif
+ return len - n;
+}
+
+long
+rb_io_fwrite(ptr, len, f)
+ const char *ptr;
+ long len;
+ FILE *f;
+{
+ OpenFile of;
+
+ of.f = f;
+ of.f2 = NULL;
+ of.mode = FMODE_WRITABLE;
+ of.path = NULL;
+ return io_fwrite(rb_str_new(ptr, len), &of);
+}
+
+/*
+ * call-seq:
+ * ios.write(string) => integer
+ *
+ * Writes the given string to <em>ios</em>. The stream must be opened
+ * for writing. If the argument is not a string, it will be converted
+ * to a string using <code>to_s</code>. Returns the number of bytes
+ * written.
+ *
+ * count = $stdout.write( "This is a test\n" )
+ * puts "That was #{count} bytes of data"
+ *
+ * <em>produces:</em>
+ *
+ * This is a test
+ * That was 15 bytes of data
+ */
+
+static VALUE
+io_write(io, str)
+ VALUE io, str;
+{
+ OpenFile *fptr;
+ long n;
+
+ rb_secure(4);
+ if (TYPE(str) != T_STRING)
+ str = rb_obj_as_string(str);
+
+ if (TYPE(io) != T_FILE) {
+ /* port is not IO, call write method for it. */
+ return rb_funcall(io, id_write, 1, str);
+ }
+ if (RSTRING(str)->len == 0) return INT2FIX(0);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+
+ n = io_fwrite(str, fptr);
+ if (n == -1L) rb_sys_fail(fptr->path);
+ if (!(fptr->mode & FMODE_SYNC)) {
+ fptr->mode |= FMODE_WBUF;
+ }
+
+ return LONG2FIX(n);
+}
+
+VALUE
+rb_io_write(io, str)
+ VALUE io, str;
+{
+ return rb_funcall(io, id_write, 1, str);
+}
+
+/*
+ * call-seq:
+ * ios << obj => ios
+ *
+ * String Output---Writes <i>obj</i> to <em>ios</em>.
+ * <i>obj</i> will be converted to a string using
+ * <code>to_s</code>.
+ *
+ * $stdout << "Hello " << "world!\n"
+ *
+ * <em>produces:</em>
+ *
+ * Hello world!
+ */
+
+VALUE
+rb_io_addstr(io, str)
+ VALUE io, str;
+{
+ rb_io_write(io, str);
+ return io;
+}
+
+/*
+ * call-seq:
+ * ios.flush => ios
+ *
+ * Flushes any buffered data within <em>ios</em> to the underlying
+ * operating system (note that this is Ruby internal buffering only;
+ * the OS may buffer the data as well).
+ *
+ * $stdout.print "no newline"
+ * $stdout.flush
+ *
+ * <em>produces:</em>
+ *
+ * no newline
+ */
+
+static VALUE
+rb_io_flush(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ FILE *f;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+ f = GetWriteFile(fptr);
+
+ io_fflush(f, fptr);
+
+ return io;
+}
+
+/*
+ * call-seq:
+ * ios.pos => integer
+ * ios.tell => integer
+ *
+ * Returns the current offset (in bytes) of <em>ios</em>.
+ *
+ * f = File.new("testfile")
+ * f.pos #=> 0
+ * f.gets #=> "This is line one\n"
+ * f.pos #=> 17
+ */
+
+static VALUE
+rb_io_tell(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ off_t pos;
+
+ GetOpenFile(io, fptr);
+ pos = io_tell(fptr);
+ if (pos < 0 && errno) rb_sys_fail(fptr->path);
+ return OFFT2NUM(pos);
+}
+
+static VALUE
+rb_io_seek(io, offset, whence)
+ VALUE io, offset;
+ int whence;
+{
+ OpenFile *fptr;
+ off_t pos;
+
+ pos = NUM2OFFT(offset);
+ GetOpenFile(io, fptr);
+ pos = io_seek(fptr, pos, whence);
+ if (pos < 0 && errno) rb_sys_fail(fptr->path);
+ clearerr(fptr->f);
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * ios.seek(amount, whence=SEEK_SET) -> 0
+ *
+ * Seeks to a given offset <i>anInteger</i> in the stream according to
+ * the value of <i>whence</i>:
+ *
+ * IO::SEEK_CUR | Seeks to _amount_ plus current position
+ * --------------+----------------------------------------------------
+ * IO::SEEK_END | Seeks to _amount_ plus end of stream (you probably
+ * | want a negative value for _amount_)
+ * --------------+----------------------------------------------------
+ * IO::SEEK_SET | Seeks to the absolute location given by _amount_
+ *
+ * Example:
+ *
+ * f = File.new("testfile")
+ * f.seek(-13, IO::SEEK_END) #=> 0
+ * f.readline #=> "And so on...\n"
+ */
+
+static VALUE
+rb_io_seek_m(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE offset, ptrname;
+ int whence = SEEK_SET;
+
+ if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
+ whence = NUM2INT(ptrname);
+ }
+
+ return rb_io_seek(io, offset, whence);
+}
+
+/*
+ * call-seq:
+ * ios.pos = integer => integer
+ *
+ * Seeks to the given position (in bytes) in <em>ios</em>.
+ *
+ * f = File.new("testfile")
+ * f.pos = 17
+ * f.gets #=> "This is line two\n"
+ */
+
+static VALUE
+rb_io_set_pos(io, offset)
+ VALUE io, offset;
+{
+ OpenFile *fptr;
+ off_t pos;
+
+ pos = NUM2OFFT(offset);
+ GetOpenFile(io, fptr);
+ pos = io_seek(fptr, pos, SEEK_SET);
+ if (pos != 0) rb_sys_fail(fptr->path);
+ clearerr(fptr->f);
+
+ return OFFT2NUM(pos);
+}
+
+/*
+ * call-seq:
+ * ios.rewind => 0
+ *
+ * Positions <em>ios</em> to the beginning of input, resetting
+ * <code>lineno</code> to zero.
+ *
+ * f = File.new("testfile")
+ * f.readline #=> "This is line one\n"
+ * f.rewind #=> 0
+ * f.lineno #=> 0
+ * f.readline #=> "This is line one\n"
+ */
+
+static VALUE
+rb_io_rewind(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ if (io_seek(fptr, 0L, 0) != 0) rb_sys_fail(fptr->path);
+ clearerr(fptr->f);
+ if (io == current_file) {
+ gets_lineno -= fptr->lineno;
+ }
+ fptr->lineno = 0;
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * ios.eof => true or false
+ * ios.eof? => true or false
+ *
+ * Returns true if <em>ios</em> is at end of file that means
+ * there are no more data to read.
+ * The stream must be opened for reading or an <code>IOError</code> will be
+ * raised.
+ *
+ * f = File.new("testfile")
+ * dummy = f.readlines
+ * f.eof #=> true
+ *
+ * If <em>ios</em> is a stream such as pipe or socket, <code>IO#eof?</code>
+ * blocks until the other end sends some data or closes it.
+ *
+ * r, w = IO.pipe
+ * Thread.new { sleep 1; w.close }
+ * r.eof? #=> true after 1 second blocking
+ *
+ * r, w = IO.pipe
+ * Thread.new { sleep 1; w.puts "a" }
+ * r.eof? #=> false after 1 second blocking
+ *
+ * r, w = IO.pipe
+ * r.eof? # blocks forever
+ *
+ * Note that <code>IO#eof?</code> reads data to a input buffer.
+ * So <code>IO#sysread</code> doesn't work with <code>IO#eof?</code>.
+ */
+
+VALUE
+rb_io_eof(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ int ch;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+
+ if (feof(fptr->f)) return Qtrue;
+ if (READ_DATA_PENDING(fptr->f)) return Qfalse;
+ READ_CHECK(fptr->f);
+ clearerr(fptr->f);
+ TRAP_BEG;
+ ch = getc(fptr->f);
+ TRAP_END;
+
+ if (ch != EOF) {
+ ungetc(ch, fptr->f);
+ return Qfalse;
+ }
+ rb_io_check_closed(fptr);
+ clearerr(fptr->f);
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * ios.sync => true or false
+ *
+ * Returns the current ``sync mode'' of <em>ios</em>. When sync mode is
+ * true, all output is immediately flushed to the underlying operating
+ * system and is not buffered by Ruby internally. See also
+ * <code>IO#fsync</code>.
+ *
+ * f = File.new("testfile")
+ * f.sync #=> false
+ */
+
+static VALUE
+rb_io_sync(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ return (fptr->mode & FMODE_SYNC) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * ios.sync = boolean => boolean
+ *
+ * Sets the ``sync mode'' to <code>true</code> or <code>false</code>.
+ * When sync mode is true, all output is immediately flushed to the
+ * underlying operating system and is not buffered internally. Returns
+ * the new state. See also <code>IO#fsync</code>.
+ *
+ * f = File.new("testfile")
+ * f.sync = true
+ *
+ * <em>(produces no output)</em>
+ */
+
+static VALUE
+rb_io_set_sync(io, mode)
+ VALUE io, mode;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ if (RTEST(mode)) {
+ fptr->mode |= FMODE_SYNC;
+ }
+ else {
+ fptr->mode &= ~FMODE_SYNC;
+ }
+ return mode;
+}
+
+/*
+ * call-seq:
+ * ios.fsync => 0 or nil
+ *
+ * Immediately writes all buffered data in <em>ios</em> to disk.
+ * Returns <code>nil</code> if the underlying operating system does not
+ * support <em>fsync(2)</em>. Note that <code>fsync</code> differs from
+ * using <code>IO#sync=</code>. The latter ensures that data is flushed
+ * from Ruby's buffers, but doesn't not guarantee that the underlying
+ * operating system actually writes it to disk.
+ */
+
+static VALUE
+rb_io_fsync(io)
+ VALUE io;
+{
+#ifdef HAVE_FSYNC
+ OpenFile *fptr;
+ FILE *f;
+
+ GetOpenFile(io, fptr);
+ f = GetWriteFile(fptr);
+
+ io_fflush(f, fptr);
+ if (fsync(fileno(f)) < 0)
+ rb_sys_fail(fptr->path);
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * ios.fileno => fixnum
+ * ios.to_i => fixnum
+ *
+ * Returns an integer representing the numeric file descriptor for
+ * <em>ios</em>.
+ *
+ * $stdin.fileno #=> 0
+ * $stdout.fileno #=> 1
+ */
+
+static VALUE
+rb_io_fileno(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ int fd;
+
+ GetOpenFile(io, fptr);
+ fd = fileno(fptr->f);
+ return INT2FIX(fd);
+}
+
+/*
+ * call-seq:
+ * ios.pid => fixnum
+ *
+ * Returns the process ID of a child process associated with
+ * <em>ios</em>. This will be set by <code>IO::popen</code>.
+ *
+ * pipe = IO.popen("-")
+ * if pipe
+ * $stderr.puts "In parent, child pid is #{pipe.pid}"
+ * else
+ * $stderr.puts "In child, pid is #{$$}"
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * In child, pid is 26209
+ * In parent, child pid is 26209
+ */
+
+static VALUE
+rb_io_pid(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ if (!fptr->pid)
+ return Qnil;
+ return INT2FIX(fptr->pid);
+}
+
+/*
+ * call-seq:
+ * ios.inspect => string
+ *
+ * Return a string describing this IO object.
+ */
+
+static VALUE
+rb_io_inspect(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ char *buf, *cname, *st = "";
+ long len;
+
+ fptr = RFILE(rb_io_taint_check(obj))->fptr;
+ if (!fptr || !fptr->path) return rb_any_to_s(obj);
+ cname = rb_obj_classname(obj);
+ len = strlen(cname) + strlen(fptr->path) + 5;
+ if (!(fptr->f || fptr->f2)) {
+ st = " (closed)";
+ len += 9;
+ }
+ buf = ALLOCA_N(char, len);
+ snprintf(buf, len, "#<%s:%s%s>", cname, fptr->path, st);
+ return rb_str_new2(buf);
+}
+
+/*
+ * call-seq:
+ * ios.to_io -> ios
+ *
+ * Returns <em>ios</em>.
+ */
+
+static VALUE
+rb_io_to_io(io)
+ VALUE io;
+{
+ return io;
+}
+
+/* reading functions */
+static long
+read_buffered_data(ptr, len, f)
+ char *ptr;
+ long len;
+ FILE *f;
+{
+ long n;
+
+#ifdef READ_DATA_PENDING_COUNT
+ n = READ_DATA_PENDING_COUNT(f);
+ if (n <= 0) return 0;
+ if (n > len) n = len;
+ return fread(ptr, 1, n, f);
+#else
+ int c;
+
+ for (n = 0; n < len && READ_DATA_PENDING(f) && (c = getc(f)) != EOF; ++n) {
+ *ptr++ = c;
+ }
+ return n;
+#endif
+}
+
+static long
+io_fread(ptr, len, fptr)
+ char *ptr;
+ long len;
+ OpenFile *fptr;
+{
+ long n = len;
+ int c;
+ int saved_errno;
+
+ while (n > 0) {
+ c = read_buffered_data(ptr, n, fptr->f);
+ if (c < 0) goto eof;
+ if (c > 0) {
+ ptr += c;
+ if ((n -= c) <= 0) break;
+ }
+ rb_thread_wait_fd(fileno(fptr->f));
+ rb_io_check_closed(fptr);
+ clearerr(fptr->f);
+ TRAP_BEG;
+ c = getc(fptr->f);
+ TRAP_END;
+ if (c == EOF) {
+ eof:
+ if (ferror(fptr->f)) {
+ switch (errno) {
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ clearerr(fptr->f);
+ continue;
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ if (len > n) {
+ clearerr(fptr->f);
+ }
+ saved_errno = errno;
+ rb_warning("nonblocking IO#read is obsolete; use IO#readpartial or IO#sysread");
+ errno = saved_errno;
+ }
+ if (len == n) return 0;
+ }
+ break;
+ }
+ *ptr++ = c;
+ n--;
+ }
+ return len - n;
+}
+
+long
+rb_io_fread(ptr, len, f)
+ char *ptr;
+ long len;
+ FILE *f;
+{
+ OpenFile of;
+
+ of.f = f;
+ of.f2 = NULL;
+ return io_fread(ptr, len, &of);
+}
+
+#define SMALLBUF 100
+
+static long
+remain_size(fptr)
+ OpenFile *fptr;
+{
+ struct stat st;
+ off_t siz = BUFSIZ;
+ off_t pos;
+
+ if (feof(fptr->f)) return 0;
+ if (fstat(fileno(fptr->f), &st) == 0 && S_ISREG(st.st_mode)
+#ifdef __BEOS__
+ && (st.st_dev > 3)
+#endif
+ )
+ {
+ pos = io_tell(fptr);
+ if (st.st_size >= pos && pos >= 0) {
+ siz = st.st_size - pos + 1;
+ if (siz > LONG_MAX) {
+ rb_raise(rb_eIOError, "file too big for single read");
+ }
+ }
+ }
+ return (long)siz;
+}
+
+static VALUE
+read_all(fptr, siz, str)
+ OpenFile *fptr;
+ long siz;
+ VALUE str;
+{
+ long bytes = 0;
+ long n;
+
+ if (siz == 0) siz = BUFSIZ;
+ if (NIL_P(str)) {
+ str = rb_str_new(0, siz);
+ }
+ else {
+ rb_str_resize(str, siz);
+ }
+ for (;;) {
+ rb_str_locktmp(str);
+ READ_CHECK(fptr->f);
+ n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr);
+ rb_str_unlocktmp(str);
+ if (n == 0 && bytes == 0) {
+ if (!fptr->f) break;
+ if (feof(fptr->f)) break;
+ if (!ferror(fptr->f)) break;
+ rb_sys_fail(fptr->path);
+ }
+ bytes += n;
+ if (bytes < siz) break;
+ siz += BUFSIZ;
+ rb_str_resize(str, siz);
+ }
+ if (bytes != siz) rb_str_resize(str, bytes);
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+void rb_io_set_nonblock(OpenFile *fptr)
+{
+ int flags;
+#ifdef F_GETFL
+ flags = fcntl(fileno(fptr->f), F_GETFL);
+ if (flags == -1) {
+ rb_sys_fail(fptr->path);
+ }
+#else
+ flags = 0;
+#endif
+ if ((flags & O_NONBLOCK) == 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(fileno(fptr->f), F_SETFL, flags) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ }
+ if (fptr->f2) {
+#ifdef F_GETFL
+ flags = fcntl(fileno(fptr->f2), F_GETFL);
+ if (flags == -1) {
+ rb_sys_fail(fptr->path);
+ }
+#else
+ flags = 0;
+#endif
+ if ((flags & O_NONBLOCK) == 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(fileno(fptr->f2), F_SETFL, flags) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ }
+ }
+}
+
+static VALUE
+io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock)
+{
+ OpenFile *fptr;
+ VALUE length, str;
+ long n, len;
+
+ rb_scan_args(argc, argv, "11", &length, &str);
+
+ if ((len = NUM2LONG(length)) < 0) {
+ rb_raise(rb_eArgError, "negative length %ld given", len);
+ }
+
+ if (NIL_P(str)) {
+ str = rb_str_new(0, len);
+ }
+ else {
+ StringValue(str);
+ rb_str_modify(str);
+ rb_str_resize(str, len);
+ }
+ OBJ_TAINT(str);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+
+ if (len == 0)
+ return str;
+
+ if (!nonblock) {
+ READ_CHECK(fptr->f);
+ }
+ if (RSTRING(str)->len != len) {
+ modified:
+ rb_raise(rb_eRuntimeError, "buffer string modified");
+ }
+ n = read_buffered_data(RSTRING(str)->ptr, len, fptr->f);
+ if (n <= 0) {
+ again:
+ if (RSTRING(str)->len != len) goto modified;
+ if (nonblock) {
+ rb_io_set_nonblock(fptr);
+ n = read(fileno(fptr->f), RSTRING(str)->ptr, len);
+ }
+ else {
+ TRAP_BEG;
+ n = read(fileno(fptr->f), RSTRING(str)->ptr, len);
+ TRAP_END;
+ }
+ if (n < 0) {
+ if (!nonblock && rb_io_wait_readable(fileno(fptr->f)))
+ goto again;
+ rb_sys_fail(fptr->path);
+ }
+ if (fptr->f) /* update pos in FILE structure [ruby-core:21561] */
+ fflush(fptr->f);
+ }
+ rb_str_resize(str, n);
+
+ if (n == 0)
+ return Qnil;
+ else
+ return str;
+}
+
+/*
+ * call-seq:
+ * ios.readpartial(maxlen) => string
+ * ios.readpartial(maxlen, outbuf) => outbuf
+ *
+ * Reads at most <i>maxlen</i> bytes from the I/O stream.
+ * It blocks only if <em>ios</em> has no data immediately available.
+ * It doesn't block if some data available.
+ * If the optional <i>outbuf</i> argument is present,
+ * it must reference a String, which will receive the data.
+ * It raises <code>EOFError</code> on end of file.
+ *
+ * readpartial is designed for streams such as pipe, socket, tty, etc.
+ * It blocks only when no data immediately available.
+ * This means that it blocks only when following all conditions hold.
+ * * the buffer in the IO object is empty.
+ * * the content of the stream is empty.
+ * * the stream is not reached to EOF.
+ *
+ * When readpartial blocks, it waits data or EOF on the stream.
+ * If some data is reached, readpartial returns with the data.
+ * If EOF is reached, readpartial raises EOFError.
+ *
+ * When readpartial doesn't blocks, it returns or raises immediately.
+ * If the buffer is not empty, it returns the data in the buffer.
+ * Otherwise if the stream has some content,
+ * it returns the data in the stream.
+ * Otherwise if the stream is reached to EOF, it raises EOFError.
+ *
+ * r, w = IO.pipe # buffer pipe content
+ * w << "abc" # "" "abc".
+ * r.readpartial(4096) #=> "abc" "" ""
+ * r.readpartial(4096) # blocks because buffer and pipe is empty.
+ *
+ * r, w = IO.pipe # buffer pipe content
+ * w << "abc" # "" "abc"
+ * w.close # "" "abc" EOF
+ * r.readpartial(4096) #=> "abc" "" EOF
+ * r.readpartial(4096) # raises EOFError
+ *
+ * r, w = IO.pipe # buffer pipe content
+ * w << "abc\ndef\n" # "" "abc\ndef\n"
+ * r.gets #=> "abc\n" "def\n" ""
+ * w << "ghi\n" # "def\n" "ghi\n"
+ * r.readpartial(4096) #=> "def\n" "" "ghi\n"
+ * r.readpartial(4096) #=> "ghi\n" "" ""
+ *
+ * Note that readpartial behaves similar to sysread.
+ * The differences are:
+ * * If the buffer is not empty, read from the buffer instead of "sysread for buffered IO (IOError)".
+ * * It doesn't cause Errno::EAGAIN and Errno::EINTR. When readpartial meets EAGAIN and EINTR by read system call, readpartial retry the system call.
+ *
+ * The later means that readpartial is nonblocking-flag insensitive.
+ * It blocks on the situation IO#sysread causes Errno::EAGAIN as if the fd is blocking mode.
+ *
+ */
+
+static VALUE
+io_readpartial(int argc, VALUE *argv, VALUE io)
+{
+ VALUE ret;
+
+ ret = io_getpartial(argc, argv, io, 0);
+ if (NIL_P(ret))
+ rb_eof_error();
+ else
+ return ret;
+}
+
+/*
+ * call-seq:
+ * ios.read_nonblock(maxlen) => string
+ * ios.read_nonblock(maxlen, outbuf) => outbuf
+ *
+ * Reads at most <i>maxlen</i> bytes from <em>ios</em> using
+ * read(2) system call after O_NONBLOCK is set for
+ * the underlying file descriptor.
+ *
+ * If the optional <i>outbuf</i> argument is present,
+ * it must reference a String, which will receive the data.
+ *
+ * read_nonblock just calls read(2).
+ * It causes all errors read(2) causes: EAGAIN, EINTR, etc.
+ * The caller should care such errors.
+ *
+ * read_nonblock causes EOFError on EOF.
+ *
+ * If the read buffer is not empty,
+ * read_nonblock reads from the buffer like readpartial.
+ * In this case, read(2) is not called.
+ *
+ */
+
+static VALUE
+io_read_nonblock(int argc, VALUE *argv, VALUE io)
+{
+ VALUE ret;
+
+ ret = io_getpartial(argc, argv, io, 1);
+ if (NIL_P(ret))
+ rb_eof_error();
+ else
+ return ret;
+}
+
+/*
+ * call-seq:
+ * ios.write_nonblock(string) => integer
+ *
+ * Writes the given string to <em>ios</em> using
+ * write(2) system call after O_NONBLOCK is set for
+ * the underlying file descriptor.
+ *
+ * write_nonblock just calls write(2).
+ * It causes all errors write(2) causes: EAGAIN, EINTR, etc.
+ * The result may also be smaller than string.length (partial write).
+ * The caller should care such errors and partial write.
+ *
+ */
+
+static VALUE
+rb_io_write_nonblock(VALUE io, VALUE str)
+{
+ OpenFile *fptr;
+ FILE *f;
+ long n;
+
+ rb_secure(4);
+ if (TYPE(str) != T_STRING)
+ str = rb_obj_as_string(str);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+
+ f = GetWriteFile(fptr);
+
+ rb_io_set_nonblock(fptr);
+ n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len);
+
+ if (n == -1) rb_sys_fail(fptr->path);
+
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * ios.read([length [, buffer]]) => string, buffer, or nil
+ *
+ * Reads at most <i>length</i> bytes from the I/O stream, or to the
+ * end of file if <i>length</i> is omitted or is <code>nil</code>.
+ * <i>length</i> must be a non-negative integer or nil.
+ * If the optional <i>buffer</i> argument is present, it must reference
+ * a String, which will receive the data.
+ *
+ * At end of file, it returns <code>nil</code> or <code>""</code>
+ * depend on <i>length</i>.
+ * <code><i>ios</i>.read()</code> and
+ * <code><i>ios</i>.read(nil)</code> returns <code>""</code>.
+ * <code><i>ios</i>.read(<i>positive-integer</i>)</code> returns nil.
+ *
+ * f = File.new("testfile")
+ * f.read(16) #=> "This is line one"
+ */
+
+static VALUE
+io_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ OpenFile *fptr;
+ long n, len;
+ VALUE length, str;
+
+ rb_scan_args(argc, argv, "02", &length, &str);
+
+ if (NIL_P(length)) {
+ if (!NIL_P(str)) StringValue(str);
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ return read_all(fptr, remain_size(fptr), str);
+ }
+ len = NUM2LONG(length);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative length %ld given", len);
+ }
+
+ if (NIL_P(str)) {
+ str = rb_tainted_str_new(0, len);
+ }
+ else {
+ StringValue(str);
+ rb_str_modify(str);
+ rb_str_resize(str,len);
+ }
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ if (feof(fptr->f)) return Qnil;
+ if (len == 0) return str;
+
+ rb_str_locktmp(str);
+ READ_CHECK(fptr->f);
+ if (RSTRING(str)->len != len) {
+ rb_raise(rb_eRuntimeError, "buffer string modified");
+ }
+ n = io_fread(RSTRING(str)->ptr, len, fptr);
+ rb_str_unlocktmp(str);
+ if (n == 0) {
+ if (!fptr->f) return Qnil;
+ if (feof(fptr->f)) {
+ rb_str_resize(str, 0);
+ return Qnil;
+ }
+ if (len > 0) rb_sys_fail(fptr->path);
+ }
+ rb_str_resize(str, n);
+ RSTRING(str)->len = n;
+ RSTRING(str)->ptr[n] = '\0';
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+static int
+appendline(fptr, delim, strp)
+ OpenFile *fptr;
+ int delim;
+ VALUE *strp;
+{
+ FILE *f = fptr->f;
+ VALUE str = *strp;
+ int c = EOF;
+#ifndef READ_DATA_PENDING_PTR
+ char buf[8192];
+ char *bp = buf, *bpe = buf + sizeof buf - 3;
+ int update = Qfalse;
+#endif
+
+ do {
+#ifdef READ_DATA_PENDING_PTR
+ long pending = READ_DATA_PENDING_COUNT(f);
+ if (pending > 0) {
+ const char *p = READ_DATA_PENDING_PTR(f);
+ const char *e = memchr(p, delim, pending);
+ long last = 0, len = (c != EOF);
+ if (e) pending = e - p + 1;
+ len += pending;
+ if (!NIL_P(str)) {
+ last = RSTRING(str)->len;
+ rb_str_resize(str, last + len);
+ }
+ else {
+ *strp = str = rb_str_buf_new(len);
+ RSTRING(str)->len = len;
+ RSTRING(str)->ptr[len] = '\0';
+ }
+ if (c != EOF) {
+ RSTRING(str)->ptr[last++] = c;
+ }
+ fread(RSTRING(str)->ptr + last, 1, pending, f); /* must not fail */
+ if (e) return delim;
+ }
+ else if (c != EOF) {
+ if (!NIL_P(str)) {
+ char ch = c;
+ rb_str_buf_cat(str, &ch, 1);
+ }
+ else {
+ *strp = str = rb_str_buf_new(1);
+ RSTRING(str)->ptr[RSTRING(str)->len++] = c;
+ }
+ }
+ rb_thread_wait_fd(fileno(f));
+ rb_io_check_closed(fptr);
+#else
+ READ_CHECK(f);
+#endif
+ clearerr(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+ if (c == EOF) {
+ if (ferror(f)) {
+ clearerr(f);
+ if (!rb_io_wait_readable(fileno(f)))
+ rb_sys_fail(fptr->path);
+ continue;
+ }
+#ifdef READ_DATA_PENDING_PTR
+ return c;
+#endif
+ }
+#ifndef READ_DATA_PENDING_PTR
+ if (c == EOF || (*bp++ = c) == delim || bp == bpe) {
+ int cnt = bp - buf;
+
+ if (cnt > 0) {
+ if (!NIL_P(str))
+ rb_str_cat(str, buf, cnt);
+ else
+ *strp = str = rb_str_new(buf, cnt);
+ }
+ if (c == EOF) {
+ if (update)
+ return (int)RSTRING(str)->ptr[RSTRING(str)->len-1];
+ return c;
+ }
+ bp = buf;
+ }
+ update = Qtrue;
+#endif
+ } while (c != delim);
+
+#ifdef READ_DATA_PENDING_PTR
+ {
+ char ch = c;
+ if (!NIL_P(str)) {
+ rb_str_cat(str, &ch, 1);
+ }
+ else {
+ *strp = str = rb_str_new(&ch, 1);
+ }
+ }
+#endif
+
+ return c;
+}
+
+static inline int
+swallow(fptr, term)
+ OpenFile *fptr;
+ int term;
+{
+ FILE *f = fptr->f;
+ int c;
+
+ do {
+#ifdef READ_DATA_PENDING_PTR
+ long cnt;
+ while ((cnt = READ_DATA_PENDING_COUNT(f)) > 0) {
+ char buf[1024];
+ const char *p = READ_DATA_PENDING_PTR(f);
+ int i;
+ if (cnt > sizeof buf) cnt = sizeof buf;
+ if (*p != term) return Qtrue;
+ i = cnt;
+ while (--i && *++p == term);
+ if (!fread(buf, 1, cnt - i, f)) /* must not fail */
+ rb_sys_fail(fptr->path);
+ }
+ rb_thread_wait_fd(fileno(f));
+ rb_io_check_closed(fptr);
+#else
+ READ_CHECK(f);
+#endif
+ clearerr(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+ if (c != term) {
+ ungetc(c, f);
+ return Qtrue;
+ }
+ } while (c != EOF);
+ return Qfalse;
+}
+
+static VALUE
+rb_io_getline_fast(fptr, delim)
+ OpenFile *fptr;
+ unsigned char delim;
+{
+ VALUE str = Qnil;
+ int c;
+
+ while ((c = appendline(fptr, delim, &str)) != EOF && c != delim);
+
+ if (!NIL_P(str)) {
+ fptr->lineno++;
+ lineno = INT2FIX(fptr->lineno);
+ OBJ_TAINT(str);
+ }
+
+ return str;
+}
+
+static int
+rscheck(rsptr, rslen, rs)
+ char *rsptr;
+ long rslen;
+ VALUE rs;
+{
+ if (RSTRING(rs)->ptr != rsptr && RSTRING(rs)->len != rslen)
+ rb_raise(rb_eRuntimeError, "rs modified");
+ return 1;
+}
+
+static VALUE rb_io_getline(VALUE rs, VALUE io);
+
+static VALUE
+rb_io_getline(rs, io)
+ VALUE rs, io;
+{
+ VALUE str = Qnil;
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ if (NIL_P(rs)) {
+ str = read_all(fptr, 0, Qnil);
+ if (RSTRING(str)->len == 0) return Qnil;
+ }
+ else if (rs == rb_default_rs) {
+ return rb_io_getline_fast(fptr, '\n');
+ }
+ else {
+ int c, newline;
+ char *rsptr;
+ long rslen;
+ int rspara = 0;
+
+ rslen = RSTRING(rs)->len;
+ if (rslen == 0) {
+ rsptr = "\n\n";
+ rslen = 2;
+ rspara = 1;
+ swallow(fptr, '\n');
+ }
+ else if (rslen == 1) {
+ return rb_io_getline_fast(fptr, (unsigned char)RSTRING(rs)->ptr[0]);
+ }
+ else {
+ rsptr = RSTRING(rs)->ptr;
+ }
+ newline = rsptr[rslen - 1];
+
+ while ((c = appendline(fptr, newline, &str)) != EOF &&
+ (c != newline || RSTRING(str)->len < rslen ||
+ ((rspara || rscheck(rsptr,rslen,rs)) && 0) ||
+ memcmp(RSTRING(str)->ptr+RSTRING(str)->len-rslen,rsptr,rslen)));
+
+ if (rspara) {
+ if (c != EOF) {
+ swallow(fptr, '\n');
+ }
+ }
+ }
+
+ if (!NIL_P(str)) {
+ fptr->lineno++;
+ lineno = INT2FIX(fptr->lineno);
+ OBJ_TAINT(str);
+ }
+
+ return str;
+}
+
+VALUE
+rb_io_gets(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ return rb_io_getline_fast(fptr, '\n');
+}
+
+/*
+ * call-seq:
+ * ios.gets(sep_string=$/) => string or nil
+ *
+ * Reads the next ``line'' from the I/O stream; lines are separated by
+ * <i>sep_string</i>. A separator of <code>nil</code> reads the entire
+ * contents, and a zero-length separator reads the input a paragraph at
+ * a time (two successive newlines in the input separate paragraphs).
+ * The stream must be opened for reading or an <code>IOError</code>
+ * will be raised. The line read in will be returned and also assigned
+ * to <code>$_</code>. Returns <code>nil</code> if called at end of
+ * file.
+ *
+ * File.new("testfile").gets #=> "This is line one\n"
+ * $_ #=> "This is line one\n"
+ */
+
+static VALUE
+rb_io_gets_m(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE rs, str;
+
+ if (argc == 0) {
+ rs = rb_rs;
+ }
+ else {
+ rb_scan_args(argc, argv, "1", &rs);
+ if (!NIL_P(rs)) StringValue(rs);
+ }
+ str = rb_io_getline(rs, io);
+ rb_lastline_set(str);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * ios.lineno => integer
+ *
+ * Returns the current line number in <em>ios</em>. The stream must be
+ * opened for reading. <code>lineno</code> counts the number of times
+ * <code>gets</code> is called, rather than the number of newlines
+ * encountered. The two values will differ if <code>gets</code> is
+ * called with a separator other than newline. See also the
+ * <code>$.</code> variable.
+ *
+ * f = File.new("testfile")
+ * f.lineno #=> 0
+ * f.gets #=> "This is line one\n"
+ * f.lineno #=> 1
+ * f.gets #=> "This is line two\n"
+ * f.lineno #=> 2
+ */
+
+static VALUE
+rb_io_lineno(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ return INT2NUM(fptr->lineno);
+}
+
+/*
+ * call-seq:
+ * ios.lineno = integer => integer
+ *
+ * Manually sets the current line number to the given value.
+ * <code>$.</code> is updated only on the next read.
+ *
+ * f = File.new("testfile")
+ * f.gets #=> "This is line one\n"
+ * $. #=> 1
+ * f.lineno = 1000
+ * f.lineno #=> 1000
+ * $. # lineno of last read #=> 1
+ * f.gets #=> "This is line two\n"
+ * $. # lineno of last read #=> 1001
+ */
+
+static VALUE
+rb_io_set_lineno(io, lineno)
+ VALUE io, lineno;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ fptr->lineno = NUM2INT(lineno);
+ return lineno;
+}
+
+static void
+lineno_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ gets_lineno = NUM2INT(val);
+ *var = INT2FIX(gets_lineno);
+}
+
+static VALUE
+argf_set_lineno(argf, val)
+ VALUE argf, val;
+{
+ gets_lineno = NUM2INT(val);
+ lineno = INT2FIX(gets_lineno);
+ return Qnil;
+}
+
+static VALUE
+argf_lineno()
+{
+ return lineno;
+}
+
+/*
+ * call-seq:
+ * ios.readline(sep_string=$/) => string
+ *
+ * Reads a line as with <code>IO#gets</code>, but raises an
+ * <code>EOFError</code> on end of file.
+ */
+
+static VALUE
+rb_io_readline(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE line = rb_io_gets_m(argc, argv, io);
+
+ if (NIL_P(line)) {
+ rb_eof_error();
+ }
+ return line;
+}
+
+/*
+ * call-seq:
+ * ios.readlines(sep_string=$/) => array
+ *
+ * Reads all of the lines in <em>ios</em>, and returns them in
+ * <i>anArray</i>. Lines are separated by the optional
+ * <i>sep_string</i>. If <i>sep_string</i> is <code>nil</code>, the
+ * rest of the stream is returned as a single record.
+ * The stream must be opened for reading or an
+ * <code>IOError</code> will be raised.
+ *
+ * f = File.new("testfile")
+ * f.readlines[0] #=> "This is line one\n"
+ */
+
+static VALUE
+rb_io_readlines(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE line, ary;
+ VALUE rs;
+
+ if (argc == 0) {
+ rs = rb_rs;
+ }
+ else {
+ rb_scan_args(argc, argv, "1", &rs);
+ if (!NIL_P(rs)) StringValue(rs);
+ }
+ ary = rb_ary_new();
+ while (!NIL_P(line = rb_io_getline(rs, io))) {
+ rb_ary_push(ary, line);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * ios.each(sep_string=$/) {|line| block } => ios
+ * ios.each_line(sep_string=$/) {|line| block } => ios
+ *
+ * Executes the block for every line in <em>ios</em>, where lines are
+ * separated by <i>sep_string</i>. <em>ios</em> must be opened for
+ * reading or an <code>IOError</code> will be raised.
+ *
+ * f = File.new("testfile")
+ * f.each {|line| puts "#{f.lineno}: #{line}" }
+ *
+ * <em>produces:</em>
+ *
+ * 1: This is line one
+ * 2: This is line two
+ * 3: This is line three
+ * 4: And so on...
+ */
+
+static VALUE
+rb_io_each_line(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE str;
+ VALUE rs;
+
+ if (argc == 0) {
+ rs = rb_rs;
+ }
+ else {
+ rb_scan_args(argc, argv, "1", &rs);
+ if (!NIL_P(rs)) StringValue(rs);
+ }
+ while (!NIL_P(str = rb_io_getline(rs, io))) {
+ rb_yield(str);
+ }
+ return io;
+}
+
+/*
+ * call-seq:
+ * ios.each_byte {|byte| block } => nil
+ *
+ * Calls the given block once for each byte (0..255) in <em>ios</em>,
+ * passing the byte as an argument. The stream must be opened for
+ * reading or an <code>IOError</code> will be raised.
+ *
+ * f = File.new("testfile")
+ * checksum = 0
+ * f.each_byte {|x| checksum ^= x } #=> #<File:testfile>
+ * checksum #=> 12
+ */
+
+static VALUE
+rb_io_each_byte(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ FILE *f;
+ int c;
+
+ GetOpenFile(io, fptr);
+
+ for (;;) {
+ rb_io_check_readable(fptr);
+ f = fptr->f;
+ READ_CHECK(f);
+ clearerr(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+ if (c == EOF) {
+ if (ferror(f)) {
+ clearerr(f);
+ if (!rb_io_wait_readable(fileno(f)))
+ rb_sys_fail(fptr->path);
+ continue;
+ }
+ break;
+ }
+ rb_yield(INT2FIX(c & 0xff));
+ }
+ if (ferror(f)) rb_sys_fail(fptr->path);
+ return io;
+}
+
+/*
+ * call-seq:
+ * ios.getc => fixnum or nil
+ *
+ * Gets the next 8-bit byte (0..255) from <em>ios</em>. Returns
+ * <code>nil</code> if called at end of file.
+ *
+ * f = File.new("testfile")
+ * f.getc #=> 84
+ * f.getc #=> 104
+ */
+
+VALUE
+rb_io_getc(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ FILE *f;
+ int c;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ f = fptr->f;
+
+ retry:
+ READ_CHECK(f);
+ clearerr(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+
+ if (c == EOF) {
+ if (ferror(f)) {
+ clearerr(f);
+ if (!rb_io_wait_readable(fileno(f)))
+ rb_sys_fail(fptr->path);
+ goto retry;
+ }
+ return Qnil;
+ }
+ return INT2FIX(c & 0xff);
+}
+
+int
+rb_getc(f)
+ FILE *f;
+{
+ int c;
+
+ if (!READ_DATA_PENDING(f)) {
+ rb_thread_wait_fd(fileno(f));
+ }
+ clearerr(f);
+ TRAP_BEG;
+ c = getc(f);
+ TRAP_END;
+
+ return c;
+}
+
+/*
+ * call-seq:
+ * ios.readchar => fixnum
+ *
+ * Reads a character as with <code>IO#getc</code>, but raises an
+ * <code>EOFError</code> on end of file.
+ */
+
+static VALUE
+rb_io_readchar(io)
+ VALUE io;
+{
+ VALUE c = rb_io_getc(io);
+
+ if (NIL_P(c)) {
+ rb_eof_error();
+ }
+ return c;
+}
+
+/*
+ * call-seq:
+ * ios.ungetc(integer) => nil
+ *
+ * Pushes back one character (passed as a parameter) onto <em>ios</em>,
+ * such that a subsequent buffered read will return it. Only one character
+ * may be pushed back before a subsequent read operation (that is,
+ * you will be able to read only the last of several characters that have been pushed
+ * back). Has no effect with unbuffered reads (such as <code>IO#sysread</code>).
+ *
+ * f = File.new("testfile") #=> #<File:testfile>
+ * c = f.getc #=> 84
+ * f.ungetc(c) #=> nil
+ * f.getc #=> 84
+ */
+
+VALUE
+rb_io_ungetc(io, c)
+ VALUE io, c;
+{
+ OpenFile *fptr;
+ int cc = NUM2INT(c);
+
+ GetOpenFile(io, fptr);
+ if (!(fptr->mode & FMODE_RBUF))
+ rb_raise(rb_eIOError, "unread stream");
+ rb_io_check_readable(fptr);
+
+ if (ungetc(cc, fptr->f) == EOF && cc != EOF) {
+ rb_raise(rb_eIOError, "ungetc failed");
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.isatty => true or false
+ * ios.tty? => true or false
+ *
+ * Returns <code>true</code> if <em>ios</em> is associated with a
+ * terminal device (tty), <code>false</code> otherwise.
+ *
+ * File.new("testfile").isatty #=> false
+ * File.new("/dev/tty").isatty #=> true
+ */
+
+static VALUE
+rb_io_isatty(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ if (isatty(fileno(fptr->f)) == 0)
+ return Qfalse;
+ return Qtrue;
+}
+
+static void
+fptr_finalize(fptr, noraise)
+ OpenFile *fptr;
+ int noraise;
+{
+ int n1 = 0, n2 = 0, f1, f2 = -1;
+
+ errno = 0;
+ if (fptr->f2) {
+ f2 = fileno(fptr->f2);
+ while (n2 = 0, fflush(fptr->f2) < 0) {
+ n2 = errno;
+ if (!rb_io_wait_writable(f2)) {
+ break;
+ }
+ if (!fptr->f2) break;
+ }
+ if (fclose(fptr->f2) < 0 && n2 == 0) {
+ n2 = errno;
+ }
+ fptr->f2 = 0;
+ }
+ if (fptr->f) {
+ f1 = fileno(fptr->f);
+ if ((f2 == -1) && (fptr->mode & FMODE_WBUF)) {
+ while (n1 = 0, fflush(fptr->f) < 0) {
+ n1 = errno;
+ if (!rb_io_wait_writable(f1)) break;
+ if (!fptr->f) break;
+ }
+ }
+ if (fclose(fptr->f) < 0 && n1 == 0) {
+ n1 = errno;
+ }
+ fptr->f = 0;
+ if (n1 == EBADF && f1 == f2) {
+ n1 = 0;
+ }
+ }
+ if (!noraise && (n1 || n2)) {
+ errno = (n1 ? n1 : n2);
+ rb_sys_fail(fptr->path);
+ }
+}
+
+static void
+rb_io_fptr_cleanup(fptr, noraise)
+ OpenFile *fptr;
+ int noraise;
+{
+ if (fptr->finalize) {
+ (*fptr->finalize)(fptr, noraise);
+ }
+ else {
+ fptr_finalize(fptr, noraise);
+ }
+}
+
+void
+rb_io_fptr_finalize(fptr)
+ OpenFile *fptr;
+{
+ if (!fptr) return;
+ if (fptr->path) {
+ free(fptr->path);
+ }
+ if (!fptr->f && !fptr->f2) return;
+ if (fileno(fptr->f) < 3) return;
+
+ rb_io_fptr_cleanup(fptr, Qtrue);
+}
+
+VALUE
+rb_io_close(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ int fd, fd2;
+
+ fptr = RFILE(io)->fptr;
+ if (!fptr) return Qnil;
+ if (fptr->f2) {
+ fd2 = fileno(fptr->f2);
+ }
+ else {
+ if (!fptr->f) return Qnil;
+ fd2 = -1;
+ }
+
+ fd = fileno(fptr->f);
+ rb_io_fptr_cleanup(fptr, Qfalse);
+ rb_thread_fd_close(fd);
+ if (fd2 >= 0) rb_thread_fd_close(fd2);
+
+ if (fptr->pid) {
+ rb_syswait(fptr->pid);
+ fptr->pid = 0;
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.close => nil
+ *
+ * Closes <em>ios</em> and flushes any pending writes to the operating
+ * system. The stream is unavailable for any further data operations;
+ * an <code>IOError</code> is raised if such an attempt is made. I/O
+ * streams are automatically closed when they are claimed by the
+ * garbage collector.
+ *
+ * If <em>ios</em> is opened by <code>IO.popen</code>,
+ * <code>close</code> sets <code>$?</code>.
+ */
+
+static VALUE
+rb_io_close_m(io)
+ VALUE io;
+{
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't close");
+ }
+ rb_io_check_closed(RFILE(io)->fptr);
+ rb_io_close(io);
+ return Qnil;
+}
+
+static VALUE
+io_call_close(io)
+ VALUE io;
+{
+ return rb_funcall(io, rb_intern("close"), 0, 0);
+}
+
+static VALUE
+io_close(io)
+ VALUE io;
+{
+ return rb_rescue(io_call_close, io, 0, 0);
+}
+
+/*
+ * call-seq:
+ * ios.closed? => true or false
+ *
+ * Returns <code>true</code> if <em>ios</em> is completely closed (for
+ * duplex streams, both reader and writer), <code>false</code>
+ * otherwise.
+ *
+ * f = File.new("testfile")
+ * f.close #=> nil
+ * f.closed? #=> true
+ * f = IO.popen("/bin/sh","r+")
+ * f.close_write #=> nil
+ * f.closed? #=> false
+ * f.close_read #=> nil
+ * f.closed? #=> true
+ */
+
+static VALUE
+rb_io_closed(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+
+ fptr = RFILE(io)->fptr;
+ rb_io_check_initialized(fptr);
+ return (fptr->f || fptr->f2)?Qfalse:Qtrue;
+}
+
+/*
+ * call-seq:
+ * ios.close_read => nil
+ *
+ * Closes the read end of a duplex I/O stream (i.e., one that contains
+ * both a read and a write stream, such as a pipe). Will raise an
+ * <code>IOError</code> if the stream is not duplexed.
+ *
+ * f = IO.popen("/bin/sh","r+")
+ * f.close_read
+ * f.readlines
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:3:in `readlines': not opened for reading (IOError)
+ * from prog.rb:3
+ */
+
+static VALUE
+rb_io_close_read(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ int n;
+
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't close");
+ }
+ GetOpenFile(io, fptr);
+ if (fptr->f2 == 0 && (fptr->mode & FMODE_WRITABLE)) {
+ rb_raise(rb_eIOError, "closing non-duplex IO for reading");
+ }
+ if (fptr->f2 == 0) {
+ return rb_io_close(io);
+ }
+ n = fclose(fptr->f);
+ fptr->mode &= ~FMODE_READABLE;
+ fptr->f = fptr->f2;
+ fptr->f2 = 0;
+ if (n != 0) rb_sys_fail(fptr->path);
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.close_write => nil
+ *
+ * Closes the write end of a duplex I/O stream (i.e., one that contains
+ * both a read and a write stream, such as a pipe). Will raise an
+ * <code>IOError</code> if the stream is not duplexed.
+ *
+ * f = IO.popen("/bin/sh","r+")
+ * f.close_write
+ * f.print "nowhere"
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:3:in `write': not opened for writing (IOError)
+ * from prog.rb:3:in `print'
+ * from prog.rb:3
+ */
+
+static VALUE
+rb_io_close_write(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ int n;
+
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't close");
+ }
+ GetOpenFile(io, fptr);
+ if (fptr->f2 == 0 && (fptr->mode & FMODE_READABLE)) {
+ rb_raise(rb_eIOError, "closing non-duplex IO for writing");
+ }
+ if (fptr->f2 == 0) {
+ return rb_io_close(io);
+ }
+ n = fclose(fptr->f2);
+ fptr->f2 = 0;
+ fptr->mode &= ~FMODE_WRITABLE;
+ if (n != 0) rb_sys_fail(fptr->path);
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.sysseek(offset, whence=SEEK_SET) => integer
+ *
+ * Seeks to a given <i>offset</i> in the stream according to the value
+ * of <i>whence</i> (see <code>IO#seek</code> for values of
+ * <i>whence</i>). Returns the new offset into the file.
+ *
+ * f = File.new("testfile")
+ * f.sysseek(-13, IO::SEEK_END) #=> 53
+ * f.sysread(10) #=> "And so on."
+ */
+
+static VALUE
+rb_io_sysseek(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE offset, ptrname;
+ int whence = SEEK_SET;
+ OpenFile *fptr;
+ off_t pos;
+
+ if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
+ whence = NUM2INT(ptrname);
+ }
+ pos = NUM2OFFT(offset);
+ GetOpenFile(io, fptr);
+ if ((fptr->mode & FMODE_READABLE) && READ_DATA_BUFFERED(fptr->f)) {
+ rb_raise(rb_eIOError, "sysseek for buffered IO");
+ }
+ if ((fptr->mode & FMODE_WRITABLE) && (fptr->mode & FMODE_WBUF)) {
+ rb_warn("sysseek for buffered IO");
+ }
+ pos = lseek(fileno(fptr->f), pos, whence);
+ if (pos == -1) rb_sys_fail(fptr->path);
+ clearerr(fptr->f);
+
+ return OFFT2NUM(pos);
+}
+
+/*
+ * call-seq:
+ * ios.syswrite(string) => integer
+ *
+ * Writes the given string to <em>ios</em> using a low-level write.
+ * Returns the number of bytes written. Do not mix with other methods
+ * that write to <em>ios</em> or you may get unpredictable results.
+ * Raises <code>SystemCallError</code> on error.
+ *
+ * f = File.new("out", "w")
+ * f.syswrite("ABCDEF") #=> 6
+ */
+
+static VALUE
+rb_io_syswrite(io, str)
+ VALUE io, str;
+{
+ OpenFile *fptr;
+ FILE *f;
+ long n;
+
+ rb_secure(4);
+ if (TYPE(str) != T_STRING)
+ str = rb_obj_as_string(str);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+ f = GetWriteFile(fptr);
+
+ if (fptr->mode & FMODE_WBUF) {
+ rb_warn("syswrite for buffered IO");
+ }
+ if (!rb_thread_fd_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ }
+ TRAP_BEG;
+ n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len);
+ TRAP_END;
+
+ if (n == -1) rb_sys_fail(fptr->path);
+
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * ios.sysread(integer ) => string
+ *
+ * Reads <i>integer</i> bytes from <em>ios</em> using a low-level
+ * read and returns them as a string. Do not mix with other methods
+ * that read from <em>ios</em> or you may get unpredictable results.
+ * Raises <code>SystemCallError</code> on error and
+ * <code>EOFError</code> at end of file.
+ *
+ * f = File.new("testfile")
+ * f.sysread(16) #=> "This is line one"
+ */
+
+static VALUE
+rb_io_sysread(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE len, str;
+ OpenFile *fptr;
+ long n, ilen;
+
+ rb_scan_args(argc, argv, "11", &len, &str);
+ ilen = NUM2LONG(len);
+
+ if (NIL_P(str)) {
+ str = rb_str_new(0, ilen);
+ }
+ else {
+ StringValue(str);
+ rb_str_modify(str);
+ rb_str_resize(str, ilen);
+ }
+ if (ilen == 0) return str;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+
+ if (READ_DATA_BUFFERED(fptr->f)) {
+ rb_raise(rb_eIOError, "sysread for buffered IO");
+ }
+ rb_str_locktmp(str);
+
+ n = fileno(fptr->f);
+ rb_thread_wait_fd(fileno(fptr->f));
+ rb_io_check_closed(fptr);
+ if (RSTRING(str)->len != ilen) {
+ rb_raise(rb_eRuntimeError, "buffer string modified");
+ }
+ TRAP_BEG;
+ n = read(fileno(fptr->f), RSTRING(str)->ptr, ilen);
+ TRAP_END;
+
+ rb_str_unlocktmp(str);
+ if (n == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ rb_str_resize(str, n);
+ if (n == 0 && ilen > 0) {
+ rb_eof_error();
+ }
+ RSTRING(str)->len = n;
+ RSTRING(str)->ptr[n] = '\0';
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * ios.binmode => ios
+ *
+ * Puts <em>ios</em> into binary mode. This is useful only in
+ * MS-DOS/Windows environments. Once a stream is in binary mode, it
+ * cannot be reset to nonbinary mode.
+ */
+
+VALUE
+rb_io_binmode(io)
+ VALUE io;
+{
+#if defined(_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__) || defined(__EMX__)
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+#ifdef __human68k__
+ if (fptr->f)
+ fmode(fptr->f, _IOBIN);
+ if (fptr->f2)
+ fmode(fptr->f2, _IOBIN);
+#else
+ if (fptr->f && setmode(fileno(fptr->f), O_BINARY) == -1)
+ rb_sys_fail(fptr->path);
+ if (fptr->f2 && setmode(fileno(fptr->f2), O_BINARY) == -1)
+ rb_sys_fail(fptr->path);
+#endif
+
+ fptr->mode |= FMODE_BINMODE;
+#endif
+ return io;
+}
+
+char*
+rb_io_flags_mode(flags)
+ int flags;
+{
+#ifdef O_BINARY
+# define MODE_BINMODE(a,b) ((flags & FMODE_BINMODE) ? (b) : (a))
+#else
+# define MODE_BINMODE(a,b) (a)
+#endif
+ if (flags & FMODE_APPEND) {
+ if ((flags & FMODE_READWRITE) == FMODE_READWRITE) {
+ return MODE_BINMODE("a+", "ab+");
+ }
+ return MODE_BINMODE("a", "ab");
+ }
+ switch (flags & FMODE_READWRITE) {
+ case FMODE_READABLE:
+ return MODE_BINMODE("r", "rb");
+ case FMODE_WRITABLE:
+ return MODE_BINMODE("w", "wb");
+ case FMODE_READWRITE:
+ if (flags & FMODE_CREATE) {
+ return MODE_BINMODE("w+", "wb+");
+ }
+ return MODE_BINMODE("r+", "rb+");
+ }
+ rb_raise(rb_eArgError, "illegal access modenum %o", flags);
+ return NULL; /* not reached */
+}
+
+int
+rb_io_mode_flags(mode)
+ const char *mode;
+{
+ int flags = 0;
+ const char *m = mode;
+
+ switch (*m++) {
+ case 'r':
+ flags |= FMODE_READABLE;
+ break;
+ case 'w':
+ flags |= FMODE_WRITABLE | FMODE_CREATE;
+ break;
+ case 'a':
+ flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
+ break;
+ default:
+ error:
+ rb_raise(rb_eArgError, "illegal access mode %s", mode);
+ }
+
+ while (*m) {
+ switch (*m++) {
+ case 'b':
+ flags |= FMODE_BINMODE;
+ break;
+ case '+':
+ flags |= FMODE_READWRITE;
+ break;
+ default:
+ goto error;
+ }
+ }
+
+ return flags;
+}
+
+int
+rb_io_modenum_flags(mode)
+ int mode;
+{
+ int flags = 0;
+
+ switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
+ case O_RDONLY:
+ flags = FMODE_READABLE;
+ break;
+ case O_WRONLY:
+ flags = FMODE_WRITABLE;
+ break;
+ case O_RDWR:
+ flags = FMODE_READWRITE;
+ break;
+ }
+
+ if (mode & O_APPEND) {
+ flags |= FMODE_APPEND;
+ }
+ if (mode & O_CREAT) {
+ flags |= FMODE_CREATE;
+ }
+#ifdef O_BINARY
+ if (mode & O_BINARY) {
+ flags |= FMODE_BINMODE;
+ }
+#endif
+
+ return flags;
+}
+
+static int
+rb_io_mode_modenum(mode)
+ const char *mode;
+{
+ int flags = 0;
+ const char *m = mode;
+
+ switch (*m++) {
+ case 'r':
+ flags |= O_RDONLY;
+ break;
+ case 'w':
+ flags |= O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case 'a':
+ flags |= O_WRONLY | O_CREAT | O_APPEND;
+ break;
+ default:
+ error:
+ rb_raise(rb_eArgError, "illegal access mode %s", mode);
+ }
+
+ while (*m) {
+ switch (*m++) {
+ case 'b':
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+ break;
+ case '+':
+ flags = (flags & ~O_ACCMODE) | O_RDWR;
+ break;
+ default:
+ goto error;
+ }
+ }
+
+ return flags;
+}
+
+#define MODENUM_MAX 4
+
+static char*
+rb_io_modenum_mode(flags)
+ int flags;
+{
+#ifdef O_BINARY
+# define MODE_BINARY(a,b) ((flags & O_BINARY) ? (b) : (a))
+#else
+# define MODE_BINARY(a,b) (a)
+#endif
+ if (flags & O_APPEND) {
+ if ((flags & O_RDWR) == O_RDWR) {
+ return MODE_BINARY("a+", "ab+");
+ }
+ return MODE_BINARY("a", "ab");
+ }
+ switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
+ case O_RDONLY:
+ return MODE_BINARY("r", "rb");
+ case O_WRONLY:
+ return MODE_BINARY("w", "wb");
+ case O_RDWR:
+ return MODE_BINARY("r+", "rb+");
+ }
+ rb_raise(rb_eArgError, "illegal access modenum %o", flags);
+ return NULL; /* not reached */
+}
+
+static int
+rb_sysopen(fname, flags, mode)
+ char *fname;
+ int flags;
+ unsigned int mode;
+{
+ int fd;
+
+ fd = open(fname, flags, mode);
+ if (fd < 0) {
+ if (errno == EMFILE || errno == ENFILE) {
+ rb_gc();
+ fd = open(fname, flags, mode);
+ }
+ if (fd < 0) {
+ rb_sys_fail(fname);
+ }
+ }
+ return fd;
+}
+
+FILE *
+rb_fopen(fname, mode)
+ const char *fname;
+ const char *mode;
+{
+ FILE *file;
+
+ file = fopen(fname, mode);
+ if (!file) {
+ if (errno == EMFILE || errno == ENFILE) {
+ rb_gc();
+ file = fopen(fname, mode);
+ }
+ if (!file) {
+ rb_sys_fail(fname);
+ }
+ }
+#ifdef USE_SETVBUF
+ if (setvbuf(file, NULL, _IOFBF, 0) != 0)
+ rb_warn("setvbuf() can't be honoured for %s", fname);
+#endif
+#ifdef __human68k__
+ fmode(file, _IOTEXT);
+#endif
+ return file;
+}
+
+FILE *
+rb_fdopen(fd, mode)
+ int fd;
+ const char *mode;
+{
+ FILE *file;
+
+#if defined(sun)
+ errno = 0;
+#endif
+ file = fdopen(fd, mode);
+ if (!file) {
+#if defined(sun)
+ if (errno == 0 || errno == EMFILE || errno == ENFILE) {
+#else
+ if (errno == EMFILE || errno == ENFILE) {
+#endif
+ rb_gc();
+#if defined(sun)
+ errno = 0;
+#endif
+ file = fdopen(fd, mode);
+ }
+ if (!file) {
+#ifdef _WIN32
+ if (errno == 0) errno = EINVAL;
+#endif
+#if defined(sun)
+ if (errno == 0) errno = EMFILE;
+#endif
+ rb_sys_fail(0);
+ }
+ }
+
+#ifdef USE_SETVBUF
+ if (setvbuf(file, NULL, _IOFBF, 0) != 0)
+ rb_warn("setvbuf() can't be honoured (fd=%d)", fd);
+#endif
+ return file;
+}
+
+static VALUE
+rb_file_open_internal(io, fname, mode)
+ VALUE io;
+ const char *fname, *mode;
+{
+ OpenFile *fptr;
+
+ MakeOpenFile(io, fptr);
+
+ fptr->mode = rb_io_mode_flags(mode);
+ fptr->path = strdup(fname);
+ fptr->f = rb_fopen(fptr->path, rb_io_flags_mode(fptr->mode));
+
+ return io;
+}
+
+VALUE
+rb_file_open(fname, mode)
+ const char *fname, *mode;
+{
+ return rb_file_open_internal(io_alloc(rb_cFile), fname, mode);
+}
+
+static VALUE
+rb_file_sysopen_internal(io, fname, flags, mode)
+ VALUE io;
+ char *fname;
+ int flags, mode;
+{
+ OpenFile *fptr;
+ int fd;
+ char *m;
+
+ MakeOpenFile(io, fptr);
+
+ fptr->path = strdup(fname);
+ m = rb_io_modenum_mode(flags);
+ fptr->mode = rb_io_modenum_flags(flags);
+ fd = rb_sysopen(fptr->path, flags, mode);
+ fptr->f = rb_fdopen(fd, m);
+
+ return io;
+}
+
+VALUE
+rb_file_sysopen(fname, flags, mode)
+ const char *fname;
+ int flags, mode;
+{
+ return rb_file_sysopen_internal(io_alloc(rb_cFile), fname, flags, mode);
+}
+
+#if defined (_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__) || defined(__VMS)
+static struct pipe_list {
+ OpenFile *fptr;
+ struct pipe_list *next;
+} *pipe_list;
+
+static void
+pipe_add_fptr(fptr)
+ OpenFile *fptr;
+{
+ struct pipe_list *list;
+
+ list = ALLOC(struct pipe_list);
+ list->fptr = fptr;
+ list->next = pipe_list;
+ pipe_list = list;
+}
+
+static void
+pipe_del_fptr(fptr)
+ OpenFile *fptr;
+{
+ struct pipe_list *list = pipe_list;
+ struct pipe_list *tmp;
+
+ if (list->fptr == fptr) {
+ pipe_list = list->next;
+ free(list);
+ return;
+ }
+
+ while (list->next) {
+ if (list->next->fptr == fptr) {
+ tmp = list->next;
+ list->next = list->next->next;
+ free(tmp);
+ return;
+ }
+ list = list->next;
+ }
+}
+
+static void
+pipe_atexit _((void))
+{
+ struct pipe_list *list = pipe_list;
+ struct pipe_list *tmp;
+
+ while (list) {
+ tmp = list->next;
+ rb_io_fptr_finalize(list->fptr);
+ list = tmp;
+ }
+}
+
+static void pipe_finalize _((OpenFile *fptr,int));
+
+static void
+pipe_finalize(fptr, noraise)
+ OpenFile *fptr;
+ int noraise;
+{
+#if !defined (__CYGWIN__) && !defined(_WIN32)
+ extern VALUE rb_last_status;
+ int status;
+ if (fptr->f) {
+ status = pclose(fptr->f);
+ }
+ if (fptr->f2) {
+ status = pclose(fptr->f2);
+ }
+ fptr->f = fptr->f2 = 0;
+#if defined DJGPP
+ status <<= 8;
+#endif
+ rb_last_status = INT2FIX(status);
+#else
+ fptr_finalize(fptr, noraise);
+#endif
+ pipe_del_fptr(fptr);
+}
+#endif
+
+void
+rb_io_synchronized(fptr)
+ OpenFile *fptr;
+{
+ fptr->mode |= FMODE_SYNC;
+}
+
+void
+rb_io_unbuffered(fptr)
+ OpenFile *fptr;
+{
+ rb_io_synchronized(fptr);
+}
+
+static VALUE pipe_open(VALUE pstr, char *pname, char *mode);
+
+static VALUE
+pipe_open(pstr, pname, mode)
+ VALUE pstr;
+ char *pname, *mode;
+{
+ int modef = rb_io_mode_flags(mode);
+ OpenFile *fptr;
+#if defined(DJGPP) || defined(__human68k__) || defined(__VMS)
+ FILE *f;
+#else
+ int pid;
+#ifdef _WIN32
+ FILE *fpr, *fpw;
+#else
+ int pr[2], pw[2];
+#endif
+#endif
+ volatile int doexec;
+
+ if (!pname) pname = StringValueCStr(pstr);
+ doexec = (strcmp("-", pname) != 0);
+
+#if defined(DJGPP) || defined(__human68k__) || defined(__VMS) || defined(_WIN32)
+ if (!doexec) {
+ rb_raise(rb_eNotImpError,
+ "fork() function is unimplemented on this machine");
+ }
+#endif
+
+#if defined(DJGPP) || defined(__human68k__) || defined(__VMS)
+ f = popen(pname, mode);
+
+ if (!f) rb_sys_fail(pname);
+ else {
+ VALUE port = io_alloc(rb_cIO);
+
+ MakeOpenFile(port, fptr);
+ fptr->finalize = pipe_finalize;
+ fptr->mode = modef;
+
+ pipe_add_fptr(fptr);
+ if (modef & FMODE_READABLE) fptr->f = f;
+ if (modef & FMODE_WRITABLE) {
+ if (fptr->f) fptr->f2 = f;
+ else fptr->f = f;
+ rb_io_synchronized(fptr);
+ }
+ return (VALUE)port;
+ }
+#else
+#ifdef _WIN32
+retry:
+ pid = pipe_exec(pname, rb_io_mode_modenum(mode), &fpr, &fpw);
+ if (pid == -1) { /* exec failed */
+ if (errno == EAGAIN) {
+ rb_thread_sleep(1);
+ goto retry;
+ }
+ rb_sys_fail(pname);
+ }
+ else {
+ VALUE port = io_alloc(rb_cIO);
+
+ MakeOpenFile(port, fptr);
+ fptr->mode = modef;
+ fptr->mode |= FMODE_SYNC;
+ fptr->pid = pid;
+
+ if (modef & FMODE_READABLE) {
+ fptr->f = fpr;
+ }
+ if (modef & FMODE_WRITABLE) {
+ if (fptr->f) fptr->f2 = fpw;
+ else fptr->f = fpw;
+ }
+ fptr->finalize = pipe_finalize;
+ pipe_add_fptr(fptr);
+ return (VALUE)port;
+ }
+#else
+ if (((modef & FMODE_READABLE) && pipe(pr) == -1) ||
+ ((modef & FMODE_WRITABLE) && pipe(pw) == -1))
+ rb_sys_fail(pname);
+
+ if (!doexec) {
+ fflush(stdin); /* is it really needed? */
+ fflush(stdout);
+ fflush(stderr);
+ }
+
+ retry:
+ switch ((pid = fork())) {
+ case 0: /* child */
+ if (modef & FMODE_READABLE) {
+ close(pr[0]);
+ if (pr[1] != 1) {
+ dup2(pr[1], 1);
+ close(pr[1]);
+ }
+ }
+ if (modef & FMODE_WRITABLE) {
+ close(pw[1]);
+ if (pw[0] != 0) {
+ dup2(pw[0], 0);
+ close(pw[0]);
+ }
+ }
+
+ if (doexec) {
+ int fd;
+
+ for (fd = 3; fd < NOFILE; fd++)
+ close(fd);
+ rb_proc_exec(pname);
+ fprintf(stderr, "%s:%d: command not found: %s\n",
+ ruby_sourcefile, ruby_sourceline, pname);
+ _exit(127);
+ }
+ rb_io_synchronized(RFILE(orig_stdout)->fptr);
+ rb_io_synchronized(RFILE(orig_stderr)->fptr);
+ return Qnil;
+
+ case -1: /* fork failed */
+ if (errno == EAGAIN) {
+ rb_thread_sleep(1);
+ goto retry;
+ }
+ else {
+ int e = errno;
+ if ((modef & FMODE_READABLE)) {
+ close(pr[0]);
+ close(pr[1]);
+ }
+ if ((modef & FMODE_WRITABLE)) {
+ close(pw[0]);
+ close(pw[1]);
+ }
+ errno = e;
+ rb_sys_fail(pname);
+ }
+ break;
+
+ default: /* parent */
+ if (pid < 0) rb_sys_fail(pname);
+ else {
+ VALUE port = io_alloc(rb_cIO);
+
+ MakeOpenFile(port, fptr);
+ fptr->mode = modef;
+ fptr->mode |= FMODE_SYNC;
+ fptr->pid = pid;
+
+ if (modef & FMODE_READABLE) {
+ close(pr[1]);
+ fptr->f = rb_fdopen(pr[0], "r");
+ }
+ if (modef & FMODE_WRITABLE) {
+ FILE *f = rb_fdopen(pw[1], "w");
+
+ close(pw[0]);
+ if (fptr->f) fptr->f2 = f;
+ else fptr->f = f;
+ }
+#if defined (__CYGWIN__)
+ fptr->finalize = pipe_finalize;
+ pipe_add_fptr(fptr);
+#endif
+ return port;
+ }
+ }
+#endif
+#endif
+}
+
+/*
+ * call-seq:
+ * IO.popen(cmd_string, mode="r" ) => io
+ * IO.popen(cmd_string, mode="r" ) {|io| block } => obj
+ *
+ * Runs the specified command string as a subprocess; the subprocess's
+ * standard input and output will be connected to the returned
+ * <code>IO</code> object. If <i>cmd_string</i> starts with a
+ * ``<code>-</code>'', then a new instance of Ruby is started as the
+ * subprocess. The default mode for the new file object is ``r'', but
+ * <i>mode</i> may be set to any of the modes listed in the description
+ * for class IO.
+ *
+ * If a block is given, Ruby will run the command as a child connected
+ * to Ruby with a pipe. Ruby's end of the pipe will be passed as a
+ * parameter to the block.
+ * At the end of block, Ruby close the pipe and sets <code>$?</code>.
+ * In this case <code>IO::popen</code> returns
+ * the value of the block.
+ *
+ * If a block is given with a <i>cmd_string</i> of ``<code>-</code>'',
+ * the block will be run in two separate processes: once in the parent,
+ * and once in a child. The parent process will be passed the pipe
+ * object as a parameter to the block, the child version of the block
+ * will be passed <code>nil</code>, and the child's standard in and
+ * standard out will be connected to the parent through the pipe. Not
+ * available on all platforms.
+ *
+ * f = IO.popen("uname")
+ * p f.readlines
+ * puts "Parent is #{Process.pid}"
+ * IO.popen ("date") { |f| puts f.gets }
+ * IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f}"}
+ * p $?
+ *
+ * <em>produces:</em>
+ *
+ * ["Linux\n"]
+ * Parent is 26166
+ * Wed Apr 9 08:53:52 CDT 2003
+ * 26169 is here, f is
+ * 26166 is here, f is #<IO:0x401b3d44>
+ * #<Process::Status: pid=26166,exited(0)>
+ */
+
+static VALUE
+rb_io_s_popen(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ char *mode;
+ VALUE pname, pmode, port;
+
+ if (rb_scan_args(argc, argv, "11", &pname, &pmode) == 1) {
+ mode = "r";
+ }
+ else if (FIXNUM_P(pmode)) {
+ mode = rb_io_modenum_mode(FIX2INT(pmode));
+ }
+ else {
+ mode = rb_io_flags_mode(rb_io_mode_flags(StringValueCStr(pmode)));
+ }
+ SafeStringValue(pname);
+ port = pipe_open(pname, 0, mode);
+ if (NIL_P(port)) {
+ /* child */
+ if (rb_block_given_p()) {
+ rb_yield(Qnil);
+ fflush(stdout);
+ fflush(stderr);
+ _exit(0);
+ }
+ return Qnil;
+ }
+ RBASIC(port)->klass = klass;
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, port, io_close, port);
+ }
+ return port;
+}
+
+static VALUE
+rb_open_file(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE fname, vmode, perm;
+ char *path, *mode;
+ int flags;
+ unsigned int fmode;
+
+ rb_scan_args(argc, argv, "12", &fname, &vmode, &perm);
+ SafeStringValue(fname);
+
+ path = StringValueCStr(fname);
+ if (FIXNUM_P(vmode) || !NIL_P(perm)) {
+ if (FIXNUM_P(vmode)) {
+ flags = FIX2INT(vmode);
+ }
+ else {
+ SafeStringValue(vmode);
+ flags = rb_io_mode_modenum(RSTRING(vmode)->ptr);
+ }
+ fmode = NIL_P(perm) ? 0666 : NUM2UINT(perm);
+
+ rb_file_sysopen_internal(io, path, flags, fmode);
+ }
+ else {
+ mode = NIL_P(vmode) ? "r" : StringValueCStr(vmode);
+ rb_file_open_internal(io, path, mode);
+ }
+ return io;
+}
+
+/*
+ * call-seq:
+ * IO.open(fd, mode_string="r" ) => io
+ * IO.open(fd, mode_string="r" ) {|io| block } => obj
+ *
+ * With no associated block, <code>open</code> is a synonym for
+ * <code>IO::new</code>. If the optional code block is given, it will
+ * be passed <i>io</i> as an argument, and the IO object will
+ * automatically be closed when the block terminates. In this instance,
+ * <code>IO::open</code> returns the value of the block.
+ *
+ */
+
+static VALUE
+rb_io_s_open(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE io = rb_class_new_instance(argc, argv, klass);
+
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, io, io_close, io);
+ }
+
+ return io;
+}
+
+/*
+ * call-seq:
+ * IO.sysopen(path, [mode, [perm]]) => fixnum
+ *
+ * Opens the given path, returning the underlying file descriptor as a
+ * <code>Fixnum</code>.
+ *
+ * IO.sysopen("testfile") #=> 3
+ *
+ */
+
+static VALUE
+rb_io_s_sysopen(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, vmode, perm;
+ int flags, fd;
+ unsigned int fmode;
+ char *path;
+
+ rb_scan_args(argc, argv, "12", &fname, &vmode, &perm);
+ SafeStringValue(fname);
+
+ if (NIL_P(vmode)) flags = O_RDONLY;
+ else if (FIXNUM_P(vmode)) flags = FIX2INT(vmode);
+ else {
+ SafeStringValue(vmode);
+ flags = rb_io_mode_modenum(RSTRING(vmode)->ptr);
+ }
+ if (NIL_P(perm)) fmode = 0666;
+ else fmode = NUM2UINT(perm);
+
+ path = ALLOCA_N(char, strlen(RSTRING(fname)->ptr)+1);
+ strcpy(path, RSTRING(fname)->ptr);
+ fd = rb_sysopen(path, flags, fmode);
+ return INT2NUM(fd);
+}
+
+/*
+ * call-seq:
+ * open(path [, mode [, perm]] ) => io or nil
+ * open(path [, mode [, perm]] ) {|io| block } => obj
+ *
+ * Creates an <code>IO</code> object connected to the given stream,
+ * file, or subprocess.
+ *
+ * If <i>path</i> does not start with a pipe character
+ * (``<code>|</code>''), treat it as the name of a file to open using
+ * the specified mode (defaulting to ``<code>r</code>''). (See the table
+ * of valid modes on page 331.) If a file is being created, its initial
+ * permissions may be set using the integer third parameter.
+ *
+ * If a block is specified, it will be invoked with the
+ * <code>File</code> object as a parameter, and the file will be
+ * automatically closed when the block terminates. The call
+ * returns the value of the block.
+ *
+ * If <i>path</i> starts with a pipe character, a subprocess is
+ * created, connected to the caller by a pair of pipes. The returned
+ * <code>IO</code> object may be used to write to the standard input
+ * and read from the standard output of this subprocess. If the command
+ * following the ``<code>|</code>'' is a single minus sign, Ruby forks,
+ * and this subprocess is connected to the parent. In the subprocess,
+ * the <code>open</code> call returns <code>nil</code>. If the command
+ * is not ``<code>-</code>'', the subprocess runs the command. If a
+ * block is associated with an <code>open("|-")</code> call, that block
+ * will be run twice---once in the parent and once in the child. The
+ * block parameter will be an <code>IO</code> object in the parent and
+ * <code>nil</code> in the child. The parent's <code>IO</code> object
+ * will be connected to the child's <code>$stdin</code> and
+ * <code>$stdout</code>. The subprocess will be terminated at the end
+ * of the block.
+ *
+ * open("testfile") do |f|
+ * print f.gets
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * This is line one
+ *
+ * Open a subprocess and read its output:
+ *
+ * cmd = open("|date")
+ * print cmd.gets
+ * cmd.close
+ *
+ * <em>produces:</em>
+ *
+ * Wed Apr 9 08:56:31 CDT 2003
+ *
+ * Open a subprocess running the same Ruby program:
+ *
+ * f = open("|-", "w+")
+ * if f == nil
+ * puts "in Child"
+ * exit
+ * else
+ * puts "Got: #{f.gets}"
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Got: in Child
+ *
+ * Open a subprocess using a block to receive the I/O object:
+ *
+ * open("|-") do |f|
+ * if f == nil
+ * puts "in Child"
+ * else
+ * puts "Got: #{f.gets}"
+ * end
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Got: in Child
+ */
+
+static VALUE
+rb_f_open(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ if (argc >= 1) {
+ char *str = StringValuePtr(argv[0]);
+
+ if (str[0] == '|') {
+ VALUE tmp = rb_str_new(str+1, RSTRING(argv[0])->len-1);
+ OBJ_INFECT(tmp, argv[0]);
+ argv[0] = tmp;
+ return rb_io_s_popen(argc, argv, rb_cIO);
+ }
+ }
+ return rb_io_s_open(argc, argv, rb_cFile);
+}
+
+static VALUE
+rb_io_open(fname, mode)
+ char *fname, *mode;
+{
+ if (fname[0] == '|') {
+ return pipe_open(0, fname+1, mode);
+ }
+ else {
+ return rb_file_open(fname, mode);
+ }
+}
+
+static VALUE
+rb_io_get_io(io)
+ VALUE io;
+{
+ return rb_convert_type(io, T_FILE, "IO", "to_io");
+}
+
+static VALUE
+rb_io_check_io(io)
+ VALUE io;
+{
+ return rb_check_convert_type(io, T_FILE, "IO", "to_io");
+}
+
+static char*
+rb_io_mode_string(fptr)
+ OpenFile *fptr;
+{
+ switch (fptr->mode & FMODE_READWRITE) {
+ case FMODE_READABLE:
+ default:
+ return "r";
+ case FMODE_WRITABLE:
+ return "w";
+ case FMODE_READWRITE:
+ return "r+";
+ }
+}
+
+static VALUE
+io_reopen(io, nfile)
+ VALUE io, nfile;
+{
+ OpenFile *fptr, *orig;
+ char *mode;
+ int fd, fd2;
+ off_t pos = 0;
+
+ nfile = rb_io_get_io(nfile);
+ if (rb_safe_level() >= 4 && (!OBJ_TAINTED(io) || !OBJ_TAINTED(nfile))) {
+ rb_raise(rb_eSecurityError, "Insecure: can't reopen");
+ }
+ GetOpenFile(io, fptr);
+ GetOpenFile(nfile, orig);
+
+ if (fptr == orig) return io;
+ if (orig->mode & FMODE_READABLE) {
+ pos = io_tell(orig);
+ }
+ if (orig->f2) {
+ io_fflush(orig->f2, orig);
+ }
+ else if (orig->mode & FMODE_WRITABLE) {
+ io_fflush(orig->f, orig);
+ }
+ if (fptr->mode & FMODE_WRITABLE) {
+ io_fflush(GetWriteFile(fptr), fptr);
+ }
+
+ /* copy OpenFile structure */
+ fptr->mode = orig->mode;
+ fptr->pid = orig->pid;
+ fptr->lineno = orig->lineno;
+ if (fptr->path) free(fptr->path);
+ if (orig->path) fptr->path = strdup(orig->path);
+ else fptr->path = 0;
+ fptr->finalize = orig->finalize;
+
+ mode = rb_io_mode_string(fptr);
+ fd = fileno(fptr->f);
+ fd2 = fileno(orig->f);
+ if (fd != fd2) {
+ if (fptr->f == stdin || fptr->f == stdout || fptr->f == stderr) {
+ clearerr(fptr->f);
+ /* need to keep stdio objects */
+ if (dup2(fd2, fd) < 0)
+ rb_sys_fail(orig->path);
+ }
+ else {
+ FILE *f2 = fptr->f2;
+ int m = fptr->mode;
+ fclose(fptr->f);
+ fptr->f = f2;
+ fptr->f2 = NULL;
+ fptr->mode &= (m & FMODE_READABLE) ? ~FMODE_READABLE : ~FMODE_WRITABLE;
+ if (dup2(fd2, fd) < 0)
+ rb_sys_fail(orig->path);
+ if (f2) {
+ fptr->f = rb_fdopen(fd, "r");
+ fptr->f2 = f2;
+ }
+ else {
+ fptr->f = rb_fdopen(fd, mode);
+ }
+ fptr->mode = m;
+ }
+ rb_thread_fd_close(fd);
+ if ((orig->mode & FMODE_READABLE) && pos >= 0) {
+ io_seek(fptr, pos, SEEK_SET);
+ io_seek(orig, pos, SEEK_SET);
+ }
+ }
+
+ if (fptr->f2 && fd != fileno(fptr->f2)) {
+ fd = fileno(fptr->f2);
+ if (!orig->f2) {
+ fclose(fptr->f2);
+ rb_thread_fd_close(fd);
+ fptr->f2 = 0;
+ }
+ else if (fd != (fd2 = fileno(orig->f2))) {
+ fclose(fptr->f2);
+ rb_thread_fd_close(fd);
+ if (dup2(fd2, fd) < 0)
+ rb_sys_fail(orig->path);
+ fptr->f2 = rb_fdopen(fd, "w");
+ }
+ }
+
+ if (fptr->mode & FMODE_BINMODE) {
+ rb_io_binmode(io);
+ }
+
+ RBASIC(io)->klass = RBASIC(nfile)->klass;
+ return io;
+}
+
+/*
+ * call-seq:
+ * ios.reopen(other_IO) => ios
+ * ios.reopen(path, mode_str) => ios
+ *
+ * Reassociates <em>ios</em> with the I/O stream given in
+ * <i>other_IO</i> or to a new stream opened on <i>path</i>. This may
+ * dynamically change the actual class of this stream.
+ *
+ * f1 = File.new("testfile")
+ * f2 = File.new("testfile")
+ * f2.readlines[0] #=> "This is line one\n"
+ * f2.reopen(f1) #=> #<File:testfile>
+ * f2.readlines[0] #=> "This is line one\n"
+ */
+
+static VALUE
+rb_io_reopen(argc, argv, file)
+ int argc;
+ VALUE *argv;
+ VALUE file;
+{
+ VALUE fname, nmode;
+ char *mode;
+ OpenFile *fptr;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "11", &fname, &nmode) == 1) {
+ VALUE tmp = rb_io_check_io(fname);
+ if (!NIL_P(tmp)) {
+ return io_reopen(file, tmp);
+ }
+ }
+
+ SafeStringValue(fname);
+ rb_io_taint_check(file);
+ fptr = RFILE(file)->fptr;
+ if (!fptr) {
+ fptr = RFILE(file)->fptr = ALLOC(OpenFile);
+ MEMZERO(fptr, OpenFile, 1);
+ }
+
+ if (!NIL_P(nmode)) {
+ fptr->mode = rb_io_mode_flags(StringValueCStr(nmode));
+ }
+
+ if (fptr->path) {
+ free(fptr->path);
+ fptr->path = 0;
+ }
+
+ fptr->path = strdup(StringValueCStr(fname));
+ mode = rb_io_flags_mode(fptr->mode);
+ if (!fptr->f) {
+ fptr->f = rb_fopen(fptr->path, mode);
+ if (fptr->f2) {
+ fclose(fptr->f2);
+ fptr->f2 = 0;
+ }
+ return file;
+ }
+
+ if (freopen(fptr->path, mode, fptr->f) == 0) {
+ rb_sys_fail(fptr->path);
+ }
+#ifdef USE_SETVBUF
+ if (setvbuf(fptr->f, NULL, _IOFBF, 0) != 0)
+ rb_warn("setvbuf() can't be honoured for %s", fptr->path);
+#endif
+
+ if (fptr->f2) {
+ if (freopen(fptr->path, "w", fptr->f2) == 0) {
+ rb_sys_fail(fptr->path);
+ }
+ }
+
+ return file;
+}
+
+/* :nodoc: */
+static VALUE
+rb_io_init_copy(dest, io)
+ VALUE dest, io;
+{
+ OpenFile *fptr, *orig;
+ int fd;
+ char *mode;
+
+ io = rb_io_get_io(io);
+ if (dest == io) return dest;
+ GetOpenFile(io, orig);
+ MakeOpenFile(dest, fptr);
+
+ if (orig->f2) {
+ io_fflush(orig->f2, orig);
+ fseeko(orig->f, 0L, SEEK_CUR);
+ }
+ else if (orig->mode & FMODE_WRITABLE) {
+ io_fflush(orig->f, orig);
+ }
+ else {
+ fseeko(orig->f, 0L, SEEK_CUR);
+ }
+
+ /* copy OpenFile structure */
+ fptr->mode = orig->mode;
+ fptr->pid = orig->pid;
+ fptr->lineno = orig->lineno;
+ if (orig->path) fptr->path = strdup(orig->path);
+ fptr->finalize = orig->finalize;
+
+ switch (fptr->mode & FMODE_READWRITE) {
+ case FMODE_READABLE:
+ default:
+ mode = "r"; break;
+ case FMODE_WRITABLE:
+ mode = "w"; break;
+ case FMODE_READWRITE:
+ if (orig->f2) mode = "r";
+ else mode = "r+";
+ break;
+ }
+ fd = ruby_dup(fileno(orig->f));
+ fptr->f = rb_fdopen(fd, mode);
+ fseeko(fptr->f, ftello(orig->f), SEEK_SET);
+ if (orig->f2) {
+ if (fileno(orig->f) != fileno(orig->f2)) {
+ fd = ruby_dup(fileno(orig->f2));
+ }
+ fptr->f2 = rb_fdopen(fd, "w");
+ fseeko(fptr->f2, ftello(orig->f2), SEEK_SET);
+ }
+ if (fptr->mode & FMODE_BINMODE) {
+ rb_io_binmode(dest);
+ }
+
+ return dest;
+}
+
+/*
+ * call-seq:
+ * ios.printf(format_string [, obj, ...] ) => nil
+ *
+ * Formats and writes to <em>ios</em>, converting parameters under
+ * control of the format string. See <code>Kernel#sprintf</code>
+ * for details.
+ */
+
+VALUE
+rb_io_printf(argc, argv, out)
+ int argc;
+ VALUE argv[];
+ VALUE out;
+{
+ rb_io_write(out, rb_f_sprintf(argc, argv));
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * printf(io, string [, obj ... ] ) => nil
+ * printf(string [, obj ... ] ) => nil
+ *
+ * Equivalent to:
+ * io.write(sprintf(string, obj, ...)
+ * or
+ * $stdout.write(sprintf(string, obj, ...)
+ */
+
+static VALUE
+rb_f_printf(argc, argv)
+ int argc;
+ VALUE argv[];
+{
+ VALUE out;
+
+ if (argc == 0) return Qnil;
+ if (TYPE(argv[0]) == T_STRING) {
+ out = rb_stdout;
+ }
+ else {
+ out = argv[0];
+ argv++;
+ argc--;
+ }
+ rb_io_write(out, rb_f_sprintf(argc, argv));
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.print() => nil
+ * ios.print(obj, ...) => nil
+ *
+ * Writes the given object(s) to <em>ios</em>. The stream must be
+ * opened for writing. If the output record separator (<code>$\\</code>)
+ * is not <code>nil</code>, it will be appended to the output. If no
+ * arguments are given, prints <code>$_</code>. Objects that aren't
+ * strings will be converted by calling their <code>to_s</code> method.
+ * With no argument, prints the contents of the variable <code>$_</code>.
+ * Returns <code>nil</code>.
+ *
+ * $stdout.print("This is ", 100, " percent.\n")
+ *
+ * <em>produces:</em>
+ *
+ * This is 100 percent.
+ */
+
+VALUE
+rb_io_print(argc, argv, out)
+ int argc;
+ VALUE *argv;
+ VALUE out;
+{
+ int i;
+ VALUE line;
+
+ /* if no argument given, print `$_' */
+ if (argc == 0) {
+ argc = 1;
+ line = rb_lastline_get();
+ argv = &line;
+ }
+ for (i=0; i<argc; i++) {
+ if (!NIL_P(rb_output_fs) && i>0) {
+ rb_io_write(out, rb_output_fs);
+ }
+ switch (TYPE(argv[i])) {
+ case T_NIL:
+ rb_io_write(out, rb_str_new2("nil"));
+ break;
+ default:
+ rb_io_write(out, argv[i]);
+ break;
+ }
+ }
+ if (!NIL_P(rb_output_rs)) {
+ rb_io_write(out, rb_output_rs);
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * print(obj, ...) => nil
+ *
+ * Prints each object in turn to <code>$stdout</code>. If the output
+ * field separator (<code>$,</code>) is not +nil+, its
+ * contents will appear between each field. If the output record
+ * separator (<code>$\\</code>) is not +nil+, it will be
+ * appended to the output. If no arguments are given, prints
+ * <code>$_</code>. Objects that aren't strings will be converted by
+ * calling their <code>to_s</code> method.
+ *
+ * print "cat", [1,2,3], 99, "\n"
+ * $, = ", "
+ * $\ = "\n"
+ * print "cat", [1,2,3], 99
+ *
+ * <em>produces:</em>
+ *
+ * cat12399
+ * cat, 1, 2, 3, 99
+ */
+
+static VALUE
+rb_f_print(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_io_print(argc, argv, rb_stdout);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.putc(obj) => obj
+ *
+ * If <i>obj</i> is <code>Numeric</code>, write the character whose
+ * code is <i>obj</i>, otherwise write the first character of the
+ * string representation of <i>obj</i> to <em>ios</em>.
+ *
+ * $stdout.putc "A"
+ * $stdout.putc 65
+ *
+ * <em>produces:</em>
+ *
+ * AA
+ */
+
+static VALUE
+rb_io_putc(io, ch)
+ VALUE io, ch;
+{
+ char c = NUM2CHR(ch);
+
+ rb_io_write(io, rb_str_new(&c, 1));
+ return ch;
+}
+
+/*
+ * call-seq:
+ * putc(int) => int
+ *
+ * Equivalent to:
+ *
+ * $stdout.putc(int)
+ */
+
+static VALUE
+rb_f_putc(recv, ch)
+ VALUE recv, ch;
+{
+ return rb_io_putc(rb_stdout, ch);
+}
+
+static VALUE
+io_puts_ary(ary, out)
+ VALUE ary, out;
+{
+ VALUE tmp;
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ if (rb_inspecting_p(tmp)) {
+ tmp = rb_str_new2("[...]");
+ }
+ rb_io_puts(1, &tmp, out);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ios.puts(obj, ...) => nil
+ *
+ * Writes the given objects to <em>ios</em> as with
+ * <code>IO#print</code>. Writes a record separator (typically a
+ * newline) after any that do not already end with a newline sequence.
+ * If called with an array argument, writes each element on a new line.
+ * If called without arguments, outputs a single record separator.
+ *
+ * $stdout.puts("this", "is", "a", "test")
+ *
+ * <em>produces:</em>
+ *
+ * this
+ * is
+ * a
+ * test
+ */
+
+VALUE
+rb_io_puts(argc, argv, out)
+ int argc;
+ VALUE *argv;
+ VALUE out;
+{
+ int i;
+ VALUE line;
+
+ /* if no argument given, print newline. */
+ if (argc == 0) {
+ rb_io_write(out, rb_default_rs);
+ return Qnil;
+ }
+ for (i=0; i<argc; i++) {
+ if (NIL_P(argv[i])) {
+ line = rb_str_new2("nil");
+ }
+ else {
+ line = rb_check_array_type(argv[i]);
+ if (!NIL_P(line)) {
+ rb_protect_inspect(io_puts_ary, line, out);
+ continue;
+ }
+ line = rb_obj_as_string(argv[i]);
+ }
+ rb_io_write(out, line);
+ if (RSTRING(line)->len == 0 ||
+ RSTRING(line)->ptr[RSTRING(line)->len-1] != '\n') {
+ rb_io_write(out, rb_default_rs);
+ }
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * puts(obj, ...) => nil
+ *
+ * Equivalent to
+ *
+ * $stdout.puts(obj, ...)
+ */
+
+static VALUE
+rb_f_puts(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_io_puts(argc, argv, rb_stdout);
+ return Qnil;
+}
+
+void
+rb_p(obj) /* for debug print within C code */
+ VALUE obj;
+{
+ rb_io_write(rb_stdout, rb_obj_as_string(rb_inspect(obj)));
+ rb_io_write(rb_stdout, rb_default_rs);
+}
+
+/*
+ * call-seq:
+ * p(obj, ...) => nil
+ *
+ * For each object, directly writes
+ * _obj_.+inspect+ followed by the current output
+ * record separator to the program's standard output.
+ *
+ * S = Struct.new(:name, :state)
+ * s = S['dave', 'TX']
+ * p s
+ *
+ * <em>produces:</em>
+ *
+ * #<S name="dave", state="TX">
+ */
+
+static VALUE
+rb_f_p(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_p(argv[i]);
+ }
+ if (TYPE(rb_stdout) == T_FILE) {
+ rb_io_flush(rb_stdout);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * obj.display(port=$>) => nil
+ *
+ * Prints <i>obj</i> on the given port (default <code>$></code>).
+ * Equivalent to:
+ *
+ * def display(port=$>)
+ * port.write self
+ * end
+ *
+ * For example:
+ *
+ * 1.display
+ * "cat".display
+ * [ 4, 5, 6 ].display
+ * puts
+ *
+ * <em>produces:</em>
+ *
+ * 1cat456
+ */
+
+static VALUE
+rb_obj_display(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE out;
+
+ if (rb_scan_args(argc, argv, "01", &out) == 0) {
+ out = rb_stdout;
+ }
+
+ rb_io_write(out, self);
+
+ return Qnil;
+}
+
+void
+rb_write_error2(mesg, len)
+ const char *mesg;
+ long len;
+{
+ rb_io_write(rb_stderr, rb_str_new(mesg, len));
+}
+
+void
+rb_write_error(mesg)
+ const char *mesg;
+{
+ rb_write_error2(mesg, strlen(mesg));
+}
+
+static void
+must_respond_to(mid, val, id)
+ ID mid;
+ VALUE val;
+ ID id;
+{
+ if (!rb_respond_to(val, mid)) {
+ rb_raise(rb_eTypeError, "%s must have %s method, %s given",
+ rb_id2name(id), rb_id2name(mid),
+ rb_obj_classname(val));
+ }
+}
+
+static void
+stdout_setter(val, id, variable)
+ VALUE val;
+ ID id;
+ VALUE *variable;
+{
+ must_respond_to(id_write, val, id);
+ *variable = val;
+}
+
+static void
+defout_setter(val, id, variable)
+ VALUE val;
+ ID id;
+ VALUE *variable;
+{
+ stdout_setter(val, id, variable);
+ rb_warn("$defout is obsolete; use $stdout instead");
+}
+
+static void
+deferr_setter(val, id, variable)
+ VALUE val;
+ ID id;
+ VALUE *variable;
+{
+ stdout_setter(val, id, variable);
+ rb_warn("$deferr is obsolete; use $stderr instead");
+}
+
+static VALUE
+prep_stdio(f, mode, klass)
+ FILE *f;
+ int mode;
+ VALUE klass;
+{
+ OpenFile *fp;
+ VALUE io = io_alloc(klass);
+
+ MakeOpenFile(io, fp);
+#ifdef __CYGWIN__
+ if (!isatty(fileno(f))) {
+ mode |= O_BINARY;
+ setmode(fileno(f), O_BINARY);
+ }
+#endif
+ fp->f = f;
+ fp->mode = mode;
+
+ return io;
+}
+
+static void
+prep_path(io, path)
+ VALUE io;
+ char *path;
+{
+ OpenFile *fptr;
+
+ GetOpenFile(io, fptr);
+ if (fptr->path) rb_bug("illegal prep_path() call");
+ fptr->path = strdup(path);
+}
+
+/*
+ * call-seq:
+ * IO.new(fd, mode) => io
+ *
+ * Returns a new <code>IO</code> object (a stream) for the given
+ * integer file descriptor and mode string. See also
+ * <code>IO#fileno</code> and <code>IO::for_fd</code>.
+ *
+ * a = IO.new(2,"w") # '2' is standard error
+ * $stderr.puts "Hello"
+ * a.puts "World"
+ *
+ * <em>produces:</em>
+ *
+ * Hello
+ * World
+ */
+
+static VALUE
+rb_io_initialize(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE fnum, mode;
+ OpenFile *fp;
+ int fd, flags;
+
+ rb_secure(4);
+ rb_scan_args(argc, argv, "11", &fnum, &mode);
+ fd = NUM2INT(fnum);
+ if (argc == 2) {
+ if (FIXNUM_P(mode)) {
+ flags = FIX2LONG(mode);
+ }
+ else {
+ SafeStringValue(mode);
+ flags = rb_io_mode_modenum(StringValueCStr(mode));
+ }
+ }
+ else {
+#if defined(HAVE_FCNTL) && defined(F_GETFL)
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1) rb_sys_fail(0);
+#else
+ flags = O_RDONLY;
+#endif
+ }
+ MakeOpenFile(io, fp);
+ fp->mode = rb_io_modenum_flags(flags);
+ fp->f = rb_fdopen(fd, rb_io_modenum_mode(flags));
+
+ return io;
+}
+
+/*
+ * call-seq:
+ * File.new(filename, mode="r") => file
+ * File.new(filename [, mode [, perm]]) => file
+ *
+
+ * Opens the file named by _filename_ according to
+ * _mode_ (default is ``r'') and returns a new
+ * <code>File</code> object. See the description of class +IO+ for
+ * a description of _mode_. The file mode may optionally be
+ * specified as a +Fixnum+ by _or_-ing together the
+ * flags (O_RDONLY etc, again described under +IO+). Optional
+ * permission bits may be given in _perm_. These mode and permission
+ * bits are platform dependent; on Unix systems, see
+ * <code>open(2)</code> for details.
+ *
+ * f = File.new("testfile", "r")
+ * f = File.new("newfile", "w+")
+ * f = File.new("newfile", File::CREAT|File::TRUNC|File::RDWR, 0644)
+ */
+
+static VALUE
+rb_file_initialize(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ if (RFILE(io)->fptr) {
+ rb_raise(rb_eRuntimeError, "reinitializing File");
+ }
+ if (0 < argc && argc < 3) {
+ VALUE fd = rb_check_convert_type(argv[0], T_FIXNUM, "Fixnum", "to_int");
+
+ if (!NIL_P(fd)) {
+ argv[0] = fd;
+ return rb_io_initialize(argc, argv, io);
+ }
+ }
+ rb_open_file(argc, argv, io);
+
+ return io;
+}
+
+/*
+ * call-seq:
+ * IO.new(fd, mode_string) => io
+ *
+ * Returns a new <code>IO</code> object (a stream) for the given
+ * integer file descriptor and mode string. See also
+ * <code>IO#fileno</code> and <code>IO::for_fd</code>.
+ *
+ * a = IO.new(2,"w") # '2' is standard error
+ * $stderr.puts "Hello"
+ * a.puts "World"
+ *
+ * <em>produces:</em>
+ *
+ * Hello
+ * World
+ */
+
+static VALUE
+rb_io_s_new(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ if (rb_block_given_p()) {
+ char *cname = rb_class2name(klass);
+
+ rb_warn("%s::new() does not take block; use %s::open() instead",
+ cname, cname);
+ }
+ return rb_class_new_instance(argc, argv, klass);
+}
+
+/*
+ * call-seq:
+ * IO.for_fd(fd, mode) => io
+ *
+ * Synonym for <code>IO::new</code>.
+ *
+ */
+
+static VALUE
+rb_io_s_for_fd(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE io = rb_obj_alloc(klass);
+ rb_io_initialize(argc, argv, io);
+ return io;
+}
+
+static int binmode = 0;
+
+static VALUE
+argf_forward(int argc, VALUE *argv)
+{
+ return rb_funcall3(current_file, ruby_frame->last_func, argc, argv);
+}
+
+#define ARGF_FORWARD(argc, argv) do {\
+ if (TYPE(current_file) != T_FILE)\
+ return argf_forward(argc, argv);\
+} while (0)
+#define NEXT_ARGF_FORWARD(argc, argv) do {\
+ if (!next_argv()) return Qnil;\
+ ARGF_FORWARD(argc, argv);\
+} while (0)
+
+static void
+argf_close(file)
+ VALUE file;
+{
+ if (TYPE(file) == T_FILE)
+ rb_io_close(file);
+ else
+ rb_funcall3(file, rb_intern("close"), 0, 0);
+}
+
+static int
+next_argv()
+{
+ extern VALUE rb_argv;
+ char *fn;
+ OpenFile *fptr;
+ int stdout_binmode = 0;
+
+ if (TYPE(rb_stdout) == T_FILE) {
+ GetOpenFile(rb_stdout, fptr);
+ if (fptr->mode & FMODE_BINMODE)
+ stdout_binmode = 1;
+ }
+
+ if (init_p == 0) {
+ if (RARRAY(rb_argv)->len > 0) {
+ next_p = 1;
+ }
+ else {
+ next_p = -1;
+ }
+ init_p = 1;
+ gets_lineno = 0;
+ }
+
+ if (next_p == 1) {
+ next_p = 0;
+ retry:
+ if (RARRAY(rb_argv)->len > 0) {
+ filename = rb_ary_shift(rb_argv);
+ fn = StringValueCStr(filename);
+ if (strlen(fn) == 1 && fn[0] == '-') {
+ current_file = rb_stdin;
+ if (ruby_inplace_mode) {
+ rb_warn("Can't do inplace edit for stdio; skipping");
+ goto retry;
+ }
+ }
+ else {
+ FILE *fr = rb_fopen(fn, "r");
+
+ if (ruby_inplace_mode) {
+ struct stat st, st2;
+ VALUE str;
+ FILE *fw;
+
+ if (TYPE(rb_stdout) == T_FILE && rb_stdout != orig_stdout) {
+ rb_io_close(rb_stdout);
+ }
+ fstat(fileno(fr), &st);
+ if (*ruby_inplace_mode) {
+ str = rb_str_new2(fn);
+#ifdef NO_LONG_FNAME
+ ruby_add_suffix(str, ruby_inplace_mode);
+#else
+ rb_str_cat2(str, ruby_inplace_mode);
+#endif
+#ifdef NO_SAFE_RENAME
+ (void)fclose(fr);
+ (void)unlink(RSTRING(str)->ptr);
+ (void)rename(fn, RSTRING(str)->ptr);
+ fr = rb_fopen(RSTRING(str)->ptr, "r");
+#else
+ if (rename(fn, RSTRING(str)->ptr) < 0) {
+ rb_warn("Can't rename %s to %s: %s, skipping file",
+ fn, RSTRING(str)->ptr, strerror(errno));
+ fclose(fr);
+ goto retry;
+ }
+#endif
+ }
+ else {
+#ifdef NO_SAFE_RENAME
+ rb_fatal("Can't do inplace edit without backup");
+#else
+ if (unlink(fn) < 0) {
+ rb_warn("Can't remove %s: %s, skipping file",
+ fn, strerror(errno));
+ fclose(fr);
+ goto retry;
+ }
+#endif
+ }
+ fw = rb_fopen(fn, "w");
+#ifndef NO_SAFE_RENAME
+ fstat(fileno(fw), &st2);
+#ifdef HAVE_FCHMOD
+ fchmod(fileno(fw), st.st_mode);
+#else
+ chmod(fn, st.st_mode);
+#endif
+ if (st.st_uid!=st2.st_uid || st.st_gid!=st2.st_gid) {
+ fchown(fileno(fw), st.st_uid, st.st_gid);
+ }
+#endif
+ rb_stdout = prep_stdio(fw, FMODE_WRITABLE, rb_cFile);
+ prep_path(rb_stdout, fn);
+ if (stdout_binmode) rb_io_binmode(rb_stdout);
+ }
+ current_file = prep_stdio(fr, FMODE_READABLE, rb_cFile);
+ prep_path(current_file, fn);
+ }
+ if (binmode) rb_io_binmode(current_file);
+ }
+ else {
+ next_p = 1;
+ return Qfalse;
+ }
+ }
+ else if (next_p == -1) {
+ current_file = rb_stdin;
+ filename = rb_str_new2("-");
+ if (ruby_inplace_mode) {
+ rb_warn("Can't do inplace edit for stdio");
+ rb_stdout = orig_stdout;
+ }
+ }
+ return Qtrue;
+}
+
+static VALUE
+argf_getline(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE line;
+
+ retry:
+ if (!next_argv()) return Qnil;
+ if (argc == 0 && rb_rs == rb_default_rs) {
+ line = rb_io_gets(current_file);
+ }
+ else {
+ VALUE rs;
+
+ if (argc == 0) {
+ rs = rb_rs;
+ }
+ else {
+ rb_scan_args(argc, argv, "1", &rs);
+ if (!NIL_P(rs)) StringValue(rs);
+ }
+ line = rb_io_getline(rs, current_file);
+ }
+ if (NIL_P(line) && next_p != -1) {
+ argf_close(current_file);
+ next_p = 1;
+ goto retry;
+ }
+ if (!NIL_P(line)) {
+ gets_lineno++;
+ lineno = INT2FIX(gets_lineno);
+ }
+ return line;
+}
+
+/*
+ * call-seq:
+ * gets(separator=$/) => string or nil
+ *
+ * Returns (and assigns to <code>$_</code>) the next line from the list
+ * of files in +ARGV+ (or <code>$*</code>), or from standard
+ * input if no files are present on the command line. Returns
+ * +nil+ at end of file. The optional argument specifies the
+ * record separator. The separator is included with the contents of
+ * each record. A separator of +nil+ reads the entire
+ * contents, and a zero-length separator reads the input one paragraph
+ * at a time, where paragraphs are divided by two consecutive newlines.
+ * If multiple filenames are present in +ARGV+,
+ * +gets(nil)+ will read the contents one file at a time.
+ *
+ * ARGV << "testfile"
+ * print while gets
+ *
+ * <em>produces:</em>
+ *
+ * This is line one
+ * This is line two
+ * This is line three
+ * And so on...
+ *
+ * The style of programming using <code>$_</code> as an implicit
+ * parameter is gradually losing favor in the Ruby community.
+ */
+
+static VALUE
+rb_f_gets(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE line;
+
+ if (!next_argv()) return Qnil;
+ if (TYPE(current_file) != T_FILE) {
+ line = rb_funcall3(current_file, rb_intern("gets"), argc, argv);
+ }
+ else {
+ line = argf_getline(argc, argv);
+ }
+ rb_lastline_set(line);
+ return line;
+}
+
+VALUE
+rb_gets()
+{
+ VALUE line;
+
+ if (rb_rs != rb_default_rs) {
+ return rb_f_gets(0, 0);
+ }
+
+ retry:
+ if (!next_argv()) return Qnil;
+ line = rb_io_gets(current_file);
+ if (NIL_P(line) && next_p != -1) {
+ argf_close(current_file);
+ next_p = 1;
+ goto retry;
+ }
+ rb_lastline_set(line);
+ if (!NIL_P(line)) {
+ gets_lineno++;
+ lineno = INT2FIX(gets_lineno);
+ }
+
+ return line;
+}
+
+/*
+ * call-seq:
+ * readline(separator=$/) => string
+ *
+ * Equivalent to <code>Kernel::gets</code>, except
+ * +readline+ raises +EOFError+ at end of file.
+ */
+
+static VALUE
+rb_f_readline(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE line;
+
+ if (!next_argv()) rb_eof_error();
+ ARGF_FORWARD(argc, argv);
+ line = rb_f_gets(argc, argv);
+ if (NIL_P(line)) {
+ rb_eof_error();
+ }
+
+ return line;
+}
+
+/*
+ * obsolete
+ */
+
+static VALUE
+rb_f_getc()
+{
+ rb_warn("getc is obsolete; use STDIN.getc instead");
+ if (TYPE(rb_stdin) != T_FILE) {
+ return rb_funcall3(rb_stdin, rb_intern("getc"), 0, 0);
+ }
+ return rb_io_getc(rb_stdin);
+}
+
+/*
+ * call-seq:
+ * readlines(separator=$/) => array
+ *
+ * Returns an array containing the lines returned by calling
+ * <code>Kernel.gets(<i>separator</i>)</code> until the end of file.
+ */
+
+static VALUE
+rb_f_readlines(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE line, ary;
+
+ NEXT_ARGF_FORWARD(argc, argv);
+ ary = rb_ary_new();
+ while (!NIL_P(line = argf_getline(argc, argv))) {
+ rb_ary_push(ary, line);
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * `cmd` => string
+ *
+ * Returns the standard output of running _cmd_ in a subshell.
+ * The built-in syntax <code>%x{...}</code> uses
+ * this method. Sets <code>$?</code> to the process status.
+ *
+ * `date` #=> "Wed Apr 9 08:56:30 CDT 2003\n"
+ * `ls testdir`.split[1] #=> "main.rb"
+ * `echo oops && exit 99` #=> "oops\n"
+ * $?.exitstatus #=> 99
+ */
+
+static VALUE
+rb_f_backquote(obj, str)
+ VALUE obj, str;
+{
+ volatile VALUE port;
+ VALUE result;
+ OpenFile *fptr;
+
+ SafeStringValue(str);
+ port = pipe_open(str, 0, "r");
+ if (NIL_P(port)) return rb_str_new(0,0);
+
+ GetOpenFile(port, fptr);
+ result = read_all(fptr, remain_size(fptr), Qnil);
+ rb_io_close(port);
+
+ return result;
+}
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/*
+ * call-seq:
+ * IO.select(read_array
+ * [, write_array
+ * [, error_array
+ * [, timeout]]] ) => array or nil
+ *
+ * See <code>Kernel#select</code>.
+ */
+
+static VALUE
+rb_f_select(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE read, write, except, timeout, res, list;
+ fd_set rset, wset, eset, pset;
+ fd_set *rp, *wp, *ep;
+ struct timeval *tp, timerec;
+ OpenFile *fptr;
+ long i;
+ int max = 0, n;
+ int interrupt_flag = 0;
+ int pending = 0;
+
+ rb_scan_args(argc, argv, "13", &read, &write, &except, &timeout);
+ if (NIL_P(timeout)) {
+ tp = 0;
+ }
+ else {
+ timerec = rb_time_interval(timeout);
+ tp = &timerec;
+ }
+
+ FD_ZERO(&pset);
+ if (!NIL_P(read)) {
+ Check_Type(read, T_ARRAY);
+ rp = &rset;
+ FD_ZERO(rp);
+ for (i=0; i<RARRAY(read)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(read)->ptr[i]), fptr);
+ FD_SET(fileno(fptr->f), rp);
+ if (READ_DATA_PENDING(fptr->f)) { /* check for buffered data */
+ pending++;
+ FD_SET(fileno(fptr->f), &pset);
+ }
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
+ }
+ if (pending) { /* no blocking if there's buffered data */
+ timerec.tv_sec = timerec.tv_usec = 0;
+ tp = &timerec;
+ }
+ }
+ else
+ rp = 0;
+
+ if (!NIL_P(write)) {
+ Check_Type(write, T_ARRAY);
+ wp = &wset;
+ FD_ZERO(wp);
+ for (i=0; i<RARRAY(write)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(write)->ptr[i]), fptr);
+ FD_SET(fileno(fptr->f), wp);
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
+ if (fptr->f2) {
+ FD_SET(fileno(fptr->f2), wp);
+ if (max < fileno(fptr->f2)) max = fileno(fptr->f2);
+ }
+ }
+ }
+ else
+ wp = 0;
+
+ if (!NIL_P(except)) {
+ Check_Type(except, T_ARRAY);
+ ep = &eset;
+ FD_ZERO(ep);
+ for (i=0; i<RARRAY(except)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(except)->ptr[i]), fptr);
+ FD_SET(fileno(fptr->f), ep);
+ if (max < fileno(fptr->f)) max = fileno(fptr->f);
+ if (fptr->f2) {
+ FD_SET(fileno(fptr->f2), ep);
+ if (max < fileno(fptr->f2)) max = fileno(fptr->f2);
+ }
+ }
+ }
+ else {
+ ep = 0;
+ }
+
+ max++;
+
+ n = rb_thread_select(max, rp, wp, ep, tp);
+ if (n < 0) {
+ rb_sys_fail(0);
+ }
+ if (!pending && n == 0) return Qnil; /* returns nil on timeout */
+
+ res = rb_ary_new2(3);
+ rb_ary_push(res, rp?rb_ary_new():rb_ary_new2(0));
+ rb_ary_push(res, wp?rb_ary_new():rb_ary_new2(0));
+ rb_ary_push(res, ep?rb_ary_new():rb_ary_new2(0));
+
+ if (interrupt_flag == 0) {
+ if (rp) {
+ list = RARRAY(res)->ptr[0];
+ for (i=0; i< RARRAY(read)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(read)->ptr[i]), fptr);
+ if (FD_ISSET(fileno(fptr->f), rp)
+ || FD_ISSET(fileno(fptr->f), &pset)) {
+ rb_ary_push(list, rb_ary_entry(read, i));
+ }
+ }
+ }
+
+ if (wp) {
+ list = RARRAY(res)->ptr[1];
+ for (i=0; i< RARRAY(write)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(write)->ptr[i]), fptr);
+ if (FD_ISSET(fileno(fptr->f), wp)) {
+ rb_ary_push(list, rb_ary_entry(write, i));
+ }
+ else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), wp)) {
+ rb_ary_push(list, rb_ary_entry(write, i));
+ }
+ }
+ }
+
+ if (ep) {
+ list = RARRAY(res)->ptr[2];
+ for (i=0; i< RARRAY(except)->len; i++) {
+ GetOpenFile(rb_io_get_io(RARRAY(except)->ptr[i]), fptr);
+ if (FD_ISSET(fileno(fptr->f), ep)) {
+ rb_ary_push(list, rb_ary_entry(except, i));
+ }
+ else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), ep)) {
+ rb_ary_push(list, rb_ary_entry(except, i));
+ }
+ }
+ }
+ }
+
+ return res; /* returns an empty array on interrupt */
+}
+
+#if !defined(MSDOS) && !defined(__human68k__)
+static int
+io_cntl(fd, cmd, narg, io_p)
+ int fd, cmd, io_p;
+ long narg;
+{
+ int retval;
+
+#ifdef HAVE_FCNTL
+ TRAP_BEG;
+# if defined(__CYGWIN__)
+ retval = io_p?ioctl(fd, cmd, (void*)narg):fcntl(fd, cmd, narg);
+# else
+ retval = io_p?ioctl(fd, cmd, narg):fcntl(fd, cmd, narg);
+# endif
+ TRAP_END;
+#else
+ if (!io_p) {
+ rb_notimplement();
+ }
+ TRAP_BEG;
+ retval = ioctl(fd, cmd, narg);
+ TRAP_END;
+#endif
+ return retval;
+}
+#endif
+
+static VALUE
+rb_io_ctl(io, req, arg, io_p)
+ VALUE io, req, arg;
+ int io_p;
+{
+#if !defined(MSDOS) && !defined(__human68k__)
+ int cmd = NUM2ULONG(req);
+ OpenFile *fptr;
+ long len = 0;
+ long narg = 0;
+ int retval;
+
+ rb_secure(2);
+
+ if (NIL_P(arg) || arg == Qfalse) {
+ narg = 0;
+ }
+ else if (FIXNUM_P(arg)) {
+ narg = FIX2LONG(arg);
+ }
+ else if (arg == Qtrue) {
+ narg = 1;
+ }
+ else {
+ VALUE tmp = rb_check_string_type(arg);
+
+ if (NIL_P(tmp)) {
+ narg = NUM2LONG(arg);
+ }
+ else {
+ arg = tmp;
+#ifdef IOCPARM_MASK
+#ifndef IOCPARM_LEN
+#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
+#endif
+#endif
+#ifdef IOCPARM_LEN
+ len = IOCPARM_LEN(cmd); /* on BSDish systems we're safe */
+#else
+ len = 256; /* otherwise guess at what's safe */
+#endif
+ rb_str_modify(arg);
+
+ if (len <= RSTRING(arg)->len) {
+ len = RSTRING(arg)->len;
+ }
+ if (RSTRING(arg)->len < len) {
+ rb_str_resize(arg, len+1);
+ }
+ RSTRING(arg)->ptr[len] = 17; /* a little sanity check here */
+ narg = (long)RSTRING(arg)->ptr;
+ }
+ }
+ GetOpenFile(io, fptr);
+ retval = io_cntl(fileno(fptr->f), cmd, narg, io_p);
+ if (retval < 0) rb_sys_fail(fptr->path);
+ if (TYPE(arg) == T_STRING && RSTRING(arg)->ptr[len] != 17) {
+ rb_raise(rb_eArgError, "return value overflowed string");
+ }
+
+ if (fptr->f2 && fileno(fptr->f) != fileno(fptr->f2)) {
+ /* call on f2 too; ignore result */
+ io_cntl(fileno(fptr->f2), cmd, narg, io_p);
+ }
+
+ if (!io_p && cmd == F_SETFL) {
+ if (narg & O_NONBLOCK) {
+ fptr->mode |= FMODE_WSPLIT_INITIALIZED;
+ fptr->mode &= ~FMODE_WSPLIT;
+ }
+ else {
+ fptr->mode &= ~(FMODE_WSPLIT_INITIALIZED|FMODE_WSPLIT);
+ }
+ }
+
+ return INT2NUM(retval);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * ios.ioctl(integer_cmd, arg) => integer
+ *
+ * Provides a mechanism for issuing low-level commands to control or
+ * query I/O devices. Arguments and results are platform dependent. If
+ * <i>arg</i> is a number, its value is passed directly. If it is a
+ * string, it is interpreted as a binary sequence of bytes. On Unix
+ * platforms, see <code>ioctl(2)</code> for details. Not implemented on
+ * all platforms.
+ */
+
+static VALUE
+rb_io_ioctl(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE req, arg;
+
+ rb_scan_args(argc, argv, "11", &req, &arg);
+ return rb_io_ctl(io, req, arg, 1);
+}
+
+/*
+ * call-seq:
+ * ios.fcntl(integer_cmd, arg) => integer
+ *
+ * Provides a mechanism for issuing low-level commands to control or
+ * query file-oriented I/O streams. Arguments and results are platform
+ * dependent. If <i>arg</i> is a number, its value is passed
+ * directly. If it is a string, it is interpreted as a binary sequence
+ * of bytes (<code>Array#pack</code> might be a useful way to build this
+ * string). On Unix platforms, see <code>fcntl(2)</code> for details.
+ * Not implemented on all platforms.
+ */
+
+static VALUE
+rb_io_fcntl(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+#ifdef HAVE_FCNTL
+ VALUE req, arg;
+
+ rb_scan_args(argc, argv, "11", &req, &arg);
+ return rb_io_ctl(io, req, arg, 0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * syscall(fixnum [, args...]) => integer
+ *
+ * Calls the operating system function identified by _fixnum_,
+ * passing in the arguments, which must be either +String+
+ * objects, or +Integer+ objects that ultimately fit within
+ * a native +long+. Up to nine parameters may be passed (14
+ * on the Atari-ST). The function identified by _fixnum_ is system
+ * dependent. On some Unix systems, the numbers may be obtained from a
+ * header file called <code>syscall.h</code>.
+ *
+ * syscall 4, 1, "hello\n", 6 # '4' is write(2) on our box
+ *
+ * <em>produces:</em>
+ *
+ * hello
+ */
+
+static VALUE
+rb_f_syscall(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+#if defined(HAVE_SYSCALL) && !defined(__CHECKER__)
+#ifdef atarist
+ unsigned long arg[14]; /* yes, we really need that many ! */
+#else
+ unsigned long arg[8];
+#endif
+ int retval = -1;
+ int i = 1;
+ int items = argc - 1;
+
+ /* This probably won't work on machines where sizeof(long) != sizeof(int)
+ * or where sizeof(long) != sizeof(char*). But such machines will
+ * not likely have syscall implemented either, so who cares?
+ */
+
+ rb_secure(2);
+ if (argc == 0)
+ rb_raise(rb_eArgError, "too few arguments for syscall");
+ if (argc > sizeof(arg) / sizeof(arg[0]))
+ rb_raise(rb_eArgError, "too many arguments for syscall");
+ arg[0] = NUM2LONG(argv[0]); argv++;
+ while (items--) {
+ VALUE v = rb_check_string_type(*argv);
+
+ if (!NIL_P(v)) {
+ StringValue(v);
+ rb_str_modify(v);
+ arg[i] = (unsigned long)StringValueCStr(v);
+ }
+ else {
+ arg[i] = (unsigned long)NUM2LONG(*argv);
+ }
+ argv++;
+ i++;
+ }
+ TRAP_BEG;
+ switch (argc) {
+ case 1:
+ retval = syscall(arg[0]);
+ break;
+ case 2:
+ retval = syscall(arg[0],arg[1]);
+ break;
+ case 3:
+ retval = syscall(arg[0],arg[1],arg[2]);
+ break;
+ case 4:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3]);
+ break;
+ case 5:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4]);
+ break;
+ case 6:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5]);
+ break;
+ case 7:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6]);
+ break;
+ case 8:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7]);
+ break;
+#ifdef atarist
+ case 9:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8]);
+ break;
+ case 10:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9]);
+ break;
+ case 11:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10]);
+ break;
+ case 12:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11]);
+ break;
+ case 13:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11], arg[12]);
+ break;
+ case 14:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11], arg[12], arg[13]);
+ break;
+#endif /* atarist */
+ }
+ TRAP_END;
+ if (retval < 0) rb_sys_fail(0);
+ return INT2NUM(retval);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static VALUE io_new_instance _((VALUE));
+static VALUE
+io_new_instance(args)
+ VALUE args;
+{
+ return rb_class_new_instance(2, (VALUE*)args+1, *(VALUE*)args);
+}
+
+/*
+ * call-seq:
+ * IO.pipe -> array
+ *
+ * Creates a pair of pipe endpoints (connected to each other) and
+ * returns them as a two-element array of <code>IO</code> objects:
+ * <code>[</code> <i>read_file</i>, <i>write_file</i> <code>]</code>. Not
+ * available on all platforms.
+ *
+ * In the example below, the two processes close the ends of the pipe
+ * that they are not using. This is not just a cosmetic nicety. The
+ * read end of a pipe will not generate an end of file condition if
+ * there are any writers with the pipe still open. In the case of the
+ * parent process, the <code>rd.read</code> will never return if it
+ * does not first issue a <code>wr.close</code>.
+ *
+ * rd, wr = IO.pipe
+ *
+ * if fork
+ * wr.close
+ * puts "Parent got: <#{rd.read}>"
+ * rd.close
+ * Process.wait
+ * else
+ * rd.close
+ * puts "Sending message to parent"
+ * wr.write "Hi Dad"
+ * wr.close
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Sending message to parent
+ * Parent got: <Hi Dad>
+ */
+
+static VALUE
+rb_io_s_pipe(klass)
+ VALUE klass;
+{
+#ifndef __human68k__
+ int pipes[2], state;
+ VALUE r, w, args[3];
+
+#ifdef _WIN32
+ if (_pipe(pipes, 1024, O_BINARY) == -1)
+#else
+ if (pipe(pipes) == -1)
+#endif
+ rb_sys_fail(0);
+
+ args[0] = klass;
+ args[1] = INT2NUM(pipes[0]);
+ args[2] = INT2FIX(O_RDONLY);
+ r = rb_protect(io_new_instance, (VALUE)args, &state);
+ if (state) {
+ close(pipes[0]);
+ close(pipes[1]);
+ rb_jump_tag(state);
+ }
+ args[1] = INT2NUM(pipes[1]);
+ args[2] = INT2FIX(O_WRONLY);
+ w = rb_protect(io_new_instance, (VALUE)args, &state);
+ if (state) {
+ close(pipes[1]);
+ if (!NIL_P(r)) rb_io_close(r);
+ rb_jump_tag(state);
+ }
+ rb_io_synchronized(RFILE(w)->fptr);
+
+ return rb_assoc_new(r, w);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+struct foreach_arg {
+ int argc;
+ VALUE sep;
+ VALUE io;
+};
+
+static VALUE
+io_s_foreach(arg)
+ struct foreach_arg *arg;
+{
+ VALUE str;
+
+ while (!NIL_P(str = rb_io_getline(arg->sep, arg->io))) {
+ rb_yield(str);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * IO.foreach(name, sep_string=$/) {|line| block } => nil
+ *
+ * Executes the block for every line in the named I/O port, where lines
+ * are separated by <em>sep_string</em>.
+ *
+ * IO.foreach("testfile") {|x| print "GOT ", x }
+ *
+ * <em>produces:</em>
+ *
+ * GOT This is line one
+ * GOT This is line two
+ * GOT This is line three
+ * GOT And so on...
+ */
+
+static VALUE
+rb_io_s_foreach(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname;
+ struct foreach_arg arg;
+
+ rb_scan_args(argc, argv, "11", &fname, &arg.sep);
+ SafeStringValue(fname);
+
+ if (argc == 1) {
+ arg.sep = rb_default_rs;
+ }
+ else if (!NIL_P(arg.sep)) {
+ StringValue(arg.sep);
+ }
+ arg.io = rb_io_open(StringValueCStr(fname), "r");
+ if (NIL_P(arg.io)) return Qnil;
+
+ return rb_ensure(io_s_foreach, (VALUE)&arg, rb_io_close, arg.io);
+}
+
+static VALUE
+io_s_readlines(arg)
+ struct foreach_arg *arg;
+{
+ return rb_io_readlines(arg->argc, &arg->sep, arg->io);
+}
+
+/*
+ * call-seq:
+ * IO.readlines(name, sep_string=$/) => array
+ *
+ * Reads the entire file specified by <i>name</i> as individual
+ * lines, and returns those lines in an array. Lines are separated by
+ * <i>sep_string</i>.
+ *
+ * a = IO.readlines("testfile")
+ * a[0] #=> "This is line one\n"
+ *
+ */
+
+static VALUE
+rb_io_s_readlines(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE fname;
+ struct foreach_arg arg;
+
+ rb_scan_args(argc, argv, "11", &fname, &arg.sep);
+ SafeStringValue(fname);
+
+ arg.argc = argc - 1;
+ arg.io = rb_io_open(StringValueCStr(fname), "r");
+ if (NIL_P(arg.io)) return Qnil;
+ return rb_ensure(io_s_readlines, (VALUE)&arg, rb_io_close, arg.io);
+}
+
+static VALUE
+io_s_read(arg)
+ struct foreach_arg *arg;
+{
+ return io_read(arg->argc, &arg->sep, arg->io);
+}
+
+/*
+ * call-seq:
+ * IO.read(name, [length [, offset]] ) => string
+ *
+ * Opens the file, optionally seeks to the given offset, then returns
+ * <i>length</i> bytes (defaulting to the rest of the file).
+ * <code>read</code> ensures the file is closed before returning.
+ *
+ * IO.read("testfile") #=> "This is line one\nThis is line two\nThis is line three\nAnd so on...\n"
+ * IO.read("testfile", 20) #=> "This is line one\nThi"
+ * IO.read("testfile", 20, 10) #=> "ne one\nThis is line "
+ */
+
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ VALUE fname, offset;
+ struct foreach_arg arg;
+
+ rb_scan_args(argc, argv, "12", &fname, &arg.sep, &offset);
+ SafeStringValue(fname);
+
+ arg.argc = argc ? 1 : 0;
+ arg.io = rb_io_open(StringValueCStr(fname), "r");
+ if (NIL_P(arg.io)) return Qnil;
+ if (!NIL_P(offset)) {
+ rb_io_seek(arg.io, offset, SEEK_SET);
+ }
+ return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
+}
+
+static VALUE
+argf_tell()
+{
+ if (!next_argv()) {
+ rb_raise(rb_eArgError, "no stream to tell");
+ }
+ ARGF_FORWARD(0, 0);
+ return rb_io_tell(current_file);
+}
+
+static VALUE
+argf_seek_m(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ if (!next_argv()) {
+ rb_raise(rb_eArgError, "no stream to seek");
+ }
+ ARGF_FORWARD(argc, argv);
+ return rb_io_seek_m(argc, argv, current_file);
+}
+
+static VALUE
+argf_set_pos(self, offset)
+ VALUE self, offset;
+{
+ if (!next_argv()) {
+ rb_raise(rb_eArgError, "no stream to set position");
+ }
+ ARGF_FORWARD(1, &offset);
+ return rb_io_set_pos(current_file, offset);
+}
+
+static VALUE
+argf_rewind()
+{
+ if (!next_argv()) {
+ rb_raise(rb_eArgError, "no stream to rewind");
+ }
+ ARGF_FORWARD(0, 0);
+ return rb_io_rewind(current_file);
+}
+
+static VALUE
+argf_fileno()
+{
+ if (!next_argv()) {
+ rb_raise(rb_eArgError, "no stream");
+ }
+ ARGF_FORWARD(0, 0);
+ return rb_io_fileno(current_file);
+}
+
+static VALUE
+argf_to_io()
+{
+ next_argv();
+ ARGF_FORWARD(0, 0);
+ return current_file;
+}
+
+static VALUE
+argf_eof()
+{
+ if (current_file) {
+ if (init_p == 0) return Qtrue;
+ ARGF_FORWARD(0, 0);
+ if (rb_io_eof(current_file)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+static VALUE
+argf_read(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tmp, str, length;
+ long len = 0;
+
+ rb_scan_args(argc, argv, "02", &length, &str);
+ if (!NIL_P(length)) {
+ len = NUM2LONG(argv[0]);
+ }
+ if (!NIL_P(str)) {
+ StringValue(str);
+ rb_str_resize(str,0);
+ argv[1] = Qnil;
+ }
+
+ retry:
+ if (!next_argv()) {
+ return str;
+ }
+ if (TYPE(current_file) != T_FILE) {
+ tmp = argf_forward(argc, argv);
+ }
+ else {
+ tmp = io_read(argc, argv, current_file);
+ }
+ if (NIL_P(str)) str = tmp;
+ else if (!NIL_P(tmp)) rb_str_append(str, tmp);
+ if (NIL_P(tmp) || NIL_P(length)) {
+ if (next_p != -1) {
+ argf_close(current_file);
+ next_p = 1;
+ goto retry;
+ }
+ }
+ else if (argc >= 1) {
+ if (RSTRING(str)->len < len) {
+ len -= RSTRING(str)->len;
+ argv[0] = INT2NUM(len);
+ goto retry;
+ }
+ }
+ return str;
+}
+
+static VALUE
+argf_getc()
+{
+ VALUE byte;
+
+ retry:
+ if (!next_argv()) return Qnil;
+ if (TYPE(current_file) != T_FILE) {
+ byte = rb_funcall3(current_file, rb_intern("getc"), 0, 0);
+ }
+ else {
+ byte = rb_io_getc(current_file);
+ }
+ if (NIL_P(byte) && next_p != -1) {
+ argf_close(current_file);
+ next_p = 1;
+ goto retry;
+ }
+
+ return byte;
+}
+
+static VALUE
+argf_readchar()
+{
+ VALUE c;
+
+ NEXT_ARGF_FORWARD(0, 0);
+ c = argf_getc();
+ if (NIL_P(c)) {
+ rb_eof_error();
+ }
+ return c;
+}
+
+static VALUE
+argf_each_line(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE str;
+
+ if (!next_argv()) return Qnil;
+ if (TYPE(current_file) != T_FILE) {
+ for (;;) {
+ if (!next_argv()) return argf;
+ rb_iterate(rb_each, current_file, rb_yield, 0);
+ next_p = 1;
+ }
+ }
+ while (!NIL_P(str = argf_getline(argc, argv))) {
+ rb_yield(str);
+ }
+ return argf;
+}
+
+static VALUE
+argf_each_byte()
+{
+ VALUE byte;
+
+ while (!NIL_P(byte = argf_getc())) {
+ rb_yield(byte);
+ }
+ return argf;
+}
+
+static VALUE
+argf_filename()
+{
+ next_argv();
+ return filename;
+}
+
+static VALUE
+argf_file()
+{
+ next_argv();
+ return current_file;
+}
+
+static VALUE
+argf_binmode()
+{
+ binmode = 1;
+ next_argv();
+ ARGF_FORWARD(0, 0);
+ rb_io_binmode(current_file);
+ return argf;
+}
+
+static VALUE
+argf_skip()
+{
+ if (next_p != -1) {
+ argf_close(current_file);
+ next_p = 1;
+ }
+ return argf;
+}
+
+static VALUE
+argf_close_m()
+{
+ next_argv();
+ argf_close(current_file);
+ if (next_p != -1) {
+ next_p = 1;
+ }
+ gets_lineno = 0;
+ return argf;
+}
+
+static VALUE
+argf_closed()
+{
+ next_argv();
+ ARGF_FORWARD(0, 0);
+ return rb_io_closed(current_file);
+}
+
+static VALUE
+argf_to_s()
+{
+ return rb_str_new2("ARGF");
+}
+
+static VALUE
+opt_i_get()
+{
+ if (!ruby_inplace_mode) return Qnil;
+ return rb_str_new2(ruby_inplace_mode);
+}
+
+static void
+opt_i_set(val)
+ VALUE val;
+{
+ if (!RTEST(val)) {
+ if (ruby_inplace_mode) free(ruby_inplace_mode);
+ ruby_inplace_mode = 0;
+ return;
+ }
+ StringValue(val);
+ if (ruby_inplace_mode) free(ruby_inplace_mode);
+ ruby_inplace_mode = 0;
+ ruby_inplace_mode = strdup(StringValueCStr(val));
+}
+
+/*
+ * Class <code>IO</code> is the basis for all input and output in Ruby.
+ * An I/O stream may be <em>duplexed</em> (that is, bidirectional), and
+ * so may use more than one native operating system stream.
+ *
+ * Many of the examples in this section use class <code>File</code>,
+ * the only standard subclass of <code>IO</code>. The two classes are
+ * closely associated.
+ *
+ * As used in this section, <em>portname</em> may take any of the
+ * following forms.
+ *
+ * * A plain string represents a filename suitable for the underlying
+ * operating system.
+ *
+ * * A string starting with ``<code>|</code>'' indicates a subprocess.
+ * The remainder of the string following the ``<code>|</code>'' is
+ * invoked as a process with appropriate input/output channels
+ * connected to it.
+ *
+ * * A string equal to ``<code>|-</code>'' will create another Ruby
+ * instance as a subprocess.
+ *
+ * Ruby will convert pathnames between different operating system
+ * conventions if possible. For instance, on a Windows system the
+ * filename ``<code>/gumby/ruby/test.rb</code>'' will be opened as
+ * ``<code>\gumby\ruby\test.rb</code>''. When specifying a
+ * Windows-style filename in a Ruby string, remember to escape the
+ * backslashes:
+ *
+ * "c:\\gumby\\ruby\\test.rb"
+ *
+ * Our examples here will use the Unix-style forward slashes;
+ * <code>File::SEPARATOR</code> can be used to get the
+ * platform-specific separator character.
+ *
+ * I/O ports may be opened in any one of several different modes, which
+ * are shown in this section as <em>mode</em>. The mode may
+ * either be a Fixnum or a String. If numeric, it should be
+ * one of the operating system specific constants (O_RDONLY,
+ * O_WRONLY, O_RDWR, O_APPEND and so on). See man open(2) for
+ * more information.
+ *
+ * If the mode is given as a String, it must be one of the
+ * values listed in the following table.
+ *
+ * Mode | Meaning
+ * -----+--------------------------------------------------------
+ * "r" | Read-only, starts at beginning of file (default mode).
+ * -----+--------------------------------------------------------
+ * "r+" | Read-write, starts at beginning of file.
+ * -----+--------------------------------------------------------
+ * "w" | Write-only, truncates existing file
+ * | to zero length or creates a new file for writing.
+ * -----+--------------------------------------------------------
+ * "w+" | Read-write, truncates existing file to zero length
+ * | or creates a new file for reading and writing.
+ * -----+--------------------------------------------------------
+ * "a" | Write-only, starts at end of file if file exists,
+ * | otherwise creates a new file for writing.
+ * -----+--------------------------------------------------------
+ * "a+" | Read-write, starts at end of file if file exists,
+ * | otherwise creates a new file for reading and
+ * | writing.
+ * -----+--------------------------------------------------------
+ * "b" | (DOS/Windows only) Binary file mode (may appear with
+ * | any of the key letters listed above).
+ *
+ *
+ * The global constant ARGF (also accessible as $<) provides an
+ * IO-like stream which allows access to all files mentioned on the
+ * command line (or STDIN if no files are mentioned). ARGF provides
+ * the methods <code>#path</code> and <code>#filename</code> to access
+ * the name of the file currently being read.
+ */
+
+void
+Init_IO()
+{
+#ifdef __CYGWIN__
+#include <sys/cygwin.h>
+ static struct __cygwin_perfile pf[] =
+ {
+ {"", O_RDONLY | O_BINARY},
+ {"", O_WRONLY | O_BINARY},
+ {"", O_RDWR | O_BINARY},
+ {"", O_APPEND | O_BINARY},
+ {NULL, 0}
+ };
+ cygwin_internal(CW_PERFILE, pf);
+#endif
+
+ rb_eIOError = rb_define_class("IOError", rb_eStandardError);
+ rb_eEOFError = rb_define_class("EOFError", rb_eIOError);
+
+ id_write = rb_intern("write");
+ id_read = rb_intern("read");
+ id_getc = rb_intern("getc");
+
+ rb_define_global_function("syscall", rb_f_syscall, -1);
+
+ rb_define_global_function("open", rb_f_open, -1);
+ rb_define_global_function("printf", rb_f_printf, -1);
+ rb_define_global_function("print", rb_f_print, -1);
+ rb_define_global_function("putc", rb_f_putc, 1);
+ rb_define_global_function("puts", rb_f_puts, -1);
+ rb_define_global_function("gets", rb_f_gets, -1);
+ rb_define_global_function("readline", rb_f_readline, -1);
+ rb_define_global_function("getc", rb_f_getc, 0);
+ rb_define_global_function("select", rb_f_select, -1);
+
+ rb_define_global_function("readlines", rb_f_readlines, -1);
+
+ rb_define_global_function("`", rb_f_backquote, 1);
+
+ rb_define_global_function("p", rb_f_p, -1);
+ rb_define_method(rb_mKernel, "display", rb_obj_display, -1);
+
+ rb_cIO = rb_define_class("IO", rb_cObject);
+ rb_include_module(rb_cIO, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cIO, io_alloc);
+ rb_define_singleton_method(rb_cIO, "new", rb_io_s_new, -1);
+ rb_define_singleton_method(rb_cIO, "open", rb_io_s_open, -1);
+ rb_define_singleton_method(rb_cIO, "sysopen", rb_io_s_sysopen, -1);
+ rb_define_singleton_method(rb_cIO, "for_fd", rb_io_s_for_fd, -1);
+ rb_define_singleton_method(rb_cIO, "popen", rb_io_s_popen, -1);
+ rb_define_singleton_method(rb_cIO, "foreach", rb_io_s_foreach, -1);
+ rb_define_singleton_method(rb_cIO, "readlines", rb_io_s_readlines, -1);
+ rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
+ rb_define_singleton_method(rb_cIO, "select", rb_f_select, -1);
+ rb_define_singleton_method(rb_cIO, "pipe", rb_io_s_pipe, 0);
+
+ rb_define_method(rb_cIO, "initialize", rb_io_initialize, -1);
+
+ rb_output_fs = Qnil;
+ rb_define_hooked_variable("$,", &rb_output_fs, 0, rb_str_setter);
+
+ rb_global_variable(&rb_default_rs);
+ rb_rs = rb_default_rs = rb_str_new2("\n");
+ rb_output_rs = Qnil;
+ OBJ_FREEZE(rb_default_rs); /* avoid modifying RS_default */
+ rb_define_hooked_variable("$/", &rb_rs, 0, rb_str_setter);
+ rb_define_hooked_variable("$-0", &rb_rs, 0, rb_str_setter);
+ rb_define_hooked_variable("$\\", &rb_output_rs, 0, rb_str_setter);
+
+ rb_define_hooked_variable("$.", &lineno, 0, lineno_setter);
+ rb_define_virtual_variable("$_", rb_lastline_get, rb_lastline_set);
+
+ rb_define_method(rb_cIO, "initialize_copy", rb_io_init_copy, 1);
+ rb_define_method(rb_cIO, "reopen", rb_io_reopen, -1);
+
+ rb_define_method(rb_cIO, "print", rb_io_print, -1);
+ rb_define_method(rb_cIO, "putc", rb_io_putc, 1);
+ rb_define_method(rb_cIO, "puts", rb_io_puts, -1);
+ rb_define_method(rb_cIO, "printf", rb_io_printf, -1);
+
+ rb_define_method(rb_cIO, "each", rb_io_each_line, -1);
+ rb_define_method(rb_cIO, "each_line", rb_io_each_line, -1);
+ rb_define_method(rb_cIO, "each_byte", rb_io_each_byte, 0);
+
+ rb_define_method(rb_cIO, "syswrite", rb_io_syswrite, 1);
+ rb_define_method(rb_cIO, "sysread", rb_io_sysread, -1);
+
+ rb_define_method(rb_cIO, "fileno", rb_io_fileno, 0);
+ rb_define_alias(rb_cIO, "to_i", "fileno");
+ rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0);
+
+ rb_define_method(rb_cIO, "fsync", rb_io_fsync, 0);
+ rb_define_method(rb_cIO, "sync", rb_io_sync, 0);
+ rb_define_method(rb_cIO, "sync=", rb_io_set_sync, 1);
+
+ rb_define_method(rb_cIO, "lineno", rb_io_lineno, 0);
+ rb_define_method(rb_cIO, "lineno=", rb_io_set_lineno, 1);
+
+ rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
+
+ rb_define_method(rb_cIO, "read_nonblock", io_read_nonblock, -1);
+ rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, 1);
+ rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
+ rb_define_method(rb_cIO, "read", io_read, -1);
+ rb_define_method(rb_cIO, "write", io_write, 1);
+ rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
+ rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
+ rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
+ rb_define_method(rb_cIO, "readchar", rb_io_readchar, 0);
+ rb_define_method(rb_cIO, "ungetc",rb_io_ungetc, 1);
+ rb_define_method(rb_cIO, "<<", rb_io_addstr, 1);
+ rb_define_method(rb_cIO, "flush", rb_io_flush, 0);
+ rb_define_method(rb_cIO, "tell", rb_io_tell, 0);
+ rb_define_method(rb_cIO, "seek", rb_io_seek_m, -1);
+ rb_define_const(rb_cIO, "SEEK_SET", INT2FIX(SEEK_SET));
+ rb_define_const(rb_cIO, "SEEK_CUR", INT2FIX(SEEK_CUR));
+ rb_define_const(rb_cIO, "SEEK_END", INT2FIX(SEEK_END));
+ rb_define_method(rb_cIO, "rewind", rb_io_rewind, 0);
+ rb_define_method(rb_cIO, "pos", rb_io_tell, 0);
+ rb_define_method(rb_cIO, "pos=", rb_io_set_pos, 1);
+ rb_define_method(rb_cIO, "eof", rb_io_eof, 0);
+ rb_define_method(rb_cIO, "eof?", rb_io_eof, 0);
+
+ rb_define_method(rb_cIO, "close", rb_io_close_m, 0);
+ rb_define_method(rb_cIO, "closed?", rb_io_closed, 0);
+ rb_define_method(rb_cIO, "close_read", rb_io_close_read, 0);
+ rb_define_method(rb_cIO, "close_write", rb_io_close_write, 0);
+
+ rb_define_method(rb_cIO, "isatty", rb_io_isatty, 0);
+ rb_define_method(rb_cIO, "tty?", rb_io_isatty, 0);
+ rb_define_method(rb_cIO, "binmode", rb_io_binmode, 0);
+ rb_define_method(rb_cIO, "sysseek", rb_io_sysseek, -1);
+
+ rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
+ rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
+ rb_define_method(rb_cIO, "pid", rb_io_pid, 0);
+ rb_define_method(rb_cIO, "inspect", rb_io_inspect, 0);
+
+ rb_define_variable("$stdin", &rb_stdin);
+ rb_stdin = prep_stdio(stdin, FMODE_READABLE, rb_cIO);
+ rb_define_hooked_variable("$stdout", &rb_stdout, 0, stdout_setter);
+ rb_stdout = prep_stdio(stdout, FMODE_WRITABLE, rb_cIO);
+ rb_define_hooked_variable("$stderr", &rb_stderr, 0, stdout_setter);
+ rb_stderr = prep_stdio(stderr, FMODE_WRITABLE, rb_cIO);
+ rb_define_hooked_variable("$>", &rb_stdout, 0, stdout_setter);
+ orig_stdout = rb_stdout;
+ rb_deferr = orig_stderr = rb_stderr;
+
+ /* variables to be removed in 1.8.1 */
+ rb_define_hooked_variable("$defout", &rb_stdout, 0, defout_setter);
+ rb_define_hooked_variable("$deferr", &rb_stderr, 0, deferr_setter);
+
+ /* constants to hold original stdin/stdout/stderr */
+ rb_define_global_const("STDIN", rb_stdin);
+ rb_define_global_const("STDOUT", rb_stdout);
+ rb_define_global_const("STDERR", rb_stderr);
+
+ rb_define_readonly_variable("$<", &argf);
+ argf = rb_obj_alloc(rb_cObject);
+ rb_extend_object(argf, rb_mEnumerable);
+ rb_define_global_const("ARGF", argf);
+
+ rb_define_singleton_method(argf, "to_s", argf_to_s, 0);
+
+ rb_define_singleton_method(argf, "fileno", argf_fileno, 0);
+ rb_define_singleton_method(argf, "to_i", argf_fileno, 0);
+ rb_define_singleton_method(argf, "to_io", argf_to_io, 0);
+ rb_define_singleton_method(argf, "each", argf_each_line, -1);
+ rb_define_singleton_method(argf, "each_line", argf_each_line, -1);
+ rb_define_singleton_method(argf, "each_byte", argf_each_byte, 0);
+
+ rb_define_singleton_method(argf, "read", argf_read, -1);
+ rb_define_singleton_method(argf, "readlines", rb_f_readlines, -1);
+ rb_define_singleton_method(argf, "to_a", rb_f_readlines, -1);
+ rb_define_singleton_method(argf, "gets", rb_f_gets, -1);
+ rb_define_singleton_method(argf, "readline", rb_f_readline, -1);
+ rb_define_singleton_method(argf, "getc", argf_getc, 0);
+ rb_define_singleton_method(argf, "readchar", argf_readchar, 0);
+ rb_define_singleton_method(argf, "tell", argf_tell, 0);
+ rb_define_singleton_method(argf, "seek", argf_seek_m, -1);
+ rb_define_singleton_method(argf, "rewind", argf_rewind, 0);
+ rb_define_singleton_method(argf, "pos", argf_tell, 0);
+ rb_define_singleton_method(argf, "pos=", argf_set_pos, 1);
+ rb_define_singleton_method(argf, "eof", argf_eof, 0);
+ rb_define_singleton_method(argf, "eof?", argf_eof, 0);
+ rb_define_singleton_method(argf, "binmode", argf_binmode, 0);
+
+ rb_define_singleton_method(argf, "filename", argf_filename, 0);
+ rb_define_singleton_method(argf, "path", argf_filename, 0);
+ rb_define_singleton_method(argf, "file", argf_file, 0);
+ rb_define_singleton_method(argf, "skip", argf_skip, 0);
+ rb_define_singleton_method(argf, "close", argf_close_m, 0);
+ rb_define_singleton_method(argf, "closed?", argf_closed, 0);
+
+ rb_define_singleton_method(argf, "lineno", argf_lineno, 0);
+ rb_define_singleton_method(argf, "lineno=", argf_set_lineno, 1);
+
+ rb_global_variable(&current_file);
+ rb_define_readonly_variable("$FILENAME", &filename);
+ filename = rb_str_new2("-");
+
+ rb_define_virtual_variable("$-i", opt_i_get, opt_i_set);
+
+#if defined (_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__)
+ atexit(pipe_atexit);
+#endif
+
+ Init_File();
+
+ rb_define_method(rb_cFile, "initialize", rb_file_initialize, -1);
+
+ rb_file_const("RDONLY", INT2FIX(O_RDONLY));
+ rb_file_const("WRONLY", INT2FIX(O_WRONLY));
+ rb_file_const("RDWR", INT2FIX(O_RDWR));
+ rb_file_const("APPEND", INT2FIX(O_APPEND));
+ rb_file_const("CREAT", INT2FIX(O_CREAT));
+ rb_file_const("EXCL", INT2FIX(O_EXCL));
+#if defined(O_NDELAY) || defined(O_NONBLOCK)
+# ifdef O_NONBLOCK
+ rb_file_const("NONBLOCK", INT2FIX(O_NONBLOCK));
+# else
+ rb_file_const("NONBLOCK", INT2FIX(O_NDELAY));
+# endif
+#endif
+ rb_file_const("TRUNC", INT2FIX(O_TRUNC));
+#ifdef O_NOCTTY
+ rb_file_const("NOCTTY", INT2FIX(O_NOCTTY));
+#endif
+#ifdef O_BINARY
+ rb_file_const("BINARY", INT2FIX(O_BINARY));
+#endif
+#ifdef O_SYNC
+ rb_file_const("SYNC", INT2FIX(O_SYNC));
+#endif
+}
+/* C code produced by gperf version 2.7.2 */
+/* Command-line: gperf -p -j1 -i 1 -g -o -t -N rb_reserved_word -k'1,3,$' ./keywords */
+struct kwtable {char *name; int id[2]; enum lex_state state;};
+
+#define TOTAL_KEYWORDS 40
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 8
+#define MIN_HASH_VALUE 6
+#define MAX_HASH_VALUE 55
+/* maximum key range = 50, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash (str, len)
+ register const char *str;
+ register unsigned int len;
+{
+ static unsigned char asso_values[] =
+ {
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 11, 56, 56, 36, 56, 1, 37,
+ 31, 1, 56, 56, 56, 56, 29, 56, 1, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 1, 56, 32, 1, 2,
+ 1, 1, 4, 23, 56, 17, 56, 20, 9, 2,
+ 9, 26, 14, 56, 5, 1, 1, 16, 56, 21,
+ 20, 9, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ case 3:
+ hval += asso_values[(unsigned char)str[2]];
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval + asso_values[(unsigned char)str[len - 1]];
+}
+
+#ifdef __GNUC__
+__inline
+#endif
+struct kwtable *
+rb_reserved_word (str, len)
+ register const char *str;
+ register unsigned int len;
+{
+ static struct kwtable wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""},
+ {"end", {kEND, kEND}, EXPR_END},
+ {"else", {kELSE, kELSE}, EXPR_BEG},
+ {"case", {kCASE, kCASE}, EXPR_BEG},
+ {"ensure", {kENSURE, kENSURE}, EXPR_BEG},
+ {"module", {kMODULE, kMODULE}, EXPR_BEG},
+ {"elsif", {kELSIF, kELSIF}, EXPR_BEG},
+ {"def", {kDEF, kDEF}, EXPR_FNAME},
+ {"rescue", {kRESCUE, kRESCUE_MOD}, EXPR_MID},
+ {"not", {kNOT, kNOT}, EXPR_BEG},
+ {"then", {kTHEN, kTHEN}, EXPR_BEG},
+ {"yield", {kYIELD, kYIELD}, EXPR_ARG},
+ {"for", {kFOR, kFOR}, EXPR_BEG},
+ {"self", {kSELF, kSELF}, EXPR_END},
+ {"false", {kFALSE, kFALSE}, EXPR_END},
+ {"retry", {kRETRY, kRETRY}, EXPR_END},
+ {"return", {kRETURN, kRETURN}, EXPR_MID},
+ {"true", {kTRUE, kTRUE}, EXPR_END},
+ {"if", {kIF, kIF_MOD}, EXPR_BEG},
+ {"defined?", {kDEFINED, kDEFINED}, EXPR_ARG},
+ {"super", {kSUPER, kSUPER}, EXPR_ARG},
+ {"undef", {kUNDEF, kUNDEF}, EXPR_FNAME},
+ {"break", {kBREAK, kBREAK}, EXPR_MID},
+ {"in", {kIN, kIN}, EXPR_BEG},
+ {"do", {kDO, kDO}, EXPR_BEG},
+ {"nil", {kNIL, kNIL}, EXPR_END},
+ {"until", {kUNTIL, kUNTIL_MOD}, EXPR_BEG},
+ {"unless", {kUNLESS, kUNLESS_MOD}, EXPR_BEG},
+ {"or", {kOR, kOR}, EXPR_BEG},
+ {"next", {kNEXT, kNEXT}, EXPR_MID},
+ {"when", {kWHEN, kWHEN}, EXPR_BEG},
+ {"redo", {kREDO, kREDO}, EXPR_END},
+ {"and", {kAND, kAND}, EXPR_BEG},
+ {"begin", {kBEGIN, kBEGIN}, EXPR_BEG},
+ {"__LINE__", {k__LINE__, k__LINE__}, EXPR_END},
+ {"class", {kCLASS, kCLASS}, EXPR_CLASS},
+ {"__FILE__", {k__FILE__, k__FILE__}, EXPR_END},
+ {"END", {klEND, klEND}, EXPR_END},
+ {"BEGIN", {klBEGIN, klBEGIN}, EXPR_END},
+ {"while", {kWHILE, kWHILE_MOD}, EXPR_BEG},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+ {"alias", {kALIAS, kALIAS}, EXPR_FNAME}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register const char *s = wordlist[key].name;
+
+ if (*str == *s && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
+/**********************************************************************
+
+ main.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Fri Aug 19 13:19:58 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+#ifdef __human68k__
+int _stacksize = 262144;
+#endif
+
+#if defined __MINGW32__
+int _CRT_glob = 0;
+#endif
+
+#if defined(__MACOS__) && defined(__MWERKS__)
+#include <console.h>
+#endif
+
+/* to link startup code with ObjC support */
+#if (defined(__APPLE__) || defined(__NeXT__)) && defined(__MACH__)
+static void objcdummyfunction( void ) { objc_msgSend(); }
+#endif
+
+int
+main(argc, argv, envp)
+ int argc;
+ char **argv, **envp;
+{
+#ifdef _WIN32
+ NtInitialize(&argc, &argv);
+#endif
+#if defined(__MACOS__) && defined(__MWERKS__)
+ argc = ccommand(&argv);
+#endif
+
+ {
+ RUBY_INIT_STACK
+ ruby_init();
+ ruby_options(argc, argv);
+ ruby_run();
+ }
+ return 0;
+}
+/**********************************************************************
+
+ marshal.c -
+
+ $Author: shyouhei $
+ $Date: 2009-01-28 12:59:58 +0100 (Wed, 28 Jan 2009) $
+ created at: Thu Apr 27 16:30:01 JST 1995
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubyio.h"
+#include "st.h"
+#include "util.h"
+
+#include <math.h>
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+#define BITSPERSHORT (2*CHAR_BIT)
+#define SHORTMASK ((1<<BITSPERSHORT)-1)
+#define SHORTDN(x) RSHIFT(x,BITSPERSHORT)
+
+#if SIZEOF_SHORT == SIZEOF_BDIGITS
+#define SHORTLEN(x) (x)
+#else
+static int
+shortlen(len, ds)
+ long len;
+ BDIGIT *ds;
+{
+ BDIGIT num;
+ int offset = 0;
+
+ num = ds[len-1];
+ while (num) {
+ num = SHORTDN(num);
+ offset++;
+ }
+ return (len - 1)*sizeof(BDIGIT)/2 + offset;
+}
+#define SHORTLEN(x) shortlen((x),d)
+#endif
+
+#define MARSHAL_MAJOR 4
+#define MARSHAL_MINOR 8
+
+#define TYPE_NIL '0'
+#define TYPE_TRUE 'T'
+#define TYPE_FALSE 'F'
+#define TYPE_FIXNUM 'i'
+
+#define TYPE_EXTENDED 'e'
+#define TYPE_UCLASS 'C'
+#define TYPE_OBJECT 'o'
+#define TYPE_DATA 'd'
+#define TYPE_USERDEF 'u'
+#define TYPE_USRMARSHAL 'U'
+#define TYPE_FLOAT 'f'
+#define TYPE_BIGNUM 'l'
+#define TYPE_STRING '"'
+#define TYPE_REGEXP '/'
+#define TYPE_ARRAY '['
+#define TYPE_HASH '{'
+#define TYPE_HASH_DEF '}'
+#define TYPE_STRUCT 'S'
+#define TYPE_MODULE_OLD 'M'
+#define TYPE_CLASS 'c'
+#define TYPE_MODULE 'm'
+
+#define TYPE_SYMBOL ':'
+#define TYPE_SYMLINK ';'
+
+#define TYPE_IVAR 'I'
+#define TYPE_LINK '@'
+
+static ID s_dump, s_load, s_mdump, s_mload;
+static ID s_dump_data, s_load_data, s_alloc, s_call;
+static ID s_getc, s_read, s_write, s_binmode;
+
+struct dump_arg {
+ VALUE obj;
+ VALUE str, dest;
+ st_table *symbols;
+ st_table *data;
+ int taint;
+ VALUE wrapper;
+};
+
+struct dump_call_arg {
+ VALUE obj;
+ struct dump_arg *arg;
+ int limit;
+};
+
+static void
+check_dump_arg(arg, sym)
+ struct dump_arg *arg;
+ ID sym;
+{
+ if (!DATA_PTR(arg->wrapper)) {
+ rb_raise(rb_eRuntimeError, "Marshal.dump reentered at %s",
+ rb_id2name(sym));
+ }
+}
+
+static void
+mark_dump_arg(ptr)
+ void *ptr;
+{
+ struct dump_arg *p = ptr;
+ if (!ptr)
+ return;
+ rb_mark_set(p->data);
+}
+
+static VALUE
+class2path(klass)
+ VALUE klass;
+{
+ VALUE path = rb_class_path(klass);
+ char *n = RSTRING(path)->ptr;
+
+ if (n[0] == '#') {
+ rb_raise(rb_eTypeError, "can't dump anonymous %s %s",
+ (TYPE(klass) == T_CLASS ? "class" : "module"),
+ n);
+ }
+ if (rb_path2class(n) != rb_class_real(klass)) {
+ rb_raise(rb_eTypeError, "%s can't be referred", n);
+ }
+ return path;
+}
+
+static void w_long _((long, struct dump_arg*));
+
+static void
+w_nbyte(s, n, arg)
+ char *s;
+ int n;
+ struct dump_arg *arg;
+{
+ VALUE buf = arg->str;
+ rb_str_buf_cat(buf, s, n);
+ if (arg->dest && RSTRING(buf)->len >= BUFSIZ) {
+ if (arg->taint) OBJ_TAINT(buf);
+ rb_io_write(arg->dest, buf);
+ rb_str_resize(buf, 0);
+ }
+}
+
+static void
+w_byte(c, arg)
+ char c;
+ struct dump_arg *arg;
+{
+ w_nbyte(&c, 1, arg);
+}
+
+static void
+w_bytes(s, n, arg)
+ char *s;
+ int n;
+ struct dump_arg *arg;
+{
+ w_long(n, arg);
+ w_nbyte(s, n, arg);
+}
+
+static void
+w_short(x, arg)
+ int x;
+ struct dump_arg *arg;
+{
+ w_byte((x >> 0) & 0xff, arg);
+ w_byte((x >> 8) & 0xff, arg);
+}
+
+static void
+w_long(x, arg)
+ long x;
+ struct dump_arg *arg;
+{
+ char buf[sizeof(long)+1];
+ int i, len = 0;
+
+#if SIZEOF_LONG > 4
+ if (!(RSHIFT(x, 31) == 0 || RSHIFT(x, 31) == -1)) {
+ /* big long does not fit in 4 bytes */
+ rb_raise(rb_eTypeError, "long too big to dump");
+ }
+#endif
+
+ if (x == 0) {
+ w_byte(0, arg);
+ return;
+ }
+ if (0 < x && x < 123) {
+ w_byte(x + 5, arg);
+ return;
+ }
+ if (-124 < x && x < 0) {
+ w_byte((x - 5)&0xff, arg);
+ return;
+ }
+ for (i=1;i<sizeof(long)+1;i++) {
+ buf[i] = x & 0xff;
+ x = RSHIFT(x,8);
+ if (x == 0) {
+ buf[0] = i;
+ break;
+ }
+ if (x == -1) {
+ buf[0] = -i;
+ break;
+ }
+ }
+ len = i;
+ for (i=0;i<=len;i++) {
+ w_byte(buf[i], arg);
+ }
+}
+
+#ifdef DBL_MANT_DIG
+#define DECIMAL_MANT (53-16) /* from IEEE754 double precision */
+
+#if DBL_MANT_DIG > 32
+#define MANT_BITS 32
+#elif DBL_MANT_DIG > 24
+#define MANT_BITS 24
+#elif DBL_MANT_DIG > 16
+#define MANT_BITS 16
+#else
+#define MANT_BITS 8
+#endif
+
+static int
+save_mantissa(d, buf)
+ double d;
+ char *buf;
+{
+ int e, i = 0;
+ unsigned long m;
+ double n;
+
+ d = modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
+ if (d > 0) {
+ buf[i++] = 0;
+ do {
+ d = modf(ldexp(d, MANT_BITS), &n);
+ m = (unsigned long)n;
+#if MANT_BITS > 24
+ buf[i++] = m >> 24;
+#endif
+#if MANT_BITS > 16
+ buf[i++] = m >> 16;
+#endif
+#if MANT_BITS > 8
+ buf[i++] = m >> 8;
+#endif
+ buf[i++] = m;
+ } while (d > 0);
+ while (!buf[i - 1]) --i;
+ }
+ return i;
+}
+
+static double
+load_mantissa(d, buf, len)
+ double d;
+ const char *buf;
+ int len;
+{
+ if (--len > 0 && !*buf++) { /* binary mantissa mark */
+ int e, s = d < 0, dig = 0;
+ unsigned long m;
+
+ modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
+ do {
+ m = 0;
+ switch (len) {
+ default: m = *buf++ & 0xff;
+#if MANT_BITS > 24
+ case 3: m = (m << 8) | (*buf++ & 0xff);
+#endif
+#if MANT_BITS > 16
+ case 2: m = (m << 8) | (*buf++ & 0xff);
+#endif
+#if MANT_BITS > 8
+ case 1: m = (m << 8) | (*buf++ & 0xff);
+#endif
+ }
+ dig -= len < MANT_BITS / 8 ? 8 * (unsigned)len : MANT_BITS;
+ d += ldexp((double)m, dig);
+ } while ((len -= MANT_BITS / 8) > 0);
+ d = ldexp(d, e - DECIMAL_MANT);
+ if (s) d = -d;
+ }
+ return d;
+}
+#else
+#define load_mantissa(d, buf, len) (d)
+#define save_mantissa(d, buf) 0
+#endif
+
+#ifdef DBL_DIG
+#define FLOAT_DIG (DBL_DIG+2)
+#else
+#define FLOAT_DIG 17
+#endif
+
+static void
+w_float(d, arg)
+ double d;
+ struct dump_arg *arg;
+{
+ char buf[100];
+
+ if (isinf(d)) {
+ if (d < 0) strcpy(buf, "-inf");
+ else strcpy(buf, "inf");
+ }
+ else if (isnan(d)) {
+ strcpy(buf, "nan");
+ }
+ else if (d == 0.0) {
+ if (1.0/d < 0) strcpy(buf, "-0");
+ else strcpy(buf, "0");
+ }
+ else {
+ int len;
+
+ /* xxx: should not use system's sprintf(3) */
+ sprintf(buf, "%.*g", FLOAT_DIG, d);
+ len = strlen(buf);
+ w_bytes(buf, len + save_mantissa(d, buf + len), arg);
+ return;
+ }
+ w_bytes(buf, strlen(buf), arg);
+}
+
+static void
+w_symbol(id, arg)
+ ID id;
+ struct dump_arg *arg;
+{
+ char *sym = rb_id2name(id);
+ st_data_t num;
+
+ if (st_lookup(arg->symbols, id, &num)) {
+ w_byte(TYPE_SYMLINK, arg);
+ w_long((long)num, arg);
+ }
+ else {
+ w_byte(TYPE_SYMBOL, arg);
+ w_bytes(sym, strlen(sym), arg);
+ st_add_direct(arg->symbols, id, arg->symbols->num_entries);
+ }
+}
+
+static void
+w_unique(s, arg)
+ char *s;
+ struct dump_arg *arg;
+{
+ if (s[0] == '#') {
+ rb_raise(rb_eTypeError, "can't dump anonymous class %s", s);
+ }
+ w_symbol(rb_intern(s), arg);
+}
+
+static void w_object _((VALUE,struct dump_arg*,int));
+
+static int
+hash_each(key, value, arg)
+ VALUE key, value;
+ struct dump_call_arg *arg;
+{
+ w_object(key, arg->arg, arg->limit);
+ w_object(value, arg->arg, arg->limit);
+ return ST_CONTINUE;
+}
+
+static void
+w_extended(klass, arg, check)
+ VALUE klass;
+ struct dump_arg *arg;
+ int check;
+{
+ char *path;
+
+ if (check && FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS(klass)->m_tbl->num_entries ||
+ (RCLASS(klass)->iv_tbl && RCLASS(klass)->iv_tbl->num_entries > 1)) {
+ rb_raise(rb_eTypeError, "singleton can't be dumped");
+ }
+ klass = RCLASS(klass)->super;
+ }
+ while (BUILTIN_TYPE(klass) == T_ICLASS) {
+ path = rb_class2name(RBASIC(klass)->klass);
+ w_byte(TYPE_EXTENDED, arg);
+ w_unique(path, arg);
+ klass = RCLASS(klass)->super;
+ }
+}
+
+static void
+w_class(type, obj, arg, check)
+ int type;
+ VALUE obj;
+ struct dump_arg *arg;
+ int check;
+{
+ char *path;
+
+ VALUE klass = CLASS_OF(obj);
+ w_extended(klass, arg, check);
+ w_byte(type, arg);
+ path = RSTRING(class2path(rb_class_real(klass)))->ptr;
+ w_unique(path, arg);
+}
+
+static void
+w_uclass(obj, base_klass, arg)
+ VALUE obj, base_klass;
+ struct dump_arg *arg;
+{
+ VALUE klass = CLASS_OF(obj);
+
+ w_extended(klass, arg, Qtrue);
+ klass = rb_class_real(klass);
+ if (klass != base_klass) {
+ w_byte(TYPE_UCLASS, arg);
+ w_unique(RSTRING(class2path(klass))->ptr, arg);
+ }
+}
+
+static int
+w_obj_each(id, value, arg)
+ ID id;
+ VALUE value;
+ struct dump_call_arg *arg;
+{
+ w_symbol(id, arg->arg);
+ w_object(value, arg->arg, arg->limit);
+ return ST_CONTINUE;
+}
+
+static void
+w_ivar(tbl, arg)
+ st_table *tbl;
+ struct dump_call_arg *arg;
+{
+ if (tbl) {
+ w_long(tbl->num_entries, arg->arg);
+ st_foreach_safe(tbl, w_obj_each, (st_data_t)arg);
+ }
+ else {
+ w_long(0, arg->arg);
+ }
+}
+
+static void
+w_object(obj, arg, limit)
+ VALUE obj;
+ struct dump_arg *arg;
+ int limit;
+{
+ struct dump_call_arg c_arg;
+ st_table *ivtbl = 0;
+ st_data_t num;
+
+ if (limit == 0) {
+ rb_raise(rb_eArgError, "exceed depth limit");
+ }
+
+ limit--;
+ c_arg.limit = limit;
+ c_arg.arg = arg;
+
+ if (st_lookup(arg->data, obj, &num)) {
+ w_byte(TYPE_LINK, arg);
+ w_long((long)num, arg);
+ return;
+ }
+
+ if ((ivtbl = rb_generic_ivar_table(obj)) != 0) {
+ w_byte(TYPE_IVAR, arg);
+ }
+ if (obj == Qnil) {
+ w_byte(TYPE_NIL, arg);
+ }
+ else if (obj == Qtrue) {
+ w_byte(TYPE_TRUE, arg);
+ }
+ else if (obj == Qfalse) {
+ w_byte(TYPE_FALSE, arg);
+ }
+ else if (FIXNUM_P(obj)) {
+#if SIZEOF_LONG <= 4
+ w_byte(TYPE_FIXNUM, arg);
+ w_long(FIX2INT(obj), arg);
+#else
+ if (RSHIFT((long)obj, 31) == 0 || RSHIFT((long)obj, 31) == -1) {
+ w_byte(TYPE_FIXNUM, arg);
+ w_long(FIX2LONG(obj), arg);
+ }
+ else {
+ w_object(rb_int2big(FIX2LONG(obj)), arg, limit);
+ }
+#endif
+ }
+ else if (SYMBOL_P(obj)) {
+ w_symbol(SYM2ID(obj), arg);
+ }
+ else {
+ if (OBJ_TAINTED(obj)) arg->taint = Qtrue;
+
+ st_add_direct(arg->data, obj, arg->data->num_entries);
+ if (rb_respond_to(obj, s_mdump)) {
+ volatile VALUE v;
+
+ v = rb_funcall(obj, s_mdump, 0, 0);
+ check_dump_arg(arg, s_mdump);
+ w_class(TYPE_USRMARSHAL, obj, arg, Qfalse);
+ w_object(v, arg, limit);
+ if (ivtbl) w_ivar(0, &c_arg);
+ return;
+ }
+ if (rb_respond_to(obj, s_dump)) {
+ VALUE v;
+
+ v = rb_funcall(obj, s_dump, 1, INT2NUM(limit));
+ check_dump_arg(arg, s_dump);
+ if (TYPE(v) != T_STRING) {
+ rb_raise(rb_eTypeError, "_dump() must return string");
+ }
+ if (!ivtbl && (ivtbl = rb_generic_ivar_table(v))) {
+ w_byte(TYPE_IVAR, arg);
+ }
+ w_class(TYPE_USERDEF, obj, arg, Qfalse);
+ w_bytes(RSTRING(v)->ptr, RSTRING(v)->len, arg);
+ if (ivtbl) {
+ w_ivar(ivtbl, &c_arg);
+ }
+ return;
+ }
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ if (FL_TEST(obj, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton class can't be dumped");
+ }
+ w_byte(TYPE_CLASS, arg);
+ {
+ VALUE path = class2path(obj);
+ w_bytes(RSTRING(path)->ptr, RSTRING(path)->len, arg);
+ }
+ break;
+
+ case T_MODULE:
+ w_byte(TYPE_MODULE, arg);
+ {
+ VALUE path = class2path(obj);
+ w_bytes(RSTRING(path)->ptr, RSTRING(path)->len, arg);
+ }
+ break;
+
+ case T_FLOAT:
+ w_byte(TYPE_FLOAT, arg);
+ w_float(RFLOAT(obj)->value, arg);
+ break;
+
+ case T_BIGNUM:
+ w_byte(TYPE_BIGNUM, arg);
+ {
+ char sign = RBIGNUM(obj)->sign ? '+' : '-';
+ long len = RBIGNUM(obj)->len;
+ BDIGIT *d = RBIGNUM(obj)->digits;
+
+ w_byte(sign, arg);
+ w_long(SHORTLEN(len), arg); /* w_short? */
+ while (len--) {
+#if SIZEOF_BDIGITS > SIZEOF_SHORT
+ BDIGIT num = *d;
+ int i;
+
+ for (i=0; i<SIZEOF_BDIGITS; i+=SIZEOF_SHORT) {
+ w_short(num & SHORTMASK, arg);
+ num = SHORTDN(num);
+ if (len == 0 && num == 0) break;
+ }
+#else
+ w_short(*d, arg);
+#endif
+ d++;
+ }
+ }
+ break;
+
+ case T_STRING:
+ w_uclass(obj, rb_cString, arg);
+ w_byte(TYPE_STRING, arg);
+ w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, arg);
+ break;
+
+ case T_REGEXP:
+ w_uclass(obj, rb_cRegexp, arg);
+ w_byte(TYPE_REGEXP, arg);
+ w_bytes(RREGEXP(obj)->str, RREGEXP(obj)->len, arg);
+ w_byte(rb_reg_options(obj), arg);
+ break;
+
+ case T_ARRAY:
+ w_uclass(obj, rb_cArray, arg);
+ w_byte(TYPE_ARRAY, arg);
+ {
+ long len = RARRAY(obj)->len;
+ VALUE *ptr = RARRAY(obj)->ptr;
+
+ w_long(len, arg);
+ while (len--) {
+ w_object(*ptr, arg, limit);
+ ptr++;
+ }
+ }
+ break;
+
+ case T_HASH:
+ w_uclass(obj, rb_cHash, arg);
+ if (NIL_P(RHASH(obj)->ifnone)) {
+ w_byte(TYPE_HASH, arg);
+ }
+ else if (FL_TEST(obj, FL_USER2)) {
+ /* FL_USER2 means HASH_PROC_DEFAULT (see hash.c) */
+ rb_raise(rb_eTypeError, "can't dump hash with default proc");
+ }
+ else {
+ w_byte(TYPE_HASH_DEF, arg);
+ }
+ w_long(RHASH(obj)->tbl->num_entries, arg);
+ rb_hash_foreach(obj, hash_each, (st_data_t)&c_arg);
+ if (!NIL_P(RHASH(obj)->ifnone)) {
+ w_object(RHASH(obj)->ifnone, arg, limit);
+ }
+ break;
+
+ case T_STRUCT:
+ w_class(TYPE_STRUCT, obj, arg, Qtrue);
+ {
+ long len = RSTRUCT(obj)->len;
+ VALUE mem;
+ long i;
+
+ w_long(len, arg);
+ mem = rb_struct_members(obj);
+ for (i=0; i<len; i++) {
+ w_symbol(SYM2ID(RARRAY(mem)->ptr[i]), arg);
+ w_object(RSTRUCT(obj)->ptr[i], arg, limit);
+ }
+ }
+ break;
+
+ case T_OBJECT:
+ w_class(TYPE_OBJECT, obj, arg, Qtrue);
+ w_ivar(ROBJECT(obj)->iv_tbl, &c_arg);
+ break;
+
+ case T_DATA:
+ {
+ VALUE v;
+
+ if (!rb_respond_to(obj, s_dump_data)) {
+ rb_raise(rb_eTypeError,
+ "no marshal_dump is defined for class %s",
+ rb_obj_classname(obj));
+ }
+ v = rb_funcall(obj, s_dump_data, 0);
+ check_dump_arg(arg, s_dump_data);
+ w_class(TYPE_DATA, obj, arg, Qtrue);
+ w_object(v, arg, limit);
+ }
+ break;
+
+ default:
+ rb_raise(rb_eTypeError, "can't dump %s",
+ rb_obj_classname(obj));
+ break;
+ }
+ }
+ if (ivtbl) {
+ w_ivar(ivtbl, &c_arg);
+ }
+}
+
+static VALUE
+dump(arg)
+ struct dump_call_arg *arg;
+{
+ w_object(arg->obj, arg->arg, arg->limit);
+ if (arg->arg->dest) {
+ rb_io_write(arg->arg->dest, arg->arg->str);
+ rb_str_resize(arg->arg->str, 0);
+ }
+ return 0;
+}
+
+static VALUE
+dump_ensure(arg)
+ struct dump_arg *arg;
+{
+ if (!DATA_PTR(arg->wrapper)) return 0;
+ st_free_table(arg->symbols);
+ st_free_table(arg->data);
+ DATA_PTR(arg->wrapper) = 0;
+ arg->wrapper = 0;
+ if (arg->taint) {
+ OBJ_TAINT(arg->str);
+ }
+
+ return 0;
+}
+
+/*
+ * call-seq:
+ * dump( obj [, anIO] , limit=--1 ) => anIO
+ *
+ * Serializes obj and all descendent objects. If anIO is
+ * specified, the serialized data will be written to it, otherwise the
+ * data will be returned as a String. If limit is specified, the
+ * traversal of subobjects will be limited to that depth. If limit is
+ * negative, no checking of depth will be performed.
+ *
+ * class Klass
+ * def initialize(str)
+ * @str = str
+ * end
+ * def sayHello
+ * @str
+ * end
+ * end
+ *
+ * (produces no output)
+ *
+ * o = Klass.new("hello\n")
+ * data = Marshal.dump(o)
+ * obj = Marshal.load(data)
+ * obj.sayHello #=> "hello\n"
+ */
+static VALUE
+marshal_dump(argc, argv)
+ int argc;
+ VALUE* argv;
+{
+ VALUE obj, port, a1, a2;
+ int limit = -1;
+ struct dump_arg arg;
+ struct dump_call_arg c_arg;
+
+ port = Qnil;
+ rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
+ if (argc == 3) {
+ if (!NIL_P(a2)) limit = NUM2INT(a2);
+ if (NIL_P(a1)) goto type_error;
+ port = a1;
+ }
+ else if (argc == 2) {
+ if (FIXNUM_P(a1)) limit = FIX2INT(a1);
+ else if (NIL_P(a1)) goto type_error;
+ else port = a1;
+ }
+ arg.dest = 0;
+ arg.symbols = st_init_numtable();
+ arg.data = st_init_numtable();
+ arg.taint = Qfalse;
+ arg.str = rb_str_buf_new(0);
+ RBASIC(arg.str)->klass = 0;
+ arg.wrapper = Data_Wrap_Struct(rb_cData, mark_dump_arg, 0, &arg);
+ if (!NIL_P(port)) {
+ if (!rb_respond_to(port, s_write)) {
+ type_error:
+ rb_raise(rb_eTypeError, "instance of IO needed");
+ }
+ arg.dest = port;
+ if (rb_respond_to(port, s_binmode)) {
+ rb_funcall2(port, s_binmode, 0, 0);
+ check_dump_arg(&arg, s_binmode);
+ }
+ }
+ else {
+ port = arg.str;
+ }
+
+ c_arg.obj = obj;
+ c_arg.arg = &arg;
+ c_arg.limit = limit;
+
+ w_byte(MARSHAL_MAJOR, &arg);
+ w_byte(MARSHAL_MINOR, &arg);
+
+ rb_ensure(dump, (VALUE)&c_arg, dump_ensure, (VALUE)&arg);
+ RBASIC(arg.str)->klass = rb_cString;
+
+ return port;
+}
+
+struct load_arg {
+ VALUE src;
+ long offset;
+ st_table *symbols;
+ st_table *data;
+ VALUE proc;
+ int taint;
+ VALUE wrapper;
+};
+
+static void
+check_load_arg(arg, sym)
+ struct load_arg *arg;
+ ID sym;
+{
+ if (!DATA_PTR(arg->wrapper)) {
+ rb_raise(rb_eRuntimeError, "Marshal.load reentered at %s",
+ rb_id2name(sym));
+ }
+}
+
+static void
+mark_load_arg(ptr)
+ void *ptr;
+{
+ struct load_arg *p = ptr;
+ if (!ptr)
+ return;
+ rb_mark_tbl(p->data);
+}
+
+static VALUE r_object _((struct load_arg *arg));
+
+static int
+r_byte(arg)
+ struct load_arg *arg;
+{
+ int c;
+
+ if (TYPE(arg->src) == T_STRING) {
+ if (RSTRING(arg->src)->len > arg->offset) {
+ c = (unsigned char)RSTRING(arg->src)->ptr[arg->offset++];
+ }
+ else {
+ rb_raise(rb_eArgError, "marshal data too short");
+ }
+ }
+ else {
+ VALUE src = arg->src;
+ VALUE v = rb_funcall2(src, s_getc, 0, 0);
+ check_load_arg(arg, s_getc);
+ if (NIL_P(v)) rb_eof_error();
+ c = (unsigned char)FIX2INT(v);
+ }
+ return c;
+}
+
+static void
+long_toobig(size)
+ int size;
+{
+ rb_raise(rb_eTypeError, "long too big for this architecture (size %d, given %d)",
+ sizeof(long), size);
+}
+
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+# define SIGN_EXTEND_CHAR(c) ((signed char)(c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
+#endif
+
+static long
+r_long(arg)
+ struct load_arg *arg;
+{
+ register long x;
+ int c = SIGN_EXTEND_CHAR(r_byte(arg));
+ long i;
+
+ if (c == 0) return 0;
+ if (c > 0) {
+ if (4 < c && c < 128) {
+ return c - 5;
+ }
+ if (c > sizeof(long)) long_toobig(c);
+ x = 0;
+ for (i=0;i<c;i++) {
+ x |= (long)r_byte(arg) << (8*i);
+ }
+ }
+ else {
+ if (-129 < c && c < -4) {
+ return c + 5;
+ }
+ c = -c;
+ if (c > sizeof(long)) long_toobig(c);
+ x = -1;
+ for (i=0;i<c;i++) {
+ x &= ~((long)0xff << (8*i));
+ x |= (long)r_byte(arg) << (8*i);
+ }
+ }
+ return x;
+}
+
+#define r_bytes(arg) r_bytes0(r_long(arg), (arg))
+
+static VALUE
+r_bytes0(len, arg)
+ long len;
+ struct load_arg *arg;
+{
+ VALUE str;
+
+ if (len == 0) return rb_str_new(0, 0);
+ if (TYPE(arg->src) == T_STRING) {
+ if (RSTRING(arg->src)->len - arg->offset >= len) {
+ str = rb_str_new(RSTRING(arg->src)->ptr+arg->offset, len);
+ arg->offset += len;
+ }
+ else {
+ too_short:
+ rb_raise(rb_eArgError, "marshal data too short");
+ }
+ }
+ else {
+ VALUE src = arg->src;
+ VALUE n = LONG2NUM(len);
+ str = rb_funcall2(src, s_read, 1, &n);
+ check_load_arg(arg, s_read);
+ if (NIL_P(str)) goto too_short;
+ StringValue(str);
+ if (RSTRING(str)->len != len) goto too_short;
+ if (OBJ_TAINTED(str)) arg->taint = Qtrue;
+ }
+ return str;
+}
+
+static ID
+r_symlink(arg)
+ struct load_arg *arg;
+{
+ ID id;
+ long num = r_long(arg);
+
+ if (st_lookup(arg->symbols, num, &id)) {
+ return id;
+ }
+ rb_raise(rb_eArgError, "bad symbol");
+}
+
+static ID
+r_symreal(arg)
+ struct load_arg *arg;
+{
+ ID id;
+
+ id = rb_intern(RSTRING(r_bytes(arg))->ptr);
+ st_insert(arg->symbols, arg->symbols->num_entries, id);
+
+ return id;
+}
+
+static ID
+r_symbol(arg)
+ struct load_arg *arg;
+{
+ if (r_byte(arg) == TYPE_SYMLINK) {
+ return r_symlink(arg);
+ }
+ return r_symreal(arg);
+}
+
+static char*
+r_unique(arg)
+ struct load_arg *arg;
+{
+ return rb_id2name(r_symbol(arg));
+}
+
+static VALUE
+r_string(arg)
+ struct load_arg *arg;
+{
+ return r_bytes(arg);
+}
+
+static VALUE
+r_entry(v, arg)
+ VALUE v;
+ struct load_arg *arg;
+{
+ st_insert(arg->data, arg->data->num_entries, (st_data_t)v);
+ if (arg->taint) OBJ_TAINT(v);
+ return v;
+}
+
+static void
+r_ivar(obj, arg)
+ VALUE obj;
+ struct load_arg *arg;
+{
+ long len;
+
+ len = r_long(arg);
+ if (len > 0) {
+ while (len--) {
+ ID id = r_symbol(arg);
+ VALUE val = r_object(arg);
+ rb_ivar_set(obj, id, val);
+ }
+ }
+}
+
+static VALUE
+path2class(path)
+ char *path;
+{
+ VALUE v = rb_path2class(path);
+
+ if (TYPE(v) != T_CLASS) {
+ rb_raise(rb_eArgError, "%s does not refer class", path);
+ }
+ return v;
+}
+
+static VALUE
+path2module(path)
+ char *path;
+{
+ VALUE v = rb_path2class(path);
+
+ if (TYPE(v) != T_MODULE) {
+ rb_raise(rb_eArgError, "%s does not refer module", path);
+ }
+ return v;
+}
+
+static VALUE
+r_object0(arg, proc, ivp, extmod)
+ struct load_arg *arg;
+ VALUE proc;
+ int *ivp;
+ VALUE extmod;
+{
+ VALUE v = Qnil;
+ int type = r_byte(arg);
+ long id;
+ st_data_t link;
+
+ switch (type) {
+ case TYPE_LINK:
+ id = r_long(arg);
+ if (!st_lookup(arg->data, (st_data_t)id, &link)) {
+ rb_raise(rb_eArgError, "dump format error (unlinked)");
+ }
+ v = (st_data_t)link;
+ return v;
+
+ case TYPE_IVAR:
+ {
+ int ivar = Qtrue;
+
+ v = r_object0(arg, 0, &ivar, extmod);
+ if (ivar) r_ivar(v, arg);
+ }
+ break;
+
+ case TYPE_EXTENDED:
+ {
+ VALUE m = path2module(r_unique(arg));
+
+ if (NIL_P(extmod)) extmod = rb_ary_new2(0);
+ rb_ary_push(extmod, m);
+
+ v = r_object0(arg, 0, 0, extmod);
+ while (RARRAY(extmod)->len > 0) {
+ m = rb_ary_pop(extmod);
+ rb_extend_object(v, m);
+ }
+ }
+ break;
+
+ case TYPE_UCLASS:
+ {
+ VALUE c = path2class(r_unique(arg));
+
+ if (FL_TEST(c, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton can't be loaded");
+ }
+ v = r_object0(arg, 0, 0, extmod);
+ if (rb_special_const_p(v) || TYPE(v) == T_OBJECT || TYPE(v) == T_CLASS) {
+ format_error:
+ rb_raise(rb_eArgError, "dump format error (user class)");
+ }
+ if (TYPE(v) == T_MODULE || !RTEST(rb_class_inherited_p(c, RBASIC(v)->klass))) {
+ VALUE tmp = rb_obj_alloc(c);
+
+ if (TYPE(v) != TYPE(tmp)) goto format_error;
+ }
+ RBASIC(v)->klass = c;
+ }
+ break;
+
+ case TYPE_NIL:
+ v = Qnil;
+ break;
+
+ case TYPE_TRUE:
+ v = Qtrue;
+ break;
+
+ case TYPE_FALSE:
+ v = Qfalse;
+ break;
+
+ case TYPE_FIXNUM:
+ {
+ long i = r_long(arg);
+ v = LONG2FIX(i);
+ }
+ break;
+
+ case TYPE_FLOAT:
+ {
+ double d, t = 0.0;
+ VALUE str = r_bytes(arg);
+ const char *ptr = RSTRING(str)->ptr;
+
+ if (strcmp(ptr, "nan") == 0) {
+ d = t / t;
+ }
+ else if (strcmp(ptr, "inf") == 0) {
+ d = 1.0 / t;
+ }
+ else if (strcmp(ptr, "-inf") == 0) {
+ d = -1.0 / t;
+ }
+ else {
+ char *e;
+ d = strtod(ptr, &e);
+ d = load_mantissa(d, e, RSTRING(str)->len - (e - ptr));
+ }
+ v = rb_float_new(d);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_BIGNUM:
+ {
+ long len;
+ BDIGIT *digits;
+ volatile VALUE data;
+
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, rb_cBignum, T_BIGNUM);
+ big->sign = (r_byte(arg) == '+');
+ len = r_long(arg);
+ data = r_bytes0(len * 2, arg);
+#if SIZEOF_BDIGITS == SIZEOF_SHORT
+ big->len = len;
+#else
+ big->len = (len + 1) * 2 / sizeof(BDIGIT);
+#endif
+ big->digits = digits = ALLOC_N(BDIGIT, big->len);
+ MEMCPY(digits, RSTRING(data)->ptr, char, len * 2);
+#if SIZEOF_BDIGITS > SIZEOF_SHORT
+ MEMZERO((char *)digits + len * 2, char,
+ big->len * sizeof(BDIGIT) - len * 2);
+#endif
+ len = big->len;
+ while (len > 0) {
+ unsigned char *p = (unsigned char *)digits;
+ BDIGIT num = 0;
+#if SIZEOF_BDIGITS > SIZEOF_SHORT
+ int shift = 0;
+ int i;
+
+ for (i=0; i<SIZEOF_BDIGITS; i++) {
+ num |= (int)p[i] << shift;
+ shift += 8;
+ }
+#else
+ num = p[0] | (p[1] << 8);
+#endif
+ *digits++ = num;
+ len--;
+ }
+ v = rb_big_norm((VALUE)big);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_STRING:
+ v = r_entry(r_string(arg), arg);
+ break;
+
+ case TYPE_REGEXP:
+ {
+ volatile VALUE str = r_bytes(arg);
+ int options = r_byte(arg);
+ v = r_entry(rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len, options), arg);
+ }
+ break;
+
+ case TYPE_ARRAY:
+ {
+ volatile long len = r_long(arg); /* gcc 2.7.2.3 -O2 bug?? */
+
+ v = rb_ary_new2(len);
+ r_entry(v, arg);
+ while (len--) {
+ rb_ary_push(v, r_object(arg));
+ }
+ }
+ break;
+
+ case TYPE_HASH:
+ case TYPE_HASH_DEF:
+ {
+ long len = r_long(arg);
+
+ v = rb_hash_new();
+ r_entry(v, arg);
+ while (len--) {
+ VALUE key = r_object(arg);
+ VALUE value = r_object(arg);
+ rb_hash_aset(v, key, value);
+ }
+ if (type == TYPE_HASH_DEF) {
+ RHASH(v)->ifnone = r_object(arg);
+ }
+ }
+ break;
+
+ case TYPE_STRUCT:
+ {
+ VALUE klass, mem, values;
+ volatile long i; /* gcc 2.7.2.3 -O2 bug?? */
+ long len;
+ ID slot;
+
+ klass = path2class(r_unique(arg));
+ mem = rb_struct_s_members(klass);
+ if (mem == Qnil) {
+ rb_raise(rb_eTypeError, "uninitialized struct");
+ }
+ len = r_long(arg);
+
+ values = rb_ary_new2(len);
+ for (i=0; i<len; i++) {
+ rb_ary_push(values, Qnil);
+ }
+ v = rb_struct_alloc(klass, values);
+ r_entry(v, arg);
+ for (i=0; i<len; i++) {
+ slot = r_symbol(arg);
+
+ if (RARRAY(mem)->ptr[i] != ID2SYM(slot)) {
+ rb_raise(rb_eTypeError, "struct %s not compatible (:%s for :%s)",
+ rb_class2name(klass),
+ rb_id2name(slot),
+ rb_id2name(SYM2ID(RARRAY(mem)->ptr[i])));
+ }
+ rb_struct_aset(v, LONG2FIX(i), r_object(arg));
+ }
+ }
+ break;
+
+ case TYPE_USERDEF:
+ {
+ VALUE klass = path2class(r_unique(arg));
+ VALUE data;
+
+ if (!rb_respond_to(klass, s_load)) {
+ rb_raise(rb_eTypeError, "class %s needs to have method `_load'",
+ rb_class2name(klass));
+ }
+ data = r_string(arg);
+ if (ivp) {
+ r_ivar(data, arg);
+ *ivp = Qfalse;
+ }
+ v = rb_funcall(klass, s_load, 1, data);
+ check_load_arg(arg, s_load);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_USRMARSHAL:
+ {
+ VALUE klass = path2class(r_unique(arg));
+ VALUE data;
+
+ v = rb_obj_alloc(klass);
+ if (! NIL_P(extmod)) {
+ while (RARRAY(extmod)->len > 0) {
+ VALUE m = rb_ary_pop(extmod);
+ rb_extend_object(v, m);
+ }
+ }
+ if (!rb_respond_to(v, s_mload)) {
+ rb_raise(rb_eTypeError, "instance of %s needs to have method `marshal_load'",
+ rb_class2name(klass));
+ }
+ r_entry(v, arg);
+ data = r_object(arg);
+ rb_funcall(v, s_mload, 1, data);
+ check_load_arg(arg, s_mload);
+ }
+ break;
+
+ case TYPE_OBJECT:
+ {
+ VALUE klass = path2class(r_unique(arg));
+
+ v = rb_obj_alloc(klass);
+ if (TYPE(v) != T_OBJECT) {
+ rb_raise(rb_eArgError, "dump format error");
+ }
+ r_entry(v, arg);
+ r_ivar(v, arg);
+ }
+ break;
+
+ case TYPE_DATA:
+ {
+ VALUE klass = path2class(r_unique(arg));
+ if (rb_respond_to(klass, s_alloc)) {
+ static int warn = Qtrue;
+ if (warn) {
+ rb_warn("define `allocate' instead of `_alloc'");
+ warn = Qfalse;
+ }
+ v = rb_funcall(klass, s_alloc, 0);
+ check_load_arg(arg, s_alloc);
+ }
+ else {
+ v = rb_obj_alloc(klass);
+ }
+ if (TYPE(v) != T_DATA) {
+ rb_raise(rb_eArgError, "dump format error");
+ }
+ r_entry(v, arg);
+ if (!rb_respond_to(v, s_load_data)) {
+ rb_raise(rb_eTypeError,
+ "class %s needs to have instance method `_load_data'",
+ rb_class2name(klass));
+ }
+ rb_funcall(v, s_load_data, 1, r_object0(arg, 0, 0, extmod));
+ check_load_arg(arg, s_load_data);
+ }
+ break;
+
+ case TYPE_MODULE_OLD:
+ {
+ volatile VALUE str = r_bytes(arg);
+
+ v = rb_path2class(RSTRING(str)->ptr);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_CLASS:
+ {
+ volatile VALUE str = r_bytes(arg);
+
+ v = path2class(RSTRING(str)->ptr);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_MODULE:
+ {
+ volatile VALUE str = r_bytes(arg);
+
+ v = path2module(RSTRING(str)->ptr);
+ r_entry(v, arg);
+ }
+ break;
+
+ case TYPE_SYMBOL:
+ v = ID2SYM(r_symreal(arg));
+ break;
+
+ case TYPE_SYMLINK:
+ return ID2SYM(r_symlink(arg));
+
+ default:
+ rb_raise(rb_eArgError, "dump format error(0x%x)", type);
+ break;
+ }
+ if (proc) {
+ rb_funcall(proc, s_call, 1, v);
+ check_load_arg(arg, s_call);
+ }
+ return v;
+}
+
+static VALUE
+r_object(arg)
+ struct load_arg *arg;
+{
+ return r_object0(arg, arg->proc, 0, Qnil);
+}
+
+static VALUE
+load(arg)
+ struct load_arg *arg;
+{
+ return r_object(arg);
+}
+
+static VALUE
+load_ensure(arg)
+ struct load_arg *arg;
+{
+ if (!DATA_PTR(arg->wrapper)) return 0;
+ st_free_table(arg->symbols);
+ st_free_table(arg->data);
+ DATA_PTR(arg->wrapper) = 0;
+ arg->wrapper = 0;
+ return 0;
+}
+
+/*
+ * call-seq:
+ * load( source [, proc] ) => obj
+ * restore( source [, proc] ) => obj
+ *
+ * Returns the result of converting the serialized data in source into a
+ * Ruby object (possibly with associated subordinate objects). source
+ * may be either an instance of IO or an object that responds to
+ * to_str. If proc is specified, it will be passed each object as it
+ * is deserialized.
+ */
+static VALUE
+marshal_load(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE port, proc;
+ int major, minor;
+ VALUE v;
+ struct load_arg arg;
+
+ rb_scan_args(argc, argv, "11", &port, &proc);
+ v = rb_check_string_type(port);
+ if (!NIL_P(v)) {
+ arg.taint = OBJ_TAINTED(port); /* original taintedness */
+ port = v;
+ }
+ else if (rb_respond_to(port, s_getc) && rb_respond_to(port, s_read)) {
+ if (rb_respond_to(port, s_binmode)) {
+ rb_funcall2(port, s_binmode, 0, 0);
+ }
+ arg.taint = Qtrue;
+ }
+ else {
+ rb_raise(rb_eTypeError, "instance of IO needed");
+ }
+ arg.src = port;
+ arg.offset = 0;
+ arg.symbols = st_init_numtable();
+ arg.data = st_init_numtable();
+ arg.proc = 0;
+ arg.wrapper = Data_Wrap_Struct(rb_cData, mark_load_arg, 0, &arg);
+
+ major = r_byte(&arg);
+ minor = r_byte(&arg);
+ if (major != MARSHAL_MAJOR || minor > MARSHAL_MINOR) {
+ rb_raise(rb_eTypeError, "incompatible marshal file format (can't be read)\n\
+\tformat version %d.%d required; %d.%d given",
+ MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
+ }
+ if (RTEST(ruby_verbose) && minor != MARSHAL_MINOR) {
+ rb_warn("incompatible marshal file format (can be read)\n\
+\tformat version %d.%d required; %d.%d given",
+ MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
+ }
+
+ if (!NIL_P(proc)) arg.proc = proc;
+ v = rb_ensure(load, (VALUE)&arg, load_ensure, (VALUE)&arg);
+
+ return v;
+}
+
+/*
+ * The marshaling library converts collections of Ruby objects into a
+ * byte stream, allowing them to be stored outside the currently
+ * active script. This data may subsequently be read and the original
+ * objects reconstituted.
+ * Marshaled data has major and minor version numbers stored along
+ * with the object information. In normal use, marshaling can only
+ * load data written with the same major version number and an equal
+ * or lower minor version number. If Ruby's ``verbose'' flag is set
+ * (normally using -d, -v, -w, or --verbose) the major and minor
+ * numbers must match exactly. Marshal versioning is independent of
+ * Ruby's version numbers. You can extract the version by reading the
+ * first two bytes of marshaled data.
+ *
+ * str = Marshal.dump("thing")
+ * RUBY_VERSION #=> "1.8.0"
+ * str[0] #=> 4
+ * str[1] #=> 8
+ *
+ * Some objects cannot be dumped: if the objects to be dumped include
+ * bindings, procedure or method objects, instances of class IO, or
+ * singleton objects, a TypeError will be raised.
+ * If your class has special serialization needs (for example, if you
+ * want to serialize in some specific format), or if it contains
+ * objects that would otherwise not be serializable, you can implement
+ * your own serialization strategy by defining two methods, _dump and
+ * _load:
+ * The instance method _dump should return a String object containing
+ * all the information necessary to reconstitute objects of this class
+ * and all referenced objects up to a maximum depth given as an integer
+ * parameter (a value of -1 implies that you should disable depth checking).
+ * The class method _load should take a String and return an object of this class.
+ */
+void
+Init_marshal()
+{
+ VALUE rb_mMarshal = rb_define_module("Marshal");
+
+ s_dump = rb_intern("_dump");
+ s_load = rb_intern("_load");
+ s_mdump = rb_intern("marshal_dump");
+ s_mload = rb_intern("marshal_load");
+ s_dump_data = rb_intern("_dump_data");
+ s_load_data = rb_intern("_load_data");
+ s_alloc = rb_intern("_alloc");
+ s_call = rb_intern("call");
+ s_getc = rb_intern("getc");
+ s_read = rb_intern("read");
+ s_write = rb_intern("write");
+ s_binmode = rb_intern("binmode");
+
+ rb_define_module_function(rb_mMarshal, "dump", marshal_dump, -1);
+ rb_define_module_function(rb_mMarshal, "load", marshal_load, -1);
+ rb_define_module_function(rb_mMarshal, "restore", marshal_load, -1);
+
+ rb_define_const(rb_mMarshal, "MAJOR_VERSION", INT2FIX(MARSHAL_MAJOR));
+ rb_define_const(rb_mMarshal, "MINOR_VERSION", INT2FIX(MARSHAL_MINOR));
+}
+
+VALUE
+rb_marshal_dump(obj, port)
+ VALUE obj, port;
+{
+ int argc = 1;
+ VALUE argv[2];
+
+ argv[0] = obj;
+ argv[1] = port;
+ if (!NIL_P(port)) argc = 2;
+ return marshal_dump(argc, argv);
+}
+
+VALUE
+rb_marshal_load(port)
+ VALUE port;
+{
+ return marshal_load(1, &port);
+}
+/**********************************************************************
+
+ math.c -
+
+ $Author: shyouhei $
+ $Date: 2008-07-02 11:25:48 +0200 (Wed, 02 Jul 2008) $
+ created at: Tue Jan 25 14:12:56 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include <math.h>
+#include <errno.h>
+
+VALUE rb_mMath;
+
+#define Need_Float(x) (x) = rb_Float(x)
+#define Need_Float2(x,y) do {\
+ Need_Float(x);\
+ Need_Float(y);\
+} while (0)
+
+static void
+domain_check(x, msg)
+ double x;
+ char *msg;
+{
+ while(1) {
+ if (errno) {
+ rb_sys_fail(msg);
+ }
+ if (isnan(x)) {
+#if defined(EDOM)
+ errno = EDOM;
+#elif defined(ERANGE)
+ errno = ERANGE;
+#endif
+ continue;
+ }
+ break;
+ }
+}
+
+
+/*
+ * call-seq:
+ * Math.atan2(y, x) => float
+ *
+ * Computes the arc tangent given <i>y</i> and <i>x</i>. Returns
+ * -PI..PI.
+ *
+ */
+
+static VALUE
+math_atan2(obj, y, x)
+ VALUE obj, x, y;
+{
+ Need_Float2(y, x);
+ return rb_float_new(atan2(RFLOAT(y)->value, RFLOAT(x)->value));
+}
+
+
+/*
+ * call-seq:
+ * Math.cos(x) => float
+ *
+ * Computes the cosine of <i>x</i> (expressed in radians). Returns
+ * -1..1.
+ */
+
+static VALUE
+math_cos(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(cos(RFLOAT(x)->value));
+}
+
+/*
+ * call-seq:
+ * Math.sin(x) => float
+ *
+ * Computes the sine of <i>x</i> (expressed in radians). Returns
+ * -1..1.
+ */
+
+static VALUE
+math_sin(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+
+ return rb_float_new(sin(RFLOAT(x)->value));
+}
+
+
+/*
+ * call-seq:
+ * Math.tan(x) => float
+ *
+ * Returns the tangent of <i>x</i> (expressed in radians).
+ */
+
+static VALUE
+math_tan(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+
+ return rb_float_new(tan(RFLOAT(x)->value));
+}
+
+/*
+ * call-seq:
+ * Math.acos(x) => float
+ *
+ * Computes the arc cosine of <i>x</i>. Returns 0..PI.
+ */
+
+static VALUE
+math_acos(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = acos(RFLOAT(x)->value);
+ domain_check(d, "acos");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.asin(x) => float
+ *
+ * Computes the arc sine of <i>x</i>. Returns 0..PI.
+ */
+
+static VALUE
+math_asin(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = asin(RFLOAT(x)->value);
+ domain_check(d, "asin");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.atan(x) => float
+ *
+ * Computes the arc tangent of <i>x</i>. Returns -{PI/2} .. {PI/2}.
+ */
+
+static VALUE
+math_atan(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(atan(RFLOAT(x)->value));
+}
+
+#ifndef HAVE_COSH
+double
+cosh(x)
+ double x;
+{
+ return (exp(x) + exp(-x)) / 2;
+}
+#endif
+
+/*
+ * call-seq:
+ * Math.cosh(x) => float
+ *
+ * Computes the hyperbolic cosine of <i>x</i> (expressed in radians).
+ */
+
+static VALUE
+math_cosh(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+
+ return rb_float_new(cosh(RFLOAT(x)->value));
+}
+
+#ifndef HAVE_SINH
+double
+sinh(x)
+ double x;
+{
+ return (exp(x) - exp(-x)) / 2;
+}
+#endif
+
+/*
+ * call-seq:
+ * Math.sinh(x) => float
+ *
+ * Computes the hyperbolic sine of <i>x</i> (expressed in
+ * radians).
+ */
+
+static VALUE
+math_sinh(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(sinh(RFLOAT(x)->value));
+}
+
+#ifndef HAVE_TANH
+double
+tanh(x)
+ double x;
+{
+ return sinh(x) / cosh(x);
+}
+#endif
+
+/*
+ * call-seq:
+ * Math.tanh() => float
+ *
+ * Computes the hyperbolic tangent of <i>x</i> (expressed in
+ * radians).
+ */
+
+static VALUE
+math_tanh(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(tanh(RFLOAT(x)->value));
+}
+
+/*
+ * call-seq:
+ * Math.acosh(x) => float
+ *
+ * Computes the inverse hyperbolic cosine of <i>x</i>.
+ */
+
+static VALUE
+math_acosh(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = acosh(RFLOAT(x)->value);
+ domain_check(d, "acosh");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.asinh(x) => float
+ *
+ * Computes the inverse hyperbolic sine of <i>x</i>.
+ */
+
+static VALUE
+math_asinh(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(asinh(RFLOAT(x)->value));
+}
+
+/*
+ * call-seq:
+ * Math.atanh(x) => float
+ *
+ * Computes the inverse hyperbolic tangent of <i>x</i>.
+ */
+
+static VALUE
+math_atanh(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = atanh(RFLOAT(x)->value);
+ domain_check(d, "atanh");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.exp(x) => float
+ *
+ * Returns e**x.
+ */
+
+static VALUE
+math_exp(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(exp(RFLOAT(x)->value));
+}
+
+#if defined __CYGWIN__
+# include <cygwin/version.h>
+# if CYGWIN_VERSION_DLL_MAJOR < 1005
+# define nan(x) nan()
+# endif
+# define log(x) ((x) < 0.0 ? nan("") : log(x))
+# define log10(x) ((x) < 0.0 ? nan("") : log10(x))
+#endif
+
+/*
+ * call-seq:
+ * Math.log(numeric) => float
+ *
+ * Returns the natural logarithm of <i>numeric</i>.
+ */
+
+static VALUE
+math_log(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = log(RFLOAT(x)->value);
+ domain_check(d, "log");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.log10(numeric) => float
+ *
+ * Returns the base 10 logarithm of <i>numeric</i>.
+ */
+
+static VALUE
+math_log10(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = log10(RFLOAT(x)->value);
+ domain_check(d, "log10");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.sqrt(numeric) => float
+ *
+ * Returns the non-negative square root of <i>numeric</i>.
+ */
+
+static VALUE
+math_sqrt(obj, x)
+ VALUE obj, x;
+{
+ double d;
+
+ Need_Float(x);
+ errno = 0;
+ d = sqrt(RFLOAT(x)->value);
+ domain_check(d, "sqrt");
+ return rb_float_new(d);
+}
+
+/*
+ * call-seq:
+ * Math.frexp(numeric) => [ fraction, exponent ]
+ *
+ * Returns a two-element array containing the normalized fraction (a
+ * <code>Float</code>) and exponent (a <code>Fixnum</code>) of
+ * <i>numeric</i>.
+ *
+ * fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11]
+ * fraction * 2**exponent #=> 1234.0
+ */
+
+static VALUE
+math_frexp(obj, x)
+ VALUE obj, x;
+{
+ double d;
+ int exp;
+
+ Need_Float(x);
+
+ d = frexp(RFLOAT(x)->value, &exp);
+ return rb_assoc_new(rb_float_new(d), INT2NUM(exp));
+}
+
+/*
+ * call-seq:
+ * Math.ldexp(flt, int) -> float
+ *
+ * Returns the value of <i>flt</i>*(2**<i>int</i>).
+ *
+ * fraction, exponent = Math.frexp(1234)
+ * Math.ldexp(fraction, exponent) #=> 1234.0
+ */
+
+static VALUE
+math_ldexp(obj, x, n)
+ VALUE obj, x, n;
+{
+ Need_Float(x);
+ return rb_float_new(ldexp(RFLOAT(x)->value, NUM2INT(n)));
+}
+
+/*
+ * call-seq:
+ * Math.hypot(x, y) => float
+ *
+ * Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle
+ * with sides <i>x</i> and <i>y</i>.
+ *
+ * Math.hypot(3, 4) #=> 5.0
+ */
+
+static VALUE
+math_hypot(obj, x, y)
+ VALUE obj, x, y;
+{
+ Need_Float2(x, y);
+ return rb_float_new(hypot(RFLOAT(x)->value, RFLOAT(y)->value));
+}
+
+/*
+ * call-seq:
+ * Math.erf(x) => float
+ *
+ * Calculates the error function of x.
+ */
+
+static VALUE
+math_erf(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(erf(RFLOAT(x)->value));
+}
+
+/*
+ * call-seq:
+ * Math.erfc(x) => float
+ *
+ * Calculates the complementary error function of x.
+ */
+
+static VALUE
+math_erfc(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(erfc(RFLOAT(x)->value));
+}
+
+/*
+ * The <code>Math</code> module contains module functions for basic
+ * trigonometric and transcendental functions. See class
+ * <code>Float</code> for a list of constants that
+ * define Ruby's floating point accuracy.
+ */
+
+
+void
+Init_Math()
+{
+ rb_mMath = rb_define_module("Math");
+
+#ifdef M_PI
+ rb_define_const(rb_mMath, "PI", rb_float_new(M_PI));
+#else
+ rb_define_const(rb_mMath, "PI", rb_float_new(atan(1.0)*4.0));
+#endif
+
+#ifdef M_E
+ rb_define_const(rb_mMath, "E", rb_float_new(M_E));
+#else
+ rb_define_const(rb_mMath, "E", rb_float_new(exp(1.0)));
+#endif
+
+ rb_define_module_function(rb_mMath, "atan2", math_atan2, 2);
+ rb_define_module_function(rb_mMath, "cos", math_cos, 1);
+ rb_define_module_function(rb_mMath, "sin", math_sin, 1);
+ rb_define_module_function(rb_mMath, "tan", math_tan, 1);
+
+ rb_define_module_function(rb_mMath, "acos", math_acos, 1);
+ rb_define_module_function(rb_mMath, "asin", math_asin, 1);
+ rb_define_module_function(rb_mMath, "atan", math_atan, 1);
+
+ rb_define_module_function(rb_mMath, "cosh", math_cosh, 1);
+ rb_define_module_function(rb_mMath, "sinh", math_sinh, 1);
+ rb_define_module_function(rb_mMath, "tanh", math_tanh, 1);
+
+ rb_define_module_function(rb_mMath, "acosh", math_acosh, 1);
+ rb_define_module_function(rb_mMath, "asinh", math_asinh, 1);
+ rb_define_module_function(rb_mMath, "atanh", math_atanh, 1);
+
+ rb_define_module_function(rb_mMath, "exp", math_exp, 1);
+ rb_define_module_function(rb_mMath, "log", math_log, 1);
+ rb_define_module_function(rb_mMath, "log10", math_log10, 1);
+ rb_define_module_function(rb_mMath, "sqrt", math_sqrt, 1);
+
+ rb_define_module_function(rb_mMath, "frexp", math_frexp, 1);
+ rb_define_module_function(rb_mMath, "ldexp", math_ldexp, 2);
+
+ rb_define_module_function(rb_mMath, "hypot", math_hypot, 2);
+
+ rb_define_module_function(rb_mMath, "erf", math_erf, 1);
+ rb_define_module_function(rb_mMath, "erfc", math_erfc, 1);
+}
+/**********************************************************************
+
+ numeric.c -
+
+ $Author: wyhaines $
+ $Date: 2009-07-14 17:05:27 +0200 (Tue, 14 Jul 2009) $
+ created at: Fri Aug 13 18:33:09 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include <ctype.h>
+#include <math.h>
+#include <stdio.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ < 4
+#include <floatingpoint.h>
+#endif
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+/* use IEEE 64bit values if not defined */
+#ifndef FLT_RADIX
+#define FLT_RADIX 2
+#endif
+#ifndef FLT_ROUNDS
+#define FLT_ROUNDS 1
+#endif
+#ifndef DBL_MIN
+#define DBL_MIN 2.2250738585072014e-308
+#endif
+#ifndef DBL_MAX
+#define DBL_MAX 1.7976931348623157e+308
+#endif
+#ifndef DBL_MIN_EXP
+#define DBL_MIN_EXP (-1021)
+#endif
+#ifndef DBL_MAX_EXP
+#define DBL_MAX_EXP 1024
+#endif
+#ifndef DBL_MIN_10_EXP
+#define DBL_MIN_10_EXP (-307)
+#endif
+#ifndef DBL_MAX_10_EXP
+#define DBL_MAX_10_EXP 308
+#endif
+#ifndef DBL_DIG
+#define DBL_DIG 15
+#endif
+#ifndef DBL_MANT_DIG
+#define DBL_MANT_DIG 53
+#endif
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 2.2204460492503131e-16
+#endif
+
+#ifndef HAVE_ROUND
+double
+round(x)
+ double x;
+{
+ double f;
+
+ if (x > 0.0) {
+ f = floor(x);
+ x = f + (x - f >= 0.5);
+ }
+ else if (x < 0.0) {
+ f = ceil(x);
+ x = f - (f - x >= 0.5);
+ }
+ return x;
+}
+#endif
+
+static ID id_coerce, id_to_i, id_eq;
+
+VALUE rb_cNumeric;
+VALUE rb_cFloat;
+VALUE rb_cInteger;
+VALUE rb_cFixnum;
+
+VALUE rb_eZeroDivError;
+VALUE rb_eFloatDomainError;
+
+void
+rb_num_zerodiv()
+{
+ rb_raise(rb_eZeroDivError, "divided by 0");
+}
+
+
+/*
+ * call-seq:
+ * num.coerce(numeric) => array
+ *
+ * If <i>aNumeric</i> is the same type as <i>num</i>, returns an array
+ * containing <i>aNumeric</i> and <i>num</i>. Otherwise, returns an
+ * array with both <i>aNumeric</i> and <i>num</i> represented as
+ * <code>Float</code> objects. This coercion mechanism is used by
+ * Ruby to handle mixed-type numeric operations: it is intended to
+ * find a compatible common type between the two operands of the operator.
+ *
+ * 1.coerce(2.5) #=> [2.5, 1.0]
+ * 1.2.coerce(3) #=> [3.0, 1.2]
+ * 1.coerce(2) #=> [2, 1]
+ */
+
+static VALUE
+num_coerce(x, y)
+ VALUE x, y;
+{
+ if (CLASS_OF(x) == CLASS_OF(y))
+ return rb_assoc_new(y, x);
+ x = rb_Float(x);
+ y = rb_Float(y);
+ return rb_assoc_new(y, x);
+}
+
+static VALUE
+coerce_body(x)
+ VALUE *x;
+{
+ return rb_funcall(x[1], id_coerce, 1, x[0]);
+}
+
+static VALUE
+coerce_rescue(x)
+ VALUE *x;
+{
+ volatile VALUE v = rb_inspect(x[1]);
+
+ rb_raise(rb_eTypeError, "%s can't be coerced into %s",
+ rb_special_const_p(x[1])?
+ RSTRING(v)->ptr:
+ rb_obj_classname(x[1]),
+ rb_obj_classname(x[0]));
+ return Qnil; /* dummy */
+}
+
+static int
+do_coerce(x, y, err)
+ VALUE *x, *y;
+ int err;
+{
+ VALUE ary;
+ VALUE a[2];
+
+ a[0] = *x; a[1] = *y;
+
+ ary = rb_rescue(coerce_body, (VALUE)a, err?coerce_rescue:0, (VALUE)a);
+ if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) {
+ if (err) {
+ rb_raise(rb_eTypeError, "coerce must return [x, y]");
+ }
+ return Qfalse;
+ }
+
+ *x = RARRAY(ary)->ptr[0];
+ *y = RARRAY(ary)->ptr[1];
+ return Qtrue;
+}
+
+VALUE
+rb_num_coerce_bin(x, y)
+ VALUE x, y;
+{
+ do_coerce(&x, &y, Qtrue);
+ return rb_funcall(x, ruby_frame->orig_func, 1, y);
+}
+
+VALUE
+rb_num_coerce_cmp(x, y)
+ VALUE x, y;
+{
+ if (do_coerce(&x, &y, Qfalse))
+ return rb_funcall(x, ruby_frame->orig_func, 1, y);
+ return Qnil;
+}
+
+VALUE
+rb_num_coerce_relop(x, y)
+ VALUE x, y;
+{
+ VALUE c, x0 = x, y0 = y;
+
+ if (!do_coerce(&x, &y, Qfalse) ||
+ NIL_P(c = rb_funcall(x, ruby_frame->orig_func, 1, y))) {
+ rb_cmperr(x0, y0);
+ return Qnil; /* not reached */
+ }
+ return c;
+}
+
+/*
+ * Trap attempts to add methods to <code>Numeric</code> objects. Always
+ * raises a <code>TypeError</code>
+ */
+
+static VALUE
+num_sadded(x, name)
+ VALUE x, name;
+{
+ ruby_frame = ruby_frame->prev; /* pop frame for "singleton_method_added" */
+ /* Numerics should be values; singleton_methods should not be added to them */
+ rb_raise(rb_eTypeError,
+ "can't define singleton method \"%s\" for %s",
+ rb_id2name(rb_to_id(name)),
+ rb_obj_classname(x));
+ return Qnil; /* not reached */
+}
+
+/* :nodoc: */
+static VALUE
+num_init_copy(x, y)
+ VALUE x, y;
+{
+ /* Numerics are immutable values, which should not be copied */
+ rb_raise(rb_eTypeError, "can't copy %s", rb_obj_classname(x));
+ return Qnil; /* not reached */
+}
+
+/*
+ * call-seq:
+ * +num => num
+ *
+ * Unary Plus---Returns the receiver's value.
+ */
+
+static VALUE
+num_uplus(num)
+ VALUE num;
+{
+ return num;
+}
+
+/*
+ * call-seq:
+ * -num => numeric
+ *
+ * Unary Minus---Returns the receiver's value, negated.
+ */
+
+static VALUE
+num_uminus(num)
+ VALUE num;
+{
+ VALUE zero;
+
+ zero = INT2FIX(0);
+ do_coerce(&zero, &num, Qtrue);
+
+ return rb_funcall(zero, '-', 1, num);
+}
+
+/*
+ * call-seq:
+ * num.quo(numeric) => result
+ *
+ * Equivalent to <code>Numeric#/</code>, but overridden in subclasses.
+ */
+
+static VALUE
+num_quo(x, y)
+ VALUE x, y;
+{
+ return rb_funcall(x, '/', 1, y);
+}
+
+
+static VALUE num_floor(VALUE num);
+
+/*
+ * call-seq:
+ * num.div(numeric) => integer
+ *
+ * Uses <code>/</code> to perform division, then converts the result to
+ * an integer. <code>Numeric</code> does not define the <code>/</code>
+ * operator; this is left to subclasses.
+ */
+
+static VALUE
+num_div(x, y)
+ VALUE x, y;
+{
+ return num_floor(rb_funcall(x, '/', 1, y));
+}
+
+
+
+/*
+ * call-seq:
+ * num.divmod( aNumeric ) -> anArray
+ *
+ * Returns an array containing the quotient and modulus obtained by
+ * dividing <i>num</i> by <i>aNumeric</i>. If <code>q, r =
+ * x.divmod(y)</code>, then
+ *
+ * q = floor(float(x)/float(y))
+ * x = q*y + r
+ *
+ * The quotient is rounded toward -infinity, as shown in the following table:
+ *
+ * a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b)
+ * ------+-----+---------------+---------+-------------+---------------
+ * 13 | 4 | 3, 1 | 3 | 1 | 1
+ * ------+-----+---------------+---------+-------------+---------------
+ * 13 | -4 | -4, -3 | -3 | -3 | 1
+ * ------+-----+---------------+---------+-------------+---------------
+ * -13 | 4 | -4, 3 | -4 | 3 | -1
+ * ------+-----+---------------+---------+-------------+---------------
+ * -13 | -4 | 3, -1 | 3 | -1 | -1
+ * ------+-----+---------------+---------+-------------+---------------
+ * 11.5 | 4 | 2, 3.5 | 2.875 | 3.5 | 3.5
+ * ------+-----+---------------+---------+-------------+---------------
+ * 11.5 | -4 | -3, -0.5 | -2.875 | -0.5 | 3.5
+ * ------+-----+---------------+---------+-------------+---------------
+ * -11.5 | 4 | -3, 0.5 | -2.875 | 0.5 | -3.5
+ * ------+-----+---------------+---------+-------------+---------------
+ * -11.5 | -4 | 2 -3.5 | 2.875 | -3.5 | -3.5
+ *
+ *
+ * Examples
+ * 11.divmod(3) #=> [3, 2]
+ * 11.divmod(-3) #=> [-4, -1]
+ * 11.divmod(3.5) #=> [3, 0.5]
+ * (-11).divmod(3.5) #=> [-4, 3.0]
+ * (11.5).divmod(3.5) #=> [3, 1.0]
+ */
+
+static VALUE
+num_divmod(x, y)
+ VALUE x, y;
+{
+ return rb_assoc_new(num_div(x, y), rb_funcall(x, '%', 1, y));
+}
+
+/*
+ * call-seq:
+ * num.modulo(numeric) => result
+ *
+ * Equivalent to
+ * <i>num</i>.<code>divmod(</code><i>aNumeric</i><code>)[1]</code>.
+ */
+
+static VALUE
+num_modulo(x, y)
+ VALUE x, y;
+{
+ return rb_funcall(x, '%', 1, y);
+}
+
+/*
+ * call-seq:
+ * num.remainder(numeric) => result
+ *
+ * If <i>num</i> and <i>numeric</i> have different signs, returns
+ * <em>mod</em>-<i>numeric</i>; otherwise, returns <em>mod</em>. In
+ * both cases <em>mod</em> is the value
+ * <i>num</i>.<code>modulo(</code><i>numeric</i><code>)</code>. The
+ * differences between <code>remainder</code> and modulo
+ * (<code>%</code>) are shown in the table under <code>Numeric#divmod</code>.
+ */
+
+static VALUE
+num_remainder(x, y)
+ VALUE x, y;
+{
+ VALUE z = rb_funcall(x, '%', 1, y);
+
+ if ((!rb_equal(z, INT2FIX(0))) &&
+ ((RTEST(rb_funcall(x, '<', 1, INT2FIX(0))) &&
+ RTEST(rb_funcall(y, '>', 1, INT2FIX(0)))) ||
+ (RTEST(rb_funcall(x, '>', 1, INT2FIX(0))) &&
+ RTEST(rb_funcall(y, '<', 1, INT2FIX(0)))))) {
+ return rb_funcall(z, '-', 1, y);
+ }
+ return z;
+}
+
+/*
+ * call-seq:
+ * num.integer? -> true or false
+ *
+ * Returns <code>true</code> if <i>num</i> is an <code>Integer</code>
+ * (including <code>Fixnum</code> and <code>Bignum</code>).
+ */
+
+static VALUE
+num_int_p(num)
+ VALUE num;
+{
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * num.abs => num or numeric
+ *
+ * Returns the absolute value of <i>num</i>.
+ *
+ * 12.abs #=> 12
+ * (-34.56).abs #=> 34.56
+ * -34.56.abs #=> 34.56
+ */
+
+static VALUE
+num_abs(num)
+ VALUE num;
+{
+ if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) {
+ return rb_funcall(num, rb_intern("-@"), 0);
+ }
+ return num;
+}
+
+
+/*
+ * call-seq:
+ * num.zero? => true or false
+ *
+ * Returns <code>true</code> if <i>num</i> has a zero value.
+ */
+
+static VALUE
+num_zero_p(num)
+ VALUE num;
+{
+ if (rb_equal(num, INT2FIX(0))) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * num.nonzero? => num or nil
+ *
+ * Returns <i>num</i> if <i>num</i> is not zero, <code>nil</code>
+ * otherwise. This behavior is useful when chaining comparisons:
+ *
+ * a = %w( z Bb bB bb BB a aA Aa AA A )
+ * b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
+ * b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]
+ */
+
+static VALUE
+num_nonzero_p(num)
+ VALUE num;
+{
+ if (RTEST(rb_funcall(num, rb_intern("zero?"), 0, 0))) {
+ return Qnil;
+ }
+ return num;
+}
+
+/*
+ * call-seq:
+ * num.to_int => integer
+ *
+ * Invokes the child class's <code>to_i</code> method to convert
+ * <i>num</i> to an integer.
+ */
+
+static VALUE
+num_to_int(num)
+ VALUE num;
+{
+ return rb_funcall(num, id_to_i, 0, 0);
+}
+
+
+/********************************************************************
+ *
+ * Document-class: Float
+ *
+ * <code>Float</code> objects represent real numbers using the native
+ * architecture's double-precision floating point representation.
+ */
+
+VALUE
+rb_float_new(d)
+ double d;
+{
+ NEWOBJ(flt, struct RFloat);
+ OBJSETUP(flt, rb_cFloat, T_FLOAT);
+
+ flt->value = d;
+ return (VALUE)flt;
+}
+
+/*
+ * call-seq:
+ * flt.to_s => string
+ *
+ * Returns a string containing a representation of self. As well as a
+ * fixed or exponential form of the number, the call may return
+ * ``<code>NaN</code>'', ``<code>Infinity</code>'', and
+ * ``<code>-Infinity</code>''.
+ */
+
+static VALUE
+flo_to_s(flt)
+ VALUE flt;
+{
+ char buf[32];
+ double value = RFLOAT(flt)->value;
+ char *p, *e;
+
+ if (isinf(value))
+ return rb_str_new2(value < 0 ? "-Infinity" : "Infinity");
+ else if(isnan(value))
+ return rb_str_new2("NaN");
+
+ sprintf(buf, "%#.15g", value); /* ensure to print decimal point */
+ if (!(e = strchr(buf, 'e'))) {
+ e = buf + strlen(buf);
+ }
+ if (!ISDIGIT(e[-1])) { /* reformat if ended with decimal point (ex 111111111111111.) */
+ sprintf(buf, "%#.14e", value);
+ if (!(e = strchr(buf, 'e'))) {
+ e = buf + strlen(buf);
+ }
+ }
+ p = e;
+ while (p[-1]=='0' && ISDIGIT(p[-2]))
+ p--;
+ memmove(p, e, strlen(e)+1);
+ return rb_str_new2(buf);
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+flo_coerce(x, y)
+ VALUE x, y;
+{
+ return rb_assoc_new(rb_Float(y), x);
+}
+
+/*
+ * call-seq:
+ * -float => float
+ *
+ * Returns float, negated.
+ */
+
+static VALUE
+flo_uminus(flt)
+ VALUE flt;
+{
+ return rb_float_new(-RFLOAT(flt)->value);
+}
+
+/*
+ * call-seq:
+ * float + other => float
+ *
+ * Returns a new float which is the sum of <code>float</code>
+ * and <code>other</code>.
+ */
+
+static VALUE
+flo_plus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return rb_float_new(RFLOAT(x)->value + (double)FIX2LONG(y));
+ case T_BIGNUM:
+ return rb_float_new(RFLOAT(x)->value + rb_big2dbl(y));
+ case T_FLOAT:
+ return rb_float_new(RFLOAT(x)->value + RFLOAT(y)->value);
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * float + other => float
+ *
+ * Returns a new float which is the difference of <code>float</code>
+ * and <code>other</code>.
+ */
+
+static VALUE
+flo_minus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return rb_float_new(RFLOAT(x)->value - (double)FIX2LONG(y));
+ case T_BIGNUM:
+ return rb_float_new(RFLOAT(x)->value - rb_big2dbl(y));
+ case T_FLOAT:
+ return rb_float_new(RFLOAT(x)->value - RFLOAT(y)->value);
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * float * other => float
+ *
+ * Returns a new float which is the product of <code>float</code>
+ * and <code>other</code>.
+ */
+
+static VALUE
+flo_mul(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return rb_float_new(RFLOAT(x)->value * (double)FIX2LONG(y));
+ case T_BIGNUM:
+ return rb_float_new(RFLOAT(x)->value * rb_big2dbl(y));
+ case T_FLOAT:
+ return rb_float_new(RFLOAT(x)->value * RFLOAT(y)->value);
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * float / other => float
+ *
+ * Returns a new float which is the result of dividing
+ * <code>float</code> by <code>other</code>.
+ */
+
+static VALUE
+flo_div(x, y)
+ VALUE x, y;
+{
+ long f_y;
+ double d;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ f_y = FIX2LONG(y);
+ return rb_float_new(RFLOAT(x)->value / (double)f_y);
+ case T_BIGNUM:
+ d = rb_big2dbl(y);
+ return rb_float_new(RFLOAT(x)->value / d);
+ case T_FLOAT:
+ return rb_float_new(RFLOAT(x)->value / RFLOAT(y)->value);
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+
+static void
+flodivmod(x, y, divp, modp)
+ double x, y;
+ double *divp, *modp;
+{
+ double div, mod;
+
+#ifdef HAVE_FMOD
+ mod = fmod(x, y);
+#else
+ {
+ double z;
+
+ modf(x/y, &z);
+ mod = x - z * y;
+ }
+#endif
+ if (isinf(x) && !isinf(y) && !isnan(y))
+ div = x;
+ else
+ div = (x - mod) / y;
+ if (y*mod < 0) {
+ mod += y;
+ div -= 1.0;
+ }
+ if (modp) *modp = mod;
+ if (divp) *divp = div;
+}
+
+
+/*
+ * call-seq:
+ * flt % other => float
+ * flt.modulo(other) => float
+ *
+ * Return the modulo after division of <code>flt</code> by <code>other</code>.
+ *
+ * 6543.21.modulo(137) #=> 104.21
+ * 6543.21.modulo(137.24) #=> 92.9299999999996
+ */
+
+static VALUE
+flo_mod(x, y)
+ VALUE x, y;
+{
+ double fy, mod;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ fy = (double)FIX2LONG(y);
+ break;
+ case T_BIGNUM:
+ fy = rb_big2dbl(y);
+ break;
+ case T_FLOAT:
+ fy = RFLOAT(y)->value;
+ break;
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ flodivmod(RFLOAT(x)->value, fy, 0, &mod);
+ return rb_float_new(mod);
+}
+
+/*
+ * call-seq:
+ * flt.divmod(numeric) => array
+ *
+ * See <code>Numeric#divmod</code>.
+ */
+
+static VALUE
+flo_divmod(x, y)
+ VALUE x, y;
+{
+ double fy, div, mod, val;
+ volatile VALUE a, b;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ fy = (double)FIX2LONG(y);
+ break;
+ case T_BIGNUM:
+ fy = rb_big2dbl(y);
+ break;
+ case T_FLOAT:
+ fy = RFLOAT(y)->value;
+ break;
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ flodivmod(RFLOAT(x)->value, fy, &div, &mod);
+ if (FIXABLE(div)) {
+ val = round(div);
+ a = LONG2FIX(val);
+ }
+ else {
+ a = rb_dbl2big(div);
+ }
+ b = rb_float_new(mod);
+ return rb_assoc_new(a, b);
+}
+
+/*
+ * call-seq:
+ *
+ * flt ** other => float
+ *
+ * Raises <code>float</code> the <code>other</code> power.
+ */
+
+static VALUE
+flo_pow(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ return rb_float_new(pow(RFLOAT(x)->value, (double)FIX2LONG(y)));
+ case T_BIGNUM:
+ return rb_float_new(pow(RFLOAT(x)->value, rb_big2dbl(y)));
+ case T_FLOAT:
+ return rb_float_new(pow(RFLOAT(x)->value, RFLOAT(y)->value));
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * num.eql?(numeric) => true or false
+ *
+ * Returns <code>true</code> if <i>num</i> and <i>numeric</i> are the
+ * same type and have equal values.
+ *
+ * 1 == 1.0 #=> true
+ * 1.eql?(1.0) #=> false
+ * (1.0).eql?(1.0) #=> true
+ */
+
+static VALUE
+num_eql(x, y)
+ VALUE x, y;
+{
+ if (TYPE(x) != TYPE(y)) return Qfalse;
+
+ return rb_equal(x, y);
+}
+
+/*
+ * call-seq:
+ * num <=> other -> 0 or nil
+ *
+ * Returns zero if <i>num</i> equals <i>other</i>, <code>nil</code>
+ * otherwise.
+ */
+
+static VALUE
+num_cmp(x, y)
+ VALUE x, y;
+{
+ if (x == y) return INT2FIX(0);
+ return Qnil;
+}
+
+static VALUE
+num_equal(x, y)
+ VALUE x, y;
+{
+ if (x == y) return Qtrue;
+ return rb_funcall(y, id_eq, 1, x);
+}
+
+/*
+ * call-seq:
+ * flt == obj => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> has the same value
+ * as <i>flt</i>. Contrast this with <code>Float#eql?</code>, which
+ * requires <i>obj</i> to be a <code>Float</code>.
+ *
+ * 1.0 == 1 #=> true
+ *
+ */
+
+static VALUE
+flo_eq(x, y)
+ VALUE x, y;
+{
+ volatile double a, b;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = FIX2LONG(y);
+ break;
+ case T_BIGNUM:
+ b = rb_big2dbl(y);
+ break;
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ if (isnan(b)) return Qfalse;
+ break;
+ default:
+ return num_equal(x, y);
+ }
+ a = RFLOAT(x)->value;
+ if (isnan(a)) return Qfalse;
+ return (a == b)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt.hash => integer
+ *
+ * Returns a hash code for this float.
+ */
+
+static VALUE
+flo_hash(num)
+ VALUE num;
+{
+ double d;
+ char *c;
+ int i, hash;
+
+ d = RFLOAT(num)->value;
+ if (d == 0) d = fabs(d);
+ c = (char*)&d;
+ for (hash=0, i=0; i<sizeof(double);i++) {
+ hash = (hash * 971) ^ (unsigned char)c[i];
+ }
+ if (hash < 0) hash = -hash;
+ return INT2FIX(hash);
+}
+
+VALUE
+rb_dbl_cmp(a, b)
+ double a, b;
+{
+ if (isnan(a) || isnan(b)) return Qnil;
+ if (a == b) return INT2FIX(0);
+ if (a > b) return INT2FIX(1);
+ if (a < b) return INT2FIX(-1);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * flt <=> numeric => -1, 0, +1
+ *
+ * Returns -1, 0, or +1 depending on whether <i>flt</i> is less than,
+ * equal to, or greater than <i>numeric</i>. This is the basis for the
+ * tests in <code>Comparable</code>.
+ */
+
+static VALUE
+flo_cmp(x, y)
+ VALUE x, y;
+{
+ double a, b;
+
+ a = RFLOAT(x)->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ if (isinf(a)) {
+ if (a > 0.0) return INT2FIX(1);
+ else return INT2FIX(-1);
+ }
+ b = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ break;
+
+ default:
+ return rb_num_coerce_cmp(x, y);
+ }
+ return rb_dbl_cmp(a, b);
+}
+
+/*
+ * call-seq:
+ * flt > other => true or false
+ *
+ * <code>true</code> if <code>flt</code> is greater than <code>other</code>.
+ */
+
+static VALUE
+flo_gt(x, y)
+ VALUE x, y;
+{
+ double a, b;
+
+ a = RFLOAT(x)->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ b = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ if (isnan(b)) return Qfalse;
+ break;
+
+ default:
+ return rb_num_coerce_relop(x, y);
+ }
+ if (isnan(a)) return Qfalse;
+ return (a > b)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt >= other => true or false
+ *
+ * <code>true</code> if <code>flt</code> is greater than
+ * or equal to <code>other</code>.
+ */
+
+static VALUE
+flo_ge(x, y)
+ VALUE x, y;
+{
+ double a, b;
+
+ a = RFLOAT(x)->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ b = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ if (isnan(b)) return Qfalse;
+ break;
+
+ default:
+ return rb_num_coerce_relop(x, y);
+ }
+ if (isnan(a)) return Qfalse;
+ return (a >= b)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt < other => true or false
+ *
+ * <code>true</code> if <code>flt</code> is less than <code>other</code>.
+ */
+
+static VALUE
+flo_lt(x, y)
+ VALUE x, y;
+{
+ double a, b;
+
+ a = RFLOAT(x)->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ b = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ if (isnan(b)) return Qfalse;
+ break;
+
+ default:
+ return rb_num_coerce_relop(x, y);
+ }
+ if (isnan(a)) return Qfalse;
+ return (a < b)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt <= other => true or false
+ *
+ * <code>true</code> if <code>flt</code> is less than
+ * or equal to <code>other</code>.
+ */
+
+static VALUE
+flo_le(x, y)
+ VALUE x, y;
+{
+ double a, b;
+
+ a = RFLOAT(x)->value;
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ b = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ b = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ b = RFLOAT(y)->value;
+ if (isnan(b)) return Qfalse;
+ break;
+
+ default:
+ return rb_num_coerce_relop(x, y);
+ }
+ if (isnan(a)) return Qfalse;
+ return (a <= b)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt.eql?(obj) => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a
+ * <code>Float</code> with the same value as <i>flt</i>. Contrast this
+ * with <code>Float#==</code>, which performs type conversions.
+ *
+ * 1.0.eql?(1) #=> false
+ */
+
+static VALUE
+flo_eql(x, y)
+ VALUE x, y;
+{
+ if (TYPE(y) == T_FLOAT) {
+ double a = RFLOAT(x)->value;
+ double b = RFLOAT(y)->value;
+
+ if (isnan(a) || isnan(b)) return Qfalse;
+ if (a == b) return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt.to_f => flt
+ *
+ * As <code>flt</code> is already a float, returns <i>self</i>.
+ */
+
+static VALUE
+flo_to_f(num)
+ VALUE num;
+{
+ return num;
+}
+
+/*
+ * call-seq:
+ * flt.abs => float
+ *
+ * Returns the absolute value of <i>flt</i>.
+ *
+ * (-34.56).abs #=> 34.56
+ * -34.56.abs #=> 34.56
+ *
+ */
+
+static VALUE
+flo_abs(flt)
+ VALUE flt;
+{
+ double val = fabs(RFLOAT(flt)->value);
+ return rb_float_new(val);
+}
+
+/*
+ * call-seq:
+ * flt.zero? -> true or false
+ *
+ * Returns <code>true</code> if <i>flt</i> is 0.0.
+ *
+ */
+
+static VALUE
+flo_zero_p(num)
+ VALUE num;
+{
+ if (RFLOAT(num)->value == 0.0) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt.nan? -> true or false
+ *
+ * Returns <code>true</code> if <i>flt</i> is an invalid IEEE floating
+ * point number.
+ *
+ * a = -1.0 #=> -1.0
+ * a.nan? #=> false
+ * a = 0.0/0.0 #=> NaN
+ * a.nan? #=> true
+ */
+
+static VALUE
+flo_is_nan_p(num)
+ VALUE num;
+{
+ double value = RFLOAT(num)->value;
+
+ return isnan(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * flt.infinite? -> nil, -1, +1
+ *
+ * Returns <code>nil</code>, -1, or +1 depending on whether <i>flt</i>
+ * is finite, -infinity, or +infinity.
+ *
+ * (0.0).infinite? #=> nil
+ * (-1.0/0.0).infinite? #=> -1
+ * (+1.0/0.0).infinite? #=> 1
+ */
+
+static VALUE
+flo_is_infinite_p(num)
+ VALUE num;
+{
+ double value = RFLOAT(num)->value;
+
+ if (isinf(value)) {
+ return INT2FIX( value < 0 ? -1 : 1 );
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * flt.finite? -> true or false
+ *
+ * Returns <code>true</code> if <i>flt</i> is a valid IEEE floating
+ * point number (it is not infinite, and <code>nan?</code> is
+ * <code>false</code>).
+ *
+ */
+
+static VALUE
+flo_is_finite_p(num)
+ VALUE num;
+{
+ double value = RFLOAT(num)->value;
+
+#if HAVE_FINITE
+ if (!finite(value))
+ return Qfalse;
+#else
+ if (isinf(value) || isnan(value))
+ return Qfalse;
+#endif
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * flt.floor => integer
+ *
+ * Returns the largest integer less than or equal to <i>flt</i>.
+ *
+ * 1.2.floor #=> 1
+ * 2.0.floor #=> 2
+ * (-1.2).floor #=> -2
+ * (-2.0).floor #=> -2
+ */
+
+static VALUE
+flo_floor(num)
+ VALUE num;
+{
+ double f = floor(RFLOAT(num)->value);
+ long val;
+
+ if (!FIXABLE(f)) {
+ return rb_dbl2big(f);
+ }
+ val = f;
+ return LONG2FIX(val);
+}
+
+/*
+ * call-seq:
+ * flt.ceil => integer
+ *
+ * Returns the smallest <code>Integer</code> greater than or equal to
+ * <i>flt</i>.
+ *
+ * 1.2.ceil #=> 2
+ * 2.0.ceil #=> 2
+ * (-1.2).ceil #=> -1
+ * (-2.0).ceil #=> -2
+ */
+
+static VALUE
+flo_ceil(num)
+ VALUE num;
+{
+ double f = ceil(RFLOAT(num)->value);
+ long val;
+
+ if (!FIXABLE(f)) {
+ return rb_dbl2big(f);
+ }
+ val = f;
+ return LONG2FIX(val);
+}
+
+/*
+ * call-seq:
+ * flt.round => integer
+ *
+ * Rounds <i>flt</i> to the nearest integer. Equivalent to:
+ *
+ * def round
+ * return (self+0.5).floor if self > 0.0
+ * return (self-0.5).ceil if self < 0.0
+ * return 0
+ * end
+ *
+ * 1.5.round #=> 2
+ * (-1.5).round #=> -2
+ *
+ */
+
+static VALUE
+flo_round(num)
+ VALUE num;
+{
+ double f = RFLOAT(num)->value;
+ long val;
+
+ f = round(f);
+
+ if (!FIXABLE(f)) {
+ return rb_dbl2big(f);
+ }
+ val = f;
+ return LONG2FIX(val);
+}
+
+/*
+ * call-seq:
+ * flt.to_i => integer
+ * flt.to_int => integer
+ * flt.truncate => integer
+ *
+ * Returns <i>flt</i> truncated to an <code>Integer</code>.
+ */
+
+static VALUE
+flo_truncate(num)
+ VALUE num;
+{
+ double f = RFLOAT(num)->value;
+ long val;
+
+ if (f > 0.0) f = floor(f);
+ if (f < 0.0) f = ceil(f);
+
+ if (!FIXABLE(f)) {
+ return rb_dbl2big(f);
+ }
+ val = f;
+ return LONG2FIX(val);
+}
+
+
+/*
+ * call-seq:
+ * num.floor => integer
+ *
+ * Returns the largest integer less than or equal to <i>num</i>.
+ * <code>Numeric</code> implements this by converting <i>anInteger</i>
+ * to a <code>Float</code> and invoking <code>Float#floor</code>.
+ *
+ * 1.floor #=> 1
+ * (-1).floor #=> -1
+ */
+
+static VALUE
+num_floor(num)
+ VALUE num;
+{
+ return flo_floor(rb_Float(num));
+}
+
+
+/*
+ * call-seq:
+ * num.ceil => integer
+ *
+ * Returns the smallest <code>Integer</code> greater than or equal to
+ * <i>num</i>. Class <code>Numeric</code> achieves this by converting
+ * itself to a <code>Float</code> then invoking
+ * <code>Float#ceil</code>.
+ *
+ * 1.ceil #=> 1
+ * 1.2.ceil #=> 2
+ * (-1.2).ceil #=> -1
+ * (-1.0).ceil #=> -1
+ */
+
+static VALUE
+num_ceil(num)
+ VALUE num;
+{
+ return flo_ceil(rb_Float(num));
+}
+
+/*
+ * call-seq:
+ * num.round => integer
+ *
+ * Rounds <i>num</i> to the nearest integer. <code>Numeric</code>
+ * implements this by converting itself to a
+ * <code>Float</code> and invoking <code>Float#round</code>.
+ */
+
+static VALUE
+num_round(num)
+ VALUE num;
+{
+ return flo_round(rb_Float(num));
+}
+
+/*
+ * call-seq:
+ * num.truncate => integer
+ *
+ * Returns <i>num</i> truncated to an integer. <code>Numeric</code>
+ * implements this by converting its value to a float and invoking
+ * <code>Float#truncate</code>.
+ */
+
+static VALUE
+num_truncate(num)
+ VALUE num;
+{
+ return flo_truncate(rb_Float(num));
+}
+
+
+/*
+ * call-seq:
+ * num.step(limit, step ) {|i| block } => num
+ *
+ * Invokes <em>block</em> with the sequence of numbers starting at
+ * <i>num</i>, incremented by <i>step</i> on each call. The loop
+ * finishes when the value to be passed to the block is greater than
+ * <i>limit</i> (if <i>step</i> is positive) or less than
+ * <i>limit</i> (if <i>step</i> is negative). If all the arguments are
+ * integers, the loop operates using an integer counter. If any of the
+ * arguments are floating point numbers, all are converted to floats,
+ * and the loop is executed <i>floor(n + n*epsilon)+ 1</i> times,
+ * where <i>n = (limit - num)/step</i>. Otherwise, the loop
+ * starts at <i>num</i>, uses either the <code><</code> or
+ * <code>></code> operator to compare the counter against
+ * <i>limit</i>, and increments itself using the <code>+</code>
+ * operator.
+ *
+ * 1.step(10, 2) { |i| print i, " " }
+ * Math::E.step(Math::PI, 0.2) { |f| print f, " " }
+ *
+ * <em>produces:</em>
+ *
+ * 1 3 5 7 9
+ * 2.71828182845905 2.91828182845905 3.11828182845905
+ */
+
+static VALUE
+num_step(argc, argv, from)
+ int argc;
+ VALUE *argv;
+ VALUE from;
+{
+ VALUE to, step;
+
+ if (argc == 1) {
+ to = argv[0];
+ step = INT2FIX(1);
+ }
+ else {
+ if (argc == 2) {
+ to = argv[0];
+ step = argv[1];
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ if (rb_equal(step, INT2FIX(0))) {
+ rb_raise(rb_eArgError, "step can't be 0");
+ }
+ }
+
+ if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) {
+ long i, end, diff;
+
+ i = FIX2LONG(from);
+ end = FIX2LONG(to);
+ diff = FIX2LONG(step);
+
+ if (diff > 0) {
+ while (i <= end) {
+ rb_yield(LONG2FIX(i));
+ i += diff;
+ }
+ }
+ else {
+ while (i >= end) {
+ rb_yield(LONG2FIX(i));
+ i += diff;
+ }
+ }
+ }
+ else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) {
+ const double epsilon = DBL_EPSILON;
+ double beg = NUM2DBL(from);
+ double end = NUM2DBL(to);
+ double unit = NUM2DBL(step);
+ double n = (end - beg)/unit;
+ double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
+ long i;
+
+ if (err>0.5) err=0.5;
+ n = floor(n + err) + 1;
+ for (i=0; i<n; i++) {
+ rb_yield(rb_float_new(i*unit+beg));
+ }
+ }
+ else {
+ VALUE i = from;
+ ID cmp;
+
+ if (RTEST(rb_funcall(step, '>', 1, INT2FIX(0)))) {
+ cmp = '>';
+ }
+ else {
+ cmp = '<';
+ }
+ for (;;) {
+ if (RTEST(rb_funcall(i, cmp, 1, to))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, step);
+ }
+ }
+ return from;
+}
+
+long
+rb_num2long(val)
+ VALUE val;
+{
+ again:
+ if (NIL_P(val)) {
+ rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
+ }
+
+ if (FIXNUM_P(val)) return FIX2LONG(val);
+
+ switch (TYPE(val)) {
+ case T_FLOAT:
+ if (RFLOAT(val)->value <= (double)LONG_MAX
+ && RFLOAT(val)->value >= (double)LONG_MIN) {
+ return (long)(RFLOAT(val)->value);
+ }
+ else {
+ char buf[24];
+ char *s;
+
+ sprintf(buf, "%-.10g", RFLOAT(val)->value);
+ if ((s = strchr(buf, ' ')) != 0) *s = '\0';
+ rb_raise(rb_eRangeError, "float %s out of range of integer", buf);
+ }
+
+ case T_BIGNUM:
+ return rb_big2long(val);
+
+ default:
+ val = rb_to_int(val);
+ goto again;
+ }
+}
+
+unsigned long
+rb_num2ulong(val)
+ VALUE val;
+{
+ if (TYPE(val) == T_BIGNUM) {
+ return rb_big2ulong(val);
+ }
+ return (unsigned long)rb_num2long(val);
+}
+
+#if SIZEOF_INT < SIZEOF_LONG
+static void
+check_int(num)
+ long num;
+{
+ const char *s;
+
+ if (num < INT_MIN) {
+ s = "small";
+ }
+ else if (num > INT_MAX) {
+ s = "big";
+ }
+ else {
+ return;
+ }
+ rb_raise(rb_eRangeError, "integer %ld too %s to convert to `int'", num, s);
+}
+
+static void
+check_uint(num, sign)
+ unsigned long num;
+ VALUE sign;
+{
+ static const unsigned long mask = ~(unsigned long)UINT_MAX;
+
+ if (RTEST(sign)) {
+ /* minus */
+ if ((num & mask) != mask || (num & ~mask) <= INT_MAX + 1UL)
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned int'", num);
+ }
+ else {
+ /* plus */
+ if ((num & mask) != 0)
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned int'", num);
+ }
+}
+
+long
+rb_num2int(val)
+ VALUE val;
+{
+ long num = rb_num2long(val);
+
+ check_int(num);
+ return num;
+}
+
+long
+rb_fix2int(val)
+ VALUE val;
+{
+ long num = FIXNUM_P(val)?FIX2LONG(val):rb_num2long(val);
+
+ check_int(num);
+ return num;
+}
+
+unsigned long
+rb_num2uint(val)
+ VALUE val;
+{
+ unsigned long num = rb_num2ulong(val);
+
+ check_uint(num, rb_funcall(val, '<', 1, INT2FIX(0)));
+ return num;
+}
+
+unsigned long
+rb_fix2uint(val)
+ VALUE val;
+{
+ unsigned long num;
+
+ if (!FIXNUM_P(val)) {
+ return rb_num2uint(val);
+ }
+ num = FIX2ULONG(val);
+
+ check_uint(num, rb_funcall(val, '<', 1, INT2FIX(0)));
+ return num;
+}
+#else
+long
+rb_num2int(val)
+ VALUE val;
+{
+ return rb_num2long(val);
+}
+
+long
+rb_fix2int(val)
+ VALUE val;
+{
+ return FIX2INT(val);
+}
+#endif
+
+VALUE
+rb_num2fix(val)
+ VALUE val;
+{
+ long v;
+
+ if (FIXNUM_P(val)) return val;
+
+ v = rb_num2long(val);
+ if (!FIXABLE(v))
+ rb_raise(rb_eRangeError, "integer %ld out of range of fixnum", v);
+ return LONG2FIX(v);
+}
+
+#if HAVE_LONG_LONG
+
+LONG_LONG
+rb_num2ll(val)
+ VALUE val;
+{
+ if (NIL_P(val)) {
+ rb_raise(rb_eTypeError, "no implicit conversion from nil");
+ }
+
+ if (FIXNUM_P(val)) return (LONG_LONG)FIX2LONG(val);
+
+ switch (TYPE(val)) {
+ case T_FLOAT:
+ if (RFLOAT(val)->value <= (double)LLONG_MAX
+ && RFLOAT(val)->value >= (double)LLONG_MIN) {
+ return (LONG_LONG)(RFLOAT(val)->value);
+ }
+ else {
+ char buf[24];
+ char *s;
+
+ sprintf(buf, "%-.10g", RFLOAT(val)->value);
+ if ((s = strchr(buf, ' ')) != 0) *s = '\0';
+ rb_raise(rb_eRangeError, "float %s out of range of long long", buf);
+ }
+
+ case T_BIGNUM:
+ return rb_big2ll(val);
+
+ case T_STRING:
+ rb_raise(rb_eTypeError, "no implicit conversion from string");
+ return Qnil; /* not reached */
+
+ case T_TRUE:
+ case T_FALSE:
+ rb_raise(rb_eTypeError, "no implicit conversion from boolean");
+ return Qnil; /* not reached */
+
+ default:
+ val = rb_to_int(val);
+ return NUM2LL(val);
+ }
+}
+
+unsigned LONG_LONG
+rb_num2ull(val)
+ VALUE val;
+{
+ if (TYPE(val) == T_BIGNUM) {
+ return rb_big2ull(val);
+ }
+ return (unsigned LONG_LONG)rb_num2ll(val);
+}
+
+#endif /* HAVE_LONG_LONG */
+
+
+/*
+ * Document-class: Integer
+ *
+ * <code>Integer</code> is the basis for the two concrete classes that
+ * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>.
+ *
+ */
+
+
+/*
+ * call-seq:
+ * int.to_i => int
+ * int.to_int => int
+ * int.floor => int
+ * int.ceil => int
+ * int.round => int
+ * int.truncate => int
+ *
+ * As <i>int</i> is already an <code>Integer</code>, all these
+ * methods simply return the receiver.
+ */
+
+static VALUE
+int_to_i(num)
+ VALUE num;
+{
+ return num;
+}
+
+/*
+ * call-seq:
+ * int.integer? -> true
+ *
+ * Always returns <code>true</code>.
+ */
+
+static VALUE
+int_int_p(num)
+ VALUE num;
+{
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * int.next => integer
+ * int.succ => integer
+ *
+ * Returns the <code>Integer</code> equal to <i>int</i> + 1.
+ *
+ * 1.next #=> 2
+ * (-1).next #=> 0
+ */
+
+static VALUE
+int_succ(num)
+ VALUE num;
+{
+ if (FIXNUM_P(num)) {
+ long i = FIX2LONG(num) + 1;
+ return LONG2NUM(i);
+ }
+ return rb_funcall(num, '+', 1, INT2FIX(1));
+}
+
+/*
+ * call-seq:
+ * int.chr => string
+ *
+ * Returns a string containing the ASCII character represented by the
+ * receiver's value.
+ *
+ * 65.chr #=> "A"
+ * ?a.chr #=> "a"
+ * 230.chr #=> "\346"
+ */
+
+static VALUE
+int_chr(num)
+ VALUE num;
+{
+ char c;
+ long i = NUM2LONG(num);
+
+ if (i < 0 || 0xff < i)
+ rb_raise(rb_eRangeError, "%ld out of char range", i);
+ c = i;
+ return rb_str_new(&c, 1);
+}
+
+/********************************************************************
+ *
+ * Document-class: Fixnum
+ *
+ * A <code>Fixnum</code> holds <code>Integer</code> values that can be
+ * represented in a native machine word (minus 1 bit). If any operation
+ * on a <code>Fixnum</code> exceeds this range, the value is
+ * automatically converted to a <code>Bignum</code>.
+ *
+ * <code>Fixnum</code> objects have immediate value. This means that
+ * when they are assigned or passed as parameters, the actual object is
+ * passed, rather than a reference to that object. Assignment does not
+ * alias <code>Fixnum</code> objects. There is effectively only one
+ * <code>Fixnum</code> object instance for any given integer value, so,
+ * for example, you cannot add a singleton method to a
+ * <code>Fixnum</code>.
+ */
+
+
+/*
+ * call-seq:
+ * Fixnum.induced_from(obj) => fixnum
+ *
+ * Convert <code>obj</code> to a Fixnum. Works with numeric parameters.
+ * Also works with Symbols, but this is deprecated.
+ */
+
+static VALUE
+rb_fix_induced_from(klass, x)
+ VALUE klass, x;
+{
+ return rb_num2fix(x);
+}
+
+/*
+ * call-seq:
+ * Integer.induced_from(obj) => fixnum, bignum
+ *
+ * Convert <code>obj</code> to an Integer.
+ */
+
+static VALUE
+rb_int_induced_from(klass, x)
+ VALUE klass, x;
+{
+ switch (TYPE(x)) {
+ case T_FIXNUM:
+ case T_BIGNUM:
+ return x;
+ case T_FLOAT:
+ return rb_funcall(x, id_to_i, 0);
+ default:
+ rb_raise(rb_eTypeError, "failed to convert %s into Integer",
+ rb_obj_classname(x));
+ }
+}
+
+/*
+ * call-seq:
+ * Float.induced_from(obj) => float
+ *
+ * Convert <code>obj</code> to a float.
+ */
+
+static VALUE
+rb_flo_induced_from(klass, x)
+ VALUE klass, x;
+{
+ switch (TYPE(x)) {
+ case T_FIXNUM:
+ case T_BIGNUM:
+ return rb_funcall(x, rb_intern("to_f"), 0);
+ case T_FLOAT:
+ return x;
+ default:
+ rb_raise(rb_eTypeError, "failed to convert %s into Float",
+ rb_obj_classname(x));
+ }
+}
+
+/*
+ * call-seq:
+ * -fix => integer
+ *
+ * Negates <code>fix</code> (which might return a Bignum).
+ */
+
+static VALUE
+fix_uminus(num)
+ VALUE num;
+{
+ return LONG2NUM(-FIX2LONG(num));
+}
+
+VALUE
+rb_fix2str(x, base)
+ VALUE x;
+ int base;
+{
+ extern const char ruby_digitmap[];
+ char buf[SIZEOF_LONG*CHAR_BIT + 2], *b = buf + sizeof buf;
+ long val = FIX2LONG(x);
+ int neg = 0;
+
+ if (base < 2 || 36 < base) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ if (val == 0) {
+ return rb_str_new2("0");
+ }
+ if (val < 0) {
+ val = -val;
+ neg = 1;
+ }
+ *--b = '\0';
+ do {
+ *--b = ruby_digitmap[(int)(val % base)];
+ } while (val /= base);
+ if (neg) {
+ *--b = '-';
+ }
+
+ return rb_str_new2(b);
+}
+
+/*
+ * call-seq:
+ * fix.to_s( base=10 ) -> aString
+ *
+ * Returns a string containing the representation of <i>fix</i> radix
+ * <i>base</i> (between 2 and 36).
+ *
+ * 12345.to_s #=> "12345"
+ * 12345.to_s(2) #=> "11000000111001"
+ * 12345.to_s(8) #=> "30071"
+ * 12345.to_s(10) #=> "12345"
+ * 12345.to_s(16) #=> "3039"
+ * 12345.to_s(36) #=> "9ix"
+ *
+ */
+static VALUE
+fix_to_s(argc, argv, x)
+ int argc;
+ VALUE *argv;
+ VALUE x;
+{
+ VALUE b;
+ int base;
+
+ rb_scan_args(argc, argv, "01", &b);
+ if (argc == 0) base = 10;
+ else base = NUM2INT(b);
+
+ return rb_fix2str(x, base);
+}
+
+/*
+ * call-seq:
+ * fix + numeric => numeric_result
+ *
+ * Performs addition: the class of the resulting object depends on
+ * the class of <code>numeric</code> and on the magnitude of the
+ * result.
+ */
+
+static VALUE
+fix_plus(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a, b, c;
+ VALUE r;
+
+ a = FIX2LONG(x);
+ b = FIX2LONG(y);
+ c = a + b;
+ r = LONG2NUM(c);
+
+ return r;
+ }
+ if (TYPE(y) == T_FLOAT) {
+ return rb_float_new((double)FIX2LONG(x) + RFLOAT(y)->value);
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+/*
+ * call-seq:
+ * fix - numeric => numeric_result
+ *
+ * Performs subtraction: the class of the resulting object depends on
+ * the class of <code>numeric</code> and on the magnitude of the
+ * result.
+ */
+
+static VALUE
+fix_minus(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a, b, c;
+ VALUE r;
+
+ a = FIX2LONG(x);
+ b = FIX2LONG(y);
+ c = a - b;
+ r = LONG2NUM(c);
+
+ return r;
+ }
+ if (TYPE(y) == T_FLOAT) {
+ return rb_float_new((double)FIX2LONG(x) - RFLOAT(y)->value);
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+/*
+ * call-seq:
+ * fix * numeric => numeric_result
+ *
+ * Performs multiplication: the class of the resulting object depends on
+ * the class of <code>numeric</code> and on the magnitude of the
+ * result.
+ */
+
+static VALUE
+fix_mul(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+#ifdef __HP_cc
+ /* avoids an optimization bug of HP aC++/ANSI C B3910B A.06.05 [Jul 25 2005] */
+ volatile
+#endif
+ long a, b, c;
+ VALUE r;
+
+ a = FIX2LONG(x);
+ if (a == 0) return x;
+
+ b = FIX2LONG(y);
+ c = a * b;
+ r = LONG2FIX(c);
+
+ if (FIX2LONG(r) != c || c/a != b) {
+ r = rb_big_mul(rb_int2big(a), rb_int2big(b));
+ }
+ return r;
+ }
+ if (TYPE(y) == T_FLOAT) {
+ return rb_float_new((double)FIX2LONG(x) * RFLOAT(y)->value);
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+static void
+fixdivmod(x, y, divp, modp)
+ long x, y;
+ long *divp, *modp;
+{
+ long div, mod;
+
+ if (y == 0) rb_num_zerodiv();
+ if (y < 0) {
+ if (x < 0)
+ div = -x / -y;
+ else
+ div = - (x / -y);
+ }
+ else {
+ if (x < 0)
+ div = - (-x / y);
+ else
+ div = x / y;
+ }
+ mod = x - div*y;
+ if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
+ mod += y;
+ div -= 1;
+ }
+ if (divp) *divp = div;
+ if (modp) *modp = mod;
+}
+
+/*
+ * call-seq:
+ * fix.quo(numeric) => float
+ *
+ * Returns the floating point result of dividing <i>fix</i> by
+ * <i>numeric</i>.
+ *
+ * 654321.quo(13731) #=> 47.6528293642124
+ * 654321.quo(13731.24) #=> 47.6519964693647
+ *
+ */
+
+static VALUE
+fix_quo(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ return rb_float_new((double)FIX2LONG(x) / (double)FIX2LONG(y));
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+/*
+ * call-seq:
+ * fix / numeric => numeric_result
+ * fix.div(numeric) => numeric_result
+ *
+ * Performs division: the class of the resulting object depends on
+ * the class of <code>numeric</code> and on the magnitude of the
+ * result.
+ */
+
+static VALUE
+fix_div(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long div;
+
+ fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, 0);
+ return LONG2NUM(div);
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+/*
+ * call-seq:
+ * fix % other => Numeric
+ * fix.modulo(other) => Numeric
+ *
+ * Returns <code>fix</code> modulo <code>other</code>.
+ * See <code>Numeric.divmod</code> for more information.
+ */
+
+static VALUE
+fix_mod(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long mod;
+
+ fixdivmod(FIX2LONG(x), FIX2LONG(y), 0, &mod);
+ return LONG2NUM(mod);
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+/*
+ * call-seq:
+ * fix.divmod(numeric) => array
+ *
+ * See <code>Numeric#divmod</code>.
+ */
+static VALUE
+fix_divmod(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long div, mod;
+
+ fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, &mod);
+
+ return rb_assoc_new(LONG2NUM(div), LONG2NUM(mod));
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+static VALUE
+int_even_p(VALUE num)
+{
+ if (rb_funcall(num, '%', 1, INT2FIX(2)) == INT2FIX(0)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * fix ** other => Numeric
+ *
+ * Raises <code>fix</code> to the <code>other</code> power, which may
+ * be negative or fractional.
+ *
+ * 2 ** 3 #=> 8
+ * 2 ** -1 #=> 0.5
+ * 2 ** 0.5 #=> 1.4142135623731
+ */
+
+static VALUE
+fix_pow(x, y)
+ VALUE x, y;
+{
+ static const double zero = 0.0;
+ long a = FIX2LONG(x);
+
+ if (FIXNUM_P(y)) {
+ long b;
+
+ b = FIX2LONG(y);
+ if (b == 0) return INT2FIX(1);
+ if (b == 1) return x;
+ a = FIX2LONG(x);
+ if (a == 0) {
+ if (b > 0) return INT2FIX(0);
+ return rb_float_new(1.0 / zero);
+ }
+ if (a == 1) return INT2FIX(1);
+ if (a == -1) {
+ if (b % 2 == 0)
+ return INT2FIX(1);
+ else
+ return INT2FIX(-1);
+ }
+ if (b > 0) {
+ return rb_big_pow(rb_int2big(a), y);
+ }
+ return rb_float_new(pow((double)a, (double)b));
+ }
+ switch (TYPE(y)) {
+ case T_BIGNUM:
+ if (a == 0) return INT2FIX(0);
+ if (a == 1) return INT2FIX(1);
+ if (a == -1) {
+ if (int_even_p(y)) return INT2FIX(1);
+ else return INT2FIX(-1);
+ }
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_pow(x, y);
+ case T_FLOAT:
+ if (a == 0) {
+ return rb_float_new(RFLOAT(y)->value < 0 ? (1.0 / zero) : 0.0);
+ }
+ if (a == 1) return rb_float_new(1.0);
+ return rb_float_new(pow((double)a, RFLOAT(y)->value));
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * fix == other
+ *
+ * Return <code>true</code> if <code>fix</code> equals <code>other</code>
+ * numerically.
+ *
+ * 1 == 2 #=> false
+ * 1 == 1.0 #=> true
+ */
+
+static VALUE
+fix_equal(x, y)
+ VALUE x, y;
+{
+ if (x == y) return Qtrue;
+ if (FIXNUM_P(y)) return Qfalse;
+ return num_equal(x, y);
+}
+
+/*
+ * call-seq:
+ * fix <=> numeric => -1, 0, +1
+ *
+ * Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is
+ * less than, equal to, or greater than <i>numeric</i>. This is the
+ * basis for the tests in <code>Comparable</code>.
+ */
+
+static VALUE
+fix_cmp(x, y)
+ VALUE x, y;
+{
+ if (x == y) return INT2FIX(0);
+ if (FIXNUM_P(y)) {
+ long a = FIX2LONG(x), b = FIX2LONG(y);
+
+ if (a > b) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+ else {
+ return rb_num_coerce_cmp(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * fix > other => true or false
+ *
+ * Returns <code>true</code> if the value of <code>fix</code> is
+ * greater than that of <code>other</code>.
+ */
+
+static VALUE
+fix_gt(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a = FIX2LONG(x), b = FIX2LONG(y);
+
+ if (a > b) return Qtrue;
+ return Qfalse;
+ }
+ else {
+ return rb_num_coerce_relop(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * fix >= other => true or false
+ *
+ * Returns <code>true</code> if the value of <code>fix</code> is
+ * greater than or equal to that of <code>other</code>.
+ */
+
+static VALUE
+fix_ge(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a = FIX2LONG(x), b = FIX2LONG(y);
+
+ if (a >= b) return Qtrue;
+ return Qfalse;
+ }
+ else {
+ return rb_num_coerce_relop(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * fix < other => true or false
+ *
+ * Returns <code>true</code> if the value of <code>fix</code> is
+ * less than that of <code>other</code>.
+ */
+
+static VALUE
+fix_lt(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a = FIX2LONG(x), b = FIX2LONG(y);
+
+ if (a < b) return Qtrue;
+ return Qfalse;
+ }
+ else {
+ return rb_num_coerce_relop(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * fix <= other => true or false
+ *
+ * Returns <code>true</code> if the value of <code>fix</code> is
+ * less thanor equal to that of <code>other</code>.
+ */
+
+static VALUE
+fix_le(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ long a = FIX2LONG(x), b = FIX2LONG(y);
+
+ if (a <= b) return Qtrue;
+ return Qfalse;
+ }
+ else {
+ return rb_num_coerce_relop(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * ~fix => integer
+ *
+ * One's complement: returns a number where each bit is flipped.
+ */
+
+static VALUE
+fix_rev(num)
+ VALUE num;
+{
+ long val = FIX2LONG(num);
+
+ val = ~val;
+ return LONG2NUM(val);
+}
+
+static VALUE
+fix_coerce(x)
+ VALUE x;
+{
+ while (!FIXNUM_P(x) && TYPE(x) != T_BIGNUM) {
+ x = rb_to_int(x);
+ }
+ return x;
+}
+
+/*
+ * call-seq:
+ * fix & other => integer
+ *
+ * Bitwise AND.
+ */
+
+static VALUE
+fix_and(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (!FIXNUM_P(y = fix_coerce(y))) {
+ return rb_big_and(y, x);
+ }
+ val = FIX2LONG(x) & FIX2LONG(y);
+ return LONG2NUM(val);
+}
+
+/*
+ * call-seq:
+ * fix | other => integer
+ *
+ * Bitwise OR.
+ */
+
+static VALUE
+fix_or(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (!FIXNUM_P(y = fix_coerce(y))) {
+ return rb_big_or(y, x);
+ }
+ val = FIX2LONG(x) | FIX2LONG(y);
+ return LONG2NUM(val);
+}
+
+/*
+ * call-seq:
+ * fix ^ other => integer
+ *
+ * Bitwise EXCLUSIVE OR.
+ */
+
+static VALUE
+fix_xor(x, y)
+ VALUE x, y;
+{
+ long val;
+
+ if (!FIXNUM_P(y = fix_coerce(y))) {
+ return rb_big_xor(y, x);
+ }
+ val = FIX2LONG(x) ^ FIX2LONG(y);
+ return LONG2NUM(val);
+}
+
+static VALUE fix_lshift _((long, unsigned long));
+static VALUE fix_rshift _((long, unsigned long));
+
+/*
+ * call-seq:
+ * fix << count => integer
+ *
+ * Shifts _fix_ left _count_ positions (right if _count_ is negative).
+ */
+
+static VALUE
+rb_fix_lshift(x, y)
+ VALUE x, y;
+{
+ long val, width;
+
+ val = NUM2LONG(x);
+ if (!FIXNUM_P(y))
+ return rb_big_lshift(rb_int2big(val), y);
+ width = FIX2LONG(y);
+ if (width < 0)
+ return fix_rshift(val, (unsigned long)-width);
+ return fix_lshift(val, width);
+}
+
+static VALUE
+fix_lshift(val, width)
+ long val;
+ unsigned long width;
+{
+ if (width > (sizeof(VALUE)*CHAR_BIT-1)
+ || ((unsigned long)val)>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) {
+ return rb_big_lshift(rb_int2big(val), ULONG2NUM(width));
+ }
+ val = val << width;
+ return LONG2NUM(val);
+}
+
+/*
+ * call-seq:
+ * fix >> count => integer
+ *
+ * Shifts _fix_ right _count_ positions (left if _count_ is negative).
+ */
+
+static VALUE
+rb_fix_rshift(x, y)
+ VALUE x, y;
+{
+ long i, val;
+
+ val = FIX2LONG(x);
+ if (!FIXNUM_P(y))
+ return rb_big_rshift(rb_int2big(val), y);
+ i = FIX2LONG(y);
+ if (i == 0) return x;
+ if (i < 0)
+ return fix_lshift(val, (unsigned long)-i);
+ return fix_rshift(val, i);
+}
+
+static VALUE
+fix_rshift(long val, unsigned long i)
+{
+ if (i >= sizeof(long)*CHAR_BIT-1) {
+ if (val < 0) return INT2FIX(-1);
+ return INT2FIX(0);
+ }
+ val = RSHIFT(val, i);
+ return LONG2FIX(val);
+}
+
+/*
+ * call-seq:
+ * fix[n] => 0, 1
+ *
+ * Bit Reference---Returns the <em>n</em>th bit in the binary
+ * representation of <i>fix</i>, where <i>fix</i>[0] is the least
+ * significant bit.
+ *
+ * a = 0b11001100101010
+ * 30.downto(0) do |n| print a[n] end
+ *
+ * <em>produces:</em>
+ *
+ * 0000000000000000011001100101010
+ */
+
+static VALUE
+fix_aref(fix, idx)
+ VALUE fix, idx;
+{
+ long val = FIX2LONG(fix);
+ long i;
+
+ if (!FIXNUM_P(idx = fix_coerce(idx))) {
+ idx = rb_big_norm(idx);
+ if (!FIXNUM_P(idx)) {
+ if (!RBIGNUM(idx)->sign || val >= 0)
+ return INT2FIX(0);
+ return INT2FIX(1);
+ }
+ }
+ i = FIX2LONG(idx);
+
+ if (i < 0) return INT2FIX(0);
+ if (sizeof(VALUE)*CHAR_BIT-1 < i) {
+ if (val < 0) return INT2FIX(1);
+ return INT2FIX(0);
+ }
+ if (val & (1L<<i))
+ return INT2FIX(1);
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * fix.to_f -> float
+ *
+ * Converts <i>fix</i> to a <code>Float</code>.
+ *
+ */
+
+static VALUE
+fix_to_f(num)
+ VALUE num;
+{
+ double val;
+
+ val = (double)FIX2LONG(num);
+
+ return rb_float_new(val);
+}
+
+/*
+ * call-seq:
+ * fix.abs -> aFixnum
+ *
+ * Returns the absolute value of <i>fix</i>.
+ *
+ * -12345.abs #=> 12345
+ * 12345.abs #=> 12345
+ *
+ */
+
+static VALUE
+fix_abs(fix)
+ VALUE fix;
+{
+ long i = FIX2LONG(fix);
+
+ if (i < 0) i = -i;
+
+ return LONG2NUM(i);
+}
+
+/*
+ * call-seq:
+ * fix.id2name -> string or nil
+ *
+ * Returns the name of the object whose symbol id is <i>fix</i>. If
+ * there is no symbol in the symbol table with this value, returns
+ * <code>nil</code>. <code>id2name</code> has nothing to do with the
+ * <code>Object.id</code> method. See also <code>Fixnum#to_sym</code>,
+ * <code>String#intern</code>, and class <code>Symbol</code>.
+ *
+ * symbol = :@inst_var #=> :@inst_var
+ * id = symbol.to_i #=> 9818
+ * id.id2name #=> "@inst_var"
+ */
+
+static VALUE
+fix_id2name(fix)
+ VALUE fix;
+{
+ char *name = rb_id2name(FIX2UINT(fix));
+ if (name) return rb_str_new2(name);
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * fix.to_sym -> aSymbol
+ *
+ * Returns the symbol whose integer value is <i>fix</i>. See also
+ * <code>Fixnum#id2name</code>.
+ *
+ * fred = :fred.to_i
+ * fred.id2name #=> "fred"
+ * fred.to_sym #=> :fred
+ */
+
+static VALUE
+fix_to_sym(fix)
+ VALUE fix;
+{
+ ID id = FIX2UINT(fix);
+
+ if (rb_id2name(id)) {
+ return ID2SYM(id);
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * fix.size -> fixnum
+ *
+ * Returns the number of <em>bytes</em> in the machine representation
+ * of a <code>Fixnum</code>.
+ *
+ * 1.size #=> 4
+ * -1.size #=> 4
+ * 2147483647.size #=> 4
+ */
+
+static VALUE
+fix_size(fix)
+ VALUE fix;
+{
+ return INT2FIX(sizeof(long));
+}
+
+/*
+ * call-seq:
+ * int.upto(limit) {|i| block } => int
+ *
+ * Iterates <em>block</em>, passing in integer values from <i>int</i>
+ * up to and including <i>limit</i>.
+ *
+ * 5.upto(10) { |i| print i, " " }
+ *
+ * <em>produces:</em>
+ *
+ * 5 6 7 8 9 10
+ */
+
+static VALUE
+int_upto(from, to)
+ VALUE from, to;
+{
+ if (FIXNUM_P(from) && FIXNUM_P(to)) {
+ long i, end;
+
+ end = FIX2LONG(to);
+ for (i = FIX2LONG(from); i <= end; i++) {
+ rb_yield(LONG2FIX(i));
+ }
+ }
+ else {
+ VALUE i = from, c;
+
+ while (!(c = rb_funcall(i, '>', 1, to))) {
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, INT2FIX(1));
+ }
+ if (NIL_P(c)) rb_cmperr(i, to);
+ }
+ return from;
+}
+
+/*
+ * call-seq:
+ * int.downto(limit) {|i| block } => int
+ *
+ * Iterates <em>block</em>, passing decreasing values from <i>int</i>
+ * down to and including <i>limit</i>.
+ *
+ * 5.downto(1) { |n| print n, ".. " }
+ * print " Liftoff!\n"
+ *
+ * <em>produces:</em>
+ *
+ * 5.. 4.. 3.. 2.. 1.. Liftoff!
+ */
+
+static VALUE
+int_downto(from, to)
+ VALUE from, to;
+{
+ if (FIXNUM_P(from) && FIXNUM_P(to)) {
+ long i, end;
+
+ end = FIX2LONG(to);
+ for (i=FIX2LONG(from); i >= end; i--) {
+ rb_yield(LONG2FIX(i));
+ }
+ }
+ else {
+ VALUE i = from, c;
+
+ while (!(c = rb_funcall(i, '<', 1, to))) {
+ rb_yield(i);
+ i = rb_funcall(i, '-', 1, INT2FIX(1));
+ }
+ if (NIL_P(c)) rb_cmperr(i, to);
+ }
+ return from;
+}
+
+/*
+ * call-seq:
+ * int.times {|i| block } => int
+ *
+ * Iterates block <i>int</i> times, passing in values from zero to
+ * <i>int</i> - 1.
+ *
+ * 5.times do |i|
+ * print i, " "
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 0 1 2 3 4
+ */
+
+static VALUE
+int_dotimes(num)
+ VALUE num;
+{
+ if (FIXNUM_P(num)) {
+ long i, end;
+
+ end = FIX2LONG(num);
+ for (i=0; i<end; i++) {
+ rb_yield(LONG2FIX(i));
+ }
+ }
+ else {
+ VALUE i = INT2FIX(0);
+
+ for (;;) {
+ if (!RTEST(rb_funcall(i, '<', 1, num))) break;
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, INT2FIX(1));
+ }
+ }
+ return num;
+}
+
+/*
+ * call-seq:
+ * fix.zero? => true or false
+ *
+ * Returns <code>true</code> if <i>fix</i> is zero.
+ *
+ */
+
+static VALUE
+fix_zero_p(num)
+ VALUE num;
+{
+ if (FIX2LONG(num) == 0) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+void
+Init_Numeric()
+{
+#if defined(__FreeBSD__) && __FreeBSD__ < 4
+ /* allow divide by zero -- Inf */
+ fpsetmask(fpgetmask() & ~(FP_X_DZ|FP_X_INV|FP_X_OFL));
+#elif defined(_UNICOSMP)
+ /* Turn off floating point exceptions for divide by zero, etc. */
+ _set_Creg(0, 0);
+#elif defined(__BORLANDC__)
+ /* Turn off floating point exceptions for overflow, etc. */
+ _control87(MCW_EM, MCW_EM);
+#endif
+ id_coerce = rb_intern("coerce");
+ id_to_i = rb_intern("to_i");
+ id_eq = rb_intern("==");
+
+ rb_eZeroDivError = rb_define_class("ZeroDivisionError", rb_eStandardError);
+ rb_eFloatDomainError = rb_define_class("FloatDomainError", rb_eRangeError);
+ rb_cNumeric = rb_define_class("Numeric", rb_cObject);
+
+ rb_define_method(rb_cNumeric, "singleton_method_added", num_sadded, 1);
+ rb_include_module(rb_cNumeric, rb_mComparable);
+ rb_define_method(rb_cNumeric, "initialize_copy", num_init_copy, 1);
+ rb_define_method(rb_cNumeric, "coerce", num_coerce, 1);
+
+ rb_define_method(rb_cNumeric, "+@", num_uplus, 0);
+ rb_define_method(rb_cNumeric, "-@", num_uminus, 0);
+ rb_define_method(rb_cNumeric, "<=>", num_cmp, 1);
+ rb_define_method(rb_cNumeric, "eql?", num_eql, 1);
+ rb_define_method(rb_cNumeric, "quo", num_quo, 1);
+ rb_define_method(rb_cNumeric, "div", num_div, 1);
+ rb_define_method(rb_cNumeric, "divmod", num_divmod, 1);
+ rb_define_method(rb_cNumeric, "modulo", num_modulo, 1);
+ rb_define_method(rb_cNumeric, "remainder", num_remainder, 1);
+ rb_define_method(rb_cNumeric, "abs", num_abs, 0);
+ rb_define_method(rb_cNumeric, "to_int", num_to_int, 0);
+
+ rb_define_method(rb_cNumeric, "integer?", num_int_p, 0);
+ rb_define_method(rb_cNumeric, "zero?", num_zero_p, 0);
+ rb_define_method(rb_cNumeric, "nonzero?", num_nonzero_p, 0);
+
+ rb_define_method(rb_cNumeric, "floor", num_floor, 0);
+ rb_define_method(rb_cNumeric, "ceil", num_ceil, 0);
+ rb_define_method(rb_cNumeric, "round", num_round, 0);
+ rb_define_method(rb_cNumeric, "truncate", num_truncate, 0);
+ rb_define_method(rb_cNumeric, "step", num_step, -1);
+
+ rb_cInteger = rb_define_class("Integer", rb_cNumeric);
+ rb_undef_alloc_func(rb_cInteger);
+ rb_undef_method(CLASS_OF(rb_cInteger), "new");
+
+ rb_define_method(rb_cInteger, "integer?", int_int_p, 0);
+ rb_define_method(rb_cInteger, "upto", int_upto, 1);
+ rb_define_method(rb_cInteger, "downto", int_downto, 1);
+ rb_define_method(rb_cInteger, "times", int_dotimes, 0);
+ rb_include_module(rb_cInteger, rb_mPrecision);
+ rb_define_method(rb_cInteger, "succ", int_succ, 0);
+ rb_define_method(rb_cInteger, "next", int_succ, 0);
+ rb_define_method(rb_cInteger, "chr", int_chr, 0);
+ rb_define_method(rb_cInteger, "to_i", int_to_i, 0);
+ rb_define_method(rb_cInteger, "to_int", int_to_i, 0);
+ rb_define_method(rb_cInteger, "floor", int_to_i, 0);
+ rb_define_method(rb_cInteger, "ceil", int_to_i, 0);
+ rb_define_method(rb_cInteger, "round", int_to_i, 0);
+ rb_define_method(rb_cInteger, "truncate", int_to_i, 0);
+
+ rb_cFixnum = rb_define_class("Fixnum", rb_cInteger);
+ rb_include_module(rb_cFixnum, rb_mPrecision);
+ rb_define_singleton_method(rb_cFixnum, "induced_from", rb_fix_induced_from, 1);
+ rb_define_singleton_method(rb_cInteger, "induced_from", rb_int_induced_from, 1);
+
+ rb_define_method(rb_cFixnum, "to_s", fix_to_s, -1);
+
+ rb_define_method(rb_cFixnum, "id2name", fix_id2name, 0);
+ rb_define_method(rb_cFixnum, "to_sym", fix_to_sym, 0);
+
+ rb_define_method(rb_cFixnum, "-@", fix_uminus, 0);
+ rb_define_method(rb_cFixnum, "+", fix_plus, 1);
+ rb_define_method(rb_cFixnum, "-", fix_minus, 1);
+ rb_define_method(rb_cFixnum, "*", fix_mul, 1);
+ rb_define_method(rb_cFixnum, "/", fix_div, 1);
+ rb_define_method(rb_cFixnum, "div", fix_div, 1);
+ rb_define_method(rb_cFixnum, "%", fix_mod, 1);
+ rb_define_method(rb_cFixnum, "modulo", fix_mod, 1);
+ rb_define_method(rb_cFixnum, "divmod", fix_divmod, 1);
+ rb_define_method(rb_cFixnum, "quo", fix_quo, 1);
+ rb_define_method(rb_cFixnum, "**", fix_pow, 1);
+
+ rb_define_method(rb_cFixnum, "abs", fix_abs, 0);
+
+ rb_define_method(rb_cFixnum, "==", fix_equal, 1);
+ rb_define_method(rb_cFixnum, "<=>", fix_cmp, 1);
+ rb_define_method(rb_cFixnum, ">", fix_gt, 1);
+ rb_define_method(rb_cFixnum, ">=", fix_ge, 1);
+ rb_define_method(rb_cFixnum, "<", fix_lt, 1);
+ rb_define_method(rb_cFixnum, "<=", fix_le, 1);
+
+ rb_define_method(rb_cFixnum, "~", fix_rev, 0);
+ rb_define_method(rb_cFixnum, "&", fix_and, 1);
+ rb_define_method(rb_cFixnum, "|", fix_or, 1);
+ rb_define_method(rb_cFixnum, "^", fix_xor, 1);
+ rb_define_method(rb_cFixnum, "[]", fix_aref, 1);
+
+ rb_define_method(rb_cFixnum, "<<", rb_fix_lshift, 1);
+ rb_define_method(rb_cFixnum, ">>", rb_fix_rshift, 1);
+
+ rb_define_method(rb_cFixnum, "to_f", fix_to_f, 0);
+ rb_define_method(rb_cFixnum, "size", fix_size, 0);
+ rb_define_method(rb_cFixnum, "zero?", fix_zero_p, 0);
+
+ rb_cFloat = rb_define_class("Float", rb_cNumeric);
+
+ rb_undef_alloc_func(rb_cFloat);
+ rb_undef_method(CLASS_OF(rb_cFloat), "new");
+
+ rb_define_singleton_method(rb_cFloat, "induced_from", rb_flo_induced_from, 1);
+ rb_include_module(rb_cFloat, rb_mPrecision);
+
+ rb_define_const(rb_cFloat, "ROUNDS", INT2FIX(FLT_ROUNDS));
+ rb_define_const(rb_cFloat, "RADIX", INT2FIX(FLT_RADIX));
+ rb_define_const(rb_cFloat, "MANT_DIG", INT2FIX(DBL_MANT_DIG));
+ rb_define_const(rb_cFloat, "DIG", INT2FIX(DBL_DIG));
+ rb_define_const(rb_cFloat, "MIN_EXP", INT2FIX(DBL_MIN_EXP));
+ rb_define_const(rb_cFloat, "MAX_EXP", INT2FIX(DBL_MAX_EXP));
+ rb_define_const(rb_cFloat, "MIN_10_EXP", INT2FIX(DBL_MIN_10_EXP));
+ rb_define_const(rb_cFloat, "MAX_10_EXP", INT2FIX(DBL_MAX_10_EXP));
+ rb_define_const(rb_cFloat, "MIN", rb_float_new(DBL_MIN));
+ rb_define_const(rb_cFloat, "MAX", rb_float_new(DBL_MAX));
+ rb_define_const(rb_cFloat, "EPSILON", rb_float_new(DBL_EPSILON));
+
+ rb_define_method(rb_cFloat, "to_s", flo_to_s, 0);
+ rb_define_method(rb_cFloat, "coerce", flo_coerce, 1);
+ rb_define_method(rb_cFloat, "-@", flo_uminus, 0);
+ rb_define_method(rb_cFloat, "+", flo_plus, 1);
+ rb_define_method(rb_cFloat, "-", flo_minus, 1);
+ rb_define_method(rb_cFloat, "*", flo_mul, 1);
+ rb_define_method(rb_cFloat, "/", flo_div, 1);
+ rb_define_method(rb_cFloat, "%", flo_mod, 1);
+ rb_define_method(rb_cFloat, "modulo", flo_mod, 1);
+ rb_define_method(rb_cFloat, "divmod", flo_divmod, 1);
+ rb_define_method(rb_cFloat, "**", flo_pow, 1);
+ rb_define_method(rb_cFloat, "==", flo_eq, 1);
+ rb_define_method(rb_cFloat, "<=>", flo_cmp, 1);
+ rb_define_method(rb_cFloat, ">", flo_gt, 1);
+ rb_define_method(rb_cFloat, ">=", flo_ge, 1);
+ rb_define_method(rb_cFloat, "<", flo_lt, 1);
+ rb_define_method(rb_cFloat, "<=", flo_le, 1);
+ rb_define_method(rb_cFloat, "eql?", flo_eql, 1);
+ rb_define_method(rb_cFloat, "hash", flo_hash, 0);
+ rb_define_method(rb_cFloat, "to_f", flo_to_f, 0);
+ rb_define_method(rb_cFloat, "abs", flo_abs, 0);
+ rb_define_method(rb_cFloat, "zero?", flo_zero_p, 0);
+
+ rb_define_method(rb_cFloat, "to_i", flo_truncate, 0);
+ rb_define_method(rb_cFloat, "to_int", flo_truncate, 0);
+ rb_define_method(rb_cFloat, "floor", flo_floor, 0);
+ rb_define_method(rb_cFloat, "ceil", flo_ceil, 0);
+ rb_define_method(rb_cFloat, "round", flo_round, 0);
+ rb_define_method(rb_cFloat, "truncate", flo_truncate, 0);
+
+ rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0);
+ rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0);
+ rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0);
+}
+/**********************************************************************
+
+ object.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-18 08:21:30 +0200 (Wed, 18 Jun 2008) $
+ created at: Thu Jul 15 12:01:24 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "st.h"
+#include "util.h"
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+VALUE rb_mKernel;
+VALUE rb_cObject;
+VALUE rb_cModule;
+VALUE rb_cClass;
+VALUE rb_cData;
+
+VALUE rb_cNilClass;
+VALUE rb_cTrueClass;
+VALUE rb_cFalseClass;
+VALUE rb_cSymbol;
+
+static ID id_eq, id_eql, id_inspect, id_init_copy;
+
+/*
+ * call-seq:
+ * obj === other => true or false
+ *
+ * Case Equality---For class <code>Object</code>, effectively the same
+ * as calling <code>#==</code>, but typically overridden by descendents
+ * to provide meaningful semantics in <code>case</code> statements.
+ */
+
+VALUE
+rb_equal(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ VALUE result;
+
+ if (obj1 == obj2) return Qtrue;
+ result = rb_funcall(obj1, id_eq, 1, obj2);
+ if (RTEST(result)) return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_eql(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ return RTEST(rb_funcall(obj1, id_eql, 1, obj2));
+}
+
+/*
+ * call-seq:
+ * obj == other => true or false
+ * obj.equal?(other) => true or false
+ * obj.eql?(other) => true or false
+ *
+ * Equality---At the <code>Object</code> level, <code>==</code> returns
+ * <code>true</code> only if <i>obj</i> and <i>other</i> are the
+ * same object. Typically, this method is overridden in descendent
+ * classes to provide class-specific meaning.
+ *
+ * Unlike <code>==</code>, the <code>equal?</code> method should never be
+ * overridden by subclasses: it is used to determine object identity
+ * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
+ * object as <code>b</code>).
+ *
+ * The <code>eql?</code> method returns <code>true</code> if
+ <i>obj</i> and <i>anObject</i> have the
+ * same value. Used by <code>Hash</code> to test members for equality.
+ * For objects of class <code>Object</code>, <code>eql?</code> is
+ * synonymous with <code>==</code>. Subclasses normally continue this
+ * tradition, but there are exceptions. <code>Numeric</code> types, for
+ * example, perform type conversion across <code>==</code>, but not
+ * across <code>eql?</code>, so:
+ *
+ * 1 == 1.0 #=> true
+ * 1.eql? 1.0 #=> false
+ */
+
+static VALUE
+rb_obj_equal(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ if (obj1 == obj2) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj.id => fixnum
+ *
+ * Soon-to-be deprecated version of <code>Object#object_id</code>.
+ */
+
+VALUE
+rb_obj_id_obsolete(obj)
+ VALUE obj;
+{
+ rb_warn("Object#id will be deprecated; use Object#object_id");
+ return rb_obj_id(obj);
+}
+
+VALUE
+rb_class_real(cl)
+ VALUE cl;
+{
+ while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) {
+ cl = RCLASS(cl)->super;
+ }
+ return cl;
+}
+
+/*
+ * call-seq:
+ * obj.type => class
+ *
+ * Deprecated synonym for <code>Object#class</code>.
+ */
+
+VALUE
+rb_obj_type(obj)
+ VALUE obj;
+{
+ rb_warn("Object#type is deprecated; use Object#class");
+ return rb_class_real(CLASS_OF(obj));
+}
+
+
+/*
+ * call-seq:
+ * obj.class => class
+ *
+ * Returns the class of <i>obj</i>, now preferred over
+ * <code>Object#type</code>, as an object's type in Ruby is only
+ * loosely tied to that object's class. This method must always be
+ * called with an explicit receiver, as <code>class</code> is also a
+ * reserved word in Ruby.
+ *
+ * 1.class #=> Fixnum
+ * self.class #=> Object
+ */
+
+VALUE
+rb_obj_class(obj)
+ VALUE obj;
+{
+ return rb_class_real(CLASS_OF(obj));
+}
+
+static void
+init_copy(dest, obj)
+ VALUE dest, obj;
+{
+ if (OBJ_FROZEN(dest)) {
+ rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest));
+ }
+ RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR);
+ RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR|FL_TAINT);
+ if (FL_TEST(obj, FL_EXIVAR)) {
+ rb_copy_generic_ivar(dest, obj);
+ }
+ rb_gc_copy_finalizer(dest, obj);
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(dest)->iv_tbl) {
+ st_free_table(ROBJECT(dest)->iv_tbl);
+ ROBJECT(dest)->iv_tbl = 0;
+ }
+ if (ROBJECT(obj)->iv_tbl) {
+ ROBJECT(dest)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl);
+ }
+ }
+ rb_funcall(dest, id_init_copy, 1, obj);
+}
+
+/*
+ * call-seq:
+ * obj.clone -> an_object
+ *
+ * Produces a shallow copy of <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference. Copies
+ * the frozen and tainted state of <i>obj</i>. See also the discussion
+ * under <code>Object#dup</code>.
+ *
+ * class Klass
+ * attr_accessor :str
+ * end
+ * s1 = Klass.new #=> #<Klass:0x401b3a38>
+ * s1.str = "Hello" #=> "Hello"
+ * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
+ * s2.str[1,4] = "i" #=> "i"
+ * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
+ * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
+ *
+ * This method may have class-specific behavior. If so, that
+ * behavior will be documented under the #+initialize_copy+ method of
+ * the class.
+ */
+
+VALUE
+rb_obj_clone(obj)
+ VALUE obj;
+{
+ VALUE clone;
+
+ if (rb_special_const_p(obj)) {
+ rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
+ }
+ clone = rb_obj_alloc(rb_obj_class(obj));
+ RBASIC(clone)->klass = rb_singleton_class_clone(obj);
+ RBASIC(clone)->flags = (RBASIC(obj)->flags | FL_TEST(clone, FL_TAINT)) & ~(FL_FREEZE|FL_FINALIZE);
+ init_copy(clone, obj);
+ RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
+
+ return clone;
+}
+
+/*
+ * call-seq:
+ * obj.dup -> an_object
+ *
+ * Produces a shallow copy of <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference.
+ * <code>dup</code> copies the tainted state of <i>obj</i>. See also
+ * the discussion under <code>Object#clone</code>. In general,
+ * <code>clone</code> and <code>dup</code> may have different semantics
+ * in descendent classes. While <code>clone</code> is used to duplicate
+ * an object, including its internal state, <code>dup</code> typically
+ * uses the class of the descendent object to create the new instance.
+ *
+ * This method may have class-specific behavior. If so, that
+ * behavior will be documented under the #+initialize_copy+ method of
+ * the class.
+ */
+
+VALUE
+rb_obj_dup(obj)
+ VALUE obj;
+{
+ VALUE dup;
+
+ if (rb_special_const_p(obj)) {
+ rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));
+ }
+ dup = rb_obj_alloc(rb_obj_class(obj));
+ init_copy(dup, obj);
+
+ return dup;
+}
+
+/* :nodoc: */
+VALUE
+rb_obj_init_copy(obj, orig)
+ VALUE obj, orig;
+{
+ if (obj == orig) return obj;
+ rb_check_frozen(obj);
+ if (TYPE(obj) != TYPE(orig) || rb_obj_class(obj) != rb_obj_class(orig)) {
+ rb_raise(rb_eTypeError, "initialize_copy should take same class object");
+ }
+ return obj;
+}
+
+/*
+ * call-seq:
+ * obj.to_a -> anArray
+ *
+ * Returns an array representation of <i>obj</i>. For objects of class
+ * <code>Object</code> and others that don't explicitly override the
+ * method, the return value is an array containing <code>self</code>.
+ * However, this latter behavior will soon be obsolete.
+ *
+ * self.to_a #=> -:1: warning: default `to_a' will be obsolete
+ * "hello".to_a #=> ["hello"]
+ * Time.new.to_a #=> [39, 54, 8, 9, 4, 2003, 3, 99, true, "CDT"]
+ */
+
+
+static VALUE
+rb_any_to_a(obj)
+ VALUE obj;
+{
+ rb_warn("default `to_a' will be obsolete");
+ return rb_ary_new3(1, obj);
+}
+
+
+/*
+ * call-seq:
+ * obj.to_s => string
+ *
+ * Returns a string representing <i>obj</i>. The default
+ * <code>to_s</code> prints the object's class and an encoding of the
+ * object id. As a special case, the top-level object that is the
+ * initial execution context of Ruby programs returns ``main.''
+ */
+
+VALUE
+rb_any_to_s(obj)
+ VALUE obj;
+{
+ char *cname = rb_obj_classname(obj);
+ size_t len;
+ VALUE str;
+
+ len = strlen(cname)+6+16;
+ str = rb_str_new(0, len); /* 6:tags 16:addr */
+ snprintf(RSTRING(str)->ptr, len+1, "#<%s:0x%lx>", cname, obj);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
+
+ return str;
+}
+
+VALUE
+rb_inspect(obj)
+ VALUE obj;
+{
+ return rb_obj_as_string(rb_funcall(obj, id_inspect, 0, 0));
+}
+
+static int
+inspect_i(id, value, str)
+ ID id;
+ VALUE value;
+ VALUE str;
+{
+ VALUE str2;
+ char *ivname;
+
+ /* need not to show internal data */
+ if (CLASS_OF(value) == 0) return ST_CONTINUE;
+ if (!rb_is_instance_id(id)) return ST_CONTINUE;
+ if (RSTRING(str)->ptr[0] == '-') { /* first element */
+ RSTRING(str)->ptr[0] = '#';
+ rb_str_cat2(str, " ");
+ }
+ else {
+ rb_str_cat2(str, ", ");
+ }
+ ivname = rb_id2name(id);
+ rb_str_cat2(str, ivname);
+ rb_str_cat2(str, "=");
+ str2 = rb_inspect(value);
+ rb_str_append(str, str2);
+ OBJ_INFECT(str, str2);
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+inspect_obj(obj, str)
+ VALUE obj, str;
+{
+ st_foreach_safe(ROBJECT(obj)->iv_tbl, inspect_i, str);
+ rb_str_cat2(str, ">");
+ RSTRING(str)->ptr[0] = '#';
+ OBJ_INFECT(str, obj);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * obj.inspect => string
+ *
+ * Returns a string containing a human-readable representation of
+ * <i>obj</i>. If not overridden, uses the <code>to_s</code> method to
+ * generate the string.
+ *
+ * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
+ * Time.new.inspect #=> "Wed Apr 09 08:54:39 CDT 2003"
+ */
+
+
+static VALUE
+rb_obj_inspect(obj)
+ VALUE obj;
+{
+ if (TYPE(obj) == T_OBJECT
+ && ROBJECT(obj)->iv_tbl
+ && ROBJECT(obj)->iv_tbl->num_entries > 0) {
+ VALUE str;
+ size_t len;
+ char *c;
+
+ c = rb_obj_classname(obj);
+ if (rb_inspecting_p(obj)) {
+ len = strlen(c)+10+16+1;
+ str = rb_str_new(0, len); /* 10:tags 16:addr 1:nul */
+ snprintf(RSTRING(str)->ptr, len, "#<%s:0x%lx ...>", c, obj);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ return str;
+ }
+ len = strlen(c)+6+16+1;
+ str = rb_str_new(0, len); /* 6:tags 16:addr 1:nul */
+ snprintf(RSTRING(str)->ptr, len, "-<%s:0x%lx", c, obj);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ return rb_protect_inspect(inspect_obj, obj, str);
+ }
+ return rb_funcall(obj, rb_intern("to_s"), 0, 0);
+}
+
+
+/*
+ * call-seq:
+ * obj.instance_of?(class) => true or false
+ *
+ * Returns <code>true</code> if <i>obj</i> is an instance of the given
+ * class. See also <code>Object#kind_of?</code>.
+ */
+
+VALUE
+rb_obj_is_instance_of(obj, c)
+ VALUE obj, c;
+{
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ case T_ICLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "class or module required");
+ }
+
+ if (rb_obj_class(obj) == c) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * obj.is_a?(class) => true or false
+ * obj.kind_of?(class) => true or false
+ *
+ * Returns <code>true</code> if <i>class</i> is the class of
+ * <i>obj</i>, or if <i>class</i> is one of the superclasses of
+ * <i>obj</i> or modules included in <i>obj</i>.
+ *
+ * module M; end
+ * class A
+ * include M
+ * end
+ * class B < A; end
+ * class C < B; end
+ * b = B.new
+ * b.instance_of? A #=> false
+ * b.instance_of? B #=> true
+ * b.instance_of? C #=> false
+ * b.instance_of? M #=> false
+ * b.kind_of? A #=> true
+ * b.kind_of? B #=> true
+ * b.kind_of? C #=> false
+ * b.kind_of? M #=> true
+ */
+
+VALUE
+rb_obj_is_kind_of(obj, c)
+ VALUE obj, c;
+{
+ VALUE cl = CLASS_OF(obj);
+
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ case T_ICLASS:
+ break;
+
+ default:
+ rb_raise(rb_eTypeError, "class or module required");
+ }
+
+ while (cl) {
+ if (cl == c || RCLASS(cl)->m_tbl == RCLASS(c)->m_tbl)
+ return Qtrue;
+ cl = RCLASS(cl)->super;
+ }
+ return Qfalse;
+}
+
+
+/*
+ * Document-method: inherited
+ *
+ * call-seq:
+ * inherited(subclass)
+ *
+ * Callback invoked whenever a subclass of the current class is created.
+ *
+ * Example:
+ *
+ * class Foo
+ * def self.inherited(subclass)
+ * puts "New subclass: #{subclass}"
+ * end
+ * end
+ *
+ * class Bar < Foo
+ * end
+ *
+ * class Baz < Bar
+ * end
+ *
+ * produces:
+ *
+ * New subclass: Bar
+ * New subclass: Baz
+ */
+
+/*
+ * Document-method: singleton_method_added
+ *
+ * call-seq:
+ * singleton_method_added(symbol)
+ *
+ * Invoked as a callback whenever a singleton method is added to the
+ * receiver.
+ *
+ * module Chatty
+ * def Chatty.singleton_method_added(id)
+ * puts "Adding #{id.id2name}"
+ * end
+ * def self.one() end
+ * def two() end
+ * def Chatty.three() end
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Adding singleton_method_added
+ * Adding one
+ * Adding three
+ *
+ */
+
+/*
+ * Document-method: singleton_method_removed
+ *
+ * call-seq:
+ * singleton_method_removed(symbol)
+ *
+ * Invoked as a callback whenever a singleton method is removed from
+ * the receiver.
+ *
+ * module Chatty
+ * def Chatty.singleton_method_removed(id)
+ * puts "Removing #{id.id2name}"
+ * end
+ * def self.one() end
+ * def two() end
+ * def Chatty.three() end
+ * class <<self
+ * remove_method :three
+ * remove_method :one
+ * end
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Removing three
+ * Removing one
+ */
+
+/*
+ * Document-method: singleton_method_undefined
+ *
+ * call-seq:
+ * singleton_method_undefined(symbol)
+ *
+ * Invoked as a callback whenever a singleton method is undefined in
+ * the receiver.
+ *
+ * module Chatty
+ * def Chatty.singleton_method_undefined(id)
+ * puts "Undefining #{id.id2name}"
+ * end
+ * def Chatty.one() end
+ * class << self
+ * undef_method(:one)
+ * end
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Undefining one
+ */
+
+
+/*
+ * Document-method: included
+ *
+ * call-seq:
+ * included( othermod )
+ *
+ * Callback invoked whenever the receiver is included in another
+ * module or class. This should be used in preference to
+ * <tt>Module.append_features</tt> if your code wants to perform some
+ * action when a module is included in another.
+ *
+ * module A
+ * def A.included(mod)
+ * puts "#{self} included in #{mod}"
+ * end
+ * end
+ * module Enumerable
+ * include A
+ * end
+ */
+
+
+/*
+ * Not documented
+ */
+
+static VALUE
+rb_obj_dummy()
+{
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * obj.tainted? => true or false
+ *
+ * Returns <code>true</code> if the object is tainted.
+ */
+
+VALUE
+rb_obj_tainted(obj)
+ VALUE obj;
+{
+ if (OBJ_TAINTED(obj))
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj.taint -> obj
+ *
+ * Marks <i>obj</i> as tainted---if the <code>$SAFE</code> level is
+ * set appropriately, many method calls which might alter the running
+ * programs environment will refuse to accept tainted strings.
+ */
+
+VALUE
+rb_obj_taint(obj)
+ VALUE obj;
+{
+ rb_secure(4);
+ if (!OBJ_TAINTED(obj)) {
+ if (OBJ_FROZEN(obj)) {
+ rb_error_frozen("object");
+ }
+ OBJ_TAINT(obj);
+ }
+ return obj;
+}
+
+
+/*
+ * call-seq:
+ * obj.untaint => obj
+ *
+ * Removes the taint from <i>obj</i>.
+ */
+
+VALUE
+rb_obj_untaint(obj)
+ VALUE obj;
+{
+ rb_secure(3);
+ if (OBJ_TAINTED(obj)) {
+ if (OBJ_FROZEN(obj)) {
+ rb_error_frozen("object");
+ }
+ FL_UNSET(obj, FL_TAINT);
+ }
+ return obj;
+}
+
+void
+rb_obj_infect(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ OBJ_INFECT(obj1, obj2);
+}
+
+
+/*
+ * call-seq:
+ * obj.freeze => obj
+ *
+ * Prevents further modifications to <i>obj</i>. A
+ * <code>TypeError</code> will be raised if modification is attempted.
+ * There is no way to unfreeze a frozen object. See also
+ * <code>Object#frozen?</code>.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.freeze
+ * a << "z"
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:3:in `<<': can't modify frozen array (TypeError)
+ * from prog.rb:3
+ */
+
+VALUE
+rb_obj_freeze(obj)
+ VALUE obj;
+{
+ if (!OBJ_FROZEN(obj)) {
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(obj)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't freeze object");
+ }
+ OBJ_FREEZE(obj);
+ }
+ return obj;
+}
+
+/*
+ * call-seq:
+ * obj.frozen? => true or false
+ *
+ * Returns the freeze status of <i>obj</i>.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.freeze #=> ["a", "b", "c"]
+ * a.frozen? #=> true
+ */
+
+static VALUE
+rb_obj_frozen_p(obj)
+ VALUE obj;
+{
+ if (OBJ_FROZEN(obj)) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * Document-class: NilClass
+ *
+ * The class of the singleton object <code>nil</code>.
+ */
+
+/*
+ * call-seq:
+ * nil.to_i => 0
+ *
+ * Always returns zero.
+ *
+ * nil.to_i #=> 0
+ */
+
+
+static VALUE
+nil_to_i(obj)
+ VALUE obj;
+{
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * nil.to_f => 0.0
+ *
+ * Always returns zero.
+ *
+ * nil.to_f #=> 0.0
+ */
+
+static VALUE
+nil_to_f(obj)
+ VALUE obj;
+{
+ return rb_float_new(0.0);
+}
+
+/*
+ * call-seq:
+ * nil.to_s => ""
+ *
+ * Always returns the empty string.
+ *
+ * nil.to_s #=> ""
+ */
+
+static VALUE
+nil_to_s(obj)
+ VALUE obj;
+{
+ return rb_str_new2("");
+}
+
+/*
+ * call-seq:
+ * nil.to_a => []
+ *
+ * Always returns an empty array.
+ *
+ * nil.to_a #=> []
+ */
+
+static VALUE
+nil_to_a(obj)
+ VALUE obj;
+{
+ return rb_ary_new2(0);
+}
+
+/*
+ * call-seq:
+ * nil.inspect => "nil"
+ *
+ * Always returns the string "nil".
+ */
+
+static VALUE
+nil_inspect(obj)
+ VALUE obj;
+{
+ return rb_str_new2("nil");
+}
+
+static VALUE
+main_to_s(obj)
+ VALUE obj;
+{
+ return rb_str_new2("main");
+}
+
+
+/***********************************************************************
+ * Document-class: TrueClass
+ *
+ * The global value <code>true</code> is the only instance of class
+ * <code>TrueClass</code> and represents a logically true value in
+ * boolean expressions. The class provides operators allowing
+ * <code>true</code> to be used in logical expressions.
+ */
+
+
+/*
+ * call-seq:
+ * true.to_s => "true"
+ *
+ * The string representation of <code>true</code> is "true".
+ */
+
+static VALUE
+true_to_s(obj)
+ VALUE obj;
+{
+ return rb_str_new2("true");
+}
+
+
+/*
+ * call-seq:
+ * true & obj => true or false
+ *
+ * And---Returns <code>false</code> if <i>obj</i> is
+ * <code>nil</code> or <code>false</code>, <code>true</code> otherwise.
+ */
+
+static VALUE
+true_and(obj, obj2)
+ VALUE obj, obj2;
+{
+ return RTEST(obj2)?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * true | obj => true
+ *
+ * Or---Returns <code>true</code>. As <i>anObject</i> is an argument to
+ * a method call, it is always evaluated; there is no short-circuit
+ * evaluation in this case.
+ *
+ * true | puts("or")
+ * true || puts("logical or")
+ *
+ * <em>produces:</em>
+ *
+ * or
+ */
+
+static VALUE
+true_or(obj, obj2)
+ VALUE obj, obj2;
+{
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * true ^ obj => !obj
+ *
+ * Exclusive Or---Returns <code>true</code> if <i>obj</i> is
+ * <code>nil</code> or <code>false</code>, <code>false</code>
+ * otherwise.
+ */
+
+static VALUE
+true_xor(obj, obj2)
+ VALUE obj, obj2;
+{
+ return RTEST(obj2)?Qfalse:Qtrue;
+}
+
+
+/*
+ * Document-class: FalseClass
+ *
+ * The global value <code>false</code> is the only instance of class
+ * <code>FalseClass</code> and represents a logically false value in
+ * boolean expressions. The class provides operators allowing
+ * <code>false</code> to participate correctly in logical expressions.
+ *
+ */
+
+/*
+ * call-seq:
+ * false.to_s => "false"
+ *
+ * 'nuf said...
+ */
+
+static VALUE
+false_to_s(obj)
+ VALUE obj;
+{
+ return rb_str_new2("false");
+}
+
+/*
+ * call-seq:
+ * false & obj => false
+ * nil & obj => false
+ *
+ * And---Returns <code>false</code>. <i>obj</i> is always
+ * evaluated as it is the argument to a method call---there is no
+ * short-circuit evaluation in this case.
+ */
+
+static VALUE
+false_and(obj, obj2)
+ VALUE obj, obj2;
+{
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * false | obj => true or false
+ * nil | obj => true or false
+ *
+ * Or---Returns <code>false</code> if <i>obj</i> is
+ * <code>nil</code> or <code>false</code>; <code>true</code> otherwise.
+ */
+
+static VALUE
+false_or(obj, obj2)
+ VALUE obj, obj2;
+{
+ return RTEST(obj2)?Qtrue:Qfalse;
+}
+
+
+
+/*
+ * call-seq:
+ * false ^ obj => true or false
+ * nil ^ obj => true or false
+ *
+ * Exclusive Or---If <i>obj</i> is <code>nil</code> or
+ * <code>false</code>, returns <code>false</code>; otherwise, returns
+ * <code>true</code>.
+ *
+ */
+
+static VALUE
+false_xor(obj, obj2)
+ VALUE obj, obj2;
+{
+ return RTEST(obj2)?Qtrue:Qfalse;
+}
+
+/*
+ * call_seq:
+ * nil.nil? => true
+ *
+ * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
+ */
+
+static VALUE
+rb_true(obj)
+ VALUE obj;
+{
+ return Qtrue;
+}
+
+/*
+ * call_seq:
+ * nil.nil? => true
+ * <anything_else>.nil? => false
+ *
+ * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
+ */
+
+
+static VALUE
+rb_false(obj)
+ VALUE obj;
+{
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * obj =~ other => false
+ *
+ * Pattern Match---Overridden by descendents (notably
+ * <code>Regexp</code> and <code>String</code>) to provide meaningful
+ * pattern-match semantics.
+ */
+
+static VALUE
+rb_obj_pattern_match(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ return Qfalse;
+}
+
+/**********************************************************************
+ * Document-class: Symbol
+ *
+ * <code>Symbol</code> objects represent names and some strings
+ * inside the Ruby
+ * interpreter. They are generated using the <code>:name</code> and
+ * <code>:"string"</code> literals
+ * syntax, and by the various <code>to_sym</code> methods. The same
+ * <code>Symbol</code> object will be created for a given name or string
+ * for the duration of a program's execution, regardless of the context
+ * or meaning of that name. Thus if <code>Fred</code> is a constant in
+ * one context, a method in another, and a class in a third, the
+ * <code>Symbol</code> <code>:Fred</code> will be the same object in
+ * all three contexts.
+ *
+ * module One
+ * class Fred
+ * end
+ * $f1 = :Fred
+ * end
+ * module Two
+ * Fred = 1
+ * $f2 = :Fred
+ * end
+ * def Fred()
+ * end
+ * $f3 = :Fred
+ * $f1.id #=> 2514190
+ * $f2.id #=> 2514190
+ * $f3.id #=> 2514190
+ *
+ */
+
+/*
+ * call-seq:
+ * sym.to_i => fixnum
+ *
+ * Returns an integer that is unique for each symbol within a
+ * particular execution of a program.
+ *
+ * :fred.to_i #=> 9809
+ * "fred".to_sym.to_i #=> 9809
+ */
+
+static VALUE
+sym_to_i(sym)
+ VALUE sym;
+{
+ ID id = SYM2ID(sym);
+
+ return LONG2FIX(id);
+}
+
+
+/* :nodoc: */
+
+static VALUE
+sym_to_int(sym)
+ VALUE sym;
+{
+ rb_warning("treating Symbol as an integer");
+ return sym_to_i(sym);
+}
+
+
+/*
+ * call-seq:
+ * sym.inspect => string
+ *
+ * Returns the representation of <i>sym</i> as a symbol literal.
+ *
+ * :fred.inspect #=> ":fred"
+ */
+
+static VALUE
+sym_inspect(sym)
+ VALUE sym;
+{
+ VALUE str;
+ char *name;
+ ID id = SYM2ID(sym);
+
+ name = rb_id2name(id);
+ str = rb_str_new(0, strlen(name)+1);
+ RSTRING(str)->ptr[0] = ':';
+ strcpy(RSTRING(str)->ptr+1, name);
+ if (!rb_symname_p(name)) {
+ str = rb_str_dump(str);
+ strncpy(RSTRING(str)->ptr, ":\"", 2);
+ }
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * sym.id2name => string
+ * sym.to_s => string
+ *
+ * Returns the name or string corresponding to <i>sym</i>.
+ *
+ * :fred.id2name #=> "fred"
+ */
+
+
+static VALUE
+sym_to_s(sym)
+ VALUE sym;
+{
+ return rb_str_new2(rb_id2name(SYM2ID(sym)));
+}
+
+
+/*
+ * call-seq:
+ * sym.to_sym => sym
+ *
+ * In general, <code>to_sym</code> returns the <code>Symbol</code> corresponding
+ * to an object. As <i>sym</i> is already a symbol, <code>self</code> is returned
+ * in this case.
+ */
+
+static VALUE
+sym_to_sym(sym)
+ VALUE sym;
+{
+ return sym;
+}
+
+
+/***********************************************************************
+ *
+ * Document-class: Module
+ *
+ * A <code>Module</code> is a collection of methods and constants. The
+ * methods in a module may be instance methods or module methods.
+ * Instance methods appear as methods in a class when the module is
+ * included, module methods do not. Conversely, module methods may be
+ * called without creating an encapsulating object, while instance
+ * methods may not. (See <code>Module#module_function</code>)
+ *
+ * In the descriptions that follow, the parameter <i>syml</i> refers
+ * to a symbol, which is either a quoted string or a
+ * <code>Symbol</code> (such as <code>:name</code>).
+ *
+ * module Mod
+ * include Math
+ * CONST = 1
+ * def meth
+ * # ...
+ * end
+ * end
+ * Mod.class #=> Module
+ * Mod.constants #=> ["E", "PI", "CONST"]
+ * Mod.instance_methods #=> ["meth"]
+ *
+ */
+
+/*
+ * call-seq:
+ * mod.to_s => string
+ *
+ * Return a string representing this module or class. For basic
+ * classes and modules, this is the name. For singletons, we
+ * show information on the thing we're attached to as well.
+ */
+
+static VALUE
+rb_mod_to_s(klass)
+ VALUE klass;
+
+{
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ VALUE s = rb_str_new2("#<");
+ VALUE v = rb_iv_get(klass, "__attached__");
+
+ rb_str_cat2(s, "Class:");
+ switch (TYPE(v)) {
+ case T_CLASS: case T_MODULE:
+ rb_str_append(s, rb_inspect(v));
+ break;
+ default:
+ rb_str_append(s, rb_any_to_s(v));
+ break;
+ }
+ rb_str_cat2(s, ">");
+
+ return s;
+ }
+ return rb_str_dup(rb_class_name(klass));
+}
+
+/*
+ * call-seq:
+ * mod.freeze
+ *
+ * Prevents further modifications to <i>mod</i>.
+ */
+
+static VALUE
+rb_mod_freeze(mod)
+ VALUE mod;
+{
+ rb_mod_to_s(mod);
+ return rb_obj_freeze(mod);
+}
+
+/*
+ * call-seq:
+ * mod === obj => true or false
+ *
+ * Case Equality---Returns <code>true</code> if <i>anObject</i> is an
+ * instance of <i>mod</i> or one of <i>mod</i>'s descendents. Of
+ * limited use for modules, but can be used in <code>case</code>
+ * statements to classify objects by class.
+ */
+
+static VALUE
+rb_mod_eqq(mod, arg)
+ VALUE mod, arg;
+{
+ return rb_obj_is_kind_of(arg, mod);
+}
+
+/*
+ * call-seq:
+ * mod <= other => true, false, or nil
+ *
+ * Returns true if <i>mod</i> is a subclass of <i>other</i> or
+ * is the same as <i>other</i>. Returns
+ * <code>nil</code> if there's no relationship between the two.
+ * (Think of the relationship in terms of the class definition:
+ * "class A<B" implies "A<B").
+ *
+ */
+
+VALUE
+rb_class_inherited_p(mod, arg)
+ VALUE mod, arg;
+{
+ VALUE start = mod;
+
+ if (mod == arg) return Qtrue;
+ switch (TYPE(arg)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "compared with non class/module");
+ }
+
+ if (FL_TEST(mod, FL_SINGLETON)) {
+ if (RCLASS(mod)->m_tbl == RCLASS(arg)->m_tbl)
+ return Qtrue;
+ mod = RBASIC(mod)->klass;
+ }
+ while (mod) {
+ if (RCLASS(mod)->m_tbl == RCLASS(arg)->m_tbl)
+ return Qtrue;
+ mod = RCLASS(mod)->super;
+ }
+ /* not mod < arg; check if mod > arg */
+ while (arg) {
+ if (RCLASS(arg)->m_tbl == RCLASS(start)->m_tbl)
+ return Qfalse;
+ arg = RCLASS(arg)->super;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * mod < other => true, false, or nil
+ *
+ * Returns true if <i>mod</i> is a subclass of <i>other</i>. Returns
+ * <code>nil</code> if there's no relationship between the two.
+ * (Think of the relationship in terms of the class definition:
+ * "class A<B" implies "A<B").
+ *
+ */
+
+static VALUE
+rb_mod_lt(mod, arg)
+ VALUE mod, arg;
+{
+ if (mod == arg) return Qfalse;
+ return rb_class_inherited_p(mod, arg);
+}
+
+
+/*
+ * call-seq:
+ * mod >= other => true, false, or nil
+ *
+ * Returns true if <i>mod</i> is an ancestor of <i>other</i>, or the
+ * two modules are the same. Returns
+ * <code>nil</code> if there's no relationship between the two.
+ * (Think of the relationship in terms of the class definition:
+ * "class A<B" implies "B>A").
+ *
+ */
+
+static VALUE
+rb_mod_ge(mod, arg)
+ VALUE mod, arg;
+{
+ switch (TYPE(arg)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "compared with non class/module");
+ }
+
+ return rb_class_inherited_p(arg, mod);
+}
+
+/*
+ * call-seq:
+ * mod > other => true, false, or nil
+ *
+ * Returns true if <i>mod</i> is an ancestor of <i>other</i>. Returns
+ * <code>nil</code> if there's no relationship between the two.
+ * (Think of the relationship in terms of the class definition:
+ * "class A<B" implies "B>A").
+ *
+ */
+
+static VALUE
+rb_mod_gt(mod, arg)
+ VALUE mod, arg;
+{
+ if (mod == arg) return Qfalse;
+ return rb_mod_ge(mod, arg);
+}
+
+/*
+ * call-seq:
+ * mod <=> other_mod => -1, 0, +1, or nil
+ *
+ * Comparison---Returns -1 if <i>mod</i> includes <i>other_mod</i>, 0 if
+ * <i>mod</i> is the same as <i>other_mod</i>, and +1 if <i>mod</i> is
+ * included by <i>other_mod</i> or if <i>mod</i> has no relationship with
+ * <i>other_mod</i>. Returns <code>nil</code> if <i>other_mod</i> is
+ * not a module.
+ */
+
+static VALUE
+rb_mod_cmp(mod, arg)
+ VALUE mod, arg;
+{
+ VALUE cmp;
+
+ if (mod == arg) return INT2FIX(0);
+ switch (TYPE(arg)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ return Qnil;
+ }
+
+ cmp = rb_class_inherited_p(mod, arg);
+ if (NIL_P(cmp)) return Qnil;
+ if (cmp) {
+ return INT2FIX(-1);
+ }
+ return INT2FIX(1);
+}
+
+static VALUE rb_module_s_alloc _((VALUE));
+static VALUE
+rb_module_s_alloc(klass)
+ VALUE klass;
+{
+ VALUE mod = rb_module_new();
+
+ RBASIC(mod)->klass = klass;
+ return mod;
+}
+
+static VALUE rb_class_s_alloc _((VALUE));
+static VALUE
+rb_class_s_alloc(klass)
+ VALUE klass;
+{
+ return rb_class_boot(0);
+}
+
+/*
+ * call-seq:
+ * Module.new => mod
+ * Module.new {|mod| block } => mod
+ *
+ * Creates a new anonymous module. If a block is given, it is passed
+ * the module object, and the block is evaluated in the context of this
+ * module using <code>module_eval</code>.
+ *
+ * Fred = Module.new do
+ * def meth1
+ * "hello"
+ * end
+ * def meth2
+ * "bye"
+ * end
+ * end
+ * a = "my string"
+ * a.extend(Fred) #=> "my string"
+ * a.meth1 #=> "hello"
+ * a.meth2 #=> "bye"
+ */
+
+static VALUE
+rb_mod_initialize(module)
+ VALUE module;
+{
+ if (rb_block_given_p()) {
+ rb_mod_module_eval(0, 0, module);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Class.new(super_class=Object) => a_class
+ *
+ * Creates a new anonymous (unnamed) class with the given superclass
+ * (or <code>Object</code> if no parameter is given). You can give a
+ * class a name by assigning the class object to a constant.
+ *
+ */
+
+static VALUE
+rb_class_initialize(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE super;
+
+ if (RCLASS(klass)->super != 0) {
+ rb_raise(rb_eTypeError, "already initialized class");
+ }
+ if (rb_scan_args(argc, argv, "01", &super) == 0) {
+ super = rb_cObject;
+ }
+ else {
+ rb_check_inheritable(super);
+ }
+ RCLASS(klass)->super = super;
+ rb_make_metaclass(klass, RBASIC(super)->klass);
+ rb_mod_initialize(klass);
+ rb_class_inherited(super, klass);
+
+ return klass;
+}
+
+/*
+ * call-seq:
+ * class.allocate() => obj
+ *
+ * Allocates space for a new object of <i>class</i>'s class. The
+ * returned object must be an instance of <i>class</i>.
+ *
+ */
+
+VALUE
+rb_obj_alloc(klass)
+ VALUE klass;
+{
+ VALUE obj;
+
+ if (RCLASS(klass)->super == 0) {
+ rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
+ }
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "can't create instance of virtual class");
+ }
+ obj = rb_funcall(klass, ID_ALLOCATOR, 0, 0);
+ if (rb_obj_class(obj) != rb_class_real(klass)) {
+ rb_raise(rb_eTypeError, "wrong instance allocation");
+ }
+ return obj;
+}
+
+static VALUE rb_class_allocate_instance _((VALUE));
+static VALUE
+rb_class_allocate_instance(klass)
+ VALUE klass;
+{
+ NEWOBJ(obj, struct RObject);
+ OBJSETUP(obj, klass, T_OBJECT);
+ return (VALUE)obj;
+}
+
+/*
+ * call-seq:
+ * class.new(args, ...) => obj
+ *
+ * Calls <code>allocate</code> to create a new object of
+ * <i>class</i>'s class, then invokes that object's
+ * <code>initialize</code> method, passing it <i>args</i>.
+ * This is the method that ends up getting called whenever
+ * an object is constructed using .new.
+ *
+ */
+
+VALUE
+rb_class_new_instance(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE obj;
+
+ obj = rb_obj_alloc(klass);
+ rb_obj_call_init(obj, argc, argv);
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * class.superclass -> a_super_class or nil
+ *
+ * Returns the superclass of <i>class</i>, or <code>nil</code>.
+ *
+ * File.superclass #=> IO
+ * IO.superclass #=> Object
+ * Object.superclass #=> nil
+ *
+ */
+
+static VALUE
+rb_class_superclass(klass)
+ VALUE klass;
+{
+ VALUE super = RCLASS(klass)->super;
+
+ if (!super) {
+ rb_raise(rb_eTypeError, "uninitialized class");
+ }
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ super = RBASIC(klass)->klass;
+ }
+ while (TYPE(super) == T_ICLASS) {
+ super = RCLASS(super)->super;
+ }
+ if (!super) {
+ return Qnil;
+ }
+ return super;
+}
+
+static ID
+str_to_id(str)
+ VALUE str;
+{
+ VALUE sym = rb_str_intern(str);
+
+ return SYM2ID(sym);
+}
+
+ID
+rb_to_id(name)
+ VALUE name;
+{
+ VALUE tmp;
+ ID id;
+
+ switch (TYPE(name)) {
+ case T_STRING:
+ return str_to_id(name);
+ case T_FIXNUM:
+ rb_warn("do not use Fixnums as Symbols");
+ id = FIX2LONG(name);
+ if (!rb_id2name(id)) {
+ rb_raise(rb_eArgError, "%ld is not a symbol", id);
+ }
+ break;
+ case T_SYMBOL:
+ id = SYM2ID(name);
+ break;
+ default:
+ tmp = rb_check_string_type(name);
+ if (!NIL_P(tmp)) {
+ return str_to_id(tmp);
+ }
+ rb_raise(rb_eTypeError, "%s is not a symbol", RSTRING(rb_inspect(name))->ptr);
+ }
+ return id;
+}
+
+/*
+ * call-seq:
+ * attr(symbol, writable=false) => nil
+ *
+ * Defines a named attribute for this module, where the name is
+ * <i>symbol.</i><code>id2name</code>, creating an instance variable
+ * (<code>@name</code>) and a corresponding access method to read it.
+ * If the optional <i>writable</i> argument is <code>true</code>, also
+ * creates a method called <code>name=</code> to set the attribute.
+ *
+ * module Mod
+ * attr :size, true
+ * end
+ *
+ * <em>is equivalent to:</em>
+ *
+ * module Mod
+ * def size
+ * @size
+ * end
+ * def size=(val)
+ * @size = val
+ * end
+ * end
+ */
+
+static VALUE
+rb_mod_attr(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE name, pub;
+
+ rb_scan_args(argc, argv, "11", &name, &pub);
+ rb_attr(klass, rb_to_id(name), 1, RTEST(pub), Qtrue);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * attr_reader(symbol, ...) => nil
+ *
+ * Creates instance variables and corresponding methods that return the
+ * value of each instance variable. Equivalent to calling
+ * ``<code>attr</code><i>:name</i>'' on each name in turn.
+ */
+
+static VALUE
+rb_mod_attr_reader(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_attr(klass, rb_to_id(argv[i]), 1, 0, Qtrue);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * attr_writer(symbol, ...) => nil
+ *
+ * Creates an accessor method to allow assignment to the attribute
+ * <i>aSymbol</i><code>.id2name</code>.
+ */
+
+static VALUE
+rb_mod_attr_writer(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_attr(klass, rb_to_id(argv[i]), 0, 1, Qtrue);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * attr_accessor(symbol, ...) => nil
+ *
+ * Equivalent to calling ``<code>attr</code><i>symbol</i><code>,
+ * true</code>'' on each <i>symbol</i> in turn.
+ *
+ * module Mod
+ * attr_accessor(:one, :two)
+ * end
+ * Mod.instance_methods.sort #=> ["one", "one=", "two", "two="]
+ */
+
+static VALUE
+rb_mod_attr_accessor(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_attr(klass, rb_to_id(argv[i]), 1, 1, Qtrue);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * mod.const_get(sym) => obj
+ *
+ * Returns the value of the named constant in <i>mod</i>.
+ *
+ * Math.const_get(:PI) #=> 3.14159265358979
+ */
+
+static VALUE
+rb_mod_const_get(mod, name)
+ VALUE mod, name;
+{
+ ID id = rb_to_id(name);
+
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ }
+ return rb_const_get(mod, id);
+}
+
+/*
+ * call-seq:
+ * mod.const_set(sym, obj) => obj
+ *
+ * Sets the named constant to the given object, returning that object.
+ * Creates a new constant if no constant with the given name previously
+ * existed.
+ *
+ * Math.const_set("HIGH_SCHOOL_PI", 22.0/7.0) #=> 3.14285714285714
+ * Math::HIGH_SCHOOL_PI - Math::PI #=> 0.00126448926734968
+ */
+
+static VALUE
+rb_mod_const_set(mod, name, value)
+ VALUE mod, name, value;
+{
+ ID id = rb_to_id(name);
+
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ }
+ rb_const_set(mod, id, value);
+ return value;
+}
+
+/*
+ * call-seq:
+ * mod.const_defined?(sym) => true or false
+ *
+ * Returns <code>true</code> if a constant with the given name is
+ * defined by <i>mod</i>.
+ *
+ * Math.const_defined? "PI" #=> true
+ */
+
+static VALUE
+rb_mod_const_defined(mod, name)
+ VALUE mod, name;
+{
+ ID id = rb_to_id(name);
+
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ }
+ return rb_const_defined_at(mod, id);
+}
+
+/*
+ * call-seq:
+ * obj.methods => array
+ *
+ * Returns a list of the names of methods publicly accessible in
+ * <i>obj</i>. This will include all the methods accessible in
+ * <i>obj</i>'s ancestors.
+ *
+ * class Klass
+ * def kMethod()
+ * end
+ * end
+ * k = Klass.new
+ * k.methods[0..9] #=> ["kMethod", "freeze", "nil?", "is_a?",
+ * "class", "instance_variable_set",
+ * "methods", "extend", "__send__", "instance_eval"]
+ * k.methods.length #=> 42
+ */
+
+static VALUE
+rb_obj_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ retry:
+ if (argc == 0) {
+ VALUE args[1];
+
+ args[0] = Qtrue;
+ return rb_class_instance_methods(1, args, CLASS_OF(obj));
+ }
+ else {
+ VALUE recur;
+
+ rb_scan_args(argc, argv, "1", &recur);
+ if (RTEST(recur)) {
+ argc = 0;
+ goto retry;
+ }
+ return rb_obj_singleton_methods(argc, argv, obj);
+ }
+}
+
+/*
+ * call-seq:
+ * obj.protected_methods(all=true) => array
+ *
+ * Returns the list of protected methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+
+static VALUE
+rb_obj_protected_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
+
+ args[0] = Qtrue;
+ return rb_class_protected_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_protected_instance_methods(argc, argv, CLASS_OF(obj));
+}
+
+/*
+ * call-seq:
+ * obj.private_methods(all=true) => array
+ *
+ * Returns the list of private methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+
+static VALUE
+rb_obj_private_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
+
+ args[0] = Qtrue;
+ return rb_class_private_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_private_instance_methods(argc, argv, CLASS_OF(obj));
+}
+
+/*
+ * call-seq:
+ * obj.public_methods(all=true) => array
+ *
+ * Returns the list of public methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+
+static VALUE
+rb_obj_public_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
+
+ args[0] = Qtrue;
+ return rb_class_public_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_public_instance_methods(argc, argv, CLASS_OF(obj));
+}
+
+/*
+ * call-seq:
+ * obj.instance_variable_get(symbol) => obj
+ *
+ * Returns the value of the given instance variable, or nil if the
+ * instance variable is not set. The <code>@</code> part of the
+ * variable name should be included for regular instance
+ * variables. Throws a <code>NameError</code> exception if the
+ * supplied symbol is not valid as an instance variable name.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_get(:@a) #=> "cat"
+ * fred.instance_variable_get("@b") #=> 99
+ */
+
+static VALUE
+rb_obj_ivar_get(obj, iv)
+ VALUE obj, iv;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+ return rb_ivar_get(obj, id);
+}
+
+/*
+ * call-seq:
+ * obj.instance_variable_set(symbol, obj) => obj
+ *
+ * Sets the instance variable names by <i>symbol</i> to
+ * <i>object</i>, thereby frustrating the efforts of the class's
+ * author to attempt to provide proper encapsulation. The variable
+ * did not have to exist prior to this call.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_set(:@a, 'dog') #=> "dog"
+ * fred.instance_variable_set(:@c, 'cat') #=> "cat"
+ * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
+ */
+
+static VALUE
+rb_obj_ivar_set(obj, iv, val)
+ VALUE obj, iv, val;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+ return rb_ivar_set(obj, id, val);
+}
+
+/*
+ * call-seq:
+ * obj.instance_variable_defined?(symbol) => true or false
+ *
+ * Returns <code>true</code> if the given instance variable is
+ * defined in <i>obj</i>.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_defined?(:@a) #=> true
+ * fred.instance_variable_defined?("@b") #=> true
+ * fred.instance_variable_defined?("@c") #=> false
+ */
+
+static VALUE
+rb_obj_ivar_defined(obj, iv)
+ VALUE obj, iv;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+ return rb_ivar_defined(obj, id);
+}
+
+/*
+ * call-seq:
+ * mod.class_variable_get(symbol) => obj
+ *
+ * Returns the value of the given class variable (or throws a
+ * <code>NameError</code> exception). The <code>@@</code> part of the
+ * variable name should be included for regular class variables
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ *
+ * def Fred.foo
+ * class_variable_get(:@@foo) #=> 99
+ * end
+ */
+
+static VALUE
+rb_mod_cvar_get(obj, iv)
+ VALUE obj, iv;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as a class variable name", rb_id2name(id));
+ }
+ return rb_cvar_get(obj, id);
+}
+
+/*
+ * call-seq:
+ * obj.class_variable_set(symbol, obj) => obj
+ *
+ * Sets the class variable names by <i>symbol</i> to
+ * <i>object</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * def foo
+ * @@foo
+ * end
+ * end
+ *
+ * def Fred.foo
+ * class_variable_set(:@@foo, 101) #=> 101
+ * end
+ * Fred.foo
+ * Fred.new.foo #=> 101
+ */
+
+static VALUE
+rb_mod_cvar_set(obj, iv, val)
+ VALUE obj, iv, val;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as a class variable name", rb_id2name(id));
+ }
+ rb_cvar_set(obj, id, val, Qfalse);
+ return val;
+}
+
+/*
+ * call-seq:
+ * obj.class_variable_defined?(symbol) => true or false
+ *
+ * Returns <code>true</code> if the given class variable is defined
+ * in <i>obj</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ * Fred.class_variable_defined?(:@@foo) #=> true
+ * Fred.class_variable_defined?(:@@bar) #=> false
+ */
+
+static VALUE
+rb_mod_cvar_defined(obj, iv)
+ VALUE obj, iv;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as a class variable name", rb_id2name(id));
+ }
+ return rb_cvar_defined(obj, id);
+}
+
+static VALUE
+convert_type(val, tname, method, raise)
+ VALUE val;
+ const char *tname, *method;
+ int raise;
+{
+ ID m;
+
+ m = rb_intern(method);
+ if (!rb_respond_to(val, m)) {
+ if (raise) {
+ rb_raise(rb_eTypeError, "can't convert %s into %s",
+ NIL_P(val) ? "nil" :
+ val == Qtrue ? "true" :
+ val == Qfalse ? "false" :
+ rb_obj_classname(val),
+ tname);
+ }
+ else {
+ return Qnil;
+ }
+ }
+ return rb_funcall(val, m, 0);
+}
+
+VALUE
+rb_convert_type(val, type, tname, method)
+ VALUE val;
+ int type;
+ const char *tname, *method;
+{
+ VALUE v;
+
+ if (TYPE(val) == type) return val;
+ v = convert_type(val, tname, method, Qtrue);
+ if (TYPE(v) != type) {
+ rb_raise(rb_eTypeError, "%s#%s should return %s",
+ rb_obj_classname(val), method, tname);
+ }
+ return v;
+}
+
+VALUE
+rb_check_convert_type(val, type, tname, method)
+ VALUE val;
+ int type;
+ const char *tname, *method;
+{
+ VALUE v;
+
+ /* always convert T_DATA */
+ if (TYPE(val) == type && type != T_DATA) return val;
+ v = convert_type(val, tname, method, Qfalse);
+ if (NIL_P(v)) return Qnil;
+ if (TYPE(v) != type) {
+ rb_raise(rb_eTypeError, "%s#%s should return %s",
+ rb_obj_classname(val), method, tname);
+ }
+ return v;
+}
+
+
+static VALUE
+rb_to_integer(val, method)
+ VALUE val;
+ const char *method;
+{
+ VALUE v = convert_type(val, "Integer", method, Qtrue);
+ if (!rb_obj_is_kind_of(v, rb_cInteger)) {
+ rb_raise(rb_eTypeError, "%s#%s should return Integer",
+ rb_obj_classname(val), method);
+ }
+ return v;
+}
+
+VALUE
+rb_to_int(val)
+ VALUE val;
+{
+ return rb_to_integer(val, "to_int");
+}
+
+VALUE
+rb_Integer(val)
+ VALUE val;
+{
+ VALUE tmp;
+
+ switch (TYPE(val)) {
+ case T_FLOAT:
+ if (RFLOAT(val)->value <= (double)FIXNUM_MAX
+ && RFLOAT(val)->value >= (double)FIXNUM_MIN) {
+ break;
+ }
+ return rb_dbl2big(RFLOAT(val)->value);
+
+ case T_FIXNUM:
+ case T_BIGNUM:
+ return val;
+
+ case T_STRING:
+ return rb_str_to_inum(val, 0, Qtrue);
+
+ default:
+ break;
+ }
+ tmp = convert_type(val, "Integer", "to_int", Qfalse);
+ if (NIL_P(tmp)) {
+ return rb_to_integer(val, "to_i");
+ }
+ return tmp;
+}
+
+/*
+ * call-seq:
+ * Integer(arg) => integer
+ *
+ * Converts <i>arg</i> to a <code>Fixnum</code> or <code>Bignum</code>.
+ * Numeric types are converted directly (with floating point numbers
+ * being truncated). If <i>arg</i> is a <code>String</code>, leading
+ * radix indicators (<code>0</code>, <code>0b</code>, and
+ * <code>0x</code>) are honored. Others are converted using
+ * <code>to_int</code> and <code>to_i</code>. This behavior is
+ * different from that of <code>String#to_i</code>.
+ *
+ * Integer(123.999) #=> 123
+ * Integer("0x1a") #=> 26
+ * Integer(Time.new) #=> 1049896590
+ */
+
+static VALUE
+rb_f_integer(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_Integer(arg);
+}
+
+double
+rb_cstr_to_dbl(p, badcheck)
+ const char *p;
+ int badcheck;
+{
+ const char *q;
+ char *end;
+ double d;
+ const char *ellipsis = "";
+ int w;
+#define OutOfRange() (((w = end - p) > 20) ? (w = 20, ellipsis = "...") : (ellipsis = ""))
+
+ if (!p) return 0.0;
+ q = p;
+ if (badcheck) {
+ while (ISSPACE(*p)) p++;
+ }
+ else {
+ while (ISSPACE(*p) || *p == '_') p++;
+ }
+ errno = 0;
+ d = strtod(p, &end);
+ if (errno == ERANGE) {
+ OutOfRange();
+ rb_warn("Float %.*s%s out of range", w, p, ellipsis);
+ errno = 0;
+ }
+ if (p == end) {
+ if (badcheck) {
+ bad:
+ rb_invalid_str(q, "Float()");
+ }
+ return d;
+ }
+ if (*end) {
+ char *buf = ALLOCA_N(char, strlen(p)+1);
+ char *n = buf;
+
+ while (p < end) *n++ = *p++;
+ while (*p) {
+ if (*p == '_') {
+ /* remove underscores between digits */
+ if (badcheck) {
+ if (n == buf || !ISDIGIT(n[-1])) goto bad;
+ ++p;
+ if (!ISDIGIT(*p)) goto bad;
+ }
+ else {
+ while (*++p == '_');
+ continue;
+ }
+ }
+ *n++ = *p++;
+ }
+ *n = '\0';
+ p = buf;
+ d = strtod(p, &end);
+ if (errno == ERANGE) {
+ OutOfRange();
+ rb_warn("Float %.*s%s out of range", w, p, ellipsis);
+ errno = 0;
+ }
+ if (badcheck) {
+ if (!end || p == end) goto bad;
+ while (*end && ISSPACE(*end)) end++;
+ if (*end) goto bad;
+ }
+ }
+ if (errno == ERANGE) {
+ errno = 0;
+ OutOfRange();
+ rb_raise(rb_eArgError, "Float %.*s%s out of range", w, q, ellipsis);
+ }
+ return d;
+}
+
+double
+rb_str_to_dbl(str, badcheck)
+ VALUE str;
+ int badcheck;
+{
+ char *s;
+ long len;
+
+ StringValue(str);
+ s = RSTRING(str)->ptr;
+ len = RSTRING(str)->len;
+ if (s) {
+ if (s[len]) { /* no sentinel somehow */
+ char *p = ALLOCA_N(char, len+1);
+
+ MEMCPY(p, s, char, len);
+ p[len] = '\0';
+ s = p;
+ }
+ if (badcheck && len != strlen(s)) {
+ rb_raise(rb_eArgError, "string for Float contains null byte");
+ }
+ }
+ return rb_cstr_to_dbl(s, badcheck);
+}
+
+VALUE
+rb_Float(val)
+ VALUE val;
+{
+ switch (TYPE(val)) {
+ case T_FIXNUM:
+ return rb_float_new((double)FIX2LONG(val));
+
+ case T_FLOAT:
+ return val;
+
+ case T_BIGNUM:
+ return rb_float_new(rb_big2dbl(val));
+
+ case T_STRING:
+ return rb_float_new(rb_str_to_dbl(val, Qtrue));
+
+ case T_NIL:
+ rb_raise(rb_eTypeError, "can't convert nil into Float");
+ break;
+
+ default:
+ {
+ VALUE f = rb_convert_type(val, T_FLOAT, "Float", "to_f");
+ if (isnan(RFLOAT(f)->value)) {
+ rb_raise(rb_eArgError, "invalid value for Float()");
+ }
+ return f;
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * Float(arg) => float
+ *
+ * Returns <i>arg</i> converted to a float. Numeric types are converted
+ * directly, the rest are converted using <i>arg</i>.to_f. As of Ruby
+ * 1.8, converting <code>nil</code> generates a <code>TypeError</code>.
+ *
+ * Float(1) #=> 1.0
+ * Float("123.456") #=> 123.456
+ */
+
+static VALUE
+rb_f_float(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_Float(arg);
+}
+
+double
+rb_num2dbl(val)
+ VALUE val;
+{
+ switch (TYPE(val)) {
+ case T_FLOAT:
+ return RFLOAT(val)->value;
+
+ case T_STRING:
+ rb_raise(rb_eTypeError, "no implicit conversion to float from string");
+ break;
+
+ case T_NIL:
+ rb_raise(rb_eTypeError, "no implicit conversion to float from nil");
+ break;
+
+ default:
+ break;
+ }
+
+ return RFLOAT(rb_Float(val))->value;
+}
+
+char*
+rb_str2cstr(str, len)
+ VALUE str;
+ long *len;
+{
+ StringValue(str);
+ if (len) *len = RSTRING(str)->len;
+ else if (RTEST(ruby_verbose) && RSTRING(str)->len != strlen(RSTRING(str)->ptr)) {
+ rb_warn("string contains \\0 character");
+ }
+ return RSTRING(str)->ptr;
+}
+
+VALUE
+rb_String(val)
+ VALUE val;
+{
+ return rb_convert_type(val, T_STRING, "String", "to_s");
+}
+
+
+/*
+ * call-seq:
+ * String(arg) => string
+ *
+ * Converts <i>arg</i> to a <code>String</code> by calling its
+ * <code>to_s</code> method.
+ *
+ * String(self) #=> "main"
+ * String(self.class #=> "Object"
+ * String(123456) #=> "123456"
+ */
+
+static VALUE
+rb_f_string(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_String(arg);
+}
+
+#if 0
+VALUE
+rb_Array(val)
+ VALUE val;
+{
+ VALUE tmp = rb_check_array_type(val);
+
+ if (NIL_P(tmp)) {
+ tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, val);
+ }
+ }
+ return tmp;
+}
+#endif
+
+/*
+ * call-seq:
+ * Array(arg) => array
+ *
+ * Returns <i>arg</i> as an <code>Array</code>. First tries to call
+ * <i>arg</i><code>.to_ary</code>, then <i>arg</i><code>.to_a</code>.
+ * If both fail, creates a single element array containing <i>arg</i>
+ * (unless <i>arg</i> is <code>nil</code>).
+ *
+ * Array(1..5) #=> [1, 2, 3, 4, 5]
+ */
+
+static VALUE
+rb_f_array(obj, arg)
+ VALUE obj, arg;
+{
+ return rb_Array(arg);
+}
+
+static VALUE
+boot_defclass(name, super)
+ char *name;
+ VALUE super;
+{
+ extern st_table *rb_class_tbl;
+ VALUE obj = rb_class_boot(super);
+ ID id = rb_intern(name);
+
+ rb_name_class(obj, id);
+ st_add_direct(rb_class_tbl, id, obj);
+ rb_const_set((rb_cObject ? rb_cObject : obj), id, obj);
+ return obj;
+}
+
+VALUE ruby_top_self;
+
+/*
+ * Document-class: Class
+ *
+ * Classes in Ruby are first-class objects---each is an instance of
+ * class <code>Class</code>.
+ *
+ * When a new class is created (typically using <code>class Name ...
+ * end</code>), an object of type <code>Class</code> is created and
+ * assigned to a global constant (<code>Name</code> in this case). When
+ * <code>Name.new</code> is called to create a new object, the
+ * <code>new</code> method in <code>Class</code> is run by default.
+ * This can be demonstrated by overriding <code>new</code> in
+ * <code>Class</code>:
+ *
+ * class Class
+ * alias oldNew new
+ * def new(*args)
+ * print "Creating a new ", self.name, "\n"
+ * oldNew(*args)
+ * end
+ * end
+ *
+ *
+ * class Name
+ * end
+ *
+ *
+ * n = Name.new
+ *
+ * <em>produces:</em>
+ *
+ * Creating a new Name
+ *
+ * Classes, modules, and objects are interrelated. In the diagram
+ * that follows, the vertical arrows represent inheritance, and the
+ * parentheses meta-classes. All metaclasses are instances
+ * of the class `Class'.
+ *
+ * +------------------+
+ * | |
+ * Object---->(Object) |
+ * ^ ^ ^ ^ |
+ * | | | | |
+ * | | +-----+ +---------+ |
+ * | | | | |
+ * | +-----------+ | |
+ * | | | | |
+ * +------+ | Module--->(Module) |
+ * | | ^ ^ |
+ * OtherClass-->(OtherClass) | | |
+ * | | |
+ * Class---->(Class) |
+ * ^ |
+ * | |
+ * +----------------+
+ */
+
+
+/*
+ * <code>Object</code> is the parent class of all classes in Ruby. Its
+ * methods are therefore available to all objects unless explicitly
+ * overridden.
+ *
+ * <code>Object</code> mixes in the <code>Kernel</code> module, making
+ * the built-in kernel functions globally accessible. Although the
+ * instance methods of <code>Object</code> are defined by the
+ * <code>Kernel</code> module, we have chosen to document them here for
+ * clarity.
+ *
+ * In the descriptions of Object's methods, the parameter <i>symbol</i> refers
+ * to a symbol, which is either a quoted string or a
+ * <code>Symbol</code> (such as <code>:name</code>).
+ */
+
+void
+Init_Object()
+{
+ VALUE metaclass;
+
+ rb_cObject = boot_defclass("Object", 0);
+ rb_cModule = boot_defclass("Module", rb_cObject);
+ rb_cClass = boot_defclass("Class", rb_cModule);
+
+ metaclass = rb_make_metaclass(rb_cObject, rb_cClass);
+ metaclass = rb_make_metaclass(rb_cModule, metaclass);
+ metaclass = rb_make_metaclass(rb_cClass, metaclass);
+
+ rb_mKernel = rb_define_module("Kernel");
+ rb_include_module(rb_cObject, rb_mKernel);
+ rb_define_alloc_func(rb_cObject, rb_class_allocate_instance);
+ rb_define_private_method(rb_cObject, "initialize", rb_obj_dummy, 0);
+ rb_define_private_method(rb_cClass, "inherited", rb_obj_dummy, 1);
+ rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1);
+ rb_define_private_method(rb_cModule, "extended", rb_obj_dummy, 1);
+ rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1);
+ rb_define_private_method(rb_cModule, "method_removed", rb_obj_dummy, 1);
+ rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1);
+
+
+ rb_define_method(rb_mKernel, "nil?", rb_false, 0);
+ rb_define_method(rb_mKernel, "==", rb_obj_equal, 1);
+ rb_define_method(rb_mKernel, "equal?", rb_obj_equal, 1);
+ rb_define_method(rb_mKernel, "===", rb_equal, 1);
+ rb_define_method(rb_mKernel, "=~", rb_obj_pattern_match, 1);
+
+ rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1);
+
+ rb_define_method(rb_mKernel, "id", rb_obj_id_obsolete, 0);
+ rb_define_method(rb_mKernel, "type", rb_obj_type, 0);
+ rb_define_method(rb_mKernel, "class", rb_obj_class, 0);
+
+ rb_define_method(rb_mKernel, "clone", rb_obj_clone, 0);
+ rb_define_method(rb_mKernel, "dup", rb_obj_dup, 0);
+ rb_define_method(rb_mKernel, "initialize_copy", rb_obj_init_copy, 1);
+
+ rb_define_method(rb_mKernel, "taint", rb_obj_taint, 0);
+ rb_define_method(rb_mKernel, "tainted?", rb_obj_tainted, 0);
+ rb_define_method(rb_mKernel, "untaint", rb_obj_untaint, 0);
+ rb_define_method(rb_mKernel, "freeze", rb_obj_freeze, 0);
+ rb_define_method(rb_mKernel, "frozen?", rb_obj_frozen_p, 0);
+
+ rb_define_method(rb_mKernel, "to_a", rb_any_to_a, 0); /* to be removed */
+ rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);
+ rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0);
+ rb_define_method(rb_mKernel, "methods", rb_obj_methods, -1);
+ rb_define_method(rb_mKernel, "singleton_methods",
+ rb_obj_singleton_methods, -1); /* in class.c */
+ rb_define_method(rb_mKernel, "protected_methods",
+ rb_obj_protected_methods, -1);
+ rb_define_method(rb_mKernel, "private_methods", rb_obj_private_methods, -1);
+ rb_define_method(rb_mKernel, "public_methods", rb_obj_public_methods, -1);
+ rb_define_method(rb_mKernel, "instance_variables",
+ rb_obj_instance_variables, 0); /* in variable.c */
+ rb_define_method(rb_mKernel, "instance_variable_get", rb_obj_ivar_get, 1);
+ rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set, 2);
+ rb_define_method(rb_mKernel, "instance_variable_defined?", rb_obj_ivar_defined, 1);
+ rb_define_private_method(rb_mKernel, "remove_instance_variable",
+ rb_obj_remove_instance_variable, 1); /* in variable.c */
+
+ rb_define_method(rb_mKernel, "instance_of?", rb_obj_is_instance_of, 1);
+ rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
+ rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);
+
+ rb_define_private_method(rb_mKernel, "singleton_method_added", rb_obj_dummy, 1);
+ rb_define_private_method(rb_mKernel, "singleton_method_removed", rb_obj_dummy, 1);
+ rb_define_private_method(rb_mKernel, "singleton_method_undefined", rb_obj_dummy, 1);
+
+ rb_define_global_function("sprintf", rb_f_sprintf, -1); /* in sprintf.c */
+ rb_define_global_function("format", rb_f_sprintf, -1); /* in sprintf.c */
+
+ rb_define_global_function("Integer", rb_f_integer, 1);
+ rb_define_global_function("Float", rb_f_float, 1);
+
+ rb_define_global_function("String", rb_f_string, 1);
+ rb_define_global_function("Array", rb_f_array, 1);
+
+ rb_cNilClass = rb_define_class("NilClass", rb_cObject);
+ rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0);
+ rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
+ rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0);
+ rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
+ rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0);
+ rb_define_method(rb_cNilClass, "&", false_and, 1);
+ rb_define_method(rb_cNilClass, "|", false_or, 1);
+ rb_define_method(rb_cNilClass, "^", false_xor, 1);
+
+ rb_define_method(rb_cNilClass, "nil?", rb_true, 0);
+ rb_undef_alloc_func(rb_cNilClass);
+ rb_undef_method(CLASS_OF(rb_cNilClass), "new");
+ rb_define_global_const("NIL", Qnil);
+
+ rb_cSymbol = rb_define_class("Symbol", rb_cObject);
+ rb_define_singleton_method(rb_cSymbol, "all_symbols",
+ rb_sym_all_symbols, 0); /* in parse.y */
+ rb_undef_alloc_func(rb_cSymbol);
+ rb_undef_method(CLASS_OF(rb_cSymbol), "new");
+
+ rb_define_method(rb_cSymbol, "to_i", sym_to_i, 0);
+ rb_define_method(rb_cSymbol, "to_int", sym_to_int, 0);
+ rb_define_method(rb_cSymbol, "inspect", sym_inspect, 0);
+ rb_define_method(rb_cSymbol, "to_s", sym_to_s, 0);
+ rb_define_method(rb_cSymbol, "id2name", sym_to_s, 0);
+ rb_define_method(rb_cSymbol, "to_sym", sym_to_sym, 0);
+ rb_define_method(rb_cSymbol, "===", rb_obj_equal, 1);
+
+ rb_define_method(rb_cModule, "freeze", rb_mod_freeze, 0);
+ rb_define_method(rb_cModule, "===", rb_mod_eqq, 1);
+ rb_define_method(rb_cModule, "==", rb_obj_equal, 1);
+ rb_define_method(rb_cModule, "<=>", rb_mod_cmp, 1);
+ rb_define_method(rb_cModule, "<", rb_mod_lt, 1);
+ rb_define_method(rb_cModule, "<=", rb_class_inherited_p, 1);
+ rb_define_method(rb_cModule, ">", rb_mod_gt, 1);
+ rb_define_method(rb_cModule, ">=", rb_mod_ge, 1);
+ rb_define_method(rb_cModule, "initialize_copy", rb_mod_init_copy, 1); /* in class.c */
+ rb_define_method(rb_cModule, "to_s", rb_mod_to_s, 0);
+ rb_define_method(rb_cModule, "included_modules",
+ rb_mod_included_modules, 0); /* in class.c */
+ rb_define_method(rb_cModule, "include?", rb_mod_include_p, 1); /* in class.c */
+ rb_define_method(rb_cModule, "name", rb_mod_name, 0); /* in variable.c */
+ rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); /* in class.c */
+
+ rb_define_private_method(rb_cModule, "attr", rb_mod_attr, -1);
+ rb_define_private_method(rb_cModule, "attr_reader", rb_mod_attr_reader, -1);
+ rb_define_private_method(rb_cModule, "attr_writer", rb_mod_attr_writer, -1);
+ rb_define_private_method(rb_cModule, "attr_accessor", rb_mod_attr_accessor, -1);
+
+ rb_define_alloc_func(rb_cModule, rb_module_s_alloc);
+ rb_define_method(rb_cModule, "initialize", rb_mod_initialize, 0);
+ rb_define_method(rb_cModule, "instance_methods",
+ rb_class_instance_methods, -1); /* in class.c */
+ rb_define_method(rb_cModule, "public_instance_methods",
+ rb_class_public_instance_methods, -1); /* in class.c */
+ rb_define_method(rb_cModule, "protected_instance_methods",
+ rb_class_protected_instance_methods, -1); /* in class.c */
+ rb_define_method(rb_cModule, "private_instance_methods",
+ rb_class_private_instance_methods, -1); /* in class.c */
+
+ rb_define_method(rb_cModule, "class_variable_defined?", rb_mod_cvar_defined, 1);
+ rb_define_method(rb_cModule, "constants", rb_mod_constants, 0); /* in variable.c */
+ rb_define_method(rb_cModule, "const_get", rb_mod_const_get, 1);
+ rb_define_method(rb_cModule, "const_set", rb_mod_const_set, 2);
+ rb_define_method(rb_cModule, "const_defined?", rb_mod_const_defined, 1);
+ rb_define_private_method(rb_cModule, "remove_const",
+ rb_mod_remove_const, 1); /* in variable.c */
+ rb_define_method(rb_cModule, "const_missing",
+ rb_mod_const_missing, 1); /* in variable.c */
+ rb_define_method(rb_cModule, "class_variables",
+ rb_mod_class_variables, 0); /* in variable.c */
+ rb_define_private_method(rb_cModule, "remove_class_variable",
+ rb_mod_remove_cvar, 1); /* in variable.c */
+ rb_define_private_method(rb_cModule, "class_variable_get", rb_mod_cvar_get, 1);
+ rb_define_private_method(rb_cModule, "class_variable_set", rb_mod_cvar_set, 2);
+
+ rb_define_method(rb_cClass, "allocate", rb_obj_alloc, 0);
+ rb_define_method(rb_cClass, "new", rb_class_new_instance, -1);
+ rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
+ rb_define_method(rb_cClass, "initialize_copy", rb_class_init_copy, 1); /* in class.c */
+ rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
+ rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
+ rb_undef_method(rb_cClass, "extend_object");
+ rb_undef_method(rb_cClass, "append_features");
+
+ rb_cData = rb_define_class("Data", rb_cObject);
+ rb_undef_alloc_func(rb_cData);
+
+ rb_global_variable(&ruby_top_self);
+ ruby_top_self = rb_obj_alloc(rb_cObject);
+ rb_define_singleton_method(ruby_top_self, "to_s", main_to_s, 0);
+
+ rb_cTrueClass = rb_define_class("TrueClass", rb_cObject);
+ rb_define_method(rb_cTrueClass, "to_s", true_to_s, 0);
+ rb_define_method(rb_cTrueClass, "&", true_and, 1);
+ rb_define_method(rb_cTrueClass, "|", true_or, 1);
+ rb_define_method(rb_cTrueClass, "^", true_xor, 1);
+ rb_undef_alloc_func(rb_cTrueClass);
+ rb_undef_method(CLASS_OF(rb_cTrueClass), "new");
+ rb_define_global_const("TRUE", Qtrue);
+
+ rb_cFalseClass = rb_define_class("FalseClass", rb_cObject);
+ rb_define_method(rb_cFalseClass, "to_s", false_to_s, 0);
+ rb_define_method(rb_cFalseClass, "&", false_and, 1);
+ rb_define_method(rb_cFalseClass, "|", false_or, 1);
+ rb_define_method(rb_cFalseClass, "^", false_xor, 1);
+ rb_undef_alloc_func(rb_cFalseClass);
+ rb_undef_method(CLASS_OF(rb_cFalseClass), "new");
+ rb_define_global_const("FALSE", Qfalse);
+
+ id_eq = rb_intern("==");
+ id_eql = rb_intern("eql?");
+ id_inspect = rb_intern("inspect");
+ id_init_copy = rb_intern("initialize_copy");
+}
+/**********************************************************************
+
+ pack.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-19 10:01:35 +0100 (Thu, 19 Feb 2009) $
+ created at: Thu Feb 10 15:17:05 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include <sys/types.h>
+#include <ctype.h>
+
+#define SIZE16 2
+#define SIZE32 4
+
+#if SIZEOF_SHORT != 2 || SIZEOF_LONG != 4
+# define NATINT_PACK
+#endif
+
+#ifdef NATINT_PACK
+# define OFF16B(p) ((char*)(p) + (natint?0:(sizeof(short) - SIZE16)))
+# define OFF32B(p) ((char*)(p) + (natint?0:(sizeof(long) - SIZE32)))
+# define NATINT_LEN(type,len) (natint?sizeof(type):(len))
+# ifdef WORDS_BIGENDIAN
+# define OFF16(p) OFF16B(p)
+# define OFF32(p) OFF32B(p)
+# endif
+# define NATINT_HTOVS(x) (natint?htovs(x):htov16(x))
+# define NATINT_HTOVL(x) (natint?htovl(x):htov32(x))
+# define NATINT_HTONS(x) (natint?htons(x):hton16(x))
+# define NATINT_HTONL(x) (natint?htonl(x):hton32(x))
+#else
+# define NATINT_LEN(type,len) sizeof(type)
+# define NATINT_HTOVS(x) htovs(x)
+# define NATINT_HTOVL(x) htovl(x)
+# define NATINT_HTONS(x) htons(x)
+# define NATINT_HTONL(x) htonl(x)
+#endif
+
+#ifndef OFF16
+# define OFF16(p) (char*)(p)
+# define OFF32(p) (char*)(p)
+#endif
+#ifndef OFF16B
+# define OFF16B(p) (char*)(p)
+# define OFF32B(p) (char*)(p)
+#endif
+
+#define define_swapx(x, xtype) \
+static xtype \
+TOKEN_PASTE(swap,x)(z) \
+ xtype z; \
+{ \
+ xtype r; \
+ xtype *zp; \
+ unsigned char *s, *t; \
+ int i; \
+ \
+ zp = xmalloc(sizeof(xtype)); \
+ *zp = z; \
+ s = (unsigned char*)zp; \
+ t = xmalloc(sizeof(xtype)); \
+ for (i=0; i<sizeof(xtype); i++) { \
+ t[sizeof(xtype)-i-1] = s[i]; \
+ } \
+ r = *(xtype *)t; \
+ free(t); \
+ free(zp); \
+ return r; \
+}
+
+#ifndef swap16
+#define swap16(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#endif
+#if SIZEOF_SHORT == 2
+#define swaps(x) swap16(x)
+#else
+#if SIZEOF_SHORT == 4
+#define swaps(x) ((((x)&0xFF)<<24) \
+ |(((x)>>24)&0xFF) \
+ |(((x)&0x0000FF00)<<8) \
+ |(((x)&0x00FF0000)>>8) )
+#else
+define_swapx(s,short)
+#endif
+#endif
+
+#ifndef swap32
+#define swap32(x) ((((x)&0xFF)<<24) \
+ |(((x)>>24)&0xFF) \
+ |(((x)&0x0000FF00)<<8) \
+ |(((x)&0x00FF0000)>>8) )
+#endif
+#if SIZEOF_LONG == 4
+#define swapl(x) swap32(x)
+#else
+#if SIZEOF_LONG == 8
+#define swapl(x) ((((x)&0x00000000000000FF)<<56) \
+ |(((x)&0xFF00000000000000)>>56) \
+ |(((x)&0x000000000000FF00)<<40) \
+ |(((x)&0x00FF000000000000)>>40) \
+ |(((x)&0x0000000000FF0000)<<24) \
+ |(((x)&0x0000FF0000000000)>>24) \
+ |(((x)&0x00000000FF000000)<<8) \
+ |(((x)&0x000000FF00000000)>>8))
+#else
+define_swapx(l,long)
+#endif
+#endif
+
+#if SIZEOF_FLOAT == 4
+#if SIZEOF_LONG == 4 /* SIZEOF_FLOAT == 4 == SIZEOF_LONG */
+#define swapf(x) swapl(x)
+#define FLOAT_SWAPPER unsigned long
+#else
+#if SIZEOF_SHORT == 4 /* SIZEOF_FLOAT == 4 == SIZEOF_SHORT */
+#define swapf(x) swaps(x)
+#define FLOAT_SWAPPER unsigned short
+#else /* SIZEOF_FLOAT == 4 but undivide by known size of int */
+define_swapx(f,float)
+#endif /* #if SIZEOF_SHORT == 4 */
+#endif /* #if SIZEOF_LONG == 4 */
+#else /* SIZEOF_FLOAT != 4 */
+define_swapx(f,float)
+#endif /* #if SIZEOF_FLOAT == 4 */
+
+#if SIZEOF_DOUBLE == 8
+#if SIZEOF_LONG == 8 /* SIZEOF_DOUBLE == 8 == SIZEOF_LONG */
+#define swapd(x) swapl(x)
+#define DOUBLE_SWAPPER unsigned long
+#else
+#if SIZEOF_LONG == 4 /* SIZEOF_DOUBLE == 8 && 4 == SIZEOF_LONG */
+static double
+swapd(d)
+ const double d;
+{
+ double dtmp = d;
+ unsigned long utmp[2];
+ unsigned long utmp0;
+
+ utmp[0] = 0; utmp[1] = 0;
+ memcpy(utmp,&dtmp,sizeof(double));
+ utmp0 = utmp[0];
+ utmp[0] = swapl(utmp[1]);
+ utmp[1] = swapl(utmp0);
+ memcpy(&dtmp,utmp,sizeof(double));
+ return dtmp;
+}
+#else
+#if SIZEOF_SHORT == 4 /* SIZEOF_DOUBLE == 8 && 4 == SIZEOF_SHORT */
+static double
+swapd(d)
+ const double d;
+{
+ double dtmp = d;
+ unsigned short utmp[2];
+ unsigned short utmp0;
+
+ utmp[0] = 0; utmp[1] = 0;
+ memcpy(utmp,&dtmp,sizeof(double));
+ utmp0 = utmp[0];
+ utmp[0] = swaps(utmp[1]);
+ utmp[1] = swaps(utmp0);
+ memcpy(&dtmp,utmp,sizeof(double));
+ return dtmp;
+}
+#else /* SIZEOF_DOUBLE == 8 but undivied by known size of int */
+define_swapx(d, double)
+#endif /* #if SIZEOF_SHORT == 4 */
+#endif /* #if SIZEOF_LONG == 4 */
+#endif /* #if SIZEOF_LONG == 8 */
+#else /* SIZEOF_DOUBLE != 8 */
+define_swapx(d, double)
+#endif /* #if SIZEOF_DOUBLE == 8 */
+
+#undef define_swapx
+
+#ifdef DYNAMIC_ENDIAN
+#ifdef ntohs
+#undef ntohs
+#undef ntohl
+#undef htons
+#undef htonl
+#endif
+static int
+endian()
+{
+ static int init = 0;
+ static int endian_value;
+ char *p;
+
+ if (init) return endian_value;
+ init = 1;
+ p = (char*)&init;
+ return endian_value = p[0]?0:1;
+}
+
+#define ntohs(x) (endian()?(x):swaps(x))
+#define ntohl(x) (endian()?(x):swapl(x))
+#define ntohf(x) (endian()?(x):swapf(x))
+#define ntohd(x) (endian()?(x):swapd(x))
+#define htons(x) (endian()?(x):swaps(x))
+#define htonl(x) (endian()?(x):swapl(x))
+#define htonf(x) (endian()?(x):swapf(x))
+#define htond(x) (endian()?(x):swapd(x))
+#define htovs(x) (endian()?swaps(x):(x))
+#define htovl(x) (endian()?swapl(x):(x))
+#define htovf(x) (endian()?swapf(x):(x))
+#define htovd(x) (endian()?swapd(x):(x))
+#define vtohs(x) (endian()?swaps(x):(x))
+#define vtohl(x) (endian()?swapl(x):(x))
+#define vtohf(x) (endian()?swapf(x):(x))
+#define vtohd(x) (endian()?swapd(x):(x))
+# ifdef NATINT_PACK
+#define htov16(x) (endian()?swap16(x):(x))
+#define htov32(x) (endian()?swap32(x):(x))
+#define hton16(x) (endian()?(x):swap16(x))
+#define hton32(x) (endian()?(x):swap32(x))
+# endif
+#else
+#ifdef WORDS_BIGENDIAN
+#ifndef ntohs
+#define ntohs(x) (x)
+#define ntohl(x) (x)
+#define htons(x) (x)
+#define htonl(x) (x)
+#endif
+#define ntohf(x) (x)
+#define ntohd(x) (x)
+#define htonf(x) (x)
+#define htond(x) (x)
+#define htovs(x) swaps(x)
+#define htovl(x) swapl(x)
+#define htovf(x) swapf(x)
+#define htovd(x) swapd(x)
+#define vtohs(x) swaps(x)
+#define vtohl(x) swapl(x)
+#define vtohf(x) swapf(x)
+#define vtohd(x) swapd(x)
+# ifdef NATINT_PACK
+#define htov16(x) swap16(x)
+#define htov32(x) swap32(x)
+#define hton16(x) (x)
+#define hton32(x) (x)
+# endif
+#else /* LITTLE ENDIAN */
+#ifdef ntohs
+#undef ntohs
+#undef ntohl
+#undef htons
+#undef htonl
+#endif
+#define ntohs(x) swaps(x)
+#define ntohl(x) swapl(x)
+#define htons(x) swaps(x)
+#define htonl(x) swapl(x)
+#define ntohf(x) swapf(x)
+#define ntohd(x) swapd(x)
+#define htonf(x) swapf(x)
+#define htond(x) swapd(x)
+#define htovs(x) (x)
+#define htovl(x) (x)
+#define htovf(x) (x)
+#define htovd(x) (x)
+#define vtohs(x) (x)
+#define vtohl(x) (x)
+#define vtohf(x) (x)
+#define vtohd(x) (x)
+# ifdef NATINT_PACK
+#define htov16(x) (x)
+#define htov32(x) (x)
+#define hton16(x) swap16(x)
+#define hton32(x) swap32(x)
+# endif
+#endif
+#endif
+
+#ifdef FLOAT_SWAPPER
+#define FLOAT_CONVWITH(y) FLOAT_SWAPPER y;
+#define HTONF(x,y) (memcpy(&y,&x,sizeof(float)), \
+ y = htonf((FLOAT_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(float)), \
+ x)
+#define HTOVF(x,y) (memcpy(&y,&x,sizeof(float)), \
+ y = htovf((FLOAT_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(float)), \
+ x)
+#define NTOHF(x,y) (memcpy(&y,&x,sizeof(float)), \
+ y = ntohf((FLOAT_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(float)), \
+ x)
+#define VTOHF(x,y) (memcpy(&y,&x,sizeof(float)), \
+ y = vtohf((FLOAT_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(float)), \
+ x)
+#else
+#define FLOAT_CONVWITH(y)
+#define HTONF(x,y) htonf(x)
+#define HTOVF(x,y) htovf(x)
+#define NTOHF(x,y) ntohf(x)
+#define VTOHF(x,y) vtohf(x)
+#endif
+
+#ifdef DOUBLE_SWAPPER
+#define DOUBLE_CONVWITH(y) DOUBLE_SWAPPER y;
+#define HTOND(x,y) (memcpy(&y,&x,sizeof(double)), \
+ y = htond((DOUBLE_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(double)), \
+ x)
+#define HTOVD(x,y) (memcpy(&y,&x,sizeof(double)), \
+ y = htovd((DOUBLE_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(double)), \
+ x)
+#define NTOHD(x,y) (memcpy(&y,&x,sizeof(double)), \
+ y = ntohd((DOUBLE_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(double)), \
+ x)
+#define VTOHD(x,y) (memcpy(&y,&x,sizeof(double)), \
+ y = vtohd((DOUBLE_SWAPPER)y), \
+ memcpy(&x,&y,sizeof(double)), \
+ x)
+#else
+#define DOUBLE_CONVWITH(y)
+#define HTOND(x,y) htond(x)
+#define HTOVD(x,y) htovd(x)
+#define NTOHD(x,y) ntohd(x)
+#define VTOHD(x,y) vtohd(x)
+#endif
+
+unsigned long rb_big2ulong_pack _((VALUE x));
+
+static unsigned long
+num2i32(x)
+ VALUE x;
+{
+ x = rb_to_int(x); /* is nil OK? (should not) */
+
+ if (FIXNUM_P(x)) return FIX2LONG(x);
+ if (TYPE(x) == T_BIGNUM) {
+ return rb_big2ulong_pack(x);
+ }
+ rb_raise(rb_eTypeError, "can't convert %s to `integer'", rb_obj_classname(x));
+ return 0; /* not reached */
+}
+
+#if SIZEOF_LONG == SIZE32
+# define EXTEND32(x)
+#else
+/* invariant in modulo 1<<31 */
+# define EXTEND32(x) do { if (!natint) {(x) = (((1L<<31)-1-(x))^~(~0L<<31));}} while(0)
+#endif
+#if SIZEOF_SHORT == SIZE16
+# define EXTEND16(x)
+#else
+# define EXTEND16(x) do { if (!natint) {(x) = (short)(((1<<15)-1-(x))^~(~0<<15));}} while(0)
+#endif
+
+#ifdef HAVE_LONG_LONG
+# define QUAD_SIZE sizeof(LONG_LONG)
+#else
+# define QUAD_SIZE 8
+#endif
+static const char toofew[] = "too few arguments";
+
+static void encodes _((VALUE,char*,long,int));
+static void qpencode _((VALUE,VALUE,long));
+
+static int uv_to_utf8 _((char*,unsigned long));
+static unsigned long utf8_to_uv _((char*,long*));
+
+/*
+ * call-seq:
+ * arr.pack ( aTemplateString ) -> aBinaryString
+ *
+ * Packs the contents of <i>arr</i> into a binary sequence according to
+ * the directives in <i>aTemplateString</i> (see the table below)
+ * Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
+ * which gives the width of the resulting field. The remaining
+ * directives also may take a count, indicating the number of array
+ * elements to convert. If the count is an asterisk
+ * (``<code>*</code>''), all remaining array elements will be
+ * converted. Any of the directives ``<code>sSiIlL</code>'' may be
+ * followed by an underscore (``<code>_</code>'') to use the underlying
+ * platform's native size for the specified type; otherwise, they use a
+ * platform-independent size. Spaces are ignored in the template
+ * string. See also <code>String#unpack</code>.
+ *
+ * a = [ "a", "b", "c" ]
+ * n = [ 65, 66, 67 ]
+ * a.pack("A3A3A3") #=> "a b c "
+ * a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
+ * n.pack("ccc") #=> "ABC"
+ *
+ * Directives for +pack+.
+ *
+ * Directive Meaning
+ * ---------------------------------------------------------------
+ * @ | Moves to absolute position
+ * A | ASCII string (space padded, count is width)
+ * a | ASCII string (null padded, count is width)
+ * B | Bit string (descending bit order)
+ * b | Bit string (ascending bit order)
+ * C | Unsigned char
+ * c | Char
+ * D, d | Double-precision float, native format
+ * E | Double-precision float, little-endian byte order
+ * e | Single-precision float, little-endian byte order
+ * F, f | Single-precision float, native format
+ * G | Double-precision float, network (big-endian) byte order
+ * g | Single-precision float, network (big-endian) byte order
+ * H | Hex string (high nibble first)
+ * h | Hex string (low nibble first)
+ * I | Unsigned integer
+ * i | Integer
+ * L | Unsigned long
+ * l | Long
+ * M | Quoted printable, MIME encoding (see RFC2045)
+ * m | Base64 encoded string
+ * N | Long, network (big-endian) byte order
+ * n | Short, network (big-endian) byte-order
+ * P | Pointer to a structure (fixed-length string)
+ * p | Pointer to a null-terminated string
+ * Q, q | 64-bit number
+ * S | Unsigned short
+ * s | Short
+ * U | UTF-8
+ * u | UU-encoded string
+ * V | Long, little-endian byte order
+ * v | Short, little-endian byte order
+ * w | BER-compressed integer\fnm
+ * X | Back up a byte
+ * x | Null byte
+ * Z | Same as ``a'', except that null is added with *
+ */
+
+static VALUE
+pack_pack(ary, fmt)
+ VALUE ary, fmt;
+{
+ static char *nul10 = "\0\0\0\0\0\0\0\0\0\0";
+ static char *spc10 = " ";
+ char *p, *pend;
+ VALUE res, from, associates = 0;
+ char type;
+ long items, len, idx, plen;
+ char *ptr;
+#ifdef NATINT_PACK
+ int natint; /* native integer */
+#endif
+
+ StringValue(fmt);
+ p = RSTRING(fmt)->ptr;
+ pend = p + RSTRING(fmt)->len;
+ res = rb_str_buf_new(0);
+
+ items = RARRAY(ary)->len;
+ idx = 0;
+
+#define TOO_FEW (rb_raise(rb_eArgError, toofew), 0)
+#define THISFROM (items > 0 ? RARRAY(ary)->ptr[idx] : TOO_FEW)
+#define NEXTFROM (items-- > 0 ? RARRAY(ary)->ptr[idx++] : TOO_FEW)
+
+ while (p < pend) {
+ if (RSTRING(fmt)->ptr + RSTRING(fmt)->len != pend) {
+ rb_raise(rb_eRuntimeError, "format string modified");
+ }
+ type = *p++; /* get data type */
+#ifdef NATINT_PACK
+ natint = 0;
+#endif
+
+ if (ISSPACE(type)) continue;
+ if (type == '#') {
+ while ((p < pend) && (*p != '\n')) {
+ p++;
+ }
+ continue;
+ }
+ if (*p == '_' || *p == '!') {
+ const char *natstr = "sSiIlL";
+
+ if (strchr(natstr, type)) {
+#ifdef NATINT_PACK
+ natint = 1;
+#endif
+ p++;
+ }
+ else {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
+ }
+ }
+ if (*p == '*') { /* set data length */
+ len = strchr("@Xxu", type) ? 0
+ : strchr("PMm", type) ? 1
+ : items;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ len = strtoul(p, (char**)&p, 10);
+ }
+ else {
+ len = 1;
+ }
+
+ switch (type) {
+ case 'A': case 'a': case 'Z':
+ case 'B': case 'b':
+ case 'H': case 'h':
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ ptr = "";
+ plen = 0;
+ }
+ else {
+ StringValue(from);
+ ptr = RSTRING(from)->ptr;
+ plen = RSTRING(from)->len;
+ OBJ_INFECT(res, from);
+ }
+
+ if (p[-1] == '*')
+ len = plen;
+
+ switch (type) {
+ case 'a': /* arbitrary binary string (null padded) */
+ case 'A': /* ASCII string (space padded) */
+ case 'Z': /* null terminated ASCII string */
+ if (plen >= len) {
+ rb_str_buf_cat(res, ptr, len);
+ if (p[-1] == '*' && type == 'Z')
+ rb_str_buf_cat(res, nul10, 1);
+ }
+ else {
+ rb_str_buf_cat(res, ptr, plen);
+ len -= plen;
+ while (len >= 10) {
+ rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
+ len -= 10;
+ }
+ rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
+ }
+ break;
+
+ case 'b': /* bit string (ascending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (*ptr & 1)
+ byte |= 128;
+ if (i & 7)
+ byte >>= 1;
+ else {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte >>= 7 - (len & 7);
+ c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'B': /* bit string (descending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ byte |= *ptr & 1;
+ if (i & 7)
+ byte <<= 1;
+ else {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte <<= 7 - (len & 7);
+ c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'h': /* hex string (low nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= (((*ptr & 15) + 9) & 15) << 4;
+ else
+ byte |= (*ptr & 15) << 4;
+ if (i & 1)
+ byte >>= 4;
+ else {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'H': /* hex string (high nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= ((*ptr & 15) + 9) & 15;
+ else
+ byte |= *ptr & 15;
+ if (i & 1)
+ byte <<= 4;
+ else {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = byte & 0xff;
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+ }
+ break;
+
+ case 'c': /* signed char */
+ case 'C': /* unsigned char */
+ while (len-- > 0) {
+ char c;
+
+ from = NEXTFROM;
+ c = num2i32(from);
+ rb_str_buf_cat(res, &c, sizeof(char));
+ }
+ break;
+
+ case 's': /* signed short */
+ case 'S': /* unsigned short */
+ while (len-- > 0) {
+ short s;
+
+ from = NEXTFROM;
+ s = num2i32(from);
+ rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
+ }
+ break;
+
+ case 'i': /* signed int */
+ case 'I': /* unsigned int */
+ while (len-- > 0) {
+ long i;
+
+ from = NEXTFROM;
+ i = num2i32(from);
+ rb_str_buf_cat(res, OFF32(&i), NATINT_LEN(int,4));
+ }
+ break;
+
+ case 'l': /* signed long */
+ case 'L': /* unsigned long */
+ while (len-- > 0) {
+ long l;
+
+ from = NEXTFROM;
+ l = num2i32(from);
+ rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
+ }
+ break;
+
+ case 'q': /* signed quad (64bit) int */
+ case 'Q': /* unsigned quad (64bit) int */
+ while (len-- > 0) {
+ char tmp[QUAD_SIZE];
+
+ from = NEXTFROM;
+ rb_quad_pack(tmp, from);
+ rb_str_buf_cat(res, (char*)&tmp, QUAD_SIZE);
+ }
+ break;
+
+ case 'n': /* unsigned short (network byte-order) */
+ while (len-- > 0) {
+ unsigned short s;
+
+ from = NEXTFROM;
+ s = num2i32(from);
+ s = NATINT_HTONS(s);
+ rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
+ }
+ break;
+
+ case 'N': /* unsigned long (network byte-order) */
+ while (len-- > 0) {
+ unsigned long l;
+
+ from = NEXTFROM;
+ l = num2i32(from);
+ l = NATINT_HTONL(l);
+ rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
+ }
+ break;
+
+ case 'v': /* unsigned short (VAX byte-order) */
+ while (len-- > 0) {
+ unsigned short s;
+
+ from = NEXTFROM;
+ s = num2i32(from);
+ s = NATINT_HTOVS(s);
+ rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
+ }
+ break;
+
+ case 'V': /* unsigned long (VAX byte-order) */
+ while (len-- > 0) {
+ unsigned long l;
+
+ from = NEXTFROM;
+ l = num2i32(from);
+ l = NATINT_HTOVL(l);
+ rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
+ }
+ break;
+
+ case 'f': /* single precision float in native format */
+ case 'F': /* ditto */
+ while (len-- > 0) {
+ float f;
+
+ from = NEXTFROM;
+ f = RFLOAT(rb_Float(from))->value;
+ rb_str_buf_cat(res, (char*)&f, sizeof(float));
+ }
+ break;
+
+ case 'e': /* single precision float in VAX byte-order */
+ while (len-- > 0) {
+ float f;
+ FLOAT_CONVWITH(ftmp);
+
+ from = NEXTFROM;
+ f = RFLOAT(rb_Float(from))->value;
+ f = HTOVF(f,ftmp);
+ rb_str_buf_cat(res, (char*)&f, sizeof(float));
+ }
+ break;
+
+ case 'E': /* double precision float in VAX byte-order */
+ while (len-- > 0) {
+ double d;
+ DOUBLE_CONVWITH(dtmp);
+
+ from = NEXTFROM;
+ d = RFLOAT(rb_Float(from))->value;
+ d = HTOVD(d,dtmp);
+ rb_str_buf_cat(res, (char*)&d, sizeof(double));
+ }
+ break;
+
+ case 'd': /* double precision float in native format */
+ case 'D': /* ditto */
+ while (len-- > 0) {
+ double d;
+
+ from = NEXTFROM;
+ d = RFLOAT(rb_Float(from))->value;
+ rb_str_buf_cat(res, (char*)&d, sizeof(double));
+ }
+ break;
+
+ case 'g': /* single precision float in network byte-order */
+ while (len-- > 0) {
+ float f;
+ FLOAT_CONVWITH(ftmp);
+
+ from = NEXTFROM;
+ f = RFLOAT(rb_Float(from))->value;
+ f = HTONF(f,ftmp);
+ rb_str_buf_cat(res, (char*)&f, sizeof(float));
+ }
+ break;
+
+ case 'G': /* double precision float in network byte-order */
+ while (len-- > 0) {
+ double d;
+ DOUBLE_CONVWITH(dtmp);
+
+ from = NEXTFROM;
+ d = RFLOAT(rb_Float(from))->value;
+ d = HTOND(d,dtmp);
+ rb_str_buf_cat(res, (char*)&d, sizeof(double));
+ }
+ break;
+
+ case 'x': /* null byte */
+ grow:
+ while (len >= 10) {
+ rb_str_buf_cat(res, nul10, 10);
+ len -= 10;
+ }
+ rb_str_buf_cat(res, nul10, len);
+ break;
+
+ case 'X': /* back up byte */
+ shrink:
+ plen = RSTRING(res)->len;
+ if (plen < len)
+ rb_raise(rb_eArgError, "X outside of string");
+ RSTRING(res)->len = plen - len;
+ RSTRING(res)->ptr[plen - len] = '\0';
+ break;
+
+ case '@': /* null fill to absolute position */
+ len -= RSTRING(res)->len;
+ if (len > 0) goto grow;
+ len = -len;
+ if (len > 0) goto shrink;
+ break;
+
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'U': /* Unicode character */
+ while (len-- > 0) {
+ long l;
+ char buf[8];
+ int le;
+
+ from = NEXTFROM;
+ from = rb_to_int(from);
+ l = NUM2INT(from);
+ if (l < 0) {
+ rb_raise(rb_eRangeError, "pack(U): value out of range");
+ }
+ le = uv_to_utf8(buf, l);
+ rb_str_buf_cat(res, (char*)buf, le);
+ }
+ break;
+
+ case 'u': /* uuencoded string */
+ case 'm': /* base64 encoded string */
+ from = NEXTFROM;
+ StringValue(from);
+ ptr = RSTRING(from)->ptr;
+ plen = RSTRING(from)->len;
+
+ if (len <= 2)
+ len = 45;
+ else
+ len = len / 3 * 3;
+ while (plen > 0) {
+ long todo;
+
+ if (plen > len)
+ todo = len;
+ else
+ todo = plen;
+ encodes(res, ptr, todo, type);
+ plen -= todo;
+ ptr += todo;
+ }
+ break;
+
+ case 'M': /* quoted-printable encoded string */
+ from = rb_obj_as_string(NEXTFROM);
+ if (len <= 1)
+ len = 72;
+ qpencode(res, from, len);
+ break;
+
+ case 'P': /* pointer to packed byte string */
+ from = THISFROM;
+ if (!NIL_P(from)) {
+ StringValue(from);
+ if (RSTRING(from)->len < len) {
+ rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
+ RSTRING(from)->len, len);
+ }
+ }
+ len = 1;
+ /* FALL THROUGH */
+ case 'p': /* pointer to string */
+ while (len-- > 0) {
+ char *t;
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ t = 0;
+ }
+ else {
+ t = StringValuePtr(from);
+ }
+ if (!associates) {
+ associates = rb_ary_new();
+ }
+ rb_ary_push(associates, from);
+ rb_obj_taint(from);
+ rb_str_buf_cat(res, (char*)&t, sizeof(char*));
+ }
+ break;
+
+ case 'w': /* BER compressed integer */
+ while (len-- > 0) {
+ unsigned long ul;
+ VALUE buf = rb_str_new(0, 0);
+ char c, *bufs, *bufe;
+
+ from = NEXTFROM;
+ if (TYPE(from) == T_BIGNUM) {
+ VALUE big128 = rb_uint2big(128);
+ while (TYPE(from) == T_BIGNUM) {
+ from = rb_big_divmod(from, big128);
+ c = NUM2INT(RARRAY(from)->ptr[1]) | 0x80; /* mod */
+ rb_str_buf_cat(buf, &c, sizeof(char));
+ from = RARRAY(from)->ptr[0]; /* div */
+ }
+ }
+
+ {
+ long l = NUM2LONG(from);
+ if (l < 0) {
+ rb_raise(rb_eArgError, "can't compress negative numbers");
+ }
+ ul = l;
+ }
+
+ while (ul) {
+ c = ((ul & 0x7f) | 0x80);
+ rb_str_buf_cat(buf, &c, sizeof(char));
+ ul >>= 7;
+ }
+
+ if (RSTRING(buf)->len) {
+ bufs = RSTRING(buf)->ptr;
+ bufe = bufs + RSTRING(buf)->len - 1;
+ *bufs &= 0x7f; /* clear continue bit */
+ while (bufs < bufe) { /* reverse */
+ c = *bufs;
+ *bufs++ = *bufe;
+ *bufe-- = c;
+ }
+ rb_str_buf_cat(res, RSTRING(buf)->ptr, RSTRING(buf)->len);
+ }
+ else {
+ c = 0;
+ rb_str_buf_cat(res, &c, sizeof(char));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (associates) {
+ rb_str_associate(res, associates);
+ }
+ OBJ_INFECT(res, fmt);
+ return res;
+}
+
+static char uu_table[] =
+"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
+static char b64_table[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+encodes(str, s, len, type)
+ VALUE str;
+ char *s;
+ long len;
+ int type;
+{
+ char *buff = ALLOCA_N(char, len * 4 / 3 + 6);
+ long i = 0;
+ char *trans = type == 'u' ? uu_table : b64_table;
+ int padding;
+
+ if (type == 'u') {
+ buff[i++] = len + ' ';
+ padding = '`';
+ }
+ else {
+ padding = '=';
+ }
+ while (len >= 3) {
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = trans[077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))];
+ buff[i++] = trans[077 & s[2]];
+ s += 3;
+ len -= 3;
+ }
+ if (len == 2) {
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = trans[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
+ buff[i++] = padding;
+ }
+ else if (len == 1) {
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
+ buff[i++] = padding;
+ buff[i++] = padding;
+ }
+ buff[i++] = '\n';
+ rb_str_buf_cat(str, buff, i);
+}
+
+static char hex_table[] = "0123456789ABCDEF";
+
+static void
+qpencode(str, from, len)
+ VALUE str, from;
+ long len;
+{
+ char buff[1024];
+ long i = 0, n = 0, prev = EOF;
+ unsigned char *s = (unsigned char*)RSTRING(from)->ptr;
+ unsigned char *send = s + RSTRING(from)->len;
+
+ while (s < send) {
+ if ((*s > 126) ||
+ (*s < 32 && *s != '\n' && *s != '\t') ||
+ (*s == '=')) {
+ buff[i++] = '=';
+ buff[i++] = hex_table[*s >> 4];
+ buff[i++] = hex_table[*s & 0x0f];
+ n += 3;
+ prev = EOF;
+ }
+ else if (*s == '\n') {
+ if (prev == ' ' || prev == '\t') {
+ buff[i++] = '=';
+ buff[i++] = *s;
+ }
+ buff[i++] = *s;
+ n = 0;
+ prev = *s;
+ }
+ else {
+ buff[i++] = *s;
+ n++;
+ prev = *s;
+ }
+ if (n > len) {
+ buff[i++] = '=';
+ buff[i++] = '\n';
+ n = 0;
+ prev = '\n';
+ }
+ if (i > 1024 - 5) {
+ rb_str_buf_cat(str, buff, i);
+ i = 0;
+ }
+ s++;
+ }
+ if (n > 0) {
+ buff[i++] = '=';
+ buff[i++] = '\n';
+ }
+ if (i > 0) {
+ rb_str_buf_cat(str, buff, i);
+ }
+}
+
+static inline int
+hex2num(c)
+ char c;
+{
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return c - '0';
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ return c - 'a' + 10;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ return c - 'A' + 10;
+ default:
+ return -1;
+ }
+}
+
+#define PACK_LENGTH_ADJUST_SIZE(sz) do { \
+ tmp = 0; \
+ if (len > (send-s)/sz) { \
+ if (!star) { \
+ tmp = len-(send-s)/sz; \
+ } \
+ len = (send-s)/sz; \
+ } \
+} while (0)
+
+#ifdef NATINT_PACK
+#define PACK_LENGTH_ADJUST(type,sz) do { \
+ int t__len = NATINT_LEN(type,(sz)); \
+ PACK_LENGTH_ADJUST_SIZE(t__len); \
+} while (0)
+#else
+#define PACK_LENGTH_ADJUST(type,sz) \
+ PACK_LENGTH_ADJUST_SIZE(sizeof(type))
+#endif
+
+#define PACK_ITEM_ADJUST() while (tmp--) rb_ary_push(ary, Qnil)
+
+static VALUE
+infected_str_new(ptr, len, str)
+ const char *ptr;
+ long len;
+ VALUE str;
+{
+ VALUE s = rb_str_new(ptr, len);
+
+ OBJ_INFECT(s, str);
+ return s;
+}
+
+/*
+ * call-seq:
+ * str.unpack(format) => anArray
+ *
+ * Decodes <i>str</i> (which may contain binary data) according to the
+ * format string, returning an array of each value extracted. The
+ * format string consists of a sequence of single-character directives,
+ * summarized in the table at the end of this entry.
+ * Each directive may be followed
+ * by a number, indicating the number of times to repeat with this
+ * directive. An asterisk (``<code>*</code>'') will use up all
+ * remaining elements. The directives <code>sSiIlL</code> may each be
+ * followed by an underscore (``<code>_</code>'') to use the underlying
+ * platform's native size for the specified type; otherwise, it uses a
+ * platform-independent consistent size. Spaces are ignored in the
+ * format string. See also <code>Array#pack</code>.
+ *
+ * "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
+ * "abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
+ * "abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
+ * "aa".unpack('b8B8') #=> ["10000110", "01100001"]
+ * "aaa".unpack('h2H2c') #=> ["16", "61", 97]
+ * "\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
+ * "now=20is".unpack('M*') #=> ["now is"]
+ * "whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
+ *
+ * This table summarizes the various formats and the Ruby classes
+ * returned by each.
+ *
+ * Format | Returns | Function
+ * -------+---------+-----------------------------------------
+ * A | String | with trailing nulls and spaces removed
+ * -------+---------+-----------------------------------------
+ * a | String | string
+ * -------+---------+-----------------------------------------
+ * B | String | extract bits from each character (msb first)
+ * -------+---------+-----------------------------------------
+ * b | String | extract bits from each character (lsb first)
+ * -------+---------+-----------------------------------------
+ * C | Fixnum | extract a character as an unsigned integer
+ * -------+---------+-----------------------------------------
+ * c | Fixnum | extract a character as an integer
+ * -------+---------+-----------------------------------------
+ * d,D | Float | treat sizeof(double) characters as
+ * | | a native double
+ * -------+---------+-----------------------------------------
+ * E | Float | treat sizeof(double) characters as
+ * | | a double in little-endian byte order
+ * -------+---------+-----------------------------------------
+ * e | Float | treat sizeof(float) characters as
+ * | | a float in little-endian byte order
+ * -------+---------+-----------------------------------------
+ * f,F | Float | treat sizeof(float) characters as
+ * | | a native float
+ * -------+---------+-----------------------------------------
+ * G | Float | treat sizeof(double) characters as
+ * | | a double in network byte order
+ * -------+---------+-----------------------------------------
+ * g | Float | treat sizeof(float) characters as a
+ * | | float in network byte order
+ * -------+---------+-----------------------------------------
+ * H | String | extract hex nibbles from each character
+ * | | (most significant first)
+ * -------+---------+-----------------------------------------
+ * h | String | extract hex nibbles from each character
+ * | | (least significant first)
+ * -------+---------+-----------------------------------------
+ * I | Integer | treat sizeof(int) (modified by _)
+ * | | successive characters as an unsigned
+ * | | native integer
+ * -------+---------+-----------------------------------------
+ * i | Integer | treat sizeof(int) (modified by _)
+ * | | successive characters as a signed
+ * | | native integer
+ * -------+---------+-----------------------------------------
+ * L | Integer | treat four (modified by _) successive
+ * | | characters as an unsigned native
+ * | | long integer
+ * -------+---------+-----------------------------------------
+ * l | Integer | treat four (modified by _) successive
+ * | | characters as a signed native
+ * | | long integer
+ * -------+---------+-----------------------------------------
+ * M | String | quoted-printable
+ * -------+---------+-----------------------------------------
+ * m | String | base64-encoded
+ * -------+---------+-----------------------------------------
+ * N | Integer | treat four characters as an unsigned
+ * | | long in network byte order
+ * -------+---------+-----------------------------------------
+ * n | Fixnum | treat two characters as an unsigned
+ * | | short in network byte order
+ * -------+---------+-----------------------------------------
+ * P | String | treat sizeof(char *) characters as a
+ * | | pointer, and return \emph{len} characters
+ * | | from the referenced location
+ * -------+---------+-----------------------------------------
+ * p | String | treat sizeof(char *) characters as a
+ * | | pointer to a null-terminated string
+ * -------+---------+-----------------------------------------
+ * Q | Integer | treat 8 characters as an unsigned
+ * | | quad word (64 bits)
+ * -------+---------+-----------------------------------------
+ * q | Integer | treat 8 characters as a signed
+ * | | quad word (64 bits)
+ * -------+---------+-----------------------------------------
+ * S | Fixnum | treat two (different if _ used)
+ * | | successive characters as an unsigned
+ * | | short in native byte order
+ * -------+---------+-----------------------------------------
+ * s | Fixnum | Treat two (different if _ used)
+ * | | successive characters as a signed short
+ * | | in native byte order
+ * -------+---------+-----------------------------------------
+ * U | Integer | UTF-8 characters as unsigned integers
+ * -------+---------+-----------------------------------------
+ * u | String | UU-encoded
+ * -------+---------+-----------------------------------------
+ * V | Fixnum | treat four characters as an unsigned
+ * | | long in little-endian byte order
+ * -------+---------+-----------------------------------------
+ * v | Fixnum | treat two characters as an unsigned
+ * | | short in little-endian byte order
+ * -------+---------+-----------------------------------------
+ * w | Integer | BER-compressed integer (see Array.pack)
+ * -------+---------+-----------------------------------------
+ * X | --- | skip backward one character
+ * -------+---------+-----------------------------------------
+ * x | --- | skip forward one character
+ * -------+---------+-----------------------------------------
+ * Z | String | with trailing nulls removed
+ * | | upto first null with *
+ * -------+---------+-----------------------------------------
+ * @ | --- | skip to the offset given by the
+ * | | length argument
+ * -------+---------+-----------------------------------------
+ */
+
+static VALUE
+pack_unpack(str, fmt)
+ VALUE str, fmt;
+{
+ static char *hexdigits = "0123456789abcdef0123456789ABCDEFx";
+ char *s, *send;
+ char *p, *pend;
+ VALUE ary;
+ char type;
+ long len;
+ int tmp, star;
+#ifdef NATINT_PACK
+ int natint; /* native integer */
+#endif
+
+ StringValue(str);
+ StringValue(fmt);
+ s = RSTRING(str)->ptr;
+ send = s + RSTRING(str)->len;
+ p = RSTRING(fmt)->ptr;
+ pend = p + RSTRING(fmt)->len;
+
+ ary = rb_ary_new();
+ while (p < pend) {
+ type = *p++;
+#ifdef NATINT_PACK
+ natint = 0;
+#endif
+
+ if (ISSPACE(type)) continue;
+ if (type == '#') {
+ while ((p < pend) && (*p != '\n')) {
+ p++;
+ }
+ continue;
+ }
+ star = 0;
+ if (*p == '_' || *p == '!') {
+ char *natstr = "sSiIlL";
+
+ if (strchr(natstr, type)) {
+#ifdef NATINT_PACK
+ natint = 1;
+#endif
+ p++;
+ }
+ else {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
+ }
+ }
+ if (p >= pend)
+ len = 1;
+ else if (*p == '*') {
+ star = 1;
+ len = send - s;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ len = strtoul(p, (char**)&p, 10);
+ }
+ else {
+ len = (type != '@');
+ }
+
+ switch (type) {
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'A':
+ if (len > send - s) len = send - s;
+ {
+ long end = len;
+ char *t = s + len - 1;
+
+ while (t >= s) {
+ if (*t != ' ' && *t != '\0') break;
+ t--; len--;
+ }
+ rb_ary_push(ary, infected_str_new(s, len, str));
+ s += end;
+ }
+ break;
+
+ case 'Z':
+ {
+ char *t = s;
+
+ if (len > send-s) len = send-s;
+ while (t < s+len && *t) t++;
+ rb_ary_push(ary, infected_str_new(s, t-s, str));
+ if (t < send) t++;
+ s = star ? t : s+len;
+ }
+ break;
+
+ case 'a':
+ if (len > send - s) len = send - s;
+ rb_ary_push(ary, infected_str_new(s, len, str));
+ s += len;
+ break;
+
+ case 'b':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ rb_ary_push(ary, bitstr = rb_str_new(0, len));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 7) bits >>= 1;
+ else bits = *s++;
+ *t++ = (bits & 1) ? '1' : '0';
+ }
+ }
+ break;
+
+ case 'B':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ rb_ary_push(ary, bitstr = rb_str_new(0, len));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 7) bits <<= 1;
+ else bits = *s++;
+ *t++ = (bits & 128) ? '1' : '0';
+ }
+ }
+ break;
+
+ case 'h':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ rb_ary_push(ary, bitstr = rb_str_new(0, len));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits >>= 4;
+ else
+ bits = *s++;
+ *t++ = hexdigits[bits & 15];
+ }
+ }
+ break;
+
+ case 'H':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ rb_ary_push(ary, bitstr = rb_str_new(0, len));
+ t = RSTRING(bitstr)->ptr;
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits <<= 4;
+ else
+ bits = *s++;
+ *t++ = hexdigits[(bits >> 4) & 15];
+ }
+ }
+ break;
+
+ case 'c':
+ PACK_LENGTH_ADJUST(char,sizeof(char));
+ while (len-- > 0) {
+ int c = *s++;
+ if (c > (char)127) c-=256;
+ rb_ary_push(ary, INT2FIX(c));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'C':
+ PACK_LENGTH_ADJUST(unsigned char,sizeof(unsigned char));
+ while (len-- > 0) {
+ unsigned char c = *s++;
+ rb_ary_push(ary, INT2FIX(c));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 's':
+ PACK_LENGTH_ADJUST(short,2);
+ while (len-- > 0) {
+ short tmp = 0;
+ memcpy(OFF16(&tmp), s, NATINT_LEN(short,2));
+ EXTEND16(tmp);
+ s += NATINT_LEN(short,2);
+ rb_ary_push(ary, INT2FIX(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'S':
+ PACK_LENGTH_ADJUST(unsigned short,2);
+ while (len-- > 0) {
+ unsigned short tmp = 0;
+ memcpy(OFF16(&tmp), s, NATINT_LEN(unsigned short,2));
+ s += NATINT_LEN(unsigned short,2);
+ rb_ary_push(ary, INT2FIX(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'i':
+ PACK_LENGTH_ADJUST(int,sizeof(int));
+ while (len-- > 0) {
+ int tmp;
+ memcpy(&tmp, s, sizeof(int));
+ s += sizeof(int);
+ rb_ary_push(ary, INT2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'I':
+ PACK_LENGTH_ADJUST(unsigned int,sizeof(unsigned int));
+ while (len-- > 0) {
+ unsigned int tmp;
+ memcpy(&tmp, s, sizeof(unsigned int));
+ s += sizeof(unsigned int);
+ rb_ary_push(ary, UINT2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'l':
+ PACK_LENGTH_ADJUST(long,4);
+ while (len-- > 0) {
+ long tmp = 0;
+ memcpy(OFF32(&tmp), s, NATINT_LEN(long,4));
+ EXTEND32(tmp);
+ s += NATINT_LEN(long,4);
+ rb_ary_push(ary, LONG2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'L':
+ PACK_LENGTH_ADJUST(unsigned long,4);
+ while (len-- > 0) {
+ unsigned long tmp = 0;
+ memcpy(OFF32(&tmp), s, NATINT_LEN(unsigned long,4));
+ s += NATINT_LEN(unsigned long,4);
+ rb_ary_push(ary, ULONG2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'q':
+ PACK_LENGTH_ADJUST_SIZE(QUAD_SIZE);
+ while (len-- > 0) {
+ char *tmp = (char*)s;
+ s += QUAD_SIZE;
+ rb_ary_push(ary, rb_quad_unpack(tmp, 1));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'Q':
+ PACK_LENGTH_ADJUST_SIZE(QUAD_SIZE);
+ while (len-- > 0) {
+ char *tmp = (char*)s;
+ s += QUAD_SIZE;
+ rb_ary_push(ary, rb_quad_unpack(tmp, 0));
+ }
+ break;
+
+ case 'n':
+ PACK_LENGTH_ADJUST(unsigned short,2);
+ while (len-- > 0) {
+ unsigned short tmp = 0;
+ memcpy(OFF16B(&tmp), s, NATINT_LEN(unsigned short,2));
+ s += NATINT_LEN(unsigned short,2);
+ rb_ary_push(ary, UINT2NUM(ntohs(tmp)));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'N':
+ PACK_LENGTH_ADJUST(unsigned long,4);
+ while (len-- > 0) {
+ unsigned long tmp = 0;
+ memcpy(OFF32B(&tmp), s, NATINT_LEN(unsigned long,4));
+ s += NATINT_LEN(unsigned long,4);
+ rb_ary_push(ary, ULONG2NUM(ntohl(tmp)));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'v':
+ PACK_LENGTH_ADJUST(unsigned short,2);
+ while (len-- > 0) {
+ unsigned short tmp = 0;
+ memcpy(OFF16(&tmp), s, NATINT_LEN(unsigned short,2));
+ s += NATINT_LEN(unsigned short,2);
+ rb_ary_push(ary, UINT2NUM(vtohs(tmp)));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'V':
+ PACK_LENGTH_ADJUST(unsigned long,4);
+ while (len-- > 0) {
+ unsigned long tmp = 0;
+ memcpy(OFF32(&tmp), s, NATINT_LEN(long,4));
+ s += NATINT_LEN(long,4);
+ rb_ary_push(ary, ULONG2NUM(vtohl(tmp)));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'f':
+ case 'F':
+ PACK_LENGTH_ADJUST(float,sizeof(float));
+ while (len-- > 0) {
+ float tmp;
+ memcpy(&tmp, s, sizeof(float));
+ s += sizeof(float);
+ rb_ary_push(ary, rb_float_new((double)tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'e':
+ PACK_LENGTH_ADJUST(float,sizeof(float));
+ while (len-- > 0) {
+ float tmp;
+ FLOAT_CONVWITH(ftmp);
+
+ memcpy(&tmp, s, sizeof(float));
+ s += sizeof(float);
+ tmp = VTOHF(tmp,ftmp);
+ rb_ary_push(ary, rb_float_new((double)tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'E':
+ PACK_LENGTH_ADJUST(double,sizeof(double));
+ while (len-- > 0) {
+ double tmp;
+ DOUBLE_CONVWITH(dtmp);
+
+ memcpy(&tmp, s, sizeof(double));
+ s += sizeof(double);
+ tmp = VTOHD(tmp,dtmp);
+ rb_ary_push(ary, rb_float_new(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'D':
+ case 'd':
+ PACK_LENGTH_ADJUST(double,sizeof(double));
+ while (len-- > 0) {
+ double tmp;
+ memcpy(&tmp, s, sizeof(double));
+ s += sizeof(double);
+ rb_ary_push(ary, rb_float_new(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'g':
+ PACK_LENGTH_ADJUST(float,sizeof(float));
+ while (len-- > 0) {
+ float tmp;
+ FLOAT_CONVWITH(ftmp;)
+
+ memcpy(&tmp, s, sizeof(float));
+ s += sizeof(float);
+ tmp = NTOHF(tmp,ftmp);
+ rb_ary_push(ary, rb_float_new((double)tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'G':
+ PACK_LENGTH_ADJUST(double,sizeof(double));
+ while (len-- > 0) {
+ double tmp;
+ DOUBLE_CONVWITH(dtmp);
+
+ memcpy(&tmp, s, sizeof(double));
+ s += sizeof(double);
+ tmp = NTOHD(tmp,dtmp);
+ rb_ary_push(ary, rb_float_new(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'U':
+ if (len > send - s) len = send - s;
+ while (len > 0 && s < send) {
+ long alen = send - s;
+ unsigned long l;
+
+ l = utf8_to_uv(s, &alen);
+ s += alen; len--;
+ rb_ary_push(ary, ULONG2NUM(l));
+ }
+ break;
+
+ case 'u':
+ {
+ VALUE buf = infected_str_new(0, (send - s)*3/4, str);
+ char *ptr = RSTRING(buf)->ptr;
+ long total = 0;
+
+ while (s < send && *s > ' ' && *s < 'a') {
+ long a,b,c,d;
+ char hunk[4];
+
+ hunk[3] = '\0';
+ len = (*s++ - ' ') & 077;
+ total += len;
+ if (total > RSTRING(buf)->len) {
+ len -= total - RSTRING(buf)->len;
+ total = RSTRING(buf)->len;
+ }
+
+ while (len > 0) {
+ long mlen = len > 3 ? 3 : len;
+
+ if (s < send && *s >= ' ')
+ a = (*s++ - ' ') & 077;
+ else
+ a = 0;
+ if (s < send && *s >= ' ')
+ b = (*s++ - ' ') & 077;
+ else
+ b = 0;
+ if (s < send && *s >= ' ')
+ c = (*s++ - ' ') & 077;
+ else
+ c = 0;
+ if (s < send && *s >= ' ')
+ d = (*s++ - ' ') & 077;
+ else
+ d = 0;
+ hunk[0] = a << 2 | b >> 4;
+ hunk[1] = b << 4 | c >> 2;
+ hunk[2] = c << 6 | d;
+ memcpy(ptr, hunk, mlen);
+ ptr += mlen;
+ len -= mlen;
+ }
+ if (*s == '\r') s++;
+ if (*s == '\n') s++;
+ else if (s < send && (s+1 == send || s[1] == '\n'))
+ s += 2; /* possible checksum byte */
+ }
+
+ RSTRING(buf)->ptr[total] = '\0';
+ RSTRING(buf)->len = total;
+ rb_ary_push(ary, buf);
+ }
+ break;
+
+ case 'm':
+ {
+ VALUE buf = infected_str_new(0, (send - s)*3/4, str);
+ char *ptr = RSTRING(buf)->ptr;
+ int a = -1,b = -1,c = 0,d;
+ static int first = 1;
+ static int b64_xtable[256];
+
+ if (first) {
+ int i;
+ first = 0;
+
+ for (i = 0; i < 256; i++) {
+ b64_xtable[i] = -1;
+ }
+ for (i = 0; i < 64; i++) {
+ b64_xtable[(int)b64_table[i]] = i;
+ }
+ }
+ while (s < send) {
+ a = b = c = d = -1;
+ while((a = b64_xtable[(int)(*(unsigned char*)s)]) == -1 && s < send) { s++; }
+ if( s >= send ) break;
+ s++;
+ while((b = b64_xtable[(int)(*(unsigned char*)s)]) == -1 && s < send) { s++; }
+ if( s >= send ) break;
+ s++;
+ while((c = b64_xtable[(int)(*(unsigned char*)s)]) == -1 && s < send) { if( *s == '=' ) break; s++; }
+ if( *s == '=' || s >= send ) break;
+ s++;
+ while((d = b64_xtable[(int)(*(unsigned char*)s)]) == -1 && s < send) { if( *s == '=' ) break; s++; }
+ if( *s == '=' || s >= send ) break;
+ s++;
+ *ptr++ = a << 2 | b >> 4;
+ *ptr++ = b << 4 | c >> 2;
+ *ptr++ = c << 6 | d;
+ }
+ if (a != -1 && b != -1) {
+ if (c == -1 && *s == '=')
+ *ptr++ = a << 2 | b >> 4;
+ else if (c != -1 && *s == '=') {
+ *ptr++ = a << 2 | b >> 4;
+ *ptr++ = b << 4 | c >> 2;
+ }
+ }
+ *ptr = '\0';
+ RSTRING(buf)->len = ptr - RSTRING(buf)->ptr;
+ rb_ary_push(ary, buf);
+ }
+ break;
+
+ case 'M':
+ {
+ VALUE buf = infected_str_new(0, send - s, str);
+ char *ptr = RSTRING(buf)->ptr;
+ int c1, c2;
+
+ while (s < send) {
+ if (*s == '=') {
+ if (++s == send) break;
+ if (s+1 < send && *s == '\r' && *(s+1) == '\n')
+ s++;
+ if (*s != '\n') {
+ if ((c1 = hex2num(*s)) == -1) break;
+ if (++s == send) break;
+ if ((c2 = hex2num(*s)) == -1) break;
+ *ptr++ = c1 << 4 | c2;
+ }
+ }
+ else {
+ *ptr++ = *s;
+ }
+ s++;
+ }
+ *ptr = '\0';
+ RSTRING(buf)->len = ptr - RSTRING(buf)->ptr;
+ rb_ary_push(ary, buf);
+ }
+ break;
+
+ case '@':
+ if (len > RSTRING(str)->len)
+ rb_raise(rb_eArgError, "@ outside of string");
+ s = RSTRING(str)->ptr + len;
+ break;
+
+ case 'X':
+ if (len > s - RSTRING(str)->ptr)
+ rb_raise(rb_eArgError, "X outside of string");
+ s -= len;
+ break;
+
+ case 'x':
+ if (len > send - s)
+ rb_raise(rb_eArgError, "x outside of string");
+ s += len;
+ break;
+
+ case 'P':
+ if (sizeof(char *) <= send - s) {
+ char *t;
+ VALUE tmp;
+
+ memcpy(&t, s, sizeof(char *));
+ s += sizeof(char *);
+
+ if (t) {
+ VALUE a, *p, *pend;
+
+ if (!(a = rb_str_associated(str))) {
+ rb_raise(rb_eArgError, "no associated pointer");
+ }
+ p = RARRAY(a)->ptr;
+ pend = p + RARRAY(a)->len;
+ while (p < pend) {
+ if (TYPE(*p) == T_STRING && RSTRING(*p)->ptr == t) {
+ if (len < RSTRING(*p)->len) {
+ tmp = rb_tainted_str_new(t, len);
+ rb_str_associate(tmp, a);
+ }
+ else {
+ tmp = *p;
+ }
+ break;
+ }
+ p++;
+ }
+ if (p == pend) {
+ rb_raise(rb_eArgError, "non associated pointer");
+ }
+ }
+ else {
+ tmp = Qnil;
+ }
+ rb_ary_push(ary, tmp);
+ }
+ break;
+
+ case 'p':
+ if (len > (send - s) / sizeof(char *))
+ len = (send - s) / sizeof(char *);
+ while (len-- > 0) {
+ if (send - s < sizeof(char *))
+ break;
+ else {
+ VALUE tmp;
+ char *t;
+
+ memcpy(&t, s, sizeof(char *));
+ s += sizeof(char *);
+
+ if (t) {
+ VALUE a, *p, *pend;
+
+ if (!(a = rb_str_associated(str))) {
+ rb_raise(rb_eArgError, "no associated pointer");
+ }
+ p = RARRAY(a)->ptr;
+ pend = p + RARRAY(a)->len;
+ while (p < pend) {
+ if (TYPE(*p) == T_STRING && RSTRING(*p)->ptr == t) {
+ tmp = *p;
+ break;
+ }
+ p++;
+ }
+ if (p == pend) {
+ rb_raise(rb_eArgError, "non associated pointer");
+ }
+ }
+ else {
+ tmp = Qnil;
+ }
+ rb_ary_push(ary, tmp);
+ }
+ }
+ break;
+
+ case 'w':
+ {
+ unsigned long ul = 0;
+ unsigned long ulmask = 0xfeUL << ((sizeof(unsigned long) - 1) * 8);
+
+ while (len > 0 && s < send) {
+ ul <<= 7;
+ ul |= (*s & 0x7f);
+ if (!(*s++ & 0x80)) {
+ rb_ary_push(ary, ULONG2NUM(ul));
+ len--;
+ ul = 0;
+ }
+ else if (ul & ulmask) {
+ VALUE big = rb_uint2big(ul);
+ VALUE big128 = rb_uint2big(128);
+ while (s < send) {
+ big = rb_big_mul(big, big128);
+ big = rb_big_plus(big, rb_uint2big(*s & 0x7f));
+ if (!(*s++ & 0x80)) {
+ rb_ary_push(ary, big);
+ len--;
+ ul = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return ary;
+}
+
+#define BYTEWIDTH 8
+
+static int
+uv_to_utf8(buf, uv)
+ char *buf;
+ unsigned long uv;
+{
+ if (uv <= 0x7f) {
+ buf[0] = (char)uv;
+ return 1;
+ }
+ if (uv <= 0x7ff) {
+ buf[0] = ((uv>>6)&0xff)|0xc0;
+ buf[1] = (uv&0x3f)|0x80;
+ return 2;
+ }
+ if (uv <= 0xffff) {
+ buf[0] = ((uv>>12)&0xff)|0xe0;
+ buf[1] = ((uv>>6)&0x3f)|0x80;
+ buf[2] = (uv&0x3f)|0x80;
+ return 3;
+ }
+ if (uv <= 0x1fffff) {
+ buf[0] = ((uv>>18)&0xff)|0xf0;
+ buf[1] = ((uv>>12)&0x3f)|0x80;
+ buf[2] = ((uv>>6)&0x3f)|0x80;
+ buf[3] = (uv&0x3f)|0x80;
+ return 4;
+ }
+ if (uv <= 0x3ffffff) {
+ buf[0] = ((uv>>24)&0xff)|0xf8;
+ buf[1] = ((uv>>18)&0x3f)|0x80;
+ buf[2] = ((uv>>12)&0x3f)|0x80;
+ buf[3] = ((uv>>6)&0x3f)|0x80;
+ buf[4] = (uv&0x3f)|0x80;
+ return 5;
+ }
+ if (uv <= 0x7fffffff) {
+ buf[0] = ((uv>>30)&0xff)|0xfc;
+ buf[1] = ((uv>>24)&0x3f)|0x80;
+ buf[2] = ((uv>>18)&0x3f)|0x80;
+ buf[3] = ((uv>>12)&0x3f)|0x80;
+ buf[4] = ((uv>>6)&0x3f)|0x80;
+ buf[5] = (uv&0x3f)|0x80;
+ return 6;
+ }
+ rb_raise(rb_eRangeError, "pack(U): value out of range");
+}
+
+static const unsigned long utf8_limits[] = {
+ 0x0, /* 1 */
+ 0x80, /* 2 */
+ 0x800, /* 3 */
+ 0x10000, /* 4 */
+ 0x200000, /* 5 */
+ 0x4000000, /* 6 */
+ 0x80000000, /* 7 */
+};
+
+static unsigned long
+utf8_to_uv(p, lenp)
+ char *p;
+ long *lenp;
+{
+ int c = *p++ & 0xff;
+ unsigned long uv = c;
+ long n;
+
+ if (!(uv & 0x80)) {
+ *lenp = 1;
+ return uv;
+ }
+ if (!(uv & 0x40)) {
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
+ }
+
+ if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
+ else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
+ else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
+ else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
+ else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
+ else {
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
+ }
+ if (n > *lenp) {
+ rb_raise(rb_eArgError, "malformed UTF-8 character (expected %d bytes, given %d bytes)",
+ n, *lenp);
+ }
+ *lenp = n--;
+ if (n != 0) {
+ while (n--) {
+ c = *p++ & 0xff;
+ if ((c & 0xc0) != 0x80) {
+ *lenp -= n + 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
+ }
+ else {
+ c &= 0x3f;
+ uv = uv << 6 | c;
+ }
+ }
+ }
+ n = *lenp - 1;
+ if (uv < utf8_limits[n]) {
+ rb_raise(rb_eArgError, "redundant UTF-8 sequence");
+ }
+ return uv;
+}
+
+void
+Init_pack()
+{
+ rb_define_method(rb_cArray, "pack", pack_pack, 1);
+ rb_define_method(rb_cString, "unpack", pack_unpack, 1);
+}
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kCLASS = 258,
+ kMODULE = 259,
+ kDEF = 260,
+ kUNDEF = 261,
+ kBEGIN = 262,
+ kRESCUE = 263,
+ kENSURE = 264,
+ kEND = 265,
+ kIF = 266,
+ kUNLESS = 267,
+ kTHEN = 268,
+ kELSIF = 269,
+ kELSE = 270,
+ kCASE = 271,
+ kWHEN = 272,
+ kWHILE = 273,
+ kUNTIL = 274,
+ kFOR = 275,
+ kBREAK = 276,
+ kNEXT = 277,
+ kREDO = 278,
+ kRETRY = 279,
+ kIN = 280,
+ kDO = 281,
+ kDO_COND = 282,
+ kDO_BLOCK = 283,
+ kRETURN = 284,
+ kYIELD = 285,
+ kSUPER = 286,
+ kSELF = 287,
+ kNIL = 288,
+ kTRUE = 289,
+ kFALSE = 290,
+ kAND = 291,
+ kOR = 292,
+ kNOT = 293,
+ kIF_MOD = 294,
+ kUNLESS_MOD = 295,
+ kWHILE_MOD = 296,
+ kUNTIL_MOD = 297,
+ kRESCUE_MOD = 298,
+ kALIAS = 299,
+ kDEFINED = 300,
+ klBEGIN = 301,
+ klEND = 302,
+ k__LINE__ = 303,
+ k__FILE__ = 304,
+ tIDENTIFIER = 305,
+ tFID = 306,
+ tGVAR = 307,
+ tIVAR = 308,
+ tCONSTANT = 309,
+ tCVAR = 310,
+ tINTEGER = 311,
+ tFLOAT = 312,
+ tSTRING_CONTENT = 313,
+ tNTH_REF = 314,
+ tBACK_REF = 315,
+ tREGEXP_END = 316,
+ tUPLUS = 317,
+ tUMINUS = 318,
+ tPOW = 319,
+ tCMP = 320,
+ tEQ = 321,
+ tEQQ = 322,
+ tNEQ = 323,
+ tGEQ = 324,
+ tLEQ = 325,
+ tANDOP = 326,
+ tOROP = 327,
+ tMATCH = 328,
+ tNMATCH = 329,
+ tDOT2 = 330,
+ tDOT3 = 331,
+ tAREF = 332,
+ tASET = 333,
+ tLSHFT = 334,
+ tRSHFT = 335,
+ tCOLON2 = 336,
+ tCOLON3 = 337,
+ tOP_ASGN = 338,
+ tASSOC = 339,
+ tLPAREN = 340,
+ tLPAREN_ARG = 341,
+ tRPAREN = 342,
+ tLBRACK = 343,
+ tLBRACE = 344,
+ tLBRACE_ARG = 345,
+ tSTAR = 346,
+ tAMPER = 347,
+ tSYMBEG = 348,
+ tSTRING_BEG = 349,
+ tXSTRING_BEG = 350,
+ tREGEXP_BEG = 351,
+ tWORDS_BEG = 352,
+ tQWORDS_BEG = 353,
+ tSTRING_DBEG = 354,
+ tSTRING_DVAR = 355,
+ tSTRING_END = 356,
+ tLOWEST = 357,
+ tUMINUS_NUM = 358,
+ tLAST_TOKEN = 359
+ };
+#endif
+/* Tokens. */
+#define kCLASS 258
+#define kMODULE 259
+#define kDEF 260
+#define kUNDEF 261
+#define kBEGIN 262
+#define kRESCUE 263
+#define kENSURE 264
+#define kEND 265
+#define kIF 266
+#define kUNLESS 267
+#define kTHEN 268
+#define kELSIF 269
+#define kELSE 270
+#define kCASE 271
+#define kWHEN 272
+#define kWHILE 273
+#define kUNTIL 274
+#define kFOR 275
+#define kBREAK 276
+#define kNEXT 277
+#define kREDO 278
+#define kRETRY 279
+#define kIN 280
+#define kDO 281
+#define kDO_COND 282
+#define kDO_BLOCK 283
+#define kRETURN 284
+#define kYIELD 285
+#define kSUPER 286
+#define kSELF 287
+#define kNIL 288
+#define kTRUE 289
+#define kFALSE 290
+#define kAND 291
+#define kOR 292
+#define kNOT 293
+#define kIF_MOD 294
+#define kUNLESS_MOD 295
+#define kWHILE_MOD 296
+#define kUNTIL_MOD 297
+#define kRESCUE_MOD 298
+#define kALIAS 299
+#define kDEFINED 300
+#define klBEGIN 301
+#define klEND 302
+#define k__LINE__ 303
+#define k__FILE__ 304
+#define tIDENTIFIER 305
+#define tFID 306
+#define tGVAR 307
+#define tIVAR 308
+#define tCONSTANT 309
+#define tCVAR 310
+#define tINTEGER 311
+#define tFLOAT 312
+#define tSTRING_CONTENT 313
+#define tNTH_REF 314
+#define tBACK_REF 315
+#define tREGEXP_END 316
+#define tUPLUS 317
+#define tUMINUS 318
+#define tPOW 319
+#define tCMP 320
+#define tEQ 321
+#define tEQQ 322
+#define tNEQ 323
+#define tGEQ 324
+#define tLEQ 325
+#define tANDOP 326
+#define tOROP 327
+#define tMATCH 328
+#define tNMATCH 329
+#define tDOT2 330
+#define tDOT3 331
+#define tAREF 332
+#define tASET 333
+#define tLSHFT 334
+#define tRSHFT 335
+#define tCOLON2 336
+#define tCOLON3 337
+#define tOP_ASGN 338
+#define tASSOC 339
+#define tLPAREN 340
+#define tLPAREN_ARG 341
+#define tRPAREN 342
+#define tLBRACK 343
+#define tLBRACE 344
+#define tLBRACE_ARG 345
+#define tSTAR 346
+#define tAMPER 347
+#define tSYMBEG 348
+#define tSTRING_BEG 349
+#define tXSTRING_BEG 350
+#define tREGEXP_BEG 351
+#define tWORDS_BEG 352
+#define tQWORDS_BEG 353
+#define tSTRING_DBEG 354
+#define tSTRING_DVAR 355
+#define tSTRING_END 356
+#define tLOWEST 357
+#define tUMINUS_NUM 358
+#define tLAST_TOKEN 359
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 13 "parse.y"
+
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+#ifndef YYSTACK_USE_ALLOCA
+#define YYSTACK_USE_ALLOCA 0
+#endif
+
+#include "ruby.h"
+#include "env.h"
+#include "intern.h"
+#include "node.h"
+#include "st.h"
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define YYMALLOC rb_parser_malloc
+#define YYREALLOC rb_parser_realloc
+#define YYCALLOC rb_parser_calloc
+#define YYFREE rb_parser_free
+#define malloc YYMALLOC
+#define realloc YYREALLOC
+#define calloc YYCALLOC
+#define free YYFREE
+static void *rb_parser_malloc _((size_t));
+static void *rb_parser_realloc _((void *, size_t));
+static void *rb_parser_calloc _((size_t, size_t));
+static void rb_parser_free _((void *));
+
+#define yyparse ruby_yyparse
+#define yylex ruby_yylex
+#define yyerror ruby_yyerror
+#define yylval ruby_yylval
+#define yychar ruby_yychar
+#define yydebug ruby_yydebug
+
+#define ID_SCOPE_SHIFT 3
+#define ID_SCOPE_MASK 0x07
+#define ID_LOCAL 0x01
+#define ID_INSTANCE 0x02
+#define ID_GLOBAL 0x03
+#define ID_ATTRSET 0x04
+#define ID_CONST 0x05
+#define ID_CLASS 0x06
+#define ID_JUNK 0x07
+#define ID_INTERNAL ID_JUNK
+
+#define is_notop_id(id) ((id)>tLAST_TOKEN)
+#define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
+#define is_global_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
+#define is_instance_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE)
+#define is_attrset_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
+#define is_const_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
+#define is_class_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CLASS)
+#define is_junk_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_JUNK)
+
+#define is_asgn_or_id(id) ((is_notop_id(id)) && \
+ (((id)&ID_SCOPE_MASK) == ID_GLOBAL || \
+ ((id)&ID_SCOPE_MASK) == ID_INSTANCE || \
+ ((id)&ID_SCOPE_MASK) == ID_CLASS))
+
+NODE *ruby_eval_tree_begin = 0;
+NODE *ruby_eval_tree = 0;
+
+char *ruby_sourcefile; /* current source file */
+int ruby_sourceline; /* current line no. */
+
+static int yylex();
+static int yyerror();
+
+static enum lex_state {
+ EXPR_BEG, /* ignore newline, +/- is a sign. */
+ EXPR_END, /* newline significant, +/- is an operator. */
+ EXPR_ARG, /* newline significant, +/- is an operator. */
+ EXPR_CMDARG, /* newline significant, +/- is an operator. */
+ EXPR_ENDARG, /* newline significant, +/- is an operator. */
+ EXPR_MID, /* newline significant, +/- is an operator. */
+ EXPR_FNAME, /* ignore newline, no reserved words. */
+ EXPR_DOT, /* right after `.' or `::', no reserved words. */
+ EXPR_CLASS, /* immediate after `class', no here document. */
+} lex_state;
+static NODE *lex_strterm;
+
+#ifdef HAVE_LONG_LONG
+typedef unsigned LONG_LONG stack_type;
+#else
+typedef unsigned long stack_type;
+#endif
+
+#define BITSTACK_PUSH(stack, n) (stack = (stack<<1)|((n)&1))
+#define BITSTACK_POP(stack) (stack >>= 1)
+#define BITSTACK_LEXPOP(stack) (stack = (stack >> 1) | (stack & 1))
+#define BITSTACK_SET_P(stack) (stack&1)
+
+static stack_type cond_stack = 0;
+#define COND_PUSH(n) BITSTACK_PUSH(cond_stack, n)
+#define COND_POP() BITSTACK_POP(cond_stack)
+#define COND_LEXPOP() BITSTACK_LEXPOP(cond_stack)
+#define COND_P() BITSTACK_SET_P(cond_stack)
+
+static stack_type cmdarg_stack = 0;
+#define CMDARG_PUSH(n) BITSTACK_PUSH(cmdarg_stack, n)
+#define CMDARG_POP() BITSTACK_POP(cmdarg_stack)
+#define CMDARG_LEXPOP() BITSTACK_LEXPOP(cmdarg_stack)
+#define CMDARG_P() BITSTACK_SET_P(cmdarg_stack)
+
+static int class_nest = 0;
+static int in_single = 0;
+static int in_def = 0;
+static int compile_for_eval = 0;
+static ID cur_mid = 0;
+static int command_start = Qtrue;
+
+static NODE *deferred_nodes;
+
+static NODE *cond();
+static NODE *logop();
+static int cond_negative();
+
+static NODE *newline_node();
+static void fixpos();
+
+static int value_expr0();
+static void void_expr0();
+static void void_stmts();
+static NODE *remove_begin();
+#define value_expr(node) value_expr0((node) = remove_begin(node))
+#define void_expr(node) void_expr0((node) = remove_begin(node))
+
+static NODE *block_append();
+static NODE *list_append();
+static NODE *list_concat();
+static NODE *arg_concat();
+static NODE *arg_prepend();
+static NODE *literal_concat();
+static NODE *new_evstr();
+static NODE *evstr2dstr();
+static NODE *call_op();
+static int in_defined = 0;
+
+static NODE *negate_lit();
+static NODE *ret_args();
+static NODE *arg_blk_pass();
+static NODE *new_call();
+static NODE *new_fcall();
+static NODE *new_super();
+static NODE *new_yield();
+
+static NODE *gettable();
+static NODE *assignable();
+static NODE *aryset();
+static NODE *attrset();
+static void rb_backref_error();
+static NODE *node_assign();
+
+static NODE *match_gen();
+static void local_push();
+static void local_pop();
+static int local_append();
+static int local_cnt();
+static int local_id();
+static ID *local_tbl();
+static ID internal_id();
+
+static struct RVarmap *dyna_push();
+static void dyna_pop();
+static int dyna_in_block();
+static NODE *dyna_init();
+
+static void top_local_init();
+static void top_local_setup();
+
+static void fixup_nodes();
+
+#define RE_OPTION_ONCE 0x80
+
+#define NODE_STRTERM NODE_ZARRAY /* nothing to gc */
+#define NODE_HEREDOC NODE_ARRAY /* 1, 3 to gc */
+#define SIGN_EXTEND(x,n) (((1<<(n)-1)^((x)&~(~0<<(n))))-(1<<(n)-1))
+#define nd_func u1.id
+#if SIZEOF_SHORT == 2
+#define nd_term(node) ((signed short)(node)->u2.id)
+#else
+#define nd_term(node) SIGN_EXTEND((node)->u2.id, CHAR_BIT*2)
+#endif
+#define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2)
+#define nd_nest u3.id
+
+/* Older versions of Yacc set YYMAXDEPTH to a very low value by default (150,
+ for instance). This is too low for Ruby to parse some files, such as
+ date/format.rb, therefore bump the value up to at least Bison's default. */
+#ifdef OLD_YACC
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+#endif
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 213 "parse.y"
+{
+ NODE *node;
+ ID id;
+ int num;
+ struct RVarmap *vars;
+}
+/* Line 193 of yacc.c. */
+#line 511 "parse.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 524 "parse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 9550
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 132
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 144
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 501
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 895
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 359
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 130, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 129, 117, 2, 2, 2, 115, 110, 2,
+ 128, 125, 113, 111, 126, 112, 124, 114, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 105, 131,
+ 107, 103, 106, 104, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 122, 2, 123, 109, 2, 127, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 120, 108, 121, 118, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 116, 119
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 12, 15, 17, 19, 23,
+ 26, 27, 32, 36, 40, 44, 47, 51, 55, 59,
+ 63, 67, 68, 74, 79, 83, 87, 91, 98, 104,
+ 110, 116, 120, 124, 128, 132, 134, 136, 140, 144,
+ 147, 150, 152, 154, 156, 158, 161, 164, 167, 169,
+ 174, 179, 180, 181, 188, 191, 195, 200, 206, 211,
+ 217, 220, 223, 225, 229, 231, 235, 237, 240, 244,
+ 247, 250, 252, 254, 258, 261, 265, 267, 272, 276,
+ 280, 284, 288, 291, 293, 295, 300, 304, 308, 312,
+ 316, 319, 321, 323, 325, 328, 330, 334, 336, 338,
+ 340, 342, 344, 346, 348, 350, 352, 354, 355, 360,
+ 362, 364, 366, 368, 370, 372, 374, 376, 378, 380,
+ 382, 384, 386, 388, 390, 392, 394, 396, 398, 400,
+ 402, 404, 406, 408, 410, 412, 414, 416, 418, 420,
+ 422, 424, 426, 428, 430, 432, 434, 436, 438, 440,
+ 442, 444, 446, 448, 450, 452, 454, 456, 458, 460,
+ 462, 464, 466, 468, 470, 472, 474, 476, 478, 480,
+ 482, 484, 486, 488, 490, 492, 496, 502, 506, 513,
+ 519, 525, 531, 537, 542, 546, 550, 554, 558, 562,
+ 566, 570, 574, 578, 583, 588, 591, 594, 598, 602,
+ 606, 610, 614, 618, 622, 626, 630, 634, 638, 642,
+ 646, 649, 652, 656, 660, 664, 668, 669, 674, 680,
+ 682, 684, 686, 689, 692, 698, 701, 705, 709, 714,
+ 719, 726, 728, 730, 732, 735, 741, 744, 750, 755,
+ 763, 767, 769, 774, 778, 784, 792, 795, 801, 806,
+ 813, 821, 831, 835, 837, 838, 841, 843, 844, 848,
+ 849, 854, 857, 860, 862, 864, 868, 872, 877, 880,
+ 882, 884, 886, 888, 890, 892, 894, 896, 898, 899,
+ 904, 905, 911, 915, 919, 922, 927, 931, 935, 937,
+ 942, 946, 948, 949, 956, 959, 961, 964, 971, 978,
+ 979, 980, 988, 989, 990, 998, 1004, 1009, 1015, 1016,
+ 1017, 1027, 1028, 1035, 1036, 1037, 1046, 1047, 1053, 1054,
+ 1061, 1062, 1063, 1073, 1075, 1077, 1079, 1081, 1083, 1085,
+ 1087, 1089, 1092, 1094, 1096, 1098, 1100, 1106, 1108, 1111,
+ 1113, 1115, 1117, 1120, 1122, 1126, 1127, 1128, 1135, 1138,
+ 1143, 1148, 1151, 1156, 1161, 1165, 1168, 1170, 1171, 1172,
+ 1179, 1180, 1181, 1188, 1194, 1196, 1201, 1204, 1206, 1208,
+ 1215, 1217, 1219, 1221, 1223, 1226, 1228, 1231, 1233, 1235,
+ 1237, 1239, 1241, 1243, 1246, 1250, 1254, 1258, 1262, 1266,
+ 1267, 1271, 1273, 1276, 1280, 1284, 1285, 1289, 1290, 1293,
+ 1294, 1297, 1299, 1300, 1304, 1305, 1310, 1312, 1314, 1316,
+ 1318, 1321, 1323, 1325, 1327, 1329, 1333, 1335, 1337, 1340,
+ 1343, 1345, 1347, 1349, 1351, 1353, 1355, 1357, 1359, 1361,
+ 1363, 1365, 1367, 1369, 1371, 1373, 1375, 1376, 1381, 1384,
+ 1389, 1392, 1399, 1404, 1409, 1412, 1417, 1420, 1423, 1425,
+ 1426, 1428, 1430, 1432, 1434, 1436, 1438, 1442, 1446, 1448,
+ 1452, 1454, 1456, 1459, 1461, 1463, 1465, 1468, 1471, 1473,
+ 1475, 1476, 1482, 1484, 1487, 1490, 1492, 1496, 1500, 1502,
+ 1504, 1506, 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522,
+ 1524, 1525, 1527, 1528, 1530, 1531, 1533, 1535, 1537, 1539,
+ 1541, 1544
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int16 yyrhs[] =
+{
+ 133, 0, -1, -1, 134, 136, -1, 136, 219, 203,
+ 222, -1, 137, 270, -1, 275, -1, 138, -1, 137,
+ 274, 138, -1, 1, 138, -1, -1, 44, 160, 139,
+ 160, -1, 44, 52, 52, -1, 44, 52, 60, -1,
+ 44, 52, 59, -1, 6, 161, -1, 138, 39, 142,
+ -1, 138, 40, 142, -1, 138, 41, 142, -1, 138,
+ 42, 142, -1, 138, 43, 138, -1, -1, 46, 140,
+ 120, 136, 121, -1, 47, 120, 136, 121, -1, 155,
+ 103, 143, -1, 149, 103, 143, -1, 246, 83, 143,
+ -1, 199, 122, 168, 123, 83, 143, -1, 199, 124,
+ 50, 83, 143, -1, 199, 124, 54, 83, 143, -1,
+ 199, 81, 50, 83, 143, -1, 247, 83, 143, -1,
+ 155, 103, 181, -1, 149, 103, 167, -1, 149, 103,
+ 181, -1, 141, -1, 143, -1, 141, 36, 141, -1,
+ 141, 37, 141, -1, 38, 141, -1, 117, 143, -1,
+ 165, -1, 141, -1, 148, -1, 144, -1, 29, 171,
+ -1, 21, 171, -1, 22, 171, -1, 209, -1, 209,
+ 124, 267, 173, -1, 209, 81, 267, 173, -1, -1,
+ -1, 90, 146, 205, 147, 136, 121, -1, 266, 173,
+ -1, 266, 173, 145, -1, 199, 124, 267, 173, -1,
+ 199, 124, 267, 173, 145, -1, 199, 81, 267, 173,
+ -1, 199, 81, 267, 173, 145, -1, 31, 173, -1,
+ 30, 173, -1, 151, -1, 85, 150, 125, -1, 151,
+ -1, 85, 150, 125, -1, 153, -1, 153, 152, -1,
+ 153, 91, 154, -1, 153, 91, -1, 91, 154, -1,
+ 91, -1, 154, -1, 85, 150, 125, -1, 152, 126,
+ -1, 153, 152, 126, -1, 244, -1, 199, 122, 168,
+ 123, -1, 199, 124, 50, -1, 199, 81, 50, -1,
+ 199, 124, 54, -1, 199, 81, 54, -1, 82, 54,
+ -1, 247, -1, 244, -1, 199, 122, 168, 123, -1,
+ 199, 124, 50, -1, 199, 81, 50, -1, 199, 124,
+ 54, -1, 199, 81, 54, -1, 82, 54, -1, 247,
+ -1, 50, -1, 54, -1, 82, 156, -1, 156, -1,
+ 199, 81, 156, -1, 50, -1, 54, -1, 51, -1,
+ 163, -1, 164, -1, 158, -1, 240, -1, 159, -1,
+ 242, -1, 160, -1, -1, 161, 126, 162, 160, -1,
+ 108, -1, 109, -1, 110, -1, 65, -1, 66, -1,
+ 67, -1, 73, -1, 106, -1, 69, -1, 107, -1,
+ 70, -1, 79, -1, 80, -1, 111, -1, 112, -1,
+ 113, -1, 91, -1, 114, -1, 115, -1, 64, -1,
+ 118, -1, 62, -1, 63, -1, 77, -1, 78, -1,
+ 127, -1, 48, -1, 49, -1, 46, -1, 47, -1,
+ 44, -1, 36, -1, 7, -1, 21, -1, 16, -1,
+ 3, -1, 5, -1, 45, -1, 26, -1, 15, -1,
+ 14, -1, 10, -1, 9, -1, 35, -1, 20, -1,
+ 25, -1, 4, -1, 22, -1, 33, -1, 38, -1,
+ 37, -1, 23, -1, 8, -1, 24, -1, 29, -1,
+ 32, -1, 31, -1, 13, -1, 34, -1, 6, -1,
+ 17, -1, 30, -1, 11, -1, 12, -1, 18, -1,
+ 19, -1, 155, 103, 165, -1, 155, 103, 165, 43,
+ 165, -1, 246, 83, 165, -1, 199, 122, 168, 123,
+ 83, 165, -1, 199, 124, 50, 83, 165, -1, 199,
+ 124, 54, 83, 165, -1, 199, 81, 50, 83, 165,
+ -1, 199, 81, 54, 83, 165, -1, 82, 54, 83,
+ 165, -1, 247, 83, 165, -1, 165, 75, 165, -1,
+ 165, 76, 165, -1, 165, 111, 165, -1, 165, 112,
+ 165, -1, 165, 113, 165, -1, 165, 114, 165, -1,
+ 165, 115, 165, -1, 165, 64, 165, -1, 116, 56,
+ 64, 165, -1, 116, 57, 64, 165, -1, 62, 165,
+ -1, 63, 165, -1, 165, 108, 165, -1, 165, 109,
+ 165, -1, 165, 110, 165, -1, 165, 65, 165, -1,
+ 165, 106, 165, -1, 165, 69, 165, -1, 165, 107,
+ 165, -1, 165, 70, 165, -1, 165, 66, 165, -1,
+ 165, 67, 165, -1, 165, 68, 165, -1, 165, 73,
+ 165, -1, 165, 74, 165, -1, 117, 165, -1, 118,
+ 165, -1, 165, 79, 165, -1, 165, 80, 165, -1,
+ 165, 71, 165, -1, 165, 72, 165, -1, -1, 45,
+ 271, 166, 165, -1, 165, 104, 165, 105, 165, -1,
+ 182, -1, 165, -1, 275, -1, 148, 271, -1, 180,
+ 272, -1, 180, 126, 91, 165, 271, -1, 264, 272,
+ -1, 91, 165, 271, -1, 128, 275, 125, -1, 128,
+ 171, 271, 125, -1, 128, 209, 271, 125, -1, 128,
+ 180, 126, 209, 271, 125, -1, 275, -1, 169, -1,
+ 148, -1, 180, 179, -1, 180, 126, 91, 167, 179,
+ -1, 264, 179, -1, 264, 126, 91, 167, 179, -1,
+ 180, 126, 264, 179, -1, 180, 126, 264, 126, 91,
+ 165, 179, -1, 91, 167, 179, -1, 178, -1, 167,
+ 126, 180, 179, -1, 167, 126, 178, -1, 167, 126,
+ 91, 167, 179, -1, 167, 126, 180, 126, 91, 167,
+ 179, -1, 264, 179, -1, 264, 126, 91, 167, 179,
+ -1, 167, 126, 264, 179, -1, 167, 126, 180, 126,
+ 264, 179, -1, 167, 126, 264, 126, 91, 167, 179,
+ -1, 167, 126, 180, 126, 264, 126, 91, 167, 179,
+ -1, 91, 167, 179, -1, 178, -1, -1, 174, 175,
+ -1, 171, -1, -1, 86, 176, 125, -1, -1, 86,
+ 172, 177, 125, -1, 92, 167, -1, 126, 178, -1,
+ 275, -1, 167, -1, 180, 126, 167, -1, 180, 126,
+ 167, -1, 180, 126, 91, 167, -1, 91, 167, -1,
+ 223, -1, 224, -1, 227, -1, 228, -1, 229, -1,
+ 232, -1, 245, -1, 247, -1, 51, -1, -1, 7,
+ 183, 135, 10, -1, -1, 86, 141, 184, 271, 125,
+ -1, 85, 136, 125, -1, 199, 81, 54, -1, 82,
+ 54, -1, 199, 122, 168, 123, -1, 88, 168, 123,
+ -1, 89, 263, 121, -1, 29, -1, 30, 128, 171,
+ 125, -1, 30, 128, 125, -1, 30, -1, -1, 45,
+ 271, 128, 185, 141, 125, -1, 266, 211, -1, 210,
+ -1, 210, 211, -1, 11, 142, 200, 136, 202, 10,
+ -1, 12, 142, 200, 136, 203, 10, -1, -1, -1,
+ 18, 186, 142, 201, 187, 136, 10, -1, -1, -1,
+ 19, 188, 142, 201, 189, 136, 10, -1, 16, 142,
+ 270, 216, 10, -1, 16, 270, 216, 10, -1, 16,
+ 270, 15, 136, 10, -1, -1, -1, 20, 204, 25,
+ 190, 142, 201, 191, 136, 10, -1, -1, 3, 157,
+ 248, 192, 135, 10, -1, -1, -1, 3, 79, 141,
+ 193, 273, 194, 135, 10, -1, -1, 4, 157, 195,
+ 135, 10, -1, -1, 5, 158, 196, 250, 135, 10,
+ -1, -1, -1, 5, 261, 269, 197, 158, 198, 250,
+ 135, 10, -1, 21, -1, 22, -1, 23, -1, 24,
+ -1, 182, -1, 273, -1, 105, -1, 13, -1, 273,
+ 13, -1, 273, -1, 105, -1, 27, -1, 203, -1,
+ 14, 142, 200, 136, 202, -1, 275, -1, 15, 136,
+ -1, 155, -1, 149, -1, 275, -1, 108, 108, -1,
+ 72, -1, 108, 204, 108, -1, -1, -1, 28, 207,
+ 205, 208, 136, 10, -1, 148, 206, -1, 209, 124,
+ 267, 170, -1, 209, 81, 267, 170, -1, 266, 169,
+ -1, 199, 124, 267, 170, -1, 199, 81, 267, 169,
+ -1, 199, 81, 268, -1, 31, 169, -1, 31, -1,
+ -1, -1, 120, 212, 205, 213, 136, 121, -1, -1,
+ -1, 26, 214, 205, 215, 136, 10, -1, 17, 217,
+ 200, 136, 218, -1, 180, -1, 180, 126, 91, 167,
+ -1, 91, 167, -1, 203, -1, 216, -1, 8, 220,
+ 221, 200, 136, 219, -1, 275, -1, 167, -1, 181,
+ -1, 275, -1, 84, 155, -1, 275, -1, 9, 136,
+ -1, 275, -1, 243, -1, 240, -1, 242, -1, 225,
+ -1, 226, -1, 225, 226, -1, 94, 234, 101, -1,
+ 95, 235, 101, -1, 96, 235, 61, -1, 97, 129,
+ 101, -1, 97, 230, 101, -1, -1, 230, 231, 129,
+ -1, 236, -1, 231, 236, -1, 98, 129, 101, -1,
+ 98, 233, 101, -1, -1, 233, 58, 129, -1, -1,
+ 234, 236, -1, -1, 235, 236, -1, 58, -1, -1,
+ 100, 237, 239, -1, -1, 99, 238, 136, 121, -1,
+ 52, -1, 53, -1, 55, -1, 247, -1, 93, 241,
+ -1, 158, -1, 53, -1, 52, -1, 55, -1, 93,
+ 235, 101, -1, 56, -1, 57, -1, 116, 56, -1,
+ 116, 57, -1, 50, -1, 53, -1, 52, -1, 54,
+ -1, 55, -1, 33, -1, 32, -1, 34, -1, 35,
+ -1, 49, -1, 48, -1, 244, -1, 244, -1, 59,
+ -1, 60, -1, 273, -1, -1, 107, 249, 142, 273,
+ -1, 1, 273, -1, 128, 251, 271, 125, -1, 251,
+ 273, -1, 253, 126, 255, 126, 257, 260, -1, 253,
+ 126, 255, 260, -1, 253, 126, 257, 260, -1, 253,
+ 260, -1, 255, 126, 257, 260, -1, 255, 260, -1,
+ 257, 260, -1, 259, -1, -1, 54, -1, 53, -1,
+ 52, -1, 55, -1, 50, -1, 252, -1, 253, 126,
+ 252, -1, 50, 103, 167, -1, 254, -1, 255, 126,
+ 254, -1, 113, -1, 91, -1, 256, 50, -1, 256,
+ -1, 110, -1, 92, -1, 258, 50, -1, 126, 259,
+ -1, 275, -1, 245, -1, -1, 128, 262, 141, 271,
+ 125, -1, 275, -1, 264, 272, -1, 180, 272, -1,
+ 265, -1, 264, 126, 265, -1, 167, 84, 167, -1,
+ 50, -1, 54, -1, 51, -1, 50, -1, 54, -1,
+ 51, -1, 163, -1, 50, -1, 51, -1, 163, -1,
+ 124, -1, 81, -1, -1, 274, -1, -1, 130, -1,
+ -1, 130, -1, 126, -1, 131, -1, 130, -1, 273,
+ -1, 274, 131, -1, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 350, 350, 350, 375, 395, 403, 404, 408, 412,
+ 418, 418, 422, 426, 433, 438, 442, 451, 460, 472,
+ 484, 490, 489, 503, 511, 515, 521, 546, 562, 574,
+ 586, 598, 603, 607, 612, 617, 620, 621, 625, 629,
+ 633, 637, 640, 647, 648, 649, 653, 657, 663, 664,
+ 668, 675, 679, 674, 689, 694, 706, 711, 723, 728,
+ 740, 745, 752, 753, 759, 760, 766, 770, 774, 778,
+ 782, 786, 792, 793, 799, 803, 809, 813, 817, 821,
+ 825, 829, 835, 841, 848, 852, 856, 860, 864, 868,
+ 874, 880, 887, 891, 894, 898, 902, 908, 909, 910,
+ 911, 916, 923, 924, 927, 931, 934, 938, 938, 944,
+ 945, 946, 947, 948, 949, 950, 951, 952, 953, 954,
+ 955, 956, 957, 958, 959, 960, 961, 962, 963, 964,
+ 965, 966, 967, 968, 969, 972, 972, 972, 972, 973,
+ 973, 973, 973, 973, 973, 973, 974, 974, 974, 974,
+ 974, 974, 974, 975, 975, 975, 975, 975, 975, 976,
+ 976, 976, 976, 976, 976, 976, 977, 977, 977, 977,
+ 977, 978, 978, 978, 978, 981, 985, 989, 1014, 1030,
+ 1042, 1054, 1066, 1071, 1076, 1081, 1091, 1101, 1105, 1109,
+ 1113, 1117, 1121, 1125, 1129, 1133, 1142, 1146, 1150, 1154,
+ 1158, 1162, 1166, 1170, 1174, 1178, 1182, 1186, 1190, 1194,
+ 1198, 1202, 1206, 1210, 1214, 1218, 1222, 1222, 1227, 1232,
+ 1238, 1245, 1246, 1251, 1255, 1260, 1264, 1271, 1275, 1279,
+ 1284, 1291, 1292, 1295, 1300, 1304, 1309, 1314, 1319, 1324,
+ 1330, 1334, 1337, 1341, 1345, 1350, 1355, 1360, 1365, 1370,
+ 1375, 1380, 1385, 1389, 1392, 1392, 1404, 1405, 1405, 1410,
+ 1410, 1417, 1423, 1427, 1430, 1434, 1440, 1444, 1448, 1454,
+ 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1467, 1466,
+ 1479, 1479, 1484, 1489, 1493, 1497, 1505, 1514, 1518, 1522,
+ 1526, 1530, 1534, 1534, 1539, 1545, 1546, 1555, 1568, 1581,
+ 1581, 1581, 1591, 1591, 1591, 1601, 1608, 1612, 1616, 1616,
+ 1616, 1624, 1623, 1640, 1645, 1639, 1662, 1661, 1678, 1677,
+ 1695, 1696, 1695, 1710, 1714, 1718, 1722, 1728, 1735, 1736,
+ 1737, 1738, 1741, 1742, 1743, 1746, 1747, 1756, 1757, 1763,
+ 1764, 1767, 1768, 1772, 1776, 1783, 1787, 1782, 1797, 1806,
+ 1810, 1816, 1821, 1826, 1831, 1835, 1839, 1846, 1850, 1845,
+ 1858, 1862, 1857, 1871, 1878, 1879, 1883, 1889, 1890, 1893,
+ 1904, 1907, 1911, 1912, 1915, 1919, 1922, 1930, 1933, 1934,
+ 1938, 1941, 1954, 1955, 1961, 1967, 1990, 2023, 2027, 2034,
+ 2037, 2043, 2044, 2050, 2054, 2061, 2064, 2071, 2074, 2081,
+ 2084, 2090, 2092, 2091, 2103, 2102, 2123, 2124, 2125, 2126,
+ 2129, 2136, 2137, 2138, 2139, 2142, 2176, 2177, 2178, 2182,
+ 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196, 2197,
+ 2198, 2201, 2207, 2213, 2214, 2217, 2222, 2221, 2229, 2232,
+ 2238, 2244, 2248, 2252, 2256, 2260, 2264, 2268, 2272, 2277,
+ 2282, 2286, 2290, 2294, 2298, 2309, 2310, 2316, 2326, 2331,
+ 2337, 2338, 2341, 2352, 2363, 2364, 2367, 2377, 2381, 2384,
+ 2389, 2389, 2414, 2415, 2419, 2428, 2429, 2435, 2441, 2442,
+ 2443, 2446, 2447, 2448, 2449, 2452, 2453, 2454, 2457, 2458,
+ 2461, 2462, 2465, 2466, 2469, 2470, 2471, 2474, 2475, 2478,
+ 2479, 2482
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "kCLASS", "kMODULE", "kDEF", "kUNDEF",
+ "kBEGIN", "kRESCUE", "kENSURE", "kEND", "kIF", "kUNLESS", "kTHEN",
+ "kELSIF", "kELSE", "kCASE", "kWHEN", "kWHILE", "kUNTIL", "kFOR",
+ "kBREAK", "kNEXT", "kREDO", "kRETRY", "kIN", "kDO", "kDO_COND",
+ "kDO_BLOCK", "kRETURN", "kYIELD", "kSUPER", "kSELF", "kNIL", "kTRUE",
+ "kFALSE", "kAND", "kOR", "kNOT", "kIF_MOD", "kUNLESS_MOD", "kWHILE_MOD",
+ "kUNTIL_MOD", "kRESCUE_MOD", "kALIAS", "kDEFINED", "klBEGIN", "klEND",
+ "k__LINE__", "k__FILE__", "tIDENTIFIER", "tFID", "tGVAR", "tIVAR",
+ "tCONSTANT", "tCVAR", "tINTEGER", "tFLOAT", "tSTRING_CONTENT",
+ "tNTH_REF", "tBACK_REF", "tREGEXP_END", "tUPLUS", "tUMINUS", "tPOW",
+ "tCMP", "tEQ", "tEQQ", "tNEQ", "tGEQ", "tLEQ", "tANDOP", "tOROP",
+ "tMATCH", "tNMATCH", "tDOT2", "tDOT3", "tAREF", "tASET", "tLSHFT",
+ "tRSHFT", "tCOLON2", "tCOLON3", "tOP_ASGN", "tASSOC", "tLPAREN",
+ "tLPAREN_ARG", "tRPAREN", "tLBRACK", "tLBRACE", "tLBRACE_ARG", "tSTAR",
+ "tAMPER", "tSYMBEG", "tSTRING_BEG", "tXSTRING_BEG", "tREGEXP_BEG",
+ "tWORDS_BEG", "tQWORDS_BEG", "tSTRING_DBEG", "tSTRING_DVAR",
+ "tSTRING_END", "tLOWEST", "'='", "'?'", "':'", "'>'", "'<'", "'|'",
+ "'^'", "'&'", "'+'", "'-'", "'*'", "'/'", "'%'", "tUMINUS_NUM", "'!'",
+ "'~'", "tLAST_TOKEN", "'{'", "'}'", "'['", "']'", "'.'", "')'", "','",
+ "'`'", "'('", "' '", "'\\n'", "';'", "$accept", "program", "@1",
+ "bodystmt", "compstmt", "stmts", "stmt", "@2", "@3", "expr",
+ "expr_value", "command_call", "block_command", "cmd_brace_block", "@4",
+ "@5", "command", "mlhs", "mlhs_entry", "mlhs_basic", "mlhs_item",
+ "mlhs_head", "mlhs_node", "lhs", "cname", "cpath", "fname", "fsym",
+ "fitem", "undef_list", "@6", "op", "reswords", "arg", "@7", "arg_value",
+ "aref_args", "paren_args", "opt_paren_args", "call_args", "call_args2",
+ "command_args", "@8", "open_args", "@9", "@10", "block_arg",
+ "opt_block_arg", "args", "mrhs", "primary", "@11", "@12", "@13", "@14",
+ "@15", "@16", "@17", "@18", "@19", "@20", "@21", "@22", "@23", "@24",
+ "@25", "@26", "primary_value", "then", "do", "if_tail", "opt_else",
+ "block_var", "opt_block_var", "do_block", "@27", "@28", "block_call",
+ "method_call", "brace_block", "@29", "@30", "@31", "@32", "case_body",
+ "when_args", "cases", "opt_rescue", "exc_list", "exc_var", "opt_ensure",
+ "literal", "strings", "string", "string1", "xstring", "regexp", "words",
+ "word_list", "word", "qwords", "qword_list", "string_contents",
+ "xstring_contents", "string_content", "@33", "@34", "string_dvar",
+ "symbol", "sym", "dsym", "numeric", "variable", "var_ref", "var_lhs",
+ "backref", "superclass", "@35", "f_arglist", "f_args", "f_norm_arg",
+ "f_arg", "f_opt", "f_optarg", "restarg_mark", "f_rest_arg",
+ "blkarg_mark", "f_block_arg", "opt_f_block_arg", "singleton", "@36",
+ "assoc_list", "assocs", "assoc", "operation", "operation2", "operation3",
+ "dot_or_colon", "opt_terms", "opt_nl", "trailer", "term", "terms",
+ "none", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 61, 63, 58, 62, 60, 124, 94,
+ 38, 43, 45, 42, 47, 37, 358, 33, 126, 359,
+ 123, 125, 91, 93, 46, 41, 44, 96, 40, 32,
+ 10, 59
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint16 yyr1[] =
+{
+ 0, 132, 134, 133, 135, 136, 137, 137, 137, 137,
+ 139, 138, 138, 138, 138, 138, 138, 138, 138, 138,
+ 138, 140, 138, 138, 138, 138, 138, 138, 138, 138,
+ 138, 138, 138, 138, 138, 138, 141, 141, 141, 141,
+ 141, 141, 142, 143, 143, 143, 143, 143, 144, 144,
+ 144, 146, 147, 145, 148, 148, 148, 148, 148, 148,
+ 148, 148, 149, 149, 150, 150, 151, 151, 151, 151,
+ 151, 151, 152, 152, 153, 153, 154, 154, 154, 154,
+ 154, 154, 154, 154, 155, 155, 155, 155, 155, 155,
+ 155, 155, 156, 156, 157, 157, 157, 158, 158, 158,
+ 158, 158, 159, 159, 160, 160, 161, 162, 161, 163,
+ 163, 163, 163, 163, 163, 163, 163, 163, 163, 163,
+ 163, 163, 163, 163, 163, 163, 163, 163, 163, 163,
+ 163, 163, 163, 163, 163, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 166, 165, 165, 165,
+ 167, 168, 168, 168, 168, 168, 168, 169, 169, 169,
+ 169, 170, 170, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 174, 173, 175, 176, 175, 177,
+ 175, 178, 179, 179, 180, 180, 181, 181, 181, 182,
+ 182, 182, 182, 182, 182, 182, 182, 182, 183, 182,
+ 184, 182, 182, 182, 182, 182, 182, 182, 182, 182,
+ 182, 182, 185, 182, 182, 182, 182, 182, 182, 186,
+ 187, 182, 188, 189, 182, 182, 182, 182, 190, 191,
+ 182, 192, 182, 193, 194, 182, 195, 182, 196, 182,
+ 197, 198, 182, 182, 182, 182, 182, 199, 200, 200,
+ 200, 200, 201, 201, 201, 202, 202, 203, 203, 204,
+ 204, 205, 205, 205, 205, 207, 208, 206, 209, 209,
+ 209, 210, 210, 210, 210, 210, 210, 212, 213, 211,
+ 214, 215, 211, 216, 217, 217, 217, 218, 218, 219,
+ 219, 220, 220, 220, 221, 221, 222, 222, 223, 223,
+ 223, 224, 225, 225, 226, 227, 228, 229, 229, 230,
+ 230, 231, 231, 232, 232, 233, 233, 234, 234, 235,
+ 235, 236, 237, 236, 238, 236, 239, 239, 239, 239,
+ 240, 241, 241, 241, 241, 242, 243, 243, 243, 243,
+ 244, 244, 244, 244, 244, 244, 244, 244, 244, 244,
+ 244, 245, 246, 247, 247, 248, 249, 248, 248, 250,
+ 250, 251, 251, 251, 251, 251, 251, 251, 251, 251,
+ 252, 252, 252, 252, 252, 253, 253, 254, 255, 255,
+ 256, 256, 257, 257, 258, 258, 259, 260, 260, 261,
+ 262, 261, 263, 263, 263, 264, 264, 265, 266, 266,
+ 266, 267, 267, 267, 267, 268, 268, 268, 269, 269,
+ 270, 270, 271, 271, 272, 272, 272, 273, 273, 274,
+ 274, 275
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 4, 2, 1, 1, 3, 2,
+ 0, 4, 3, 3, 3, 2, 3, 3, 3, 3,
+ 3, 0, 5, 4, 3, 3, 3, 6, 5, 5,
+ 5, 3, 3, 3, 3, 1, 1, 3, 3, 2,
+ 2, 1, 1, 1, 1, 2, 2, 2, 1, 4,
+ 4, 0, 0, 6, 2, 3, 4, 5, 4, 5,
+ 2, 2, 1, 3, 1, 3, 1, 2, 3, 2,
+ 2, 1, 1, 3, 2, 3, 1, 4, 3, 3,
+ 3, 3, 2, 1, 1, 4, 3, 3, 3, 3,
+ 2, 1, 1, 1, 2, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 0, 4, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 5, 3, 6, 5,
+ 5, 5, 5, 4, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 4, 4, 2, 2, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 3, 3, 3, 3, 0, 4, 5, 1,
+ 1, 1, 2, 2, 5, 2, 3, 3, 4, 4,
+ 6, 1, 1, 1, 2, 5, 2, 5, 4, 7,
+ 3, 1, 4, 3, 5, 7, 2, 5, 4, 6,
+ 7, 9, 3, 1, 0, 2, 1, 0, 3, 0,
+ 4, 2, 2, 1, 1, 3, 3, 4, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 4,
+ 0, 5, 3, 3, 2, 4, 3, 3, 1, 4,
+ 3, 1, 0, 6, 2, 1, 2, 6, 6, 0,
+ 0, 7, 0, 0, 7, 5, 4, 5, 0, 0,
+ 9, 0, 6, 0, 0, 8, 0, 5, 0, 6,
+ 0, 0, 9, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 5, 1, 2, 1,
+ 1, 1, 2, 1, 3, 0, 0, 6, 2, 4,
+ 4, 2, 4, 4, 3, 2, 1, 0, 0, 6,
+ 0, 0, 6, 5, 1, 4, 2, 1, 1, 6,
+ 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 2, 3, 3, 3, 3, 3, 0,
+ 3, 1, 2, 3, 3, 0, 3, 0, 2, 0,
+ 2, 1, 0, 3, 0, 4, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 3, 1, 1, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 4, 2, 4,
+ 2, 6, 4, 4, 2, 4, 2, 2, 1, 0,
+ 1, 1, 1, 1, 1, 1, 3, 3, 1, 3,
+ 1, 1, 2, 1, 1, 1, 2, 2, 1, 1,
+ 0, 5, 1, 2, 2, 1, 3, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
+ 2, 0
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint16 yydefact[] =
+{
+ 2, 0, 0, 1, 0, 0, 0, 0, 0, 278,
+ 0, 0, 490, 299, 302, 0, 323, 324, 325, 326,
+ 288, 291, 356, 426, 425, 427, 428, 0, 0, 492,
+ 21, 0, 430, 429, 420, 277, 422, 421, 423, 424,
+ 416, 417, 433, 434, 0, 0, 0, 0, 0, 501,
+ 501, 71, 399, 397, 399, 399, 389, 395, 0, 0,
+ 0, 3, 490, 7, 35, 36, 44, 43, 0, 62,
+ 0, 66, 72, 0, 41, 219, 0, 48, 295, 269,
+ 270, 381, 382, 271, 272, 273, 274, 379, 380, 378,
+ 431, 275, 0, 276, 254, 6, 9, 323, 324, 288,
+ 291, 356, 492, 92, 93, 0, 0, 0, 0, 95,
+ 0, 327, 0, 431, 276, 0, 316, 144, 155, 145,
+ 168, 141, 161, 151, 150, 171, 172, 166, 149, 148,
+ 143, 169, 173, 174, 153, 142, 156, 160, 162, 154,
+ 147, 163, 170, 165, 164, 157, 167, 152, 140, 159,
+ 158, 139, 146, 137, 138, 135, 136, 97, 99, 98,
+ 130, 131, 128, 112, 113, 114, 117, 119, 115, 132,
+ 133, 120, 121, 125, 116, 118, 109, 110, 111, 122,
+ 123, 124, 126, 127, 129, 134, 470, 318, 100, 101,
+ 469, 0, 164, 157, 167, 152, 135, 136, 97, 98,
+ 102, 104, 106, 15, 103, 105, 0, 0, 42, 0,
+ 0, 0, 431, 0, 276, 0, 498, 497, 490, 0,
+ 499, 491, 0, 0, 0, 340, 339, 0, 0, 431,
+ 276, 0, 0, 0, 233, 220, 264, 46, 241, 501,
+ 501, 475, 47, 45, 0, 61, 0, 501, 355, 60,
+ 39, 0, 10, 493, 216, 0, 0, 195, 0, 196,
+ 284, 0, 0, 0, 62, 280, 0, 492, 0, 494,
+ 494, 221, 494, 0, 494, 472, 0, 70, 0, 76,
+ 83, 413, 412, 414, 411, 0, 410, 0, 0, 0,
+ 0, 0, 0, 0, 418, 419, 40, 210, 211, 5,
+ 491, 0, 0, 0, 0, 0, 0, 0, 345, 348,
+ 0, 74, 0, 69, 67, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 501, 0, 0, 0, 360, 357, 296,
+ 383, 0, 0, 351, 54, 294, 0, 313, 92, 93,
+ 94, 418, 419, 0, 436, 311, 435, 0, 501, 0,
+ 0, 0, 449, 489, 488, 320, 107, 0, 501, 284,
+ 330, 329, 0, 328, 0, 0, 501, 0, 0, 0,
+ 0, 0, 0, 0, 0, 500, 0, 0, 284, 0,
+ 501, 0, 308, 501, 261, 0, 0, 234, 263, 0,
+ 236, 290, 0, 257, 256, 255, 233, 492, 501, 492,
+ 0, 12, 14, 13, 0, 292, 0, 0, 0, 0,
+ 0, 0, 0, 282, 63, 492, 492, 222, 286, 496,
+ 495, 223, 496, 225, 496, 474, 287, 473, 82, 0,
+ 501, 0, 401, 404, 402, 415, 400, 384, 398, 385,
+ 386, 387, 388, 0, 391, 393, 0, 394, 0, 0,
+ 8, 16, 17, 18, 19, 20, 37, 38, 501, 0,
+ 25, 33, 0, 34, 0, 68, 75, 24, 175, 264,
+ 32, 192, 200, 205, 206, 207, 202, 204, 214, 215,
+ 208, 209, 185, 186, 212, 213, 0, 201, 203, 197,
+ 198, 199, 187, 188, 189, 190, 191, 481, 486, 482,
+ 487, 254, 354, 0, 481, 483, 482, 484, 501, 481,
+ 482, 254, 254, 501, 501, 26, 177, 31, 184, 51,
+ 55, 0, 438, 0, 0, 92, 93, 96, 0, 0,
+ 501, 0, 492, 454, 452, 451, 450, 453, 461, 465,
+ 464, 460, 449, 0, 0, 455, 501, 458, 501, 463,
+ 501, 0, 448, 0, 0, 279, 501, 501, 370, 501,
+ 331, 175, 485, 283, 0, 481, 482, 501, 0, 0,
+ 0, 364, 0, 306, 334, 333, 300, 332, 303, 485,
+ 283, 0, 481, 482, 0, 0, 240, 477, 0, 265,
+ 262, 501, 0, 0, 476, 289, 0, 41, 0, 259,
+ 0, 253, 501, 0, 0, 0, 0, 0, 227, 11,
+ 0, 217, 0, 23, 183, 63, 0, 226, 0, 265,
+ 79, 81, 0, 481, 482, 0, 0, 390, 392, 396,
+ 193, 194, 343, 0, 346, 341, 268, 0, 73, 0,
+ 0, 0, 0, 353, 58, 285, 0, 0, 232, 352,
+ 56, 231, 350, 50, 349, 49, 361, 358, 501, 314,
+ 0, 0, 285, 317, 0, 0, 492, 0, 440, 0,
+ 444, 468, 0, 446, 462, 0, 447, 466, 321, 108,
+ 371, 372, 501, 373, 0, 501, 337, 0, 0, 335,
+ 0, 285, 0, 0, 0, 305, 307, 366, 0, 0,
+ 0, 0, 285, 0, 501, 0, 238, 501, 501, 0,
+ 0, 258, 0, 246, 228, 0, 492, 501, 501, 229,
+ 0, 22, 281, 492, 77, 0, 406, 407, 408, 403,
+ 409, 342, 0, 0, 0, 266, 176, 218, 30, 181,
+ 182, 59, 0, 28, 179, 29, 180, 57, 0, 0,
+ 52, 0, 437, 312, 471, 457, 0, 319, 456, 501,
+ 501, 467, 0, 459, 501, 449, 0, 0, 375, 338,
+ 0, 4, 377, 0, 297, 0, 298, 0, 501, 0,
+ 0, 309, 235, 0, 237, 252, 0, 243, 501, 501,
+ 260, 0, 0, 293, 224, 405, 344, 0, 267, 27,
+ 178, 0, 0, 0, 0, 439, 0, 442, 443, 445,
+ 0, 0, 374, 0, 84, 91, 0, 376, 0, 365,
+ 367, 368, 363, 301, 304, 0, 501, 501, 0, 242,
+ 0, 248, 501, 230, 347, 362, 359, 0, 315, 501,
+ 0, 90, 0, 501, 0, 501, 501, 0, 239, 244,
+ 0, 501, 0, 247, 53, 441, 322, 485, 89, 0,
+ 481, 482, 369, 336, 310, 501, 0, 249, 501, 85,
+ 245, 0, 250, 501, 251
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 2, 377, 378, 62, 63, 424, 255, 64,
+ 209, 65, 66, 540, 678, 823, 67, 68, 263, 69,
+ 70, 71, 72, 210, 109, 110, 200, 201, 202, 203,
+ 574, 527, 189, 74, 426, 236, 268, 668, 669, 237,
+ 619, 245, 246, 415, 620, 730, 610, 407, 269, 483,
+ 75, 206, 435, 630, 222, 720, 223, 721, 604, 845,
+ 544, 541, 771, 370, 372, 573, 785, 258, 382, 596,
+ 708, 709, 228, 654, 309, 478, 753, 77, 78, 355,
+ 534, 769, 533, 768, 394, 592, 842, 577, 702, 787,
+ 791, 79, 80, 81, 82, 83, 84, 85, 291, 463,
+ 86, 293, 287, 285, 456, 646, 645, 749, 87, 286,
+ 88, 89, 212, 91, 213, 214, 365, 543, 563, 564,
+ 565, 566, 567, 568, 569, 570, 571, 781, 690, 191,
+ 371, 273, 270, 241, 115, 548, 522, 375, 219, 254,
+ 441, 383, 221, 95
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -654
+static const yytype_int16 yypact[] =
+{
+ -654, 73, 2254, -654, 5471, 7939, 8236, 3906, 5121, -654,
+ 6810, 6810, 3777, -654, -654, 8038, 5677, 5677, -654, -654,
+ 5677, 4436, 4539, -654, -654, -654, -654, 6810, 4996, -44,
+ -654, -3, -654, -654, 2117, 4024, -654, -654, 4127, -654,
+ -654, -654, -654, -654, 7634, 7634, 50, 3187, 6810, 6913,
+ 7634, 8335, 4871, -654, -654, -654, -18, -1, 94, 7737,
+ 7634, -654, 124, 757, 249, -654, -654, 104, 74, -654,
+ 57, 8137, -654, 96, 9349, 171, 485, 11, 32, -654,
+ -654, 120, -654, -654, -654, -654, -654, -654, -654, -654,
+ 10, -654, 64, 38, 42, -654, 757, -654, -654, -654,
+ 99, 111, -44, 129, 140, 6810, 90, 3318, 325, -654,
+ 78, -654, 549, -654, -654, 42, -654, -654, -654, -654,
+ -654, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, -654, 95, 148, 282, 333, -654, -654,
+ -654, -654, -654, -654, -654, 336, 337, 465, -654, 473,
+ -654, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, 495, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, 147, -654, -654, 2615, 225, 249, 48,
+ 188, 558, 31, 213, 258, 48, -654, -654, 124, 88,
+ -654, 209, 6810, 6810, 288, -654, -654, 559, 324, 66,
+ 245, 7634, 7634, 7634, -654, 9349, 274, -654, -654, 262,
+ 269, -654, -654, -654, 5363, -654, 5780, 5677, -654, -654,
+ -654, 222, -654, -654, 275, 285, 3449, -654, 571, 366,
+ 476, 3187, 310, 315, 342, 249, 7634, -44, 371, 392,
+ 401, -654, 480, 375, 401, -654, 470, -654, 586, 595,
+ 618, -654, -654, -654, -654, 231, -654, 482, 515, 674,
+ 425, 565, 444, 30, 493, 499, -654, -654, -654, -654,
+ 3674, 6810, 6810, 6810, 6810, 5471, 6810, 6810, -654, -654,
+ 7016, -654, 3187, 8335, 438, 7016, 7634, 7634, 7634, 7634,
+ 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634,
+ 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634, 7634,
+ 7634, 7634, 1612, 6913, 8489, 8555, 8555, -654, -654, -654,
+ -654, 7737, 7737, -654, 501, -654, 275, 249, -654, 623,
+ -654, -654, -654, 124, -654, -654, -654, 8621, 6913, 8555,
+ 2615, 6810, 419, -654, -654, -654, -654, 583, 596, 283,
+ -654, -654, 2737, 590, 7634, 8687, 6913, 8753, 7634, 7634,
+ 2981, 601, 3571, 7119, 611, -654, 18, 18, 252, 8819,
+ 6913, 8885, -654, 498, -654, 7634, 5883, -654, -654, 5986,
+ -654, -654, 503, 5574, -654, -654, 104, -44, 510, 9,
+ 512, -654, -654, -654, 5121, -654, 7634, 3449, 528, 8687,
+ 8753, 7634, 529, -654, 527, -44, 9213, -654, -654, 7222,
+ -654, -654, 7634, -654, 7634, -654, -654, -654, 623, 8951,
+ 6913, 9017, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, 25, -654, -654, 534, -654, 7634, 7634,
+ 757, -654, -654, -654, -654, -654, -654, -654, 37, 7634,
+ -654, 544, 546, -654, 550, -654, -654, -654, 1775, -654,
+ -654, 366, 9418, 9418, 9418, 9418, 547, 547, 9435, 1667,
+ 9418, 9418, 9366, 9366, 394, 394, 2464, 547, 547, 714,
+ 714, 437, 43, 43, 366, 366, 366, 2365, 4642, 2484,
+ 4745, 111, -654, 554, 487, -654, 585, -654, 4539, -654,
+ -654, 1899, 1899, 37, 37, -654, 9349, -654, 9349, -654,
+ -654, 124, -654, 6810, 2615, 141, 477, -654, 111, 562,
+ 111, 681, 35, 597, -654, -654, -654, -654, -654, -654,
+ -654, -654, 429, 2615, 124, -654, 575, -654, 577, 655,
+ 588, 665, -654, 5246, 5121, -654, 7325, 703, -654, 398,
+ -654, 2345, 4230, 4333, 599, 344, 416, 703, 726, 734,
+ 7634, 626, 48, -654, -654, -654, -654, -654, -654, 60,
+ 85, 635, 259, 354, 6810, 657, -654, -654, 7634, 274,
+ -654, 628, 7634, 274, -654, -654, 7634, 9265, 16, -654,
+ 636, -654, 634, 643, 6089, 8555, 8555, 645, -654, -654,
+ 6810, 9349, 656, -654, 9349, 296, 658, -654, 7634, -654,
+ 141, 477, 661, 340, 351, 3449, 671, -654, -654, -654,
+ 366, 366, -654, 7840, -654, -654, -654, 7428, -654, 7634,
+ 7634, 7737, 7634, -654, 501, 624, 7737, 7737, -654, -654,
+ 501, -654, -654, -654, -654, -654, -654, -654, 37, -654,
+ 124, 776, -654, -654, 663, 7634, -44, 781, -654, 429,
+ -654, -654, 452, -654, -654, -12, -654, -654, -654, -654,
+ 544, -654, 708, -654, 3084, 792, -654, 6810, 797, -654,
+ 7634, 432, 7634, 7634, 798, -654, -654, -654, 7531, 2859,
+ 3571, 3571, 367, 18, 498, 6192, -654, 498, 498, 6295,
+ 685, -654, 6398, -654, -654, 104, 9, 111, 111, -654,
+ 101, -654, -654, 9213, 631, 690, -654, -654, -654, -654,
+ -654, -654, 709, 3571, 7634, 692, 9349, 9349, -654, 9349,
+ 9349, -654, 7737, -654, 9349, -654, 9349, -654, 3571, 3449,
+ -654, 2615, -654, -654, -654, -654, 691, -654, -654, 693,
+ 588, -654, 597, -654, 588, 419, 8434, 48, -654, -654,
+ 3571, -654, -654, 48, -654, 7634, -654, 7634, 165, 810,
+ 812, -654, -654, 7634, -654, -654, 7634, -654, 705, 710,
+ -654, 7634, 712, -654, -654, -654, -654, 825, -654, -654,
+ 9349, 830, 720, 3449, 832, -654, 452, -654, -654, -654,
+ 2615, 789, -654, 640, 595, 618, 2615, -654, 2737, -654,
+ -654, -654, -654, -654, -654, 3571, 9286, 498, 6501, -654,
+ 6604, -654, 498, -654, -654, -654, -654, 724, -654, 588,
+ 838, 623, 9083, 6913, 9149, 596, 398, 839, -654, -654,
+ 7634, 727, 7634, -654, -654, -654, -654, 41, 477, 732,
+ 76, 93, -654, -654, -654, 498, 6707, -654, 498, 631,
+ -654, 7634, -654, 498, -654
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -654, -654, -654, -360, 431, -654, 59, -654, -654, 958,
+ 44, 23, -654, -569, -654, -654, 1, -6, -183, 22,
+ 779, -654, -25, 927, -70, 853, 7, -654, 21, -654,
+ -654, -5, -654, -16, -654, 1378, -338, -7, -479, 82,
+ -654, 2, -654, -654, -654, -654, -9, 158, 110, -278,
+ 26, -654, -654, -654, -654, -654, -654, -654, -654, -654,
+ -654, -654, -654, -654, -654, -654, -654, 185, -209, -372,
+ 0, -520, 208, -458, -654, -654, -654, -228, -654, 785,
+ -654, -654, -654, -654, -356, -654, -654, 3, -654, -654,
+ -654, -654, -654, -654, 784, -654, -654, -654, -654, -654,
+ -654, -654, -654, 458, -217, -654, -654, -654, 12, -654,
+ 14, -654, 627, 860, 1246, 1107, -654, -654, 84, 309,
+ 183, -654, -653, 184, -654, -608, -654, -321, -504, -654,
+ -654, -654, -4, -382, 755, -226, -654, -654, -24, 20,
+ 467, 53, 814, 756
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -502
+static const yytype_int16 yytable[] =
+{
+ 235, 235, 188, 188, 235, 523, 390, 238, 238, 225,
+ 551, 238, 240, 240, 187, 248, 240, 234, 234, 419,
+ 204, 234, 205, 188, 249, 598, 277, 614, 257, 259,
+ 549, 111, 111, 235, 235, 588, 360, 490, 299, 783,
+ 204, 111, 205, 297, 298, 594, 274, 188, 584, 252,
+ 267, 572, 672, 674, -87, 215, 218, 705, 347, 284,
+ 614, 380, 601, 96, 693, 220, 696, 714, 347, 264,
+ 458, 306, 307, 3, 464, 676, 677, 111, 432, 363,
+ 559, 780, 296, 452, 784, -87, 253, 353, 466, -86,
+ 625, -84, 345, -432, 248, 761, 354, 111, 560, 242,
+ 405, 767, 243, 392, 260, 393, -88, 316, 353, 652,
+ -89, 290, 642, -84, -432, 220, 521, 256, 528, 531,
+ 532, 352, 356, 595, 453, 454, 239, 239, 292, 484,
+ 239, 467, 308, 626, -84, 346, -76, 306, 307, 253,
+ 358, -91, 729, 550, 359, 653, -87, 351, 216, 217,
+ 294, 295, 348, 381, 647, -478, 339, 340, 341, 521,
+ 272, 528, 348, 366, -83, 253, -479, -485, -87, -481,
+ 247, -87, -87, 783, -84, 550, -426, 310, 216, 217,
+ 704, -86, 393, 311, 681, 364, -79, 76, -481, 76,
+ 112, 112, -76, -89, 391, 211, 211, 211, -88, 315,
+ 227, 211, 211, 687, 550, 211, -86, -86, 216, 217,
+ -420, -81, 211, -482, 53, 235, 235, 297, 859, -426,
+ 770, -423, -485, -88, -88, 550, 813, 244, 235, -425,
+ 235, 235, 76, 211, 211, 238, 278, 238, 238, 247,
+ 240, 572, 240, 240, 211, 234, 648, 234, 416, -478,
+ 436, -420, -327, -420, 216, 217, 278, -478, 672, 674,
+ -479, -485, -423, -485, -423, -485, 396, 397, -479, -481,
+ -91, 220, -425, 376, 421, 827, 828, -90, 840, 379,
+ 829, 422, 423, 264, -86, 306, 307, 437, 485, 452,
+ 211, 384, 76, -327, 235, -327, 388, 547, 701, 488,
+ 491, 492, 493, 494, 495, 496, 497, 498, 499, 500,
+ 501, 502, 503, 504, 505, 506, 507, 508, 509, 510,
+ 511, 512, 513, 514, 515, 516, 412, 235, 414, 417,
+ 453, 454, 455, 480, 264, 536, 538, 520, 487, 111,
+ 395, 389, 398, 614, 267, 471, 472, 473, 474, 402,
+ 614, 801, 235, -91, 239, 875, 239, 418, 405, 470,
+ -90, -91, 520, -427, 475, -78, 431, -86, 581, 267,
+ 235, -83, 536, 538, 535, 537, -80, 235, -82, -88,
+ 520, 361, 362, 719, 235, -78, -90, 267, 406, 235,
+ 235, 76, -85, 235, 520, 409, 736, 617, 410, 737,
+ 738, 267, 611, 425, 621, 427, -427, 211, 211, 622,
+ 631, 824, 707, 704, -428, 634, 542, -430, -429, 188,
+ 482, -65, -73, 235, 520, 482, 235, 712, 235, 211,
+ 316, 211, 211, 61, 235, 433, 204, 623, 205, 627,
+ 434, 76, 841, -78, 520, 629, 76, -86, -78, 597,
+ 597, 267, 650, 651, -80, 636, 637, -428, 316, -80,
+ -430, -429, -88, 235, 572, -78, -78, -64, 614, 553,
+ 860, 554, 555, 556, 557, -85, -80, -80, 262, 553,
+ -80, 554, 555, 556, 557, 76, 211, 211, 211, 211,
+ 76, 211, 211, -77, 438, 211, 446, 76, 278, 713,
+ 211, 316, 782, 591, 614, 337, 338, 339, 340, 341,
+ 558, 559, 288, 289, 663, 795, 329, 330, 439, -88,
+ 558, 559, 440, 664, 448, 879, 461, 442, 211, 560,
+ 670, 440, 561, 673, 675, -85, 211, 211, 262, 560,
+ 452, 663, 561, 558, 559, 465, -420, 562, 337, 338,
+ 339, 340, 341, 211, -423, 76, 211, 468, -283, 431,
+ 235, 606, 560, 469, 486, 561, 342, 76, 188, 188,
+ 666, 211, 684, 452, 235, 76, 373, 76, 836, -90,
+ 698, 453, 454, 457, 838, 211, 204, 680, 205, -420,
+ -86, 539, 235, 575, 679, 699, 235, -423, 211, -283,
+ 235, -283, -82, 580, 576, -482, 444, 343, 235, 344,
+ 440, 316, 76, -78, 453, 454, 459, 688, 393, 374,
+ 611, 593, 743, 452, 605, 735, 329, 330, 615, 90,
+ 367, 90, 113, 113, 113, 211, 624, 628, 550, 385,
+ 399, 235, 229, 756, 757, 759, 760, 225, 723, 633,
+ 764, 766, 429, -73, 635, 334, 335, 336, 337, 338,
+ 339, 340, 341, 649, 453, 454, 462, 449, 667, 235,
+ -264, 368, 657, 369, 90, 658, -431, 665, 279, 111,
+ 386, 400, 387, 401, 758, 682, 482, 428, -88, 763,
+ 765, 683, 262, 386, 759, 430, 764, 766, 279, -276,
+ 685, 689, 235, 692, -284, 694, 776, 762, 450, 235,
+ 451, -80, -285, 235, 695, 697, 235, -431, 704, -431,
+ 807, 862, 711, 746, 747, 809, 748, -85, 211, 76,
+ 42, 43, 452, 772, 90, 460, 715, 443, 235, 445,
+ -276, 447, -276, 262, 716, -284, 820, -284, 76, 232,
+ -77, 793, 718, -285, 725, -285, 812, 94, 722, 94,
+ 732, 731, 863, 814, 864, 94, 94, 94, 734, 726,
+ 739, 94, 94, 453, 454, 94, 597, 741, 316, 820,
+ 733, 235, 94, 742, 744, 819, 773, 846, 774, 211,
+ 235, 777, 786, 329, 330, 235, 301, 302, 303, 304,
+ 305, 790, 94, 94, 94, 271, 275, 794, 796, 211,
+ 810, 815, 111, 579, 94, 211, 825, 816, -265, 826,
+ 843, 587, 844, 589, 336, 337, 338, 339, 340, 341,
+ 76, 848, 235, 90, 235, 854, 850, 853, 227, 808,
+ 855, 856, 858, 861, 871, 874, 211, 235, 876, 884,
+ 314, 211, 211, 886, 235, 889, 235, 520, 632, 116,
+ 94, 752, 94, 349, 267, 350, 883, 190, 882, 830,
+ 235, 686, 778, 779, 0, 235, 300, 0, 0, 0,
+ 0, 0, 802, 90, 0, 804, 805, 0, 90, 76,
+ 0, 0, 211, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 76, 76, 76, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 90, 0, 73,
+ 0, 73, 90, 0, 0, 0, 0, 0, 76, 90,
+ 279, 0, 226, 0, 0, 0, 0, 211, 0, 0,
+ 0, 0, 0, 76, 76, 0, 76, 0, 0, 0,
+ 0, 94, 0, 0, 0, 0, 849, 851, 208, 208,
+ 208, 833, 0, 0, 73, 76, 0, 94, 94, 0,
+ 0, 0, 0, 0, 0, 250, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 408, 408, 90, 0, 94,
+ 0, 94, 94, 420, 868, 869, 265, 0, 76, 90,
+ 873, 94, 0, 0, 0, 76, 94, 90, 0, 90,
+ 0, 76, 0, 76, 0, 0, 0, 0, 0, 887,
+ 76, 0, 0, 0, 73, 0, 0, 0, 0, 0,
+ 0, 0, 0, 890, 0, 0, 892, 0, 211, 0,
+ 0, 894, 0, 0, 90, 94, 94, 94, 94, 94,
+ 94, 94, 94, 357, 0, 94, 0, 94, 0, 0,
+ 94, 0, 0, 0, 0, 0, 745, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 94, 271,
+ 0, 0, 0, 0, 0, 0, 94, 94, 0, 93,
+ 0, 93, 114, 114, 0, 0, 0, 0, 0, 0,
+ 0, 0, 230, 94, 271, 94, 94, 0, 0, 0,
+ 0, 0, 0, 73, 578, 789, 0, 94, 0, 0,
+ 0, 94, 271, 0, 0, 94, 0, 94, 0, 0,
+ 798, 799, 800, 0, 93, 94, 271, 0, 280, 408,
+ 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,
+ 0, 90, 0, 0, 408, 0, 0, 0, 280, 0,
+ 208, 208, 94, 73, 817, 0, 0, 0, 73, 0,
+ 90, 0, 0, 0, 0, 0, 0, 0, 0, 821,
+ 822, 0, 0, 0, 0, 94, 271, 0, 0, 0,
+ 0, 0, 0, 0, 93, 0, 0, 0, 0, 0,
+ 0, 837, 0, 0, 0, 0, 0, 73, 0, 0,
+ 0, 0, 73, 0, 655, 0, 0, 0, 0, 73,
+ 0, 0, 0, 0, 0, 0, 0, 0, 92, 0,
+ 92, 0, 0, 0, 857, 0, 0, 0, 0, 208,
+ 208, 208, 208, 0, 476, 477, 0, 865, 0, 866,
+ 0, 0, 90, 0, 0, 0, 867, 0, 0, 0,
+ 229, 0, 0, 0, 671, 0, 0, 671, 671, 655,
+ 655, 0, 0, 92, 0, 0, 0, 73, 94, 94,
+ 0, 0, 0, 0, 0, 0, 671, 0, 0, 73,
+ 0, 0, 0, 93, 0, 0, 0, 73, 94, 73,
+ 0, 0, 691, 0, 691, 0, 691, 0, 0, 552,
+ 0, 90, 703, 706, 0, 706, 0, 0, 0, 0,
+ 0, 0, 0, 706, 0, 0, 90, 90, 90, 0,
+ 0, 0, 0, 92, 73, 0, 0, 0, 0, 94,
+ 0, 0, 0, 93, 0, 0, 0, 408, 93, 0,
+ 0, 265, 0, 0, 0, 0, 0, 0, 408, 94,
+ 90, 0, 0, 0, 0, 94, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 90, 90, 0, 90, 0,
+ 94, 0, 0, 0, 0, 0, 0, 93, 0, 0,
+ 0, 0, 93, 834, 0, 0, 94, 90, 0, 93,
+ 280, 94, 94, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 655, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 90, 0, 92, 0, 0, 0, 0, 90, 788, 94,
+ 0, 792, 94, 90, 0, 90, 0, 0, 0, 0,
+ 0, 73, 90, 0, 94, 94, 94, 93, 0, 0,
+ 408, 0, 0, 408, 408, 0, 0, 0, 0, 93,
+ 73, 0, 0, 671, 671, 0, 0, 93, 0, 93,
+ 0, 208, 92, 0, 0, 0, 0, 92, 94, 0,
+ 0, 0, 0, 0, 0, 0, 0, 94, 0, 0,
+ 0, 0, 0, 94, 94, 0, 94, 0, 0, 0,
+ 0, 0, 0, 0, 93, 691, 691, 0, 0, 0,
+ 691, 0, 0, 0, 0, 94, 92, 0, 0, 0,
+ 0, 92, 0, 0, 706, 0, 0, 0, 92, 0,
+ 0, 0, 208, 0, 408, 408, 0, 0, 0, 0,
+ 0, 0, 73, 0, 0, 0, 0, 0, 94, 0,
+ 226, 0, 0, 0, 0, 94, 0, 0, 740, 0,
+ 0, 94, 0, 94, 0, 0, 0, 0, 0, 0,
+ 94, 0, 408, 408, 0, 0, 0, 0, 408, 403,
+ 404, 0, 0, 0, 0, 691, 92, 0, 94, 271,
+ 0, 578, 706, 0, 0, 0, 0, 408, 92, 0,
+ 0, 73, 0, 0, 0, 0, 92, 0, 92, 0,
+ 0, 408, 0, 0, 408, 0, 73, 73, 73, 408,
+ 0, 93, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 517, 518, 0, 208, 519, 0, 0, 0,
+ 93, 0, 0, 92, 160, 161, 162, 163, 164, 165,
+ 73, 166, 167, 0, 0, 168, 0, 0, 481, 169,
+ 170, 171, 172, 489, 0, 73, 73, 0, 73, 0,
+ 0, 0, 0, 173, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 832, 0, 0, 0, 73, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 0, 0,
+ 184, 316, 317, 318, 319, 320, 321, 322, 323, 185,
+ 325, 326, 0, 0, 0, 0, 329, 330, 0, 0,
+ 73, 0, 93, 750, 0, 0, 0, 73, 0, 0,
+ 230, 0, 0, 73, 0, 73, 0, 0, 0, 0,
+ 0, 489, 73, 332, 333, 334, 335, 336, 337, 338,
+ 339, 340, 341, 607, 609, 0, 0, 613, 0, 0,
+ 92, 618, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 92,
+ 0, 93, 0, 0, 0, 0, 0, 639, 659, 0,
+ 613, 0, 639, 0, 0, 0, 93, 93, 93, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 0, 0, 329, 330, 0, 656, 0, 0,
+ 93, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 93, 93, 0, 93, 331,
+ 0, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+ 341, 92, 0, 835, 0, 0, 0, 93, 0, -501,
+ 0, -220, 0, 0, 0, 0, 0, -501, -501, -501,
+ 0, 0, -501, -501, -501, 0, -501, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -501, 0, 0, 0,
+ 93, 0, 0, 0, 0, -501, -501, 93, -501, -501,
+ -501, -501, -501, 93, 0, 93, 0, 0, 0, 0,
+ 92, 0, 93, 0, 700, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 92, 92, 92, 717, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -501, 0, 0, 0, 0, 0, 724, 0, 0, 0,
+ 727, 0, 0, 0, 728, 0, 0, 0, 0, 92,
+ 0, 0, 609, 0, -501, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 92, 92, 0, 92, 0, 0,
+ -501, 0, 0, -501, -501, 0, 0, 247, 0, -501,
+ -501, 0, 0, 0, 0, 755, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 775, 0, 0, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0, 92, 0, 0, 0,
+ 0, 0, 92, 0, 92, 0, 0, 0, 0, 0,
+ 0, 92, 0, 0, 0, 0, 639, 0, 0, 0,
+ 0, 0, 0, 613, 0, 0, 0, 0, 0, 0,
+ 613, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -478, -478, -478, 0, -478, 0, 0, 0, -478, -478,
+ 0, 0, 818, -478, 0, -478, -478, -478, -478, -478,
+ -478, -478, 0, -478, 0, 0, -478, -478, -478, -478,
+ -478, -478, -478, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -478, 0, 0, -478, -478, -478, -478, -478,
+ -478, -478, -478, -478, -478, 839, -478, -478, 0, -478,
+ -478, 0, 0, 0, 847, 0, 0, 0, 0, 852,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -478,
+ 0, 0, -478, -478, 0, -478, -478, 0, -478, -478,
+ -478, -478, -478, -478, -478, -478, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 609, 0, 613, 0,
+ 0, 0, 0, -478, -478, -478, 0, -478, 0, 0,
+ 0, 0, 0, 0, 0, -478, 0, 0, 885, 0,
+ 888, 0, 0, 0, -501, 4, 0, 5, 6, 7,
+ 8, 9, 0, 0, 613, 10, 11, 0, 0, 893,
+ 12, 0, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 27, 0, 0, 0, 0, 0, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 0, 0, 47,
+ 48, 0, 49, 50, 0, 51, 0, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -485, 0, 0, 0, 0,
+ 58, 59, 60, -485, -485, -485, 0, 0, 0, -485,
+ -485, 0, -485, 0, -501, -501, 0, 0, 659, 0,
+ 0, -485, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -485, -485, 0, -485, -485, -485, -485, -485, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 0, 0, 329, 330, 0, 0, 0, -485,
+ -485, -485, -485, -485, -485, -485, -485, -485, -485, -485,
+ -485, -485, 0, 0, -485, -485, -485, 0, 661, 331,
+ 0, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+ 341, 0, 0, 0, 0, 0, 0, 0, -87, -485,
+ 0, -485, -485, -485, -485, -485, -485, -485, -485, -485,
+ -485, 0, 0, 0, -283, -485, -485, -485, 0, -485,
+ -485, -79, -283, -283, -283, -485, -485, 0, -283, -283,
+ 0, -283, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -283, -283, 0, -283, -283, -283, -283, -283, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 0, 0, 329, 330, 0, 0, 0, -283, -283,
+ -283, -283, -283, -283, -283, -283, -283, -283, -283, -283,
+ -283, 0, 0, -283, -283, -283, 0, 662, 331, 660,
+ 332, 333, 334, 335, 336, 337, 338, 339, 340, 341,
+ 0, 0, 0, 0, 0, 0, 0, -89, -283, 0,
+ -283, -283, -283, -283, -283, -283, -283, -283, -283, -283,
+ 0, 0, 0, 0, 0, -283, -283, 0, -283, -283,
+ -81, 0, 0, 0, -283, -283, 4, 0, 5, 6,
+ 7, 8, 9, -501, -501, -501, 10, 11, 0, 0,
+ -501, 12, 0, 13, 14, 15, 16, 17, 18, 19,
+ 0, 0, 0, 0, 20, 21, 22, 23, 24, 25,
+ 26, 0, 0, 27, 0, 0, 0, 0, 0, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 0, 42, 43, 0, 44, 45, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 46, 0, 0,
+ 47, 48, 0, 49, 50, 0, 51, 0, 52, 53,
+ 54, 55, 56, 57, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 58, 59, 60, 0, 0, 0, 0, 4, 0,
+ 5, 6, 7, 8, 9, -501, -501, -501, 10, 11,
+ 0, -501, -501, 12, 0, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, 0, 0, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 27, 0, 0, 0, 0,
+ 0, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 46,
+ 0, 0, 47, 48, 0, 49, 50, 0, 51, 0,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 58, 59, 60, 0, 0, 0, 0,
+ 4, 0, 5, 6, 7, 8, 9, -501, -501, -501,
+ 10, 11, 0, 0, -501, 12, -501, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 20, 21,
+ 22, 23, 24, 25, 26, 0, 0, 27, 0, 0,
+ 0, 0, 0, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 0, 42, 43,
+ 0, 44, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 46, 0, 0, 47, 48, 0, 49, 50, 0,
+ 51, 0, 52, 53, 54, 55, 56, 57, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 58, 59, 60, 0, 0,
+ 0, 0, 4, 0, 5, 6, 7, 8, 9, -501,
+ -501, -501, 10, 11, 0, 0, -501, 12, 0, 13,
+ 14, 15, 16, 17, 18, 19, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 0, 0, 27,
+ 0, 0, 0, 0, 0, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 0,
+ 42, 43, 0, 44, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 46, 0, 0, 47, 48, 0, 49,
+ 50, 0, 51, 0, 52, 53, 54, 55, 56, 57,
+ 0, 0, 0, 0, 0, 4, 0, 5, 6, 7,
+ 8, 9, 0, -501, -501, 10, 11, 58, 59, 60,
+ 12, 0, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, -501, -501, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 27, 0, 0, 0, 0, 0, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 0, 0, 47,
+ 48, 0, 49, 50, 0, 51, 0, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 4, 0,
+ 5, 6, 7, 8, 9, 0, 0, 0, 10, 11,
+ 58, 59, 60, 12, 0, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, -501, -501, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 27, 0, 0, 0, 0,
+ 0, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 46,
+ 0, 0, 261, 48, 0, 49, 50, 0, 51, 0,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 58, 59, 60, 0, 0, 0, 0,
+ 0, 0, -501, 0, 0, 0, 0, -501, -501, 4,
+ 0, 5, 6, 7, 8, 9, 0, 0, 0, 10,
+ 11, 0, 0, 0, 12, 0, 13, 14, 15, 16,
+ 17, 18, 19, 0, 0, 0, 0, 20, 21, 22,
+ 23, 24, 25, 26, 0, 0, 27, 0, 0, 0,
+ 0, 0, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 0, 42, 43, 0,
+ 44, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 46, 0, 0, 47, 48, 0, 49, 50, 0, 51,
+ 0, 52, 53, 54, 55, 56, 57, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 58, 59, 60, 0, 0, 0,
+ 0, 0, 0, -501, 0, 0, 0, 0, -501, -501,
+ 4, 0, 5, 6, 7, 8, 9, 0, 0, 0,
+ 10, 11, 0, 0, 0, 12, 0, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 20, 21,
+ 22, 23, 24, 25, 26, 0, 0, 27, 0, 0,
+ 0, 0, 0, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 0, 42, 43,
+ 0, 44, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 46, 0, 0, 47, 48, 0, 49, 50, 0,
+ 51, 0, 52, 53, 54, 55, 56, 57, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 58, 59, 60, 0, 0,
+ -501, 0, 4, 0, 5, 6, 7, 8, 9, -501,
+ -501, -501, 10, 11, 0, 0, 0, 12, 0, 13,
+ 14, 15, 16, 17, 18, 19, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 0, 0, 27,
+ 0, 0, 0, 0, 0, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 0,
+ 42, 43, 0, 44, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 46, 0, 0, 47, 48, 0, 49,
+ 50, 0, 51, 0, 52, 53, 54, 55, 56, 57,
+ 0, 0, 0, 0, 0, 0, 0, 5, 6, 7,
+ 8, 9, 0, 0, 0, 10, 11, 58, 59, 60,
+ 12, 0, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, -501, -501, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 27, 0, 0, 0, 0, 0, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 0, 0, 47,
+ 48, 0, 49, 50, 0, 51, 0, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 0, 9, 0, 0, 0, 10, 11,
+ 58, 59, 60, 12, 0, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, 0, 395, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 27, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 207,
+ 0, 0, 107, 48, 0, 49, 50, 0, 0, 0,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 58, 59, 60, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 216, 217, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 0, 0, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 0, 0, 0, 0, 0,
+ 151, 152, 153, 154, 155, 156, 157, 158, 36, 37,
+ 159, 39, 0, 0, 0, 0, 0, 0, 160, 161,
+ 162, 163, 164, 165, 0, 166, 167, 0, 0, 168,
+ 0, 0, 0, 169, 170, 171, 172, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 173, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 0, 0, 184, 0, 0, -480, -480, -480,
+ 0, -480, 0, 185, 186, -480, -480, 0, 0, 0,
+ -480, 0, -480, -480, -480, -480, -480, -480, -480, 0,
+ -480, 0, 0, -480, -480, -480, -480, -480, -480, -480,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -480,
+ 0, 0, -480, -480, -480, -480, -480, -480, -480, -480,
+ -480, -480, 0, -480, -480, 0, -480, -480, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -480, 0, 0, -480,
+ -480, 0, -480, -480, 0, -480, -480, -480, -480, -480,
+ -480, -480, -480, 0, 0, 0, 0, 0, 0, 0,
+ -479, -479, -479, 0, -479, 0, 0, 0, -479, -479,
+ -480, -480, -480, -479, -480, -479, -479, -479, -479, -479,
+ -479, -479, -480, -479, 0, 0, -479, -479, -479, -479,
+ -479, -479, -479, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -479, 0, 0, -479, -479, -479, -479, -479,
+ -479, -479, -479, -479, -479, 0, -479, -479, 0, -479,
+ -479, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -479,
+ 0, 0, -479, -479, 0, -479, -479, 0, -479, -479,
+ -479, -479, -479, -479, -479, -479, 0, 0, 0, 0,
+ 0, 0, 0, -481, -481, -481, 0, -481, 0, 0,
+ 0, -481, -481, -479, -479, -479, -481, -479, -481, -481,
+ -481, -481, -481, -481, -481, -479, 0, 0, 0, -481,
+ -481, -481, -481, -481, -481, -481, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -481, 0, 0, -481, -481,
+ -481, -481, -481, -481, -481, -481, -481, -481, 0, -481,
+ -481, 0, -481, -481, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -481, 710, 0, -481, -481, 0, -481, -481,
+ 0, -481, -481, -481, -481, -481, -481, -481, -481, 0,
+ 0, 0, 0, -87, 0, 0, -482, -482, -482, 0,
+ -482, 0, 0, 0, -482, -482, -481, -481, -481, -482,
+ 0, -482, -482, -482, -482, -482, -482, -482, -481, 0,
+ 0, 0, -482, -482, -482, -482, -482, -482, -482, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -482, 0,
+ 0, -482, -482, -482, -482, -482, -482, -482, -482, -482,
+ -482, 0, -482, -482, 0, -482, -482, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -482, 662, 0, -482, -482,
+ 0, -482, -482, 0, -482, -482, -482, -482, -482, -482,
+ -482, -482, 0, 0, 0, 0, -89, 0, 0, -254,
+ -254, -254, 0, -254, 0, 0, 0, -254, -254, -482,
+ -482, -482, -254, 0, -254, -254, -254, -254, -254, -254,
+ -254, -482, 0, 0, 0, -254, -254, -254, -254, -254,
+ -254, -254, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -254, 0, 0, -254, -254, -254, -254, -254, -254,
+ -254, -254, -254, -254, 0, -254, -254, 0, -254, -254,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -254, 0,
+ 0, -254, -254, 0, -254, -254, 0, -254, -254, -254,
+ -254, -254, -254, -254, -254, 0, 0, 0, 0, 0,
+ 0, 0, -254, -254, -254, 0, -254, 0, 0, 0,
+ -254, -254, -254, -254, -254, -254, 0, -254, -254, -254,
+ -254, -254, -254, -254, 244, 0, 0, 0, -254, -254,
+ -254, -254, -254, -254, -254, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -254, 0, 0, -254, -254, -254,
+ -254, -254, -254, -254, -254, -254, -254, 0, -254, -254,
+ 0, -254, -254, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -254, 0, 0, -254, -254, 0, -254, -254, 0,
+ -254, -254, -254, -254, -254, -254, -254, -254, 0, 0,
+ 0, 0, 0, 0, 0, -483, -483, -483, 0, -483,
+ 0, 0, 0, -483, -483, -254, -254, -254, -483, 0,
+ -483, -483, -483, -483, -483, -483, -483, 247, 0, 0,
+ 0, -483, -483, -483, -483, -483, -483, -483, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -483, 0, 0,
+ -483, -483, -483, -483, -483, -483, -483, -483, -483, -483,
+ 0, -483, -483, 0, -483, -483, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -483, 0, 0, -483, -483, 0,
+ -483, -483, 0, -483, -483, -483, -483, -483, -483, -483,
+ -483, 0, 0, 0, 0, 0, 0, 0, -484, -484,
+ -484, 0, -484, 0, 0, 0, -484, -484, -483, -483,
+ -483, -484, 0, -484, -484, -484, -484, -484, -484, -484,
+ -483, 0, 0, 0, -484, -484, -484, -484, -484, -484,
+ -484, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -484, 0, 0, -484, -484, -484, -484, -484, -484, -484,
+ -484, -484, -484, 0, -484, -484, 0, -484, -484, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -484, 0, 0,
+ -484, -484, 0, -484, -484, 0, -484, -484, -484, -484,
+ -484, -484, -484, -484, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -484, -484, -484, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -484, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 0, 0,
+ 141, 142, 143, 192, 193, 194, 195, 148, 149, 150,
+ 0, 0, 0, 0, 0, 151, 152, 153, 154, 196,
+ 197, 198, 158, 281, 282, 199, 283, 0, 0, 0,
+ 0, 0, 0, 160, 161, 162, 163, 164, 165, 0,
+ 166, 167, 0, 0, 168, 0, 0, 0, 169, 170,
+ 171, 172, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 173, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 0, 0, 184,
+ 0, 0, 0, 0, 0, 0, 0, 0, 185, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 0, 0, 141, 142, 143, 192, 193,
+ 194, 195, 148, 149, 150, 0, 0, 0, 0, 0,
+ 151, 152, 153, 154, 196, 197, 198, 158, 251, 0,
+ 199, 0, 0, 0, 0, 0, 0, 0, 160, 161,
+ 162, 163, 164, 165, 0, 166, 167, 0, 0, 168,
+ 0, 0, 0, 169, 170, 171, 172, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 173, 0, 52,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 0, 0, 184, 0, 0, 0, 0, 0,
+ 0, 0, 0, 185, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 0, 0,
+ 141, 142, 143, 192, 193, 194, 195, 148, 149, 150,
+ 0, 0, 0, 0, 0, 151, 152, 153, 154, 196,
+ 197, 198, 158, 0, 0, 199, 0, 0, 0, 0,
+ 0, 0, 0, 160, 161, 162, 163, 164, 165, 0,
+ 166, 167, 0, 0, 168, 0, 0, 0, 169, 170,
+ 171, 172, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 173, 0, 52, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 0, 0, 184,
+ 0, 0, 0, 0, 0, 0, 0, 0, 185, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 0, 0, 141, 142, 143, 192, 193,
+ 194, 195, 148, 149, 150, 0, 0, 0, 0, 0,
+ 151, 152, 153, 154, 196, 197, 198, 158, 0, 0,
+ 199, 0, 0, 0, 0, 0, 0, 0, 160, 161,
+ 162, 163, 164, 165, 0, 166, 167, 0, 0, 168,
+ 0, 0, 0, 169, 170, 171, 172, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 173, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 0, 0, 184, 0, 5, 6, 7, 0,
+ 9, 0, 0, 185, 10, 11, 0, 0, 0, 12,
+ 0, 13, 14, 15, 97, 98, 18, 19, 0, 0,
+ 0, 0, 99, 21, 22, 23, 24, 25, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 0,
+ 0, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 0, 42, 43, 0, 44, 45, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 207, 0, 0, 107, 48,
+ 0, 49, 50, 0, 231, 232, 52, 53, 54, 55,
+ 56, 57, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 8, 9, 58,
+ 233, 60, 10, 11, 0, 0, 0, 12, 411, 13,
+ 14, 15, 16, 17, 18, 19, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 0, 0, 27,
+ 0, 0, 0, 0, 0, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 0,
+ 42, 43, 0, 44, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 46, 0, 0, 47, 48, 0, 49,
+ 50, 0, 51, 0, 52, 53, 54, 55, 56, 57,
+ 0, 0, 0, 0, 0, 0, 0, 5, 6, 7,
+ 0, 9, 0, 0, 0, 10, 11, 58, 59, 60,
+ 12, 0, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 27, 0, 0, 0, 0, 0, 0, 29,
+ 0, 0, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 207, 0, 0, 107,
+ 48, 0, 49, 50, 0, 616, 232, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 0, 9, 0, 0, 0, 10, 11,
+ 58, 59, 60, 12, 0, 13, 14, 15, 97, 98,
+ 18, 19, 0, 0, 0, 0, 99, 21, 22, 23,
+ 24, 25, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 207,
+ 0, 0, 107, 48, 0, 49, 50, 0, 231, 232,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 5, 6, 7, 0, 9, 0, 0,
+ 0, 10, 11, 58, 233, 60, 12, 0, 13, 14,
+ 15, 97, 98, 18, 19, 0, 0, 0, 0, 99,
+ 21, 22, 23, 24, 25, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 29, 0, 0, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 0, 42,
+ 43, 0, 44, 45, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 207, 0, 0, 107, 413, 0, 49, 50,
+ 0, 231, 232, 52, 53, 54, 55, 56, 57, 0,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 0,
+ 9, 0, 0, 0, 10, 11, 58, 233, 60, 12,
+ 0, 13, 14, 15, 97, 98, 18, 19, 0, 0,
+ 0, 0, 99, 100, 101, 23, 24, 25, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 0,
+ 0, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 0, 42, 43, 0, 44, 45, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 207, 0, 0, 107, 48,
+ 0, 49, 50, 0, 608, 232, 52, 53, 54, 55,
+ 56, 57, 0, 0, 0, 0, 0, 0, 0, 5,
+ 6, 7, 0, 9, 0, 0, 0, 10, 11, 58,
+ 233, 60, 12, 0, 13, 14, 15, 97, 98, 18,
+ 19, 0, 0, 0, 0, 99, 100, 101, 23, 24,
+ 25, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 29, 0, 0, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 0, 42, 43, 0, 44, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 207, 0,
+ 0, 107, 48, 0, 49, 50, 0, 612, 232, 52,
+ 53, 54, 55, 56, 57, 0, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 0, 9, 0, 0, 0,
+ 10, 11, 58, 233, 60, 12, 0, 13, 14, 15,
+ 97, 98, 18, 19, 0, 0, 0, 0, 99, 21,
+ 22, 23, 24, 25, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 29, 0, 0, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 0, 42, 43,
+ 0, 44, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 207, 0, 0, 107, 48, 0, 49, 50, 0,
+ 608, 232, 52, 53, 54, 55, 56, 57, 0, 0,
+ 0, 0, 0, 0, 0, 5, 6, 7, 0, 9,
+ 0, 0, 0, 10, 11, 58, 233, 60, 12, 0,
+ 13, 14, 15, 97, 98, 18, 19, 0, 0, 0,
+ 0, 99, 100, 101, 23, 24, 25, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 29, 0, 0,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 0, 42, 43, 0, 44, 45, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 207, 0, 0, 107, 48, 0,
+ 49, 50, 0, 803, 232, 52, 53, 54, 55, 56,
+ 57, 0, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 0, 9, 0, 0, 0, 10, 11, 58, 233,
+ 60, 12, 0, 13, 14, 15, 97, 98, 18, 19,
+ 0, 0, 0, 0, 99, 100, 101, 23, 24, 25,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 0, 42, 43, 0, 44, 45, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 207, 0, 0,
+ 107, 48, 0, 49, 50, 0, 806, 232, 52, 53,
+ 54, 55, 56, 57, 0, 0, 0, 0, 0, 0,
+ 0, 5, 6, 7, 0, 9, 0, 0, 0, 10,
+ 11, 58, 233, 60, 12, 0, 13, 14, 15, 97,
+ 98, 18, 19, 0, 0, 0, 0, 99, 100, 101,
+ 23, 24, 25, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 0, 0, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 0, 42, 43, 0,
+ 44, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 207, 0, 0, 107, 48, 0, 49, 50, 0, 811,
+ 232, 52, 53, 54, 55, 56, 57, 0, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 0, 9, 0,
+ 0, 0, 10, 11, 58, 233, 60, 12, 0, 13,
+ 14, 15, 97, 98, 18, 19, 0, 0, 0, 0,
+ 99, 100, 101, 23, 24, 25, 26, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 29, 0, 0, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 0,
+ 42, 43, 0, 44, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 207, 0, 0, 107, 48, 0, 49,
+ 50, 0, 870, 232, 52, 53, 54, 55, 56, 57,
+ 0, 0, 0, 0, 0, 0, 0, 5, 6, 7,
+ 0, 9, 0, 0, 0, 10, 11, 58, 233, 60,
+ 12, 0, 13, 14, 15, 97, 98, 18, 19, 0,
+ 0, 0, 0, 99, 100, 101, 23, 24, 25, 26,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
+ 0, 0, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 207, 0, 0, 107,
+ 48, 0, 49, 50, 0, 872, 232, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 0, 9, 0, 0, 0, 10, 11,
+ 58, 233, 60, 12, 0, 13, 14, 15, 97, 98,
+ 18, 19, 0, 0, 0, 0, 99, 100, 101, 23,
+ 24, 25, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 207,
+ 0, 0, 107, 48, 0, 49, 50, 0, 891, 232,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 5, 6, 7, 0, 9, 0, 0,
+ 0, 10, 11, 58, 233, 60, 12, 0, 13, 14,
+ 15, 16, 17, 18, 19, 0, 0, 0, 0, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 27, 0,
+ 0, 0, 0, 0, 0, 29, 0, 0, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 0, 42,
+ 43, 0, 44, 45, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 207, 0, 0, 107, 48, 0, 49, 50,
+ 0, 0, 0, 52, 53, 54, 55, 56, 57, 0,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 0,
+ 9, 0, 0, 0, 10, 11, 58, 59, 60, 12,
+ 0, 13, 14, 15, 97, 98, 18, 19, 0, 0,
+ 0, 0, 99, 21, 22, 23, 24, 25, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 0,
+ 0, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 0, 42, 43, 0, 44, 45, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 207, 0, 0, 107, 48,
+ 0, 49, 50, 0, 266, 0, 52, 53, 54, 55,
+ 56, 57, 0, 0, 0, 0, 0, 0, 0, 5,
+ 6, 7, 0, 9, 0, 0, 0, 10, 11, 58,
+ 233, 60, 12, 0, 13, 14, 15, 16, 17, 18,
+ 19, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 29, 0, 0, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 0, 42, 43, 0, 44, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 207, 0,
+ 0, 107, 48, 0, 49, 50, 0, 479, 0, 52,
+ 53, 54, 55, 56, 57, 0, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 0, 9, 0, 0, 0,
+ 10, 11, 58, 233, 60, 12, 0, 13, 14, 15,
+ 97, 98, 18, 19, 0, 0, 0, 0, 99, 100,
+ 101, 23, 24, 25, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 29, 0, 0, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 0, 42, 43,
+ 0, 44, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 207, 0, 0, 107, 48, 0, 49, 50, 0,
+ 590, 0, 52, 53, 54, 55, 56, 57, 0, 0,
+ 0, 0, 0, 0, 0, 5, 6, 7, 0, 9,
+ 0, 0, 0, 10, 11, 58, 233, 60, 12, 0,
+ 13, 14, 15, 97, 98, 18, 19, 0, 0, 0,
+ 0, 99, 100, 101, 23, 24, 25, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 29, 0, 0,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 0, 42, 43, 0, 44, 45, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 207, 0, 0, 107, 48, 0,
+ 49, 50, 0, 638, 0, 52, 53, 54, 55, 56,
+ 57, 0, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 0, 9, 0, 0, 0, 10, 11, 58, 233,
+ 60, 12, 0, 13, 14, 15, 97, 98, 18, 19,
+ 0, 0, 0, 0, 99, 100, 101, 23, 24, 25,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 0, 42, 43, 0, 44, 45, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 207, 0, 0,
+ 107, 48, 0, 49, 50, 0, 479, 0, 52, 53,
+ 54, 55, 56, 57, 0, 0, 0, 0, 0, 0,
+ 0, 5, 6, 7, 0, 9, 0, 0, 0, 10,
+ 11, 58, 233, 60, 12, 0, 13, 14, 15, 97,
+ 98, 18, 19, 0, 0, 0, 0, 99, 100, 101,
+ 23, 24, 25, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 0, 0, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 0, 42, 43, 0,
+ 44, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 207, 0, 0, 107, 48, 0, 49, 50, 0, 754,
+ 0, 52, 53, 54, 55, 56, 57, 0, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 0, 9, 0,
+ 0, 0, 10, 11, 58, 233, 60, 12, 0, 13,
+ 14, 15, 97, 98, 18, 19, 0, 0, 0, 0,
+ 99, 100, 101, 23, 24, 25, 26, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 29, 0, 0, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 0,
+ 42, 43, 0, 44, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 207, 0, 0, 107, 48, 0, 49,
+ 50, 0, 797, 0, 52, 53, 54, 55, 56, 57,
+ 0, 0, 0, 0, 0, 0, 0, 5, 6, 7,
+ 0, 9, 0, 0, 0, 10, 11, 58, 233, 60,
+ 12, 0, 13, 14, 15, 97, 98, 18, 19, 0,
+ 0, 0, 0, 99, 100, 101, 23, 24, 25, 26,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
+ 0, 0, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 44, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 207, 0, 0, 107,
+ 48, 0, 49, 50, 0, 0, 0, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 0, 9, 0, 0, 0, 10, 11,
+ 58, 233, 60, 12, 0, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, 0, 0, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 44,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 207,
+ 0, 0, 107, 48, 0, 49, 50, 0, 0, 0,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 0,
+ 0, 0, 0, 5, 6, 7, 0, 9, 0, 0,
+ 0, 10, 11, 58, 233, 60, 12, 0, 13, 14,
+ 15, 97, 98, 18, 19, 0, 0, 0, 0, 99,
+ 100, 101, 23, 24, 25, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 102, 0, 0, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 0, 42,
+ 43, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 224, 0, 0, 47, 48, 0, 49, 50,
+ 0, 51, 0, 52, 53, 54, 55, 56, 57, 0,
+ 0, 0, 5, 6, 7, 0, 9, 0, 751, 0,
+ 10, 11, 0, 0, 0, 12, 108, 13, 14, 15,
+ 97, 98, 18, 19, 0, 0, 0, 0, 99, 100,
+ 101, 23, 24, 25, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 0, 0, 32, 33, 103,
+ 35, 36, 37, 104, 39, 40, 41, 0, 42, 43,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 105, 0,
+ 0, 106, 0, 0, 107, 48, 0, 49, 50, 0,
+ 0, 0, 52, 53, 54, 55, 56, 57, 0, 0,
+ 0, 5, 6, 7, 0, 9, 0, 0, 0, 10,
+ 11, 0, 0, 0, 12, 108, 13, 14, 15, 97,
+ 98, 18, 19, 0, 0, 0, 0, 99, 100, 101,
+ 23, 24, 25, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 102, 0, 0, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 0, 42, 43, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 224, 0, 0, 47, 48, 0, 49, 50, 0, 51,
+ 0, 52, 53, 54, 55, 56, 57, 0, 0, 0,
+ 5, 6, 7, 0, 9, 0, 0, 0, 10, 11,
+ 0, 0, 0, 12, 108, 13, 14, 15, 97, 98,
+ 18, 19, 0, 0, 0, 0, 99, 100, 101, 23,
+ 24, 25, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 102, 0, 0, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 0, 42, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 276,
+ 0, 0, 312, 48, 0, 49, 50, 0, 313, 0,
+ 52, 53, 54, 55, 56, 57, 0, 0, 0, 5,
+ 6, 7, 0, 9, 0, 0, 0, 10, 11, 0,
+ 0, 0, 12, 108, 13, 14, 15, 97, 98, 18,
+ 19, 0, 0, 0, 0, 99, 100, 101, 23, 24,
+ 25, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 102, 0, 0, 32, 33, 103, 35, 36, 37,
+ 104, 39, 40, 41, 0, 42, 43, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 106, 0,
+ 0, 107, 48, 0, 49, 50, 0, 0, 0, 52,
+ 53, 54, 55, 56, 57, 0, 0, 0, 5, 6,
+ 7, 0, 9, 0, 0, 0, 10, 11, 0, 0,
+ 0, 12, 108, 13, 14, 15, 97, 98, 18, 19,
+ 0, 0, 0, 0, 99, 100, 101, 23, 24, 25,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 102, 0, 0, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 0, 42, 43, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 276, 0, 0,
+ 107, 48, 0, 49, 50, 0, 0, 0, 52, 53,
+ 54, 55, 56, 57, 0, 0, 0, 5, 6, 7,
+ 0, 9, 0, 0, 0, 10, 11, 0, 0, 0,
+ 12, 108, 13, 14, 15, 97, 98, 18, 19, 0,
+ 0, 0, 0, 99, 100, 101, 23, 24, 25, 26,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 102,
+ 0, 0, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 0, 42, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 831, 0, 0, 107,
+ 48, 0, 49, 50, 0, 0, 0, 52, 53, 54,
+ 55, 56, 57, 0, 0, 0, 0, 0, 0, 524,
+ 525, 0, 0, 526, 0, 0, 0, 0, 0, 0,
+ 108, 160, 161, 162, 163, 164, 165, 0, 166, 167,
+ 0, 0, 168, 0, 0, 0, 169, 170, 171, 172,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 173, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 529, 525, 184, 0, 530,
+ 0, 0, 0, 0, 0, 0, 185, 160, 161, 162,
+ 163, 164, 165, 0, 166, 167, 0, 0, 168, 0,
+ 0, 0, 169, 170, 171, 172, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 173, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 545, 518, 184, 0, 546, 0, 0, 0, 0,
+ 0, 0, 185, 160, 161, 162, 163, 164, 165, 0,
+ 166, 167, 0, 0, 168, 0, 0, 0, 169, 170,
+ 171, 172, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 173, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 582, 518, 184,
+ 0, 583, 0, 0, 0, 0, 0, 0, 185, 160,
+ 161, 162, 163, 164, 165, 0, 166, 167, 0, 0,
+ 168, 0, 0, 0, 169, 170, 171, 172, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 173, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 585, 525, 184, 0, 586, 0, 0,
+ 0, 0, 0, 0, 185, 160, 161, 162, 163, 164,
+ 165, 0, 166, 167, 0, 0, 168, 0, 0, 0,
+ 169, 170, 171, 172, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 173, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 599,
+ 518, 184, 0, 600, 0, 0, 0, 0, 0, 0,
+ 185, 160, 161, 162, 163, 164, 165, 0, 166, 167,
+ 0, 0, 168, 0, 0, 0, 169, 170, 171, 172,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 173, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 602, 525, 184, 0, 603,
+ 0, 0, 0, 0, 0, 0, 185, 160, 161, 162,
+ 163, 164, 165, 0, 166, 167, 0, 0, 168, 0,
+ 0, 0, 169, 170, 171, 172, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 173, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 640, 518, 184, 0, 641, 0, 0, 0, 0,
+ 0, 0, 185, 160, 161, 162, 163, 164, 165, 0,
+ 166, 167, 0, 0, 168, 0, 0, 0, 169, 170,
+ 171, 172, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 173, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 643, 525, 184,
+ 0, 644, 0, 0, 0, 0, 0, 0, 185, 160,
+ 161, 162, 163, 164, 165, 0, 166, 167, 0, 0,
+ 168, 0, 0, 0, 169, 170, 171, 172, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 173, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 877, 518, 184, 0, 878, 0, 0,
+ 0, 0, 0, 0, 185, 160, 161, 162, 163, 164,
+ 165, 0, 166, 167, 0, 0, 168, 0, 0, 0,
+ 169, 170, 171, 172, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 173, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 880,
+ 525, 184, 0, 881, 0, 0, 0, 0, 0, 0,
+ 185, 160, 161, 162, 163, 164, 165, 0, 166, 167,
+ 0, 0, 168, 0, 0, 0, 169, 170, 171, 172,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 173, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 0, 185, 316, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 326, 327, 328,
+ 0, 0, 329, 330, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 331, 0, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 0, 253, 329, 330, 0, 0, 0, -220,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 326, 327, 328, 0, 0, 329, 330, 0, 0, 331,
+ 0, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+ 341, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 331, -220, 332, 333, 334, 335, 336, 337, 338, 339,
+ 340, 341, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 605, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 0, 0, 329, 330,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 326, -502, -502, 0, 0, 329, 330, 0, 0, 0,
+ 0, 0, 0, 331, 0, 332, 333, 334, 335, 336,
+ 337, 338, 339, 340, 341, 0, 0, 0, 0, 0,
+ 0, 0, 332, 333, 334, 335, 336, 337, 338, 339,
+ 340, 341, 316, -502, -502, -502, -502, 321, 322, 0,
+ 0, -502, -502, 0, 0, 0, 0, 329, 330, 316,
+ 317, 318, 319, 320, 321, 322, 0, 0, 325, 326,
+ 0, 0, 0, 0, 329, 330, 0, 0, 0, 0,
+ 0, 0, 0, 0, 332, 333, 334, 335, 336, 337,
+ 338, 339, 340, 341, 0, 0, 0, 0, 0, 0,
+ 0, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+ 341
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 16, 17, 7, 8, 20, 343, 215, 16, 17, 15,
+ 370, 20, 16, 17, 7, 22, 20, 16, 17, 247,
+ 8, 20, 8, 28, 22, 397, 51, 409, 44, 45,
+ 368, 5, 6, 49, 50, 391, 106, 315, 62, 692,
+ 28, 15, 28, 59, 60, 27, 50, 52, 386, 28,
+ 49, 372, 531, 532, 13, 11, 12, 577, 26, 52,
+ 442, 13, 400, 4, 568, 12, 570, 587, 26, 47,
+ 287, 36, 37, 0, 291, 533, 534, 51, 261, 1,
+ 92, 689, 59, 58, 692, 25, 130, 94, 58, 13,
+ 81, 25, 81, 83, 101, 664, 94, 71, 110, 17,
+ 84, 670, 20, 15, 54, 17, 13, 64, 115, 72,
+ 25, 129, 450, 103, 83, 62, 342, 120, 344, 345,
+ 346, 83, 102, 105, 99, 100, 16, 17, 129, 312,
+ 20, 101, 28, 124, 103, 124, 126, 36, 37, 130,
+ 50, 103, 126, 369, 54, 108, 105, 83, 130, 131,
+ 56, 57, 120, 105, 129, 26, 113, 114, 115, 385,
+ 50, 387, 120, 110, 126, 130, 26, 26, 108, 128,
+ 128, 130, 131, 826, 108, 401, 81, 103, 130, 131,
+ 15, 105, 17, 126, 544, 107, 126, 2, 128, 4,
+ 5, 6, 126, 108, 218, 10, 11, 12, 105, 103,
+ 15, 16, 17, 563, 430, 20, 130, 131, 130, 131,
+ 81, 126, 27, 128, 94, 231, 232, 233, 826, 124,
+ 678, 81, 81, 130, 131, 451, 125, 128, 244, 81,
+ 246, 247, 47, 48, 49, 244, 51, 246, 247, 128,
+ 244, 562, 246, 247, 59, 244, 463, 246, 247, 120,
+ 266, 122, 81, 124, 130, 131, 71, 128, 737, 738,
+ 120, 120, 122, 122, 124, 124, 222, 223, 128, 128,
+ 25, 218, 124, 126, 52, 779, 780, 25, 798, 54,
+ 784, 59, 60, 261, 25, 36, 37, 267, 313, 58,
+ 105, 103, 107, 122, 310, 124, 83, 367, 576, 315,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 326, 327, 328, 329, 330, 331, 332, 333, 334, 335,
+ 336, 337, 338, 339, 340, 341, 244, 343, 246, 247,
+ 99, 100, 101, 310, 312, 351, 352, 342, 315, 313,
+ 131, 83, 54, 725, 343, 301, 302, 303, 304, 25,
+ 732, 723, 368, 108, 244, 859, 246, 247, 84, 300,
+ 108, 103, 367, 81, 305, 25, 83, 108, 384, 368,
+ 386, 126, 388, 389, 351, 352, 25, 393, 126, 25,
+ 385, 56, 57, 592, 400, 126, 103, 386, 126, 405,
+ 406, 206, 25, 409, 399, 126, 624, 413, 240, 625,
+ 626, 400, 406, 128, 413, 120, 124, 222, 223, 413,
+ 426, 771, 14, 15, 81, 431, 363, 81, 81, 424,
+ 310, 125, 126, 439, 429, 315, 442, 83, 444, 244,
+ 64, 246, 247, 2, 450, 125, 424, 417, 424, 419,
+ 125, 256, 798, 103, 449, 424, 261, 103, 108, 396,
+ 397, 450, 468, 469, 103, 435, 436, 124, 64, 108,
+ 124, 124, 108, 479, 785, 125, 126, 125, 850, 50,
+ 830, 52, 53, 54, 55, 108, 125, 126, 47, 50,
+ 126, 52, 53, 54, 55, 300, 301, 302, 303, 304,
+ 305, 306, 307, 126, 123, 310, 121, 312, 313, 83,
+ 315, 64, 50, 393, 886, 111, 112, 113, 114, 115,
+ 91, 92, 54, 55, 521, 83, 79, 80, 126, 103,
+ 91, 92, 130, 521, 54, 863, 101, 126, 343, 110,
+ 528, 130, 113, 531, 532, 103, 351, 352, 107, 110,
+ 58, 548, 113, 91, 92, 101, 81, 128, 111, 112,
+ 113, 114, 115, 368, 81, 370, 371, 64, 81, 83,
+ 576, 403, 110, 64, 126, 113, 81, 382, 573, 574,
+ 83, 386, 552, 58, 590, 390, 81, 392, 787, 103,
+ 573, 99, 100, 101, 793, 400, 574, 543, 574, 124,
+ 103, 90, 608, 10, 541, 574, 612, 124, 413, 122,
+ 616, 124, 126, 13, 8, 128, 126, 122, 624, 124,
+ 130, 64, 427, 126, 99, 100, 101, 564, 17, 124,
+ 624, 10, 638, 58, 126, 624, 79, 80, 125, 2,
+ 81, 4, 5, 6, 7, 450, 126, 125, 864, 81,
+ 81, 657, 15, 659, 660, 661, 662, 653, 604, 121,
+ 666, 667, 81, 126, 125, 108, 109, 110, 111, 112,
+ 113, 114, 115, 129, 99, 100, 101, 81, 83, 685,
+ 126, 122, 126, 124, 47, 125, 81, 123, 51, 653,
+ 122, 122, 124, 124, 661, 123, 576, 256, 103, 666,
+ 667, 10, 261, 122, 710, 124, 712, 713, 71, 81,
+ 103, 126, 718, 126, 81, 50, 686, 83, 122, 725,
+ 124, 126, 81, 729, 126, 50, 732, 122, 15, 124,
+ 729, 81, 123, 52, 53, 729, 55, 103, 543, 544,
+ 59, 60, 58, 680, 107, 61, 10, 270, 754, 272,
+ 122, 274, 124, 312, 10, 122, 762, 124, 563, 92,
+ 126, 707, 126, 122, 126, 124, 736, 2, 123, 4,
+ 126, 125, 122, 743, 124, 10, 11, 12, 125, 611,
+ 125, 16, 17, 99, 100, 20, 723, 121, 64, 795,
+ 622, 797, 27, 125, 123, 762, 10, 803, 125, 604,
+ 806, 10, 84, 79, 80, 811, 39, 40, 41, 42,
+ 43, 9, 47, 48, 49, 49, 50, 10, 10, 624,
+ 125, 121, 786, 382, 59, 630, 125, 108, 126, 126,
+ 10, 390, 10, 392, 110, 111, 112, 113, 114, 115,
+ 645, 126, 848, 206, 850, 10, 126, 125, 653, 729,
+ 10, 121, 10, 54, 848, 121, 661, 863, 10, 10,
+ 71, 666, 667, 126, 870, 123, 872, 862, 427, 6,
+ 105, 653, 107, 78, 863, 81, 866, 7, 865, 785,
+ 886, 562, 689, 689, -1, 891, 62, -1, -1, -1,
+ -1, -1, 724, 256, -1, 727, 728, -1, 261, 704,
+ -1, -1, 707, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 719, 720, 721, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 300, -1, 2,
+ -1, 4, 305, -1, -1, -1, -1, -1, 753, 312,
+ 313, -1, 15, -1, -1, -1, -1, 762, -1, -1,
+ -1, -1, -1, 768, 769, -1, 771, -1, -1, -1,
+ -1, 206, -1, -1, -1, -1, 808, 809, 10, 11,
+ 12, 786, -1, -1, 47, 790, -1, 222, 223, -1,
+ -1, -1, -1, -1, -1, 27, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 239, 240, 370, -1, 244,
+ -1, 246, 247, 247, 846, 847, 48, -1, 823, 382,
+ 852, 256, -1, -1, -1, 830, 261, 390, -1, 392,
+ -1, 836, -1, 838, -1, -1, -1, -1, -1, 871,
+ 845, -1, -1, -1, 107, -1, -1, -1, -1, -1,
+ -1, -1, -1, 885, -1, -1, 888, -1, 863, -1,
+ -1, 893, -1, -1, 427, 300, 301, 302, 303, 304,
+ 305, 306, 307, 105, -1, 310, -1, 312, -1, -1,
+ 315, -1, -1, -1, -1, -1, 645, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 343, 343,
+ -1, -1, -1, -1, -1, -1, 351, 352, -1, 2,
+ -1, 4, 5, 6, -1, -1, -1, -1, -1, -1,
+ -1, -1, 15, 368, 368, 370, 371, -1, -1, -1,
+ -1, -1, -1, 206, 378, 704, -1, 382, -1, -1,
+ -1, 386, 386, -1, -1, 390, -1, 392, -1, -1,
+ 719, 720, 721, -1, 47, 400, 400, -1, 51, 403,
+ -1, -1, -1, -1, -1, -1, -1, -1, 413, -1,
+ -1, 544, -1, -1, 418, -1, -1, -1, 71, -1,
+ 222, 223, 427, 256, 753, -1, -1, -1, 261, -1,
+ 563, -1, -1, -1, -1, -1, -1, -1, -1, 768,
+ 769, -1, -1, -1, -1, 450, 450, -1, -1, -1,
+ -1, -1, -1, -1, 107, -1, -1, -1, -1, -1,
+ -1, 790, -1, -1, -1, -1, -1, 300, -1, -1,
+ -1, -1, 305, -1, 478, -1, -1, -1, -1, 312,
+ -1, -1, -1, -1, -1, -1, -1, -1, 2, -1,
+ 4, -1, -1, -1, 823, -1, -1, -1, -1, 301,
+ 302, 303, 304, -1, 306, 307, -1, 836, -1, 838,
+ -1, -1, 645, -1, -1, -1, 845, -1, -1, -1,
+ 653, -1, -1, -1, 528, -1, -1, 531, 532, 533,
+ 534, -1, -1, 47, -1, -1, -1, 370, 543, 544,
+ -1, -1, -1, -1, -1, -1, 550, -1, -1, 382,
+ -1, -1, -1, 206, -1, -1, -1, 390, 563, 392,
+ -1, -1, 566, -1, 568, -1, 570, -1, -1, 371,
+ -1, 704, 576, 577, -1, 579, -1, -1, -1, -1,
+ -1, -1, -1, 587, -1, -1, 719, 720, 721, -1,
+ -1, -1, -1, 107, 427, -1, -1, -1, -1, 604,
+ -1, -1, -1, 256, -1, -1, -1, 611, 261, -1,
+ -1, 413, -1, -1, -1, -1, -1, -1, 622, 624,
+ 753, -1, -1, -1, -1, 630, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 768, 769, -1, 771, -1,
+ 645, -1, -1, -1, -1, -1, -1, 300, -1, -1,
+ -1, -1, 305, 786, -1, -1, 661, 790, -1, 312,
+ 313, 666, 667, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 678, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 823, -1, 206, -1, -1, -1, -1, 830, 702, 704,
+ -1, 705, 707, 836, -1, 838, -1, -1, -1, -1,
+ -1, 544, 845, -1, 719, 720, 721, 370, -1, -1,
+ 724, -1, -1, 727, 728, -1, -1, -1, -1, 382,
+ 563, -1, -1, 737, 738, -1, -1, 390, -1, 392,
+ -1, 543, 256, -1, -1, -1, -1, 261, 753, -1,
+ -1, -1, -1, -1, -1, -1, -1, 762, -1, -1,
+ -1, -1, -1, 768, 769, -1, 771, -1, -1, -1,
+ -1, -1, -1, -1, 427, 779, 780, -1, -1, -1,
+ 784, -1, -1, -1, -1, 790, 300, -1, -1, -1,
+ -1, 305, -1, -1, 798, -1, -1, -1, 312, -1,
+ -1, -1, 604, -1, 808, 809, -1, -1, -1, -1,
+ -1, -1, 645, -1, -1, -1, -1, -1, 823, -1,
+ 653, -1, -1, -1, -1, 830, -1, -1, 630, -1,
+ -1, 836, -1, 838, -1, -1, -1, -1, -1, -1,
+ 845, -1, 846, 847, -1, -1, -1, -1, 852, 231,
+ 232, -1, -1, -1, -1, 859, 370, -1, 863, 863,
+ -1, 865, 866, -1, -1, -1, -1, 871, 382, -1,
+ -1, 704, -1, -1, -1, -1, 390, -1, 392, -1,
+ -1, 885, -1, -1, 888, -1, 719, 720, 721, 893,
+ -1, 544, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 51, -1, 707, 54, -1, -1, -1,
+ 563, -1, -1, 427, 62, 63, 64, 65, 66, 67,
+ 753, 69, 70, -1, -1, 73, -1, -1, 310, 77,
+ 78, 79, 80, 315, -1, 768, 769, -1, 771, -1,
+ -1, -1, -1, 91, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 786, -1, -1, -1, 790, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, -1, -1,
+ 118, 64, 65, 66, 67, 68, 69, 70, 71, 127,
+ 73, 74, -1, -1, -1, -1, 79, 80, -1, -1,
+ 823, -1, 645, 646, -1, -1, -1, 830, -1, -1,
+ 653, -1, -1, 836, -1, 838, -1, -1, -1, -1,
+ -1, 393, 845, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 405, 406, -1, -1, 409, -1, -1,
+ 544, 413, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 563,
+ -1, 704, -1, -1, -1, -1, -1, 439, 43, -1,
+ 442, -1, 444, -1, -1, -1, 719, 720, 721, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, -1, 79, 80, -1, 479, -1, -1,
+ 753, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 768, 769, -1, 771, 104,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 645, -1, 786, -1, -1, -1, 790, -1, 0,
+ -1, 126, -1, -1, -1, -1, -1, 8, 9, 10,
+ -1, -1, 13, 14, 15, -1, 17, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 27, -1, -1, -1,
+ 823, -1, -1, -1, -1, 36, 37, 830, 39, 40,
+ 41, 42, 43, 836, -1, 838, -1, -1, -1, -1,
+ 704, -1, 845, -1, 576, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 719, 720, 721, 590, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 81, -1, -1, -1, -1, -1, 608, -1, -1, -1,
+ 612, -1, -1, -1, 616, -1, -1, -1, -1, 753,
+ -1, -1, 624, -1, 105, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 768, 769, -1, 771, -1, -1,
+ 121, -1, -1, 124, 125, -1, -1, 128, -1, 130,
+ 131, -1, -1, -1, -1, 657, 790, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 685, -1, -1, -1, -1, -1, 823,
+ -1, -1, -1, -1, -1, -1, 830, -1, -1, -1,
+ -1, -1, 836, -1, 838, -1, -1, -1, -1, -1,
+ -1, 845, -1, -1, -1, -1, 718, -1, -1, -1,
+ -1, -1, -1, 725, -1, -1, -1, -1, -1, -1,
+ 732, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ -1, -1, 754, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, 26, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 797, 59, 60, -1, 62,
+ 63, -1, -1, -1, 806, -1, -1, -1, -1, 811,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, 92,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 848, -1, 850, -1,
+ -1, -1, -1, 116, 117, 118, -1, 120, -1, -1,
+ -1, -1, -1, -1, -1, 128, -1, -1, 870, -1,
+ 872, -1, -1, -1, 0, 1, -1, 3, 4, 5,
+ 6, 7, -1, -1, 886, 11, 12, -1, -1, 891,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, 38, -1, -1, -1, -1, -1, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, -1, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ 116, 117, 118, 8, 9, 10, -1, -1, -1, 14,
+ 15, -1, 17, -1, 130, 131, -1, -1, 43, -1,
+ -1, 26, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 36, 37, -1, 39, 40, 41, 42, 43, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, -1, 79, 80, -1, -1, -1, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, -1, 79, 80, 81, -1, 83, 104,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, -1, -1, -1, -1, -1, -1, -1, 103, 104,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, -1, -1, -1, 0, 120, 121, 122, -1, 124,
+ 125, 126, 8, 9, 10, 130, 131, -1, 14, 15,
+ -1, 17, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, 37, -1, 39, 40, 41, 42, 43, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, -1, -1, 79, 80, -1, -1, -1, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, -1, -1, 79, 80, 81, -1, 83, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ -1, -1, -1, -1, -1, -1, -1, 103, 104, -1,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ -1, -1, -1, -1, -1, 121, 122, -1, 124, 125,
+ 126, -1, -1, -1, 130, 131, 1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
+ 15, 16, -1, 18, 19, 20, 21, 22, 23, 24,
+ -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ 35, -1, -1, 38, -1, -1, -1, -1, -1, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, -1, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 82, -1, -1,
+ 85, 86, -1, 88, 89, -1, 91, -1, 93, 94,
+ 95, 96, 97, 98, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 116, 117, 118, -1, -1, -1, -1, 1, -1,
+ 3, 4, 5, 6, 7, 130, 131, 10, 11, 12,
+ -1, 14, 15, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, 38, -1, -1, -1, -1,
+ -1, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, -1,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 116, 117, 118, -1, -1, -1, -1,
+ 1, -1, 3, 4, 5, 6, 7, 130, 131, 10,
+ 11, 12, -1, -1, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, 38, -1, -1,
+ -1, -1, -1, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ 91, -1, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 116, 117, 118, -1, -1,
+ -1, -1, 1, -1, 3, 4, 5, 6, 7, 130,
+ 131, 10, 11, 12, -1, -1, 15, 16, -1, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, -1, -1, 38,
+ -1, -1, -1, -1, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, -1,
+ 59, 60, -1, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 82, -1, -1, 85, 86, -1, 88,
+ 89, -1, 91, -1, 93, 94, 95, 96, 97, 98,
+ -1, -1, -1, -1, -1, 1, -1, 3, 4, 5,
+ 6, 7, -1, 9, 10, 11, 12, 116, 117, 118,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, 130, 131, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, 38, -1, -1, -1, -1, -1, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, -1, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, 1, -1,
+ 3, 4, 5, 6, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, 130, 131, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, 38, -1, -1, -1, -1,
+ -1, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, -1,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 116, 117, 118, -1, -1, -1, -1,
+ -1, -1, 125, -1, -1, -1, -1, 130, 131, 1,
+ -1, 3, 4, 5, 6, 7, -1, -1, -1, 11,
+ 12, -1, -1, -1, 16, -1, 18, 19, 20, 21,
+ 22, 23, 24, -1, -1, -1, -1, 29, 30, 31,
+ 32, 33, 34, 35, -1, -1, 38, -1, -1, -1,
+ -1, -1, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, -1, 59, 60, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 82, -1, -1, 85, 86, -1, 88, 89, -1, 91,
+ -1, 93, 94, 95, 96, 97, 98, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 116, 117, 118, -1, -1, -1,
+ -1, -1, -1, 125, -1, -1, -1, -1, 130, 131,
+ 1, -1, 3, 4, 5, 6, 7, -1, -1, -1,
+ 11, 12, -1, -1, -1, 16, -1, 18, 19, 20,
+ 21, 22, 23, 24, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, 38, -1, -1,
+ -1, -1, -1, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ 91, -1, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 116, 117, 118, -1, -1,
+ 121, -1, 1, -1, 3, 4, 5, 6, 7, 130,
+ 131, 10, 11, 12, -1, -1, -1, 16, -1, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, -1, -1, 38,
+ -1, -1, -1, -1, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, -1,
+ 59, 60, -1, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 82, -1, -1, 85, 86, -1, 88,
+ 89, -1, 91, -1, 93, 94, 95, 96, 97, 98,
+ -1, -1, -1, -1, -1, -1, -1, 3, 4, 5,
+ 6, 7, -1, -1, -1, 11, 12, 116, 117, 118,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, 130, 131, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, 38, -1, -1, -1, -1, -1, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, -1, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, 131, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, 38, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, -1, -1,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 116, 117, 118, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 130, 131, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, -1, -1, -1, -1, -1,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, -1, -1, -1, -1, -1, -1, 62, 63,
+ 64, 65, 66, 67, -1, 69, 70, -1, -1, 73,
+ -1, -1, -1, 77, 78, 79, 80, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 91, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, -1, -1, 118, -1, -1, 3, 4, 5,
+ -1, 7, -1, 127, 128, 11, 12, -1, -1, -1,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ 26, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, 92, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, 120, 18, 19, 20, 21, 22,
+ 23, 24, 128, 26, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, 92,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, 3, 4, 5, -1, 7, -1, -1,
+ -1, 11, 12, 116, 117, 118, 16, 120, 18, 19,
+ 20, 21, 22, 23, 24, 128, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 45, -1, -1, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, -1, 59,
+ 60, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 82, 83, -1, 85, 86, -1, 88, 89,
+ -1, 91, 92, 93, 94, 95, 96, 97, 98, -1,
+ -1, -1, -1, 103, -1, -1, 3, 4, 5, -1,
+ 7, -1, -1, -1, 11, 12, 116, 117, 118, 16,
+ -1, 18, 19, 20, 21, 22, 23, 24, 128, -1,
+ -1, -1, 29, 30, 31, 32, 33, 34, 35, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, -1, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 82, 83, -1, 85, 86,
+ -1, 88, 89, -1, 91, 92, 93, 94, 95, 96,
+ 97, 98, -1, -1, -1, -1, 103, -1, -1, 3,
+ 4, 5, -1, 7, -1, -1, -1, 11, 12, 116,
+ 117, 118, 16, -1, 18, 19, 20, 21, 22, 23,
+ 24, 128, -1, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 45, -1, -1, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, -1, 59, 60, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 82, -1,
+ -1, 85, 86, -1, 88, 89, -1, 91, 92, 93,
+ 94, 95, 96, 97, 98, -1, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, -1, 7, -1, -1, -1,
+ 11, 12, 116, 117, 118, 16, -1, 18, 19, 20,
+ 21, 22, 23, 24, 128, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ 91, 92, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, -1, -1, -1, -1, 3, 4, 5, -1, 7,
+ -1, -1, -1, 11, 12, 116, 117, 118, 16, -1,
+ 18, 19, 20, 21, 22, 23, 24, 128, -1, -1,
+ -1, 29, 30, 31, 32, 33, 34, 35, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45, -1, -1,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ -1, 59, 60, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, 85, 86, -1,
+ 88, 89, -1, 91, 92, 93, 94, 95, 96, 97,
+ 98, -1, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, -1, 7, -1, -1, -1, 11, 12, 116, 117,
+ 118, 16, -1, 18, 19, 20, 21, 22, 23, 24,
+ 128, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ 35, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, -1, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 82, -1, -1,
+ 85, 86, -1, 88, 89, -1, 91, 92, 93, 94,
+ 95, 96, 97, 98, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 116, 117, 118, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 128, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ -1, -1, -1, -1, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, -1, -1, -1,
+ -1, -1, -1, 62, 63, 64, 65, 66, 67, -1,
+ 69, 70, -1, -1, 73, -1, -1, -1, 77, 78,
+ 79, 80, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, -1, -1, 118,
+ -1, -1, -1, -1, -1, -1, -1, -1, 127, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, -1, -1, -1, -1, -1,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, -1,
+ 54, -1, -1, -1, -1, -1, -1, -1, 62, 63,
+ 64, 65, 66, 67, -1, 69, 70, -1, -1, 73,
+ -1, -1, -1, 77, 78, 79, 80, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 91, -1, 93,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, -1, -1, 118, -1, -1, -1, -1, -1,
+ -1, -1, -1, 127, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ -1, -1, -1, -1, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, 54, -1, -1, -1, -1,
+ -1, -1, -1, 62, 63, 64, 65, 66, 67, -1,
+ 69, 70, -1, -1, 73, -1, -1, -1, 77, 78,
+ 79, 80, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, 93, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, -1, -1, 118,
+ -1, -1, -1, -1, -1, -1, -1, -1, 127, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, -1, -1, -1, -1, -1,
+ 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
+ 54, -1, -1, -1, -1, -1, -1, -1, 62, 63,
+ 64, 65, 66, 67, -1, 69, 70, -1, -1, 73,
+ -1, -1, -1, 77, 78, 79, 80, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 91, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, -1, -1, 118, -1, 3, 4, 5, -1,
+ 7, -1, -1, 127, 11, 12, -1, -1, -1, 16,
+ -1, 18, 19, 20, 21, 22, 23, 24, -1, -1,
+ -1, -1, 29, 30, 31, 32, 33, 34, 35, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, -1, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 82, -1, -1, 85, 86,
+ -1, 88, 89, -1, 91, 92, 93, 94, 95, 96,
+ 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 7, 116,
+ 117, 118, 11, 12, -1, -1, -1, 16, 125, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, -1, -1, 38,
+ -1, -1, -1, -1, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, -1,
+ 59, 60, -1, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 82, -1, -1, 85, 86, -1, 88,
+ 89, -1, 91, -1, 93, 94, 95, 96, 97, 98,
+ -1, -1, -1, -1, -1, -1, -1, 3, 4, 5,
+ -1, 7, -1, -1, -1, 11, 12, 116, 117, 118,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, 38, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, 92, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, 92,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, 3, 4, 5, -1, 7, -1, -1,
+ -1, 11, 12, 116, 117, 118, 16, -1, 18, 19,
+ 20, 21, 22, 23, 24, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 45, -1, -1, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, -1, 59,
+ 60, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 82, -1, -1, 85, 86, -1, 88, 89,
+ -1, 91, 92, 93, 94, 95, 96, 97, 98, -1,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, -1,
+ 7, -1, -1, -1, 11, 12, 116, 117, 118, 16,
+ -1, 18, 19, 20, 21, 22, 23, 24, -1, -1,
+ -1, -1, 29, 30, 31, 32, 33, 34, 35, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, -1, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 82, -1, -1, 85, 86,
+ -1, 88, 89, -1, 91, 92, 93, 94, 95, 96,
+ 97, 98, -1, -1, -1, -1, -1, -1, -1, 3,
+ 4, 5, -1, 7, -1, -1, -1, 11, 12, 116,
+ 117, 118, 16, -1, 18, 19, 20, 21, 22, 23,
+ 24, -1, -1, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 45, -1, -1, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, -1, 59, 60, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 82, -1,
+ -1, 85, 86, -1, 88, 89, -1, 91, 92, 93,
+ 94, 95, 96, 97, 98, -1, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, -1, 7, -1, -1, -1,
+ 11, 12, 116, 117, 118, 16, -1, 18, 19, 20,
+ 21, 22, 23, 24, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ 91, 92, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, -1, -1, -1, -1, 3, 4, 5, -1, 7,
+ -1, -1, -1, 11, 12, 116, 117, 118, 16, -1,
+ 18, 19, 20, 21, 22, 23, 24, -1, -1, -1,
+ -1, 29, 30, 31, 32, 33, 34, 35, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45, -1, -1,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ -1, 59, 60, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, 85, 86, -1,
+ 88, 89, -1, 91, 92, 93, 94, 95, 96, 97,
+ 98, -1, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, -1, 7, -1, -1, -1, 11, 12, 116, 117,
+ 118, 16, -1, 18, 19, 20, 21, 22, 23, 24,
+ -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ 35, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, -1, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 82, -1, -1,
+ 85, 86, -1, 88, 89, -1, 91, 92, 93, 94,
+ 95, 96, 97, 98, -1, -1, -1, -1, -1, -1,
+ -1, 3, 4, 5, -1, 7, -1, -1, -1, 11,
+ 12, 116, 117, 118, 16, -1, 18, 19, 20, 21,
+ 22, 23, 24, -1, -1, -1, -1, 29, 30, 31,
+ 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 45, -1, -1, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, -1, 59, 60, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 82, -1, -1, 85, 86, -1, 88, 89, -1, 91,
+ 92, 93, 94, 95, 96, 97, 98, -1, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, -1, 7, -1,
+ -1, -1, 11, 12, 116, 117, 118, 16, -1, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, -1,
+ 59, 60, -1, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 82, -1, -1, 85, 86, -1, 88,
+ 89, -1, 91, 92, 93, 94, 95, 96, 97, 98,
+ -1, -1, -1, -1, -1, -1, -1, 3, 4, 5,
+ -1, 7, -1, -1, -1, 11, 12, 116, 117, 118,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, 91, 92, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, 92,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, 3, 4, 5, -1, 7, -1, -1,
+ -1, 11, 12, 116, 117, 118, 16, -1, 18, 19,
+ 20, 21, 22, 23, 24, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, -1, -1, 38, -1,
+ -1, -1, -1, -1, -1, 45, -1, -1, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, -1, 59,
+ 60, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 82, -1, -1, 85, 86, -1, 88, 89,
+ -1, -1, -1, 93, 94, 95, 96, 97, 98, -1,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, -1,
+ 7, -1, -1, -1, 11, 12, 116, 117, 118, 16,
+ -1, 18, 19, 20, 21, 22, 23, 24, -1, -1,
+ -1, -1, 29, 30, 31, 32, 33, 34, 35, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, -1, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 82, -1, -1, 85, 86,
+ -1, 88, 89, -1, 91, -1, 93, 94, 95, 96,
+ 97, 98, -1, -1, -1, -1, -1, -1, -1, 3,
+ 4, 5, -1, 7, -1, -1, -1, 11, 12, 116,
+ 117, 118, 16, -1, 18, 19, 20, 21, 22, 23,
+ 24, -1, -1, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 45, -1, -1, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, -1, 59, 60, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 82, -1,
+ -1, 85, 86, -1, 88, 89, -1, 91, -1, 93,
+ 94, 95, 96, 97, 98, -1, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, -1, 7, -1, -1, -1,
+ 11, 12, 116, 117, 118, 16, -1, 18, 19, 20,
+ 21, 22, 23, 24, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ 91, -1, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, -1, -1, -1, -1, 3, 4, 5, -1, 7,
+ -1, -1, -1, 11, 12, 116, 117, 118, 16, -1,
+ 18, 19, 20, 21, 22, 23, 24, -1, -1, -1,
+ -1, 29, 30, 31, 32, 33, 34, 35, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45, -1, -1,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ -1, 59, 60, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, 85, 86, -1,
+ 88, 89, -1, 91, -1, 93, 94, 95, 96, 97,
+ 98, -1, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, -1, 7, -1, -1, -1, 11, 12, 116, 117,
+ 118, 16, -1, 18, 19, 20, 21, 22, 23, 24,
+ -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ 35, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, -1, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 82, -1, -1,
+ 85, 86, -1, 88, 89, -1, 91, -1, 93, 94,
+ 95, 96, 97, 98, -1, -1, -1, -1, -1, -1,
+ -1, 3, 4, 5, -1, 7, -1, -1, -1, 11,
+ 12, 116, 117, 118, 16, -1, 18, 19, 20, 21,
+ 22, 23, 24, -1, -1, -1, -1, 29, 30, 31,
+ 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 45, -1, -1, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, -1, 59, 60, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 82, -1, -1, 85, 86, -1, 88, 89, -1, 91,
+ -1, 93, 94, 95, 96, 97, 98, -1, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, -1, 7, -1,
+ -1, -1, 11, 12, 116, 117, 118, 16, -1, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, -1,
+ 59, 60, -1, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 82, -1, -1, 85, 86, -1, 88,
+ 89, -1, 91, -1, 93, 94, 95, 96, 97, 98,
+ -1, -1, -1, -1, -1, -1, -1, 3, 4, 5,
+ -1, 7, -1, -1, -1, 11, 12, 116, 117, 118,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, -1, -1, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ 116, 117, 118, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, 62,
+ 63, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, -1, -1,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, -1,
+ -1, -1, -1, 3, 4, 5, -1, 7, -1, -1,
+ -1, 11, 12, 116, 117, 118, 16, -1, 18, 19,
+ 20, 21, 22, 23, 24, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 45, -1, -1, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, -1, 59,
+ 60, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 82, -1, -1, 85, 86, -1, 88, 89,
+ -1, 91, -1, 93, 94, 95, 96, 97, 98, -1,
+ -1, -1, 3, 4, 5, -1, 7, -1, 108, -1,
+ 11, 12, -1, -1, -1, 16, 116, 18, 19, 20,
+ 21, 22, 23, 24, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, -1, 59, 60,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 79, -1,
+ -1, 82, -1, -1, 85, 86, -1, 88, 89, -1,
+ -1, -1, 93, 94, 95, 96, 97, 98, -1, -1,
+ -1, 3, 4, 5, -1, 7, -1, -1, -1, 11,
+ 12, -1, -1, -1, 16, 116, 18, 19, 20, 21,
+ 22, 23, 24, -1, -1, -1, -1, 29, 30, 31,
+ 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 45, -1, -1, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, -1, 59, 60, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 82, -1, -1, 85, 86, -1, 88, 89, -1, 91,
+ -1, 93, 94, 95, 96, 97, 98, -1, -1, -1,
+ 3, 4, 5, -1, 7, -1, -1, -1, 11, 12,
+ -1, -1, -1, 16, 116, 18, 19, 20, 21, 22,
+ 23, 24, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, -1, 59, 60, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, 85, 86, -1, 88, 89, -1, 91, -1,
+ 93, 94, 95, 96, 97, 98, -1, -1, -1, 3,
+ 4, 5, -1, 7, -1, -1, -1, 11, 12, -1,
+ -1, -1, 16, 116, 18, 19, 20, 21, 22, 23,
+ 24, -1, -1, -1, -1, 29, 30, 31, 32, 33,
+ 34, 35, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 45, -1, -1, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, -1, 59, 60, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 82, -1,
+ -1, 85, 86, -1, 88, 89, -1, -1, -1, 93,
+ 94, 95, 96, 97, 98, -1, -1, -1, 3, 4,
+ 5, -1, 7, -1, -1, -1, 11, 12, -1, -1,
+ -1, 16, 116, 18, 19, 20, 21, 22, 23, 24,
+ -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ 35, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, -1, 59, 60, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 82, -1, -1,
+ 85, 86, -1, 88, 89, -1, -1, -1, 93, 94,
+ 95, 96, 97, 98, -1, -1, -1, 3, 4, 5,
+ -1, 7, -1, -1, -1, 11, 12, -1, -1, -1,
+ 16, 116, 18, 19, 20, 21, 22, 23, 24, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, -1, 59, 60, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 82, -1, -1, 85,
+ 86, -1, 88, 89, -1, -1, -1, 93, 94, 95,
+ 96, 97, 98, -1, -1, -1, -1, -1, -1, 50,
+ 51, -1, -1, 54, -1, -1, -1, -1, -1, -1,
+ 116, 62, 63, 64, 65, 66, 67, -1, 69, 70,
+ -1, -1, 73, -1, -1, -1, 77, 78, 79, 80,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 50, 51, 118, -1, 54,
+ -1, -1, -1, -1, -1, -1, 127, 62, 63, 64,
+ 65, 66, 67, -1, 69, 70, -1, -1, 73, -1,
+ -1, -1, 77, 78, 79, 80, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 50, 51, 118, -1, 54, -1, -1, -1, -1,
+ -1, -1, 127, 62, 63, 64, 65, 66, 67, -1,
+ 69, 70, -1, -1, 73, -1, -1, -1, 77, 78,
+ 79, 80, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 50, 51, 118,
+ -1, 54, -1, -1, -1, -1, -1, -1, 127, 62,
+ 63, 64, 65, 66, 67, -1, 69, 70, -1, -1,
+ 73, -1, -1, -1, 77, 78, 79, 80, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 91, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 50, 51, 118, -1, 54, -1, -1,
+ -1, -1, -1, -1, 127, 62, 63, 64, 65, 66,
+ 67, -1, 69, 70, -1, -1, 73, -1, -1, -1,
+ 77, 78, 79, 80, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 91, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 50,
+ 51, 118, -1, 54, -1, -1, -1, -1, -1, -1,
+ 127, 62, 63, 64, 65, 66, 67, -1, 69, 70,
+ -1, -1, 73, -1, -1, -1, 77, 78, 79, 80,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 50, 51, 118, -1, 54,
+ -1, -1, -1, -1, -1, -1, 127, 62, 63, 64,
+ 65, 66, 67, -1, 69, 70, -1, -1, 73, -1,
+ -1, -1, 77, 78, 79, 80, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 50, 51, 118, -1, 54, -1, -1, -1, -1,
+ -1, -1, 127, 62, 63, 64, 65, 66, 67, -1,
+ 69, 70, -1, -1, 73, -1, -1, -1, 77, 78,
+ 79, 80, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 50, 51, 118,
+ -1, 54, -1, -1, -1, -1, -1, -1, 127, 62,
+ 63, 64, 65, 66, 67, -1, 69, 70, -1, -1,
+ 73, -1, -1, -1, 77, 78, 79, 80, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 91, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 50, 51, 118, -1, 54, -1, -1,
+ -1, -1, -1, -1, 127, 62, 63, 64, 65, 66,
+ 67, -1, 69, 70, -1, -1, 73, -1, -1, -1,
+ 77, 78, 79, 80, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 91, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 50,
+ 51, 118, -1, 54, -1, -1, -1, -1, -1, -1,
+ 127, 62, 63, 64, 65, 66, 67, -1, 69, 70,
+ -1, -1, 73, -1, -1, -1, 77, 78, 79, 80,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, -1, -1, 118, -1, -1,
+ -1, -1, -1, -1, -1, -1, 127, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ -1, -1, 79, 80, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 104, -1, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, 130, 79, 80, -1, -1, -1, 84,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, -1, -1, 79, 80, -1, -1, 104,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 104, 126, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 126, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, -1, -1, 79, 80,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, -1, -1, 79, 80, -1, -1, -1,
+ -1, -1, -1, 104, -1, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, -1, -1, -1, -1, -1,
+ -1, -1, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 64, 65, 66, 67, 68, 69, 70, -1,
+ -1, 73, 74, -1, -1, -1, -1, 79, 80, 64,
+ 65, 66, 67, 68, 69, 70, -1, -1, 73, 74,
+ -1, -1, -1, -1, 79, 80, -1, -1, -1, -1,
+ -1, -1, -1, -1, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, -1, -1, -1, -1, -1, -1,
+ -1, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint16 yystos[] =
+{
+ 0, 133, 134, 0, 1, 3, 4, 5, 6, 7,
+ 11, 12, 16, 18, 19, 20, 21, 22, 23, 24,
+ 29, 30, 31, 32, 33, 34, 35, 38, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 59, 60, 62, 63, 82, 85, 86, 88,
+ 89, 91, 93, 94, 95, 96, 97, 98, 116, 117,
+ 118, 136, 137, 138, 141, 143, 144, 148, 149, 151,
+ 152, 153, 154, 155, 165, 182, 199, 209, 210, 223,
+ 224, 225, 226, 227, 228, 229, 232, 240, 242, 243,
+ 244, 245, 246, 247, 266, 275, 138, 21, 22, 29,
+ 30, 31, 45, 50, 54, 79, 82, 85, 116, 156,
+ 157, 182, 199, 244, 247, 266, 157, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 44, 45, 46, 47, 48, 49, 50, 51, 54,
+ 62, 63, 64, 65, 66, 67, 69, 70, 73, 77,
+ 78, 79, 80, 91, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 118, 127, 128, 158, 163, 164,
+ 245, 261, 32, 33, 34, 35, 48, 49, 50, 54,
+ 158, 159, 160, 161, 240, 242, 183, 82, 141, 142,
+ 155, 199, 244, 246, 247, 142, 130, 131, 142, 270,
+ 273, 274, 186, 188, 82, 149, 155, 199, 204, 244,
+ 247, 91, 92, 117, 148, 165, 167, 171, 178, 180,
+ 264, 265, 171, 171, 128, 173, 174, 128, 169, 173,
+ 141, 52, 160, 130, 271, 140, 120, 165, 199, 165,
+ 54, 85, 136, 150, 151, 141, 91, 148, 168, 180,
+ 264, 275, 180, 263, 264, 275, 82, 154, 199, 244,
+ 247, 52, 53, 55, 158, 235, 241, 234, 235, 235,
+ 129, 230, 129, 233, 56, 57, 143, 165, 165, 270,
+ 274, 39, 40, 41, 42, 43, 36, 37, 28, 206,
+ 103, 126, 85, 91, 152, 103, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 79,
+ 80, 104, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 81, 122, 124, 81, 124, 26, 120, 211,
+ 226, 83, 83, 169, 173, 211, 271, 141, 50, 54,
+ 156, 56, 57, 1, 107, 248, 273, 81, 122, 124,
+ 195, 262, 196, 81, 124, 269, 126, 135, 136, 54,
+ 13, 105, 200, 273, 103, 81, 122, 124, 83, 83,
+ 200, 270, 15, 17, 216, 131, 142, 142, 54, 81,
+ 122, 124, 25, 167, 167, 84, 126, 179, 275, 126,
+ 179, 125, 171, 86, 171, 175, 148, 171, 180, 209,
+ 275, 52, 59, 60, 139, 128, 166, 120, 136, 81,
+ 124, 83, 150, 125, 125, 184, 165, 271, 123, 126,
+ 130, 272, 126, 272, 126, 272, 121, 272, 54, 81,
+ 122, 124, 58, 99, 100, 101, 236, 101, 236, 101,
+ 61, 101, 101, 231, 236, 101, 58, 101, 64, 64,
+ 138, 142, 142, 142, 142, 138, 141, 141, 207, 91,
+ 143, 167, 180, 181, 150, 154, 126, 143, 165, 167,
+ 181, 165, 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 50, 51, 54,
+ 163, 267, 268, 168, 50, 51, 54, 163, 267, 50,
+ 54, 267, 267, 214, 212, 143, 165, 143, 165, 90,
+ 145, 193, 273, 249, 192, 50, 54, 156, 267, 168,
+ 267, 135, 141, 50, 52, 53, 54, 55, 91, 92,
+ 110, 113, 128, 250, 251, 252, 253, 254, 255, 256,
+ 257, 258, 259, 197, 162, 10, 8, 219, 275, 136,
+ 13, 165, 50, 54, 168, 50, 54, 136, 216, 136,
+ 91, 180, 217, 10, 27, 105, 201, 273, 201, 50,
+ 54, 168, 50, 54, 190, 126, 179, 167, 91, 167,
+ 178, 264, 91, 167, 265, 125, 91, 165, 167, 172,
+ 176, 178, 264, 271, 126, 81, 124, 271, 125, 160,
+ 185, 165, 136, 121, 165, 125, 271, 271, 91, 167,
+ 50, 54, 168, 50, 54, 238, 237, 129, 236, 129,
+ 165, 165, 72, 108, 205, 275, 167, 126, 125, 43,
+ 105, 83, 83, 169, 173, 123, 83, 83, 169, 170,
+ 173, 275, 170, 173, 170, 173, 205, 205, 146, 273,
+ 142, 135, 123, 10, 271, 103, 251, 135, 273, 126,
+ 260, 275, 126, 260, 50, 126, 260, 50, 158, 160,
+ 167, 181, 220, 275, 15, 203, 275, 14, 202, 203,
+ 83, 123, 83, 83, 203, 10, 10, 167, 126, 200,
+ 187, 189, 123, 142, 167, 126, 179, 167, 167, 126,
+ 177, 125, 126, 179, 125, 148, 209, 267, 267, 125,
+ 141, 121, 125, 165, 123, 136, 52, 53, 55, 239,
+ 247, 108, 204, 208, 91, 167, 165, 165, 143, 165,
+ 165, 145, 83, 143, 165, 143, 165, 145, 215, 213,
+ 205, 194, 273, 10, 125, 167, 271, 10, 252, 255,
+ 257, 259, 50, 254, 257, 198, 84, 221, 275, 136,
+ 9, 222, 275, 142, 10, 83, 10, 91, 136, 136,
+ 136, 201, 179, 91, 179, 179, 91, 178, 180, 264,
+ 125, 91, 271, 125, 271, 121, 108, 136, 167, 143,
+ 165, 136, 136, 147, 135, 125, 126, 260, 260, 260,
+ 250, 82, 155, 199, 244, 247, 200, 136, 200, 167,
+ 203, 216, 218, 10, 10, 191, 165, 167, 126, 179,
+ 126, 179, 167, 125, 10, 10, 121, 136, 10, 257,
+ 135, 54, 81, 122, 124, 136, 136, 136, 179, 179,
+ 91, 264, 91, 179, 121, 260, 10, 50, 54, 168,
+ 50, 54, 219, 202, 10, 167, 126, 179, 167, 123,
+ 179, 91, 179, 167, 179
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 350 "parse.y"
+ {
+ lex_state = EXPR_BEG;
+ top_local_init();
+ if (ruby_class == rb_cObject) class_nest = 0;
+ else class_nest = 1;
+ }
+ break;
+
+ case 3:
+#line 357 "parse.y"
+ {
+ if ((yyvsp[(2) - (2)].node) && !compile_for_eval) {
+ /* last expression should not be void */
+ if (nd_type((yyvsp[(2) - (2)].node)) != NODE_BLOCK) void_expr((yyvsp[(2) - (2)].node));
+ else {
+ NODE *node = (yyvsp[(2) - (2)].node);
+ while (node->nd_next) {
+ node = node->nd_next;
+ }
+ void_expr(node->nd_head);
+ }
+ }
+ ruby_eval_tree = block_append(ruby_eval_tree, (yyvsp[(2) - (2)].node));
+ top_local_setup();
+ class_nest = 0;
+ }
+ break;
+
+ case 4:
+#line 379 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (4)].node);
+ if ((yyvsp[(2) - (4)].node)) {
+ (yyval.node) = NEW_RESCUE((yyvsp[(1) - (4)].node), (yyvsp[(2) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ else if ((yyvsp[(3) - (4)].node)) {
+ rb_warn("else without rescue is useless");
+ (yyval.node) = block_append((yyval.node), (yyvsp[(3) - (4)].node));
+ }
+ if ((yyvsp[(4) - (4)].node)) {
+ (yyval.node) = NEW_ENSURE((yyval.node), (yyvsp[(4) - (4)].node));
+ }
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 5:
+#line 396 "parse.y"
+ {
+ void_stmts((yyvsp[(1) - (2)].node));
+ fixup_nodes(&deferred_nodes);
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ }
+ break;
+
+ case 7:
+#line 405 "parse.y"
+ {
+ (yyval.node) = newline_node((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 8:
+#line 409 "parse.y"
+ {
+ (yyval.node) = block_append((yyvsp[(1) - (3)].node), newline_node((yyvsp[(3) - (3)].node)));
+ }
+ break;
+
+ case 9:
+#line 413 "parse.y"
+ {
+ (yyval.node) = remove_begin((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 10:
+#line 418 "parse.y"
+ {lex_state = EXPR_FNAME;}
+ break;
+
+ case 11:
+#line 419 "parse.y"
+ {
+ (yyval.node) = NEW_ALIAS((yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 12:
+#line 423 "parse.y"
+ {
+ (yyval.node) = NEW_VALIAS((yyvsp[(2) - (3)].id), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 13:
+#line 427 "parse.y"
+ {
+ char buf[3];
+
+ sprintf(buf, "$%c", (char)(yyvsp[(3) - (3)].node)->nd_nth);
+ (yyval.node) = NEW_VALIAS((yyvsp[(2) - (3)].id), rb_intern(buf));
+ }
+ break;
+
+ case 14:
+#line 434 "parse.y"
+ {
+ yyerror("can't make alias for the number variables");
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 15:
+#line 439 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 16:
+#line 443 "parse.y"
+ {
+ (yyval.node) = NEW_IF(cond((yyvsp[(3) - (3)].node)), remove_begin((yyvsp[(1) - (3)].node)), 0);
+ fixpos((yyval.node), (yyvsp[(3) - (3)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ (yyval.node)->nd_else = (yyval.node)->nd_body;
+ (yyval.node)->nd_body = 0;
+ }
+ }
+ break;
+
+ case 17:
+#line 452 "parse.y"
+ {
+ (yyval.node) = NEW_UNLESS(cond((yyvsp[(3) - (3)].node)), remove_begin((yyvsp[(1) - (3)].node)), 0);
+ fixpos((yyval.node), (yyvsp[(3) - (3)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ (yyval.node)->nd_body = (yyval.node)->nd_else;
+ (yyval.node)->nd_else = 0;
+ }
+ }
+ break;
+
+ case 18:
+#line 461 "parse.y"
+ {
+ if ((yyvsp[(1) - (3)].node) && nd_type((yyvsp[(1) - (3)].node)) == NODE_BEGIN) {
+ (yyval.node) = NEW_WHILE(cond((yyvsp[(3) - (3)].node)), (yyvsp[(1) - (3)].node)->nd_body, 0);
+ }
+ else {
+ (yyval.node) = NEW_WHILE(cond((yyvsp[(3) - (3)].node)), (yyvsp[(1) - (3)].node), 1);
+ }
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ nd_set_type((yyval.node), NODE_UNTIL);
+ }
+ }
+ break;
+
+ case 19:
+#line 473 "parse.y"
+ {
+ if ((yyvsp[(1) - (3)].node) && nd_type((yyvsp[(1) - (3)].node)) == NODE_BEGIN) {
+ (yyval.node) = NEW_UNTIL(cond((yyvsp[(3) - (3)].node)), (yyvsp[(1) - (3)].node)->nd_body, 0);
+ }
+ else {
+ (yyval.node) = NEW_UNTIL(cond((yyvsp[(3) - (3)].node)), (yyvsp[(1) - (3)].node), 1);
+ }
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ nd_set_type((yyval.node), NODE_WHILE);
+ }
+ }
+ break;
+
+ case 20:
+#line 485 "parse.y"
+ {
+ NODE *resq = NEW_RESBODY(0, remove_begin((yyvsp[(3) - (3)].node)), 0);
+ (yyval.node) = NEW_RESCUE(remove_begin((yyvsp[(1) - (3)].node)), resq, 0);
+ }
+ break;
+
+ case 21:
+#line 490 "parse.y"
+ {
+ if (in_def || in_single) {
+ yyerror("BEGIN in method");
+ }
+ local_push(0);
+ }
+ break;
+
+ case 22:
+#line 497 "parse.y"
+ {
+ ruby_eval_tree_begin = block_append(ruby_eval_tree_begin,
+ NEW_PREEXE((yyvsp[(4) - (5)].node)));
+ local_pop();
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 23:
+#line 504 "parse.y"
+ {
+ if (in_def || in_single) {
+ rb_warn("END in method; use at_exit");
+ }
+
+ (yyval.node) = NEW_ITER(0, NEW_POSTEXE(), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 24:
+#line 512 "parse.y"
+ {
+ (yyval.node) = node_assign((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 25:
+#line 516 "parse.y"
+ {
+ value_expr((yyvsp[(3) - (3)].node));
+ (yyvsp[(1) - (3)].node)->nd_value = ((yyvsp[(1) - (3)].node)->nd_head) ? NEW_TO_ARY((yyvsp[(3) - (3)].node)) : NEW_ARRAY((yyvsp[(3) - (3)].node));
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ }
+ break;
+
+ case 26:
+#line 522 "parse.y"
+ {
+ value_expr((yyvsp[(3) - (3)].node));
+ if ((yyvsp[(1) - (3)].node)) {
+ ID vid = (yyvsp[(1) - (3)].node)->nd_vid;
+ if ((yyvsp[(2) - (3)].id) == tOROP) {
+ (yyvsp[(1) - (3)].node)->nd_value = (yyvsp[(3) - (3)].node);
+ (yyval.node) = NEW_OP_ASGN_OR(gettable(vid), (yyvsp[(1) - (3)].node));
+ if (is_asgn_or_id(vid)) {
+ (yyval.node)->nd_aid = vid;
+ }
+ }
+ else if ((yyvsp[(2) - (3)].id) == tANDOP) {
+ (yyvsp[(1) - (3)].node)->nd_value = (yyvsp[(3) - (3)].node);
+ (yyval.node) = NEW_OP_ASGN_AND(gettable(vid), (yyvsp[(1) - (3)].node));
+ }
+ else {
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ (yyval.node)->nd_value = call_op(gettable(vid),(yyvsp[(2) - (3)].id),1,(yyvsp[(3) - (3)].node));
+ }
+ }
+ else {
+ (yyval.node) = 0;
+ }
+ }
+ break;
+
+ case 27:
+#line 547 "parse.y"
+ {
+ NODE *args;
+
+ value_expr((yyvsp[(6) - (6)].node));
+ if (!(yyvsp[(3) - (6)].node)) (yyvsp[(3) - (6)].node) = NEW_ZARRAY();
+ args = arg_concat((yyvsp[(6) - (6)].node), (yyvsp[(3) - (6)].node));
+ if ((yyvsp[(5) - (6)].id) == tOROP) {
+ (yyvsp[(5) - (6)].id) = 0;
+ }
+ else if ((yyvsp[(5) - (6)].id) == tANDOP) {
+ (yyvsp[(5) - (6)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN1((yyvsp[(1) - (6)].node), (yyvsp[(5) - (6)].id), args);
+ fixpos((yyval.node), (yyvsp[(1) - (6)].node));
+ }
+ break;
+
+ case 28:
+#line 563 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 29:
+#line 575 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 30:
+#line 587 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 31:
+#line 599 "parse.y"
+ {
+ rb_backref_error((yyvsp[(1) - (3)].node));
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 32:
+#line 604 "parse.y"
+ {
+ (yyval.node) = node_assign((yyvsp[(1) - (3)].node), NEW_SVALUE((yyvsp[(3) - (3)].node)));
+ }
+ break;
+
+ case 33:
+#line 608 "parse.y"
+ {
+ (yyvsp[(1) - (3)].node)->nd_value = ((yyvsp[(1) - (3)].node)->nd_head) ? NEW_TO_ARY((yyvsp[(3) - (3)].node)) : NEW_ARRAY((yyvsp[(3) - (3)].node));
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ }
+ break;
+
+ case 34:
+#line 613 "parse.y"
+ {
+ (yyvsp[(1) - (3)].node)->nd_value = (yyvsp[(3) - (3)].node);
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ }
+ break;
+
+ case 37:
+#line 622 "parse.y"
+ {
+ (yyval.node) = logop(NODE_AND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 38:
+#line 626 "parse.y"
+ {
+ (yyval.node) = logop(NODE_OR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 39:
+#line 630 "parse.y"
+ {
+ (yyval.node) = NEW_NOT(cond((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 40:
+#line 634 "parse.y"
+ {
+ (yyval.node) = NEW_NOT(cond((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 42:
+#line 641 "parse.y"
+ {
+ value_expr((yyval.node));
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 45:
+#line 650 "parse.y"
+ {
+ (yyval.node) = NEW_RETURN(ret_args((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 46:
+#line 654 "parse.y"
+ {
+ (yyval.node) = NEW_BREAK(ret_args((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 47:
+#line 658 "parse.y"
+ {
+ (yyval.node) = NEW_NEXT(ret_args((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 49:
+#line 665 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 50:
+#line 669 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 51:
+#line 675 "parse.y"
+ {
+ (yyval.vars) = dyna_push();
+ (yyvsp[(1) - (1)].num) = ruby_sourceline;
+ }
+ break;
+
+ case 52:
+#line 679 "parse.y"
+ {(yyval.vars) = ruby_dyna_vars;}
+ break;
+
+ case 53:
+#line 682 "parse.y"
+ {
+ (yyval.node) = NEW_ITER((yyvsp[(3) - (6)].node), 0, dyna_init((yyvsp[(5) - (6)].node), (yyvsp[(4) - (6)].vars)));
+ nd_set_line((yyval.node), (yyvsp[(1) - (6)].num));
+ dyna_pop((yyvsp[(2) - (6)].vars));
+ }
+ break;
+
+ case 54:
+#line 690 "parse.y"
+ {
+ (yyval.node) = new_fcall((yyvsp[(1) - (2)].id), (yyvsp[(2) - (2)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 55:
+#line 695 "parse.y"
+ {
+ (yyval.node) = new_fcall((yyvsp[(1) - (3)].id), (yyvsp[(2) - (3)].node));
+ if ((yyvsp[(3) - (3)].node)) {
+ if (nd_type((yyval.node)) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ (yyvsp[(3) - (3)].node)->nd_iter = (yyval.node);
+ (yyval.node) = (yyvsp[(3) - (3)].node);
+ }
+ fixpos((yyval.node), (yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 56:
+#line 707 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 57:
+#line 712 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].node));
+ if ((yyvsp[(5) - (5)].node)) {
+ if (nd_type((yyval.node)) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ (yyvsp[(5) - (5)].node)->nd_iter = (yyval.node);
+ (yyval.node) = (yyvsp[(5) - (5)].node);
+ }
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 58:
+#line 724 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 59:
+#line 729 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].node));
+ if ((yyvsp[(5) - (5)].node)) {
+ if (nd_type((yyval.node)) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ (yyvsp[(5) - (5)].node)->nd_iter = (yyval.node);
+ (yyval.node) = (yyvsp[(5) - (5)].node);
+ }
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 60:
+#line 741 "parse.y"
+ {
+ (yyval.node) = new_super((yyvsp[(2) - (2)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 61:
+#line 746 "parse.y"
+ {
+ (yyval.node) = new_yield((yyvsp[(2) - (2)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 63:
+#line 754 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 65:
+#line 761 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN(NEW_LIST((yyvsp[(2) - (3)].node)), 0);
+ }
+ break;
+
+ case 66:
+#line 767 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN((yyvsp[(1) - (1)].node), 0);
+ }
+ break;
+
+ case 67:
+#line 771 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN(list_append((yyvsp[(1) - (2)].node),(yyvsp[(2) - (2)].node)), 0);
+ }
+ break;
+
+ case 68:
+#line 775 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 69:
+#line 779 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN((yyvsp[(1) - (2)].node), -1);
+ }
+ break;
+
+ case 70:
+#line 783 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN(0, (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 71:
+#line 787 "parse.y"
+ {
+ (yyval.node) = NEW_MASGN(0, -1);
+ }
+ break;
+
+ case 73:
+#line 794 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 74:
+#line 800 "parse.y"
+ {
+ (yyval.node) = NEW_LIST((yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 75:
+#line 804 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 76:
+#line 810 "parse.y"
+ {
+ (yyval.node) = assignable((yyvsp[(1) - (1)].id), 0);
+ }
+ break;
+
+ case 77:
+#line 814 "parse.y"
+ {
+ (yyval.node) = aryset((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 78:
+#line 818 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 79:
+#line 822 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 80:
+#line 826 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 81:
+#line 830 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ (yyval.node) = NEW_CDECL(0, 0, NEW_COLON2((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id)));
+ }
+ break;
+
+ case 82:
+#line 836 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ (yyval.node) = NEW_CDECL(0, 0, NEW_COLON3((yyvsp[(2) - (2)].id)));
+ }
+ break;
+
+ case 83:
+#line 842 "parse.y"
+ {
+ rb_backref_error((yyvsp[(1) - (1)].node));
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 84:
+#line 849 "parse.y"
+ {
+ (yyval.node) = assignable((yyvsp[(1) - (1)].id), 0);
+ }
+ break;
+
+ case 85:
+#line 853 "parse.y"
+ {
+ (yyval.node) = aryset((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 86:
+#line 857 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 87:
+#line 861 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 88:
+#line 865 "parse.y"
+ {
+ (yyval.node) = attrset((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 89:
+#line 869 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ (yyval.node) = NEW_CDECL(0, 0, NEW_COLON2((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id)));
+ }
+ break;
+
+ case 90:
+#line 875 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ (yyval.node) = NEW_CDECL(0, 0, NEW_COLON3((yyvsp[(2) - (2)].id)));
+ }
+ break;
+
+ case 91:
+#line 881 "parse.y"
+ {
+ rb_backref_error((yyvsp[(1) - (1)].node));
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 92:
+#line 888 "parse.y"
+ {
+ yyerror("class/module name must be CONSTANT");
+ }
+ break;
+
+ case 94:
+#line 895 "parse.y"
+ {
+ (yyval.node) = NEW_COLON3((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 95:
+#line 899 "parse.y"
+ {
+ (yyval.node) = NEW_COLON2(0, (yyval.node));
+ }
+ break;
+
+ case 96:
+#line 903 "parse.y"
+ {
+ (yyval.node) = NEW_COLON2((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 100:
+#line 912 "parse.y"
+ {
+ lex_state = EXPR_END;
+ (yyval.id) = (yyvsp[(1) - (1)].id);
+ }
+ break;
+
+ case 101:
+#line 917 "parse.y"
+ {
+ lex_state = EXPR_END;
+ (yyval.id) = (yyvsp[(1) - (1)].id);
+ }
+ break;
+
+ case 104:
+#line 928 "parse.y"
+ {
+ (yyval.node) = NEW_LIT(ID2SYM((yyvsp[(1) - (1)].id)));
+ }
+ break;
+
+ case 106:
+#line 935 "parse.y"
+ {
+ (yyval.node) = NEW_UNDEF((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 107:
+#line 938 "parse.y"
+ {lex_state = EXPR_FNAME;}
+ break;
+
+ case 108:
+#line 939 "parse.y"
+ {
+ (yyval.node) = block_append((yyvsp[(1) - (4)].node), NEW_UNDEF((yyvsp[(4) - (4)].node)));
+ }
+ break;
+
+ case 109:
+#line 944 "parse.y"
+ { (yyval.id) = '|'; }
+ break;
+
+ case 110:
+#line 945 "parse.y"
+ { (yyval.id) = '^'; }
+ break;
+
+ case 111:
+#line 946 "parse.y"
+ { (yyval.id) = '&'; }
+ break;
+
+ case 112:
+#line 947 "parse.y"
+ { (yyval.id) = tCMP; }
+ break;
+
+ case 113:
+#line 948 "parse.y"
+ { (yyval.id) = tEQ; }
+ break;
+
+ case 114:
+#line 949 "parse.y"
+ { (yyval.id) = tEQQ; }
+ break;
+
+ case 115:
+#line 950 "parse.y"
+ { (yyval.id) = tMATCH; }
+ break;
+
+ case 116:
+#line 951 "parse.y"
+ { (yyval.id) = '>'; }
+ break;
+
+ case 117:
+#line 952 "parse.y"
+ { (yyval.id) = tGEQ; }
+ break;
+
+ case 118:
+#line 953 "parse.y"
+ { (yyval.id) = '<'; }
+ break;
+
+ case 119:
+#line 954 "parse.y"
+ { (yyval.id) = tLEQ; }
+ break;
+
+ case 120:
+#line 955 "parse.y"
+ { (yyval.id) = tLSHFT; }
+ break;
+
+ case 121:
+#line 956 "parse.y"
+ { (yyval.id) = tRSHFT; }
+ break;
+
+ case 122:
+#line 957 "parse.y"
+ { (yyval.id) = '+'; }
+ break;
+
+ case 123:
+#line 958 "parse.y"
+ { (yyval.id) = '-'; }
+ break;
+
+ case 124:
+#line 959 "parse.y"
+ { (yyval.id) = '*'; }
+ break;
+
+ case 125:
+#line 960 "parse.y"
+ { (yyval.id) = '*'; }
+ break;
+
+ case 126:
+#line 961 "parse.y"
+ { (yyval.id) = '/'; }
+ break;
+
+ case 127:
+#line 962 "parse.y"
+ { (yyval.id) = '%'; }
+ break;
+
+ case 128:
+#line 963 "parse.y"
+ { (yyval.id) = tPOW; }
+ break;
+
+ case 129:
+#line 964 "parse.y"
+ { (yyval.id) = '~'; }
+ break;
+
+ case 130:
+#line 965 "parse.y"
+ { (yyval.id) = tUPLUS; }
+ break;
+
+ case 131:
+#line 966 "parse.y"
+ { (yyval.id) = tUMINUS; }
+ break;
+
+ case 132:
+#line 967 "parse.y"
+ { (yyval.id) = tAREF; }
+ break;
+
+ case 133:
+#line 968 "parse.y"
+ { (yyval.id) = tASET; }
+ break;
+
+ case 134:
+#line 969 "parse.y"
+ { (yyval.id) = '`'; }
+ break;
+
+ case 175:
+#line 982 "parse.y"
+ {
+ (yyval.node) = node_assign((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 176:
+#line 986 "parse.y"
+ {
+ (yyval.node) = node_assign((yyvsp[(1) - (5)].node), NEW_RESCUE((yyvsp[(3) - (5)].node), NEW_RESBODY(0,(yyvsp[(5) - (5)].node),0), 0));
+ }
+ break;
+
+ case 177:
+#line 990 "parse.y"
+ {
+ value_expr((yyvsp[(3) - (3)].node));
+ if ((yyvsp[(1) - (3)].node)) {
+ ID vid = (yyvsp[(1) - (3)].node)->nd_vid;
+ if ((yyvsp[(2) - (3)].id) == tOROP) {
+ (yyvsp[(1) - (3)].node)->nd_value = (yyvsp[(3) - (3)].node);
+ (yyval.node) = NEW_OP_ASGN_OR(gettable(vid), (yyvsp[(1) - (3)].node));
+ if (is_asgn_or_id(vid)) {
+ (yyval.node)->nd_aid = vid;
+ }
+ }
+ else if ((yyvsp[(2) - (3)].id) == tANDOP) {
+ (yyvsp[(1) - (3)].node)->nd_value = (yyvsp[(3) - (3)].node);
+ (yyval.node) = NEW_OP_ASGN_AND(gettable(vid), (yyvsp[(1) - (3)].node));
+ }
+ else {
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ (yyval.node)->nd_value = call_op(gettable(vid),(yyvsp[(2) - (3)].id),1,(yyvsp[(3) - (3)].node));
+ }
+ }
+ else {
+ (yyval.node) = 0;
+ }
+ }
+ break;
+
+ case 178:
+#line 1015 "parse.y"
+ {
+ NODE *args;
+
+ value_expr((yyvsp[(6) - (6)].node));
+ if (!(yyvsp[(3) - (6)].node)) (yyvsp[(3) - (6)].node) = NEW_ZARRAY();
+ args = arg_concat((yyvsp[(6) - (6)].node), (yyvsp[(3) - (6)].node));
+ if ((yyvsp[(5) - (6)].id) == tOROP) {
+ (yyvsp[(5) - (6)].id) = 0;
+ }
+ else if ((yyvsp[(5) - (6)].id) == tANDOP) {
+ (yyvsp[(5) - (6)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN1((yyvsp[(1) - (6)].node), (yyvsp[(5) - (6)].id), args);
+ fixpos((yyval.node), (yyvsp[(1) - (6)].node));
+ }
+ break;
+
+ case 179:
+#line 1031 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 180:
+#line 1043 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 181:
+#line 1055 "parse.y"
+ {
+ value_expr((yyvsp[(5) - (5)].node));
+ if ((yyvsp[(4) - (5)].id) == tOROP) {
+ (yyvsp[(4) - (5)].id) = 0;
+ }
+ else if ((yyvsp[(4) - (5)].id) == tANDOP) {
+ (yyvsp[(4) - (5)].id) = 1;
+ }
+ (yyval.node) = NEW_OP_ASGN2((yyvsp[(1) - (5)].node), (yyvsp[(3) - (5)].id), (yyvsp[(4) - (5)].id), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 182:
+#line 1067 "parse.y"
+ {
+ yyerror("constant re-assignment");
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 183:
+#line 1072 "parse.y"
+ {
+ yyerror("constant re-assignment");
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 184:
+#line 1077 "parse.y"
+ {
+ rb_backref_error((yyvsp[(1) - (3)].node));
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 185:
+#line 1082 "parse.y"
+ {
+ value_expr((yyvsp[(1) - (3)].node));
+ value_expr((yyvsp[(3) - (3)].node));
+ (yyval.node) = NEW_DOT2((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ if (nd_type((yyvsp[(1) - (3)].node)) == NODE_LIT && FIXNUM_P((yyvsp[(1) - (3)].node)->nd_lit) &&
+ nd_type((yyvsp[(3) - (3)].node)) == NODE_LIT && FIXNUM_P((yyvsp[(3) - (3)].node)->nd_lit)) {
+ deferred_nodes = list_append(deferred_nodes, (yyval.node));
+ }
+ }
+ break;
+
+ case 186:
+#line 1092 "parse.y"
+ {
+ value_expr((yyvsp[(1) - (3)].node));
+ value_expr((yyvsp[(3) - (3)].node));
+ (yyval.node) = NEW_DOT3((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ if (nd_type((yyvsp[(1) - (3)].node)) == NODE_LIT && FIXNUM_P((yyvsp[(1) - (3)].node)->nd_lit) &&
+ nd_type((yyvsp[(3) - (3)].node)) == NODE_LIT && FIXNUM_P((yyvsp[(3) - (3)].node)->nd_lit)) {
+ deferred_nodes = list_append(deferred_nodes, (yyval.node));
+ }
+ }
+ break;
+
+ case 187:
+#line 1102 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '+', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 188:
+#line 1106 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '-', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 189:
+#line 1110 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '*', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 190:
+#line 1114 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '/', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 191:
+#line 1118 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '%', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 192:
+#line 1122 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tPOW, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 193:
+#line 1126 "parse.y"
+ {
+ (yyval.node) = call_op(call_op((yyvsp[(2) - (4)].node), tPOW, 1, (yyvsp[(4) - (4)].node)), tUMINUS, 0, 0);
+ }
+ break;
+
+ case 194:
+#line 1130 "parse.y"
+ {
+ (yyval.node) = call_op(call_op((yyvsp[(2) - (4)].node), tPOW, 1, (yyvsp[(4) - (4)].node)), tUMINUS, 0, 0);
+ }
+ break;
+
+ case 195:
+#line 1134 "parse.y"
+ {
+ if ((yyvsp[(2) - (2)].node) && nd_type((yyvsp[(2) - (2)].node)) == NODE_LIT) {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ else {
+ (yyval.node) = call_op((yyvsp[(2) - (2)].node), tUPLUS, 0, 0);
+ }
+ }
+ break;
+
+ case 196:
+#line 1143 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(2) - (2)].node), tUMINUS, 0, 0);
+ }
+ break;
+
+ case 197:
+#line 1147 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '|', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 198:
+#line 1151 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '^', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 199:
+#line 1155 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '&', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 200:
+#line 1159 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tCMP, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 201:
+#line 1163 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '>', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 202:
+#line 1167 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tGEQ, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 203:
+#line 1171 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), '<', 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 204:
+#line 1175 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tLEQ, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 205:
+#line 1179 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tEQ, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 206:
+#line 1183 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tEQQ, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 207:
+#line 1187 "parse.y"
+ {
+ (yyval.node) = NEW_NOT(call_op((yyvsp[(1) - (3)].node), tEQ, 1, (yyvsp[(3) - (3)].node)));
+ }
+ break;
+
+ case 208:
+#line 1191 "parse.y"
+ {
+ (yyval.node) = match_gen((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 209:
+#line 1195 "parse.y"
+ {
+ (yyval.node) = NEW_NOT(match_gen((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)));
+ }
+ break;
+
+ case 210:
+#line 1199 "parse.y"
+ {
+ (yyval.node) = NEW_NOT(cond((yyvsp[(2) - (2)].node)));
+ }
+ break;
+
+ case 211:
+#line 1203 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(2) - (2)].node), '~', 0, 0);
+ }
+ break;
+
+ case 212:
+#line 1207 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tLSHFT, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 213:
+#line 1211 "parse.y"
+ {
+ (yyval.node) = call_op((yyvsp[(1) - (3)].node), tRSHFT, 1, (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 214:
+#line 1215 "parse.y"
+ {
+ (yyval.node) = logop(NODE_AND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 215:
+#line 1219 "parse.y"
+ {
+ (yyval.node) = logop(NODE_OR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 216:
+#line 1222 "parse.y"
+ {in_defined = 1;}
+ break;
+
+ case 217:
+#line 1223 "parse.y"
+ {
+ in_defined = 0;
+ (yyval.node) = NEW_DEFINED((yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 218:
+#line 1228 "parse.y"
+ {
+ (yyval.node) = NEW_IF(cond((yyvsp[(1) - (5)].node)), (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 219:
+#line 1233 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 220:
+#line 1239 "parse.y"
+ {
+ value_expr((yyvsp[(1) - (1)].node));
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 222:
+#line 1247 "parse.y"
+ {
+ rb_warn("parenthesize argument(s) for future version");
+ (yyval.node) = NEW_LIST((yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 223:
+#line 1252 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ }
+ break;
+
+ case 224:
+#line 1256 "parse.y"
+ {
+ value_expr((yyvsp[(4) - (5)].node));
+ (yyval.node) = arg_concat((yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 225:
+#line 1261 "parse.y"
+ {
+ (yyval.node) = NEW_LIST(NEW_HASH((yyvsp[(1) - (2)].node)));
+ }
+ break;
+
+ case 226:
+#line 1265 "parse.y"
+ {
+ value_expr((yyvsp[(2) - (3)].node));
+ (yyval.node) = NEW_NEWLINE(NEW_SPLAT((yyvsp[(2) - (3)].node)));
+ }
+ break;
+
+ case 227:
+#line 1272 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 228:
+#line 1276 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ }
+ break;
+
+ case 229:
+#line 1280 "parse.y"
+ {
+ rb_warn("parenthesize argument for future version");
+ (yyval.node) = NEW_LIST((yyvsp[(2) - (4)].node));
+ }
+ break;
+
+ case 230:
+#line 1285 "parse.y"
+ {
+ rb_warn("parenthesize argument for future version");
+ (yyval.node) = list_append((yyvsp[(2) - (6)].node), (yyvsp[(4) - (6)].node));
+ }
+ break;
+
+ case 233:
+#line 1296 "parse.y"
+ {
+ rb_warn("parenthesize argument(s) for future version");
+ (yyval.node) = NEW_LIST((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 234:
+#line 1301 "parse.y"
+ {
+ (yyval.node) = arg_blk_pass((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 235:
+#line 1305 "parse.y"
+ {
+ (yyval.node) = arg_concat((yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 236:
+#line 1310 "parse.y"
+ {
+ (yyval.node) = NEW_LIST(NEW_HASH((yyvsp[(1) - (2)].node)));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 237:
+#line 1315 "parse.y"
+ {
+ (yyval.node) = arg_concat(NEW_LIST(NEW_HASH((yyvsp[(1) - (5)].node))), (yyvsp[(4) - (5)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 238:
+#line 1320 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (4)].node), NEW_HASH((yyvsp[(3) - (4)].node)));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 239:
+#line 1325 "parse.y"
+ {
+ value_expr((yyvsp[(6) - (7)].node));
+ (yyval.node) = arg_concat(list_append((yyvsp[(1) - (7)].node), NEW_HASH((yyvsp[(3) - (7)].node))), (yyvsp[(6) - (7)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(7) - (7)].node));
+ }
+ break;
+
+ case 240:
+#line 1331 "parse.y"
+ {
+ (yyval.node) = arg_blk_pass(NEW_SPLAT((yyvsp[(2) - (3)].node)), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 242:
+#line 1338 "parse.y"
+ {
+ (yyval.node) = arg_blk_pass(list_concat(NEW_LIST((yyvsp[(1) - (4)].node)),(yyvsp[(3) - (4)].node)), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 243:
+#line 1342 "parse.y"
+ {
+ (yyval.node) = arg_blk_pass((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 244:
+#line 1346 "parse.y"
+ {
+ (yyval.node) = arg_concat(NEW_LIST((yyvsp[(1) - (5)].node)), (yyvsp[(4) - (5)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 245:
+#line 1351 "parse.y"
+ {
+ (yyval.node) = arg_concat(list_concat(NEW_LIST((yyvsp[(1) - (7)].node)),(yyvsp[(3) - (7)].node)), (yyvsp[(6) - (7)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(7) - (7)].node));
+ }
+ break;
+
+ case 246:
+#line 1356 "parse.y"
+ {
+ (yyval.node) = NEW_LIST(NEW_HASH((yyvsp[(1) - (2)].node)));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 247:
+#line 1361 "parse.y"
+ {
+ (yyval.node) = arg_concat(NEW_LIST(NEW_HASH((yyvsp[(1) - (5)].node))), (yyvsp[(4) - (5)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 248:
+#line 1366 "parse.y"
+ {
+ (yyval.node) = list_append(NEW_LIST((yyvsp[(1) - (4)].node)), NEW_HASH((yyvsp[(3) - (4)].node)));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 249:
+#line 1371 "parse.y"
+ {
+ (yyval.node) = list_append(list_concat(NEW_LIST((yyvsp[(1) - (6)].node)),(yyvsp[(3) - (6)].node)), NEW_HASH((yyvsp[(5) - (6)].node)));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(6) - (6)].node));
+ }
+ break;
+
+ case 250:
+#line 1376 "parse.y"
+ {
+ (yyval.node) = arg_concat(list_append(NEW_LIST((yyvsp[(1) - (7)].node)), NEW_HASH((yyvsp[(3) - (7)].node))), (yyvsp[(6) - (7)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(7) - (7)].node));
+ }
+ break;
+
+ case 251:
+#line 1381 "parse.y"
+ {
+ (yyval.node) = arg_concat(list_append(list_concat(NEW_LIST((yyvsp[(1) - (9)].node)), (yyvsp[(3) - (9)].node)), NEW_HASH((yyvsp[(5) - (9)].node))), (yyvsp[(8) - (9)].node));
+ (yyval.node) = arg_blk_pass((yyval.node), (yyvsp[(9) - (9)].node));
+ }
+ break;
+
+ case 252:
+#line 1386 "parse.y"
+ {
+ (yyval.node) = arg_blk_pass(NEW_SPLAT((yyvsp[(2) - (3)].node)), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 254:
+#line 1392 "parse.y"
+ {
+ (yyval.num) = cmdarg_stack;
+ CMDARG_PUSH(1);
+ }
+ break;
+
+ case 255:
+#line 1397 "parse.y"
+ {
+ /* CMDARG_POP() */
+ cmdarg_stack = (yyvsp[(1) - (2)].num);
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 257:
+#line 1405 "parse.y"
+ {lex_state = EXPR_ENDARG;}
+ break;
+
+ case 258:
+#line 1406 "parse.y"
+ {
+ rb_warn("don't put space before argument parentheses");
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 259:
+#line 1410 "parse.y"
+ {lex_state = EXPR_ENDARG;}
+ break;
+
+ case 260:
+#line 1411 "parse.y"
+ {
+ rb_warn("don't put space before argument parentheses");
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ }
+ break;
+
+ case 261:
+#line 1418 "parse.y"
+ {
+ (yyval.node) = NEW_BLOCK_PASS((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 262:
+#line 1424 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 264:
+#line 1431 "parse.y"
+ {
+ (yyval.node) = NEW_LIST((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 265:
+#line 1435 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 266:
+#line 1441 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 267:
+#line 1445 "parse.y"
+ {
+ (yyval.node) = arg_concat((yyvsp[(1) - (4)].node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 268:
+#line 1449 "parse.y"
+ {
+ (yyval.node) = NEW_SPLAT((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 277:
+#line 1463 "parse.y"
+ {
+ (yyval.node) = NEW_FCALL((yyvsp[(1) - (1)].id), 0);
+ }
+ break;
+
+ case 278:
+#line 1467 "parse.y"
+ {
+ (yyvsp[(1) - (1)].num) = ruby_sourceline;
+ }
+ break;
+
+ case 279:
+#line 1472 "parse.y"
+ {
+ if ((yyvsp[(3) - (4)].node) == NULL)
+ (yyval.node) = NEW_NIL();
+ else
+ (yyval.node) = NEW_BEGIN((yyvsp[(3) - (4)].node));
+ nd_set_line((yyval.node), (yyvsp[(1) - (4)].num));
+ }
+ break;
+
+ case 280:
+#line 1479 "parse.y"
+ {lex_state = EXPR_ENDARG;}
+ break;
+
+ case 281:
+#line 1480 "parse.y"
+ {
+ rb_warning("(...) interpreted as grouped expression");
+ (yyval.node) = (yyvsp[(2) - (5)].node);
+ }
+ break;
+
+ case 282:
+#line 1485 "parse.y"
+ {
+ if (!(yyvsp[(2) - (3)].node)) (yyval.node) = NEW_NIL();
+ else (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 283:
+#line 1490 "parse.y"
+ {
+ (yyval.node) = NEW_COLON2((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 284:
+#line 1494 "parse.y"
+ {
+ (yyval.node) = NEW_COLON3((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 285:
+#line 1498 "parse.y"
+ {
+ if ((yyvsp[(1) - (4)].node) && nd_type((yyvsp[(1) - (4)].node)) == NODE_SELF)
+ (yyval.node) = NEW_FCALL(tAREF, (yyvsp[(3) - (4)].node));
+ else
+ (yyval.node) = NEW_CALL((yyvsp[(1) - (4)].node), tAREF, (yyvsp[(3) - (4)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 286:
+#line 1506 "parse.y"
+ {
+ if ((yyvsp[(2) - (3)].node) == 0) {
+ (yyval.node) = NEW_ZARRAY(); /* zero length array*/
+ }
+ else {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ }
+ break;
+
+ case 287:
+#line 1515 "parse.y"
+ {
+ (yyval.node) = NEW_HASH((yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 288:
+#line 1519 "parse.y"
+ {
+ (yyval.node) = NEW_RETURN(0);
+ }
+ break;
+
+ case 289:
+#line 1523 "parse.y"
+ {
+ (yyval.node) = new_yield((yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 290:
+#line 1527 "parse.y"
+ {
+ (yyval.node) = NEW_YIELD(0, Qfalse);
+ }
+ break;
+
+ case 291:
+#line 1531 "parse.y"
+ {
+ (yyval.node) = NEW_YIELD(0, Qfalse);
+ }
+ break;
+
+ case 292:
+#line 1534 "parse.y"
+ {in_defined = 1;}
+ break;
+
+ case 293:
+#line 1535 "parse.y"
+ {
+ in_defined = 0;
+ (yyval.node) = NEW_DEFINED((yyvsp[(5) - (6)].node));
+ }
+ break;
+
+ case 294:
+#line 1540 "parse.y"
+ {
+ (yyvsp[(2) - (2)].node)->nd_iter = NEW_FCALL((yyvsp[(1) - (2)].id), 0);
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ fixpos((yyvsp[(2) - (2)].node)->nd_iter, (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 296:
+#line 1547 "parse.y"
+ {
+ if ((yyvsp[(1) - (2)].node) && nd_type((yyvsp[(1) - (2)].node)) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ (yyvsp[(2) - (2)].node)->nd_iter = (yyvsp[(1) - (2)].node);
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ fixpos((yyval.node), (yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 297:
+#line 1559 "parse.y"
+ {
+ (yyval.node) = NEW_IF(cond((yyvsp[(2) - (6)].node)), (yyvsp[(4) - (6)].node), (yyvsp[(5) - (6)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (6)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ NODE *tmp = (yyval.node)->nd_body;
+ (yyval.node)->nd_body = (yyval.node)->nd_else;
+ (yyval.node)->nd_else = tmp;
+ }
+ }
+ break;
+
+ case 298:
+#line 1572 "parse.y"
+ {
+ (yyval.node) = NEW_UNLESS(cond((yyvsp[(2) - (6)].node)), (yyvsp[(4) - (6)].node), (yyvsp[(5) - (6)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (6)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ NODE *tmp = (yyval.node)->nd_body;
+ (yyval.node)->nd_body = (yyval.node)->nd_else;
+ (yyval.node)->nd_else = tmp;
+ }
+ }
+ break;
+
+ case 299:
+#line 1581 "parse.y"
+ {COND_PUSH(1);}
+ break;
+
+ case 300:
+#line 1581 "parse.y"
+ {COND_POP();}
+ break;
+
+ case 301:
+#line 1584 "parse.y"
+ {
+ (yyval.node) = NEW_WHILE(cond((yyvsp[(3) - (7)].node)), (yyvsp[(6) - (7)].node), 1);
+ fixpos((yyval.node), (yyvsp[(3) - (7)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ nd_set_type((yyval.node), NODE_UNTIL);
+ }
+ }
+ break;
+
+ case 302:
+#line 1591 "parse.y"
+ {COND_PUSH(1);}
+ break;
+
+ case 303:
+#line 1591 "parse.y"
+ {COND_POP();}
+ break;
+
+ case 304:
+#line 1594 "parse.y"
+ {
+ (yyval.node) = NEW_UNTIL(cond((yyvsp[(3) - (7)].node)), (yyvsp[(6) - (7)].node), 1);
+ fixpos((yyval.node), (yyvsp[(3) - (7)].node));
+ if (cond_negative(&(yyval.node)->nd_cond)) {
+ nd_set_type((yyval.node), NODE_WHILE);
+ }
+ }
+ break;
+
+ case 305:
+#line 1604 "parse.y"
+ {
+ (yyval.node) = NEW_CASE((yyvsp[(2) - (5)].node), (yyvsp[(4) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (5)].node));
+ }
+ break;
+
+ case 306:
+#line 1609 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (4)].node);
+ }
+ break;
+
+ case 307:
+#line 1613 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(4) - (5)].node);
+ }
+ break;
+
+ case 308:
+#line 1616 "parse.y"
+ {COND_PUSH(1);}
+ break;
+
+ case 309:
+#line 1616 "parse.y"
+ {COND_POP();}
+ break;
+
+ case 310:
+#line 1619 "parse.y"
+ {
+ (yyval.node) = NEW_FOR((yyvsp[(2) - (9)].node), (yyvsp[(5) - (9)].node), (yyvsp[(8) - (9)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (9)].node));
+ }
+ break;
+
+ case 311:
+#line 1624 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("class definition in method body");
+ class_nest++;
+ local_push(0);
+ (yyval.num) = ruby_sourceline;
+ }
+ break;
+
+ case 312:
+#line 1633 "parse.y"
+ {
+ (yyval.node) = NEW_CLASS((yyvsp[(2) - (6)].node), (yyvsp[(5) - (6)].node), (yyvsp[(3) - (6)].node));
+ nd_set_line((yyval.node), (yyvsp[(4) - (6)].num));
+ local_pop();
+ class_nest--;
+ }
+ break;
+
+ case 313:
+#line 1640 "parse.y"
+ {
+ (yyval.num) = in_def;
+ in_def = 0;
+ }
+ break;
+
+ case 314:
+#line 1645 "parse.y"
+ {
+ (yyval.num) = in_single;
+ in_single = 0;
+ class_nest++;
+ local_push(0);
+ }
+ break;
+
+ case 315:
+#line 1653 "parse.y"
+ {
+ (yyval.node) = NEW_SCLASS((yyvsp[(3) - (8)].node), (yyvsp[(7) - (8)].node));
+ fixpos((yyval.node), (yyvsp[(3) - (8)].node));
+ local_pop();
+ class_nest--;
+ in_def = (yyvsp[(4) - (8)].num);
+ in_single = (yyvsp[(6) - (8)].num);
+ }
+ break;
+
+ case 316:
+#line 1662 "parse.y"
+ {
+ if (in_def || in_single)
+ yyerror("module definition in method body");
+ class_nest++;
+ local_push(0);
+ (yyval.num) = ruby_sourceline;
+ }
+ break;
+
+ case 317:
+#line 1671 "parse.y"
+ {
+ (yyval.node) = NEW_MODULE((yyvsp[(2) - (5)].node), (yyvsp[(4) - (5)].node));
+ nd_set_line((yyval.node), (yyvsp[(3) - (5)].num));
+ local_pop();
+ class_nest--;
+ }
+ break;
+
+ case 318:
+#line 1678 "parse.y"
+ {
+ (yyval.id) = cur_mid;
+ cur_mid = (yyvsp[(2) - (2)].id);
+ in_def++;
+ local_push(0);
+ }
+ break;
+
+ case 319:
+#line 1687 "parse.y"
+ {
+ if (!(yyvsp[(5) - (6)].node)) (yyvsp[(5) - (6)].node) = NEW_NIL();
+ (yyval.node) = NEW_DEFN((yyvsp[(2) - (6)].id), (yyvsp[(4) - (6)].node), (yyvsp[(5) - (6)].node), NOEX_PRIVATE);
+ fixpos((yyval.node), (yyvsp[(4) - (6)].node));
+ local_pop();
+ in_def--;
+ cur_mid = (yyvsp[(3) - (6)].id);
+ }
+ break;
+
+ case 320:
+#line 1695 "parse.y"
+ {lex_state = EXPR_FNAME;}
+ break;
+
+ case 321:
+#line 1696 "parse.y"
+ {
+ in_single++;
+ local_push(0);
+ lex_state = EXPR_END; /* force for args */
+ }
+ break;
+
+ case 322:
+#line 1704 "parse.y"
+ {
+ (yyval.node) = NEW_DEFS((yyvsp[(2) - (9)].node), (yyvsp[(5) - (9)].id), (yyvsp[(7) - (9)].node), (yyvsp[(8) - (9)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (9)].node));
+ local_pop();
+ in_single--;
+ }
+ break;
+
+ case 323:
+#line 1711 "parse.y"
+ {
+ (yyval.node) = NEW_BREAK(0);
+ }
+ break;
+
+ case 324:
+#line 1715 "parse.y"
+ {
+ (yyval.node) = NEW_NEXT(0);
+ }
+ break;
+
+ case 325:
+#line 1719 "parse.y"
+ {
+ (yyval.node) = NEW_REDO();
+ }
+ break;
+
+ case 326:
+#line 1723 "parse.y"
+ {
+ (yyval.node) = NEW_RETRY();
+ }
+ break;
+
+ case 327:
+#line 1729 "parse.y"
+ {
+ value_expr((yyvsp[(1) - (1)].node));
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 336:
+#line 1750 "parse.y"
+ {
+ (yyval.node) = NEW_IF(cond((yyvsp[(2) - (5)].node)), (yyvsp[(4) - (5)].node), (yyvsp[(5) - (5)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (5)].node));
+ }
+ break;
+
+ case 338:
+#line 1758 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 342:
+#line 1769 "parse.y"
+ {
+ (yyval.node) = (NODE*)1;
+ }
+ break;
+
+ case 343:
+#line 1773 "parse.y"
+ {
+ (yyval.node) = (NODE*)1;
+ }
+ break;
+
+ case 344:
+#line 1777 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 345:
+#line 1783 "parse.y"
+ {
+ (yyval.vars) = dyna_push();
+ (yyvsp[(1) - (1)].num) = ruby_sourceline;
+ }
+ break;
+
+ case 346:
+#line 1787 "parse.y"
+ {(yyval.vars) = ruby_dyna_vars;}
+ break;
+
+ case 347:
+#line 1790 "parse.y"
+ {
+ (yyval.node) = NEW_ITER((yyvsp[(3) - (6)].node), 0, dyna_init((yyvsp[(5) - (6)].node), (yyvsp[(4) - (6)].vars)));
+ nd_set_line((yyval.node), (yyvsp[(1) - (6)].num));
+ dyna_pop((yyvsp[(2) - (6)].vars));
+ }
+ break;
+
+ case 348:
+#line 1798 "parse.y"
+ {
+ if ((yyvsp[(1) - (2)].node) && nd_type((yyvsp[(1) - (2)].node)) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ (yyvsp[(2) - (2)].node)->nd_iter = (yyvsp[(1) - (2)].node);
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ fixpos((yyval.node), (yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 349:
+#line 1807 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 350:
+#line 1811 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 351:
+#line 1817 "parse.y"
+ {
+ (yyval.node) = new_fcall((yyvsp[(1) - (2)].id), (yyvsp[(2) - (2)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 352:
+#line 1822 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 353:
+#line 1827 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].id), (yyvsp[(4) - (4)].node));
+ fixpos((yyval.node), (yyvsp[(1) - (4)].node));
+ }
+ break;
+
+ case 354:
+#line 1832 "parse.y"
+ {
+ (yyval.node) = new_call((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].id), 0);
+ }
+ break;
+
+ case 355:
+#line 1836 "parse.y"
+ {
+ (yyval.node) = new_super((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 356:
+#line 1840 "parse.y"
+ {
+ (yyval.node) = NEW_ZSUPER();
+ }
+ break;
+
+ case 357:
+#line 1846 "parse.y"
+ {
+ (yyval.vars) = dyna_push();
+ (yyvsp[(1) - (1)].num) = ruby_sourceline;
+ }
+ break;
+
+ case 358:
+#line 1850 "parse.y"
+ {(yyval.vars) = ruby_dyna_vars;}
+ break;
+
+ case 359:
+#line 1852 "parse.y"
+ {
+ (yyval.node) = NEW_ITER((yyvsp[(3) - (6)].node), 0, dyna_init((yyvsp[(5) - (6)].node), (yyvsp[(4) - (6)].vars)));
+ nd_set_line((yyval.node), (yyvsp[(1) - (6)].num));
+ dyna_pop((yyvsp[(2) - (6)].vars));
+ }
+ break;
+
+ case 360:
+#line 1858 "parse.y"
+ {
+ (yyval.vars) = dyna_push();
+ (yyvsp[(1) - (1)].num) = ruby_sourceline;
+ }
+ break;
+
+ case 361:
+#line 1862 "parse.y"
+ {(yyval.vars) = ruby_dyna_vars;}
+ break;
+
+ case 362:
+#line 1864 "parse.y"
+ {
+ (yyval.node) = NEW_ITER((yyvsp[(3) - (6)].node), 0, dyna_init((yyvsp[(5) - (6)].node), (yyvsp[(4) - (6)].vars)));
+ nd_set_line((yyval.node), (yyvsp[(1) - (6)].num));
+ dyna_pop((yyvsp[(2) - (6)].vars));
+ }
+ break;
+
+ case 363:
+#line 1874 "parse.y"
+ {
+ (yyval.node) = NEW_WHEN((yyvsp[(2) - (5)].node), (yyvsp[(4) - (5)].node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 365:
+#line 1880 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (4)].node), NEW_WHEN((yyvsp[(4) - (4)].node), 0, 0));
+ }
+ break;
+
+ case 366:
+#line 1884 "parse.y"
+ {
+ (yyval.node) = NEW_LIST(NEW_WHEN((yyvsp[(2) - (2)].node), 0, 0));
+ }
+ break;
+
+ case 369:
+#line 1896 "parse.y"
+ {
+ if ((yyvsp[(3) - (6)].node)) {
+ (yyvsp[(3) - (6)].node) = node_assign((yyvsp[(3) - (6)].node), NEW_GVAR(rb_intern("$!")));
+ (yyvsp[(5) - (6)].node) = block_append((yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node));
+ }
+ (yyval.node) = NEW_RESBODY((yyvsp[(2) - (6)].node), (yyvsp[(5) - (6)].node), (yyvsp[(6) - (6)].node));
+ fixpos((yyval.node), (yyvsp[(2) - (6)].node)?(yyvsp[(2) - (6)].node):(yyvsp[(5) - (6)].node));
+ }
+ break;
+
+ case 371:
+#line 1908 "parse.y"
+ {
+ (yyval.node) = NEW_LIST((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 374:
+#line 1916 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 376:
+#line 1923 "parse.y"
+ {
+ if ((yyvsp[(2) - (2)].node))
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ else
+ /* place holder */
+ (yyval.node) = NEW_NIL();
+ }
+ break;
+
+ case 379:
+#line 1935 "parse.y"
+ {
+ (yyval.node) = NEW_LIT(ID2SYM((yyvsp[(1) - (1)].id)));
+ }
+ break;
+
+ case 381:
+#line 1942 "parse.y"
+ {
+ NODE *node = (yyvsp[(1) - (1)].node);
+ if (!node) {
+ node = NEW_STR(rb_str_new(0, 0));
+ }
+ else {
+ node = evstr2dstr(node);
+ }
+ (yyval.node) = node;
+ }
+ break;
+
+ case 383:
+#line 1956 "parse.y"
+ {
+ (yyval.node) = literal_concat((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 384:
+#line 1962 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 385:
+#line 1968 "parse.y"
+ {
+ NODE *node = (yyvsp[(2) - (3)].node);
+ if (!node) {
+ node = NEW_XSTR(rb_str_new(0, 0));
+ }
+ else {
+ switch (nd_type(node)) {
+ case NODE_STR:
+ nd_set_type(node, NODE_XSTR);
+ break;
+ case NODE_DSTR:
+ nd_set_type(node, NODE_DXSTR);
+ break;
+ default:
+ node = NEW_NODE(NODE_DXSTR, rb_str_new(0, 0), 1, NEW_LIST(node));
+ break;
+ }
+ }
+ (yyval.node) = node;
+ }
+ break;
+
+ case 386:
+#line 1991 "parse.y"
+ {
+ int options = (yyvsp[(3) - (3)].num);
+ NODE *node = (yyvsp[(2) - (3)].node);
+ if (!node) {
+ node = NEW_LIT(rb_reg_new("", 0, options & ~RE_OPTION_ONCE));
+ }
+ else switch (nd_type(node)) {
+ case NODE_STR:
+ {
+ VALUE src = node->nd_lit;
+ nd_set_type(node, NODE_LIT);
+ node->nd_lit = rb_reg_new(RSTRING(src)->ptr,
+ RSTRING(src)->len,
+ options & ~RE_OPTION_ONCE);
+ }
+ break;
+ default:
+ node = NEW_NODE(NODE_DSTR, rb_str_new(0, 0), 1, NEW_LIST(node));
+ case NODE_DSTR:
+ if (options & RE_OPTION_ONCE) {
+ nd_set_type(node, NODE_DREGX_ONCE);
+ }
+ else {
+ nd_set_type(node, NODE_DREGX);
+ }
+ node->nd_cflag = options & ~RE_OPTION_ONCE;
+ break;
+ }
+ (yyval.node) = node;
+ }
+ break;
+
+ case 387:
+#line 2024 "parse.y"
+ {
+ (yyval.node) = NEW_ZARRAY();
+ }
+ break;
+
+ case 388:
+#line 2028 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 389:
+#line 2034 "parse.y"
+ {
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 390:
+#line 2038 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (3)].node), evstr2dstr((yyvsp[(2) - (3)].node)));
+ }
+ break;
+
+ case 392:
+#line 2045 "parse.y"
+ {
+ (yyval.node) = literal_concat((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 393:
+#line 2051 "parse.y"
+ {
+ (yyval.node) = NEW_ZARRAY();
+ }
+ break;
+
+ case 394:
+#line 2055 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 395:
+#line 2061 "parse.y"
+ {
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 396:
+#line 2065 "parse.y"
+ {
+ (yyval.node) = list_append((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 397:
+#line 2071 "parse.y"
+ {
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 398:
+#line 2075 "parse.y"
+ {
+ (yyval.node) = literal_concat((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 399:
+#line 2081 "parse.y"
+ {
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 400:
+#line 2085 "parse.y"
+ {
+ (yyval.node) = literal_concat((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 402:
+#line 2092 "parse.y"
+ {
+ (yyval.node) = lex_strterm;
+ lex_strterm = 0;
+ lex_state = EXPR_BEG;
+ }
+ break;
+
+ case 403:
+#line 2098 "parse.y"
+ {
+ lex_strterm = (yyvsp[(2) - (3)].node);
+ (yyval.node) = NEW_EVSTR((yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 404:
+#line 2103 "parse.y"
+ {
+ (yyval.node) = lex_strterm;
+ lex_strterm = 0;
+ lex_state = EXPR_BEG;
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ }
+ break;
+
+ case 405:
+#line 2111 "parse.y"
+ {
+ lex_strterm = (yyvsp[(2) - (4)].node);
+ COND_LEXPOP();
+ CMDARG_LEXPOP();
+ if (((yyval.node) = (yyvsp[(3) - (4)].node)) && nd_type((yyval.node)) == NODE_NEWLINE) {
+ (yyval.node) = (yyval.node)->nd_next;
+ rb_gc_force_recycle((VALUE)(yyvsp[(3) - (4)].node));
+ }
+ (yyval.node) = new_evstr((yyval.node));
+ }
+ break;
+
+ case 406:
+#line 2123 "parse.y"
+ {(yyval.node) = NEW_GVAR((yyvsp[(1) - (1)].id));}
+ break;
+
+ case 407:
+#line 2124 "parse.y"
+ {(yyval.node) = NEW_IVAR((yyvsp[(1) - (1)].id));}
+ break;
+
+ case 408:
+#line 2125 "parse.y"
+ {(yyval.node) = NEW_CVAR((yyvsp[(1) - (1)].id));}
+ break;
+
+ case 410:
+#line 2130 "parse.y"
+ {
+ lex_state = EXPR_END;
+ (yyval.id) = (yyvsp[(2) - (2)].id);
+ }
+ break;
+
+ case 415:
+#line 2143 "parse.y"
+ {
+ lex_state = EXPR_END;
+ if (!((yyval.node) = (yyvsp[(2) - (3)].node))) {
+ (yyval.node) = NEW_NIL();
+ yyerror("empty symbol literal");
+ }
+ else {
+ VALUE lit;
+
+ switch (nd_type((yyval.node))) {
+ case NODE_DSTR:
+ nd_set_type((yyval.node), NODE_DSYM);
+ break;
+ case NODE_STR:
+ lit = (yyval.node)->nd_lit;
+ if (RSTRING(lit)->len == 0) {
+ yyerror("empty symbol literal");
+ break;
+ }
+ if (strlen(RSTRING(lit)->ptr) == RSTRING(lit)->len) {
+ (yyval.node)->nd_lit = ID2SYM(rb_intern(RSTRING((yyval.node)->nd_lit)->ptr));
+ nd_set_type((yyval.node), NODE_LIT);
+ break;
+ }
+ /* fall through */
+ default:
+ (yyval.node) = NEW_NODE(NODE_DSYM, rb_str_new(0, 0), 1, NEW_LIST((yyval.node)));
+ break;
+ }
+ }
+ }
+ break;
+
+ case 418:
+#line 2179 "parse.y"
+ {
+ (yyval.node) = negate_lit((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 419:
+#line 2183 "parse.y"
+ {
+ (yyval.node) = negate_lit((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 425:
+#line 2193 "parse.y"
+ {(yyval.id) = kNIL;}
+ break;
+
+ case 426:
+#line 2194 "parse.y"
+ {(yyval.id) = kSELF;}
+ break;
+
+ case 427:
+#line 2195 "parse.y"
+ {(yyval.id) = kTRUE;}
+ break;
+
+ case 428:
+#line 2196 "parse.y"
+ {(yyval.id) = kFALSE;}
+ break;
+
+ case 429:
+#line 2197 "parse.y"
+ {(yyval.id) = k__FILE__;}
+ break;
+
+ case 430:
+#line 2198 "parse.y"
+ {(yyval.id) = k__LINE__;}
+ break;
+
+ case 431:
+#line 2202 "parse.y"
+ {
+ (yyval.node) = gettable((yyvsp[(1) - (1)].id));
+ }
+ break;
+
+ case 432:
+#line 2208 "parse.y"
+ {
+ (yyval.node) = assignable((yyvsp[(1) - (1)].id), 0);
+ }
+ break;
+
+ case 435:
+#line 2218 "parse.y"
+ {
+ (yyval.node) = 0;
+ }
+ break;
+
+ case 436:
+#line 2222 "parse.y"
+ {
+ lex_state = EXPR_BEG;
+ }
+ break;
+
+ case 437:
+#line 2226 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (4)].node);
+ }
+ break;
+
+ case 438:
+#line 2229 "parse.y"
+ {yyerrok; (yyval.node) = 0;}
+ break;
+
+ case 439:
+#line 2233 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ lex_state = EXPR_BEG;
+ command_start = Qtrue;
+ }
+ break;
+
+ case 440:
+#line 2239 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ }
+ break;
+
+ case 441:
+#line 2245 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS((yyvsp[(1) - (6)].num), (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)), (yyvsp[(6) - (6)].node));
+ }
+ break;
+
+ case 442:
+#line 2249 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS((yyvsp[(1) - (4)].num), (yyvsp[(3) - (4)].node), 0), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 443:
+#line 2253 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS((yyvsp[(1) - (4)].num), 0, (yyvsp[(3) - (4)].node)), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 444:
+#line 2257 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS((yyvsp[(1) - (2)].num), 0, 0), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 445:
+#line 2261 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS(0, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 446:
+#line 2265 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS(0, (yyvsp[(1) - (2)].node), 0), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 447:
+#line 2269 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS(0, 0, (yyvsp[(1) - (2)].node)), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 448:
+#line 2273 "parse.y"
+ {
+ (yyval.node) = block_append(NEW_ARGS(0, 0, 0), (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 449:
+#line 2277 "parse.y"
+ {
+ (yyval.node) = NEW_ARGS(0, 0, 0);
+ }
+ break;
+
+ case 450:
+#line 2283 "parse.y"
+ {
+ yyerror("formal argument cannot be a constant");
+ }
+ break;
+
+ case 451:
+#line 2287 "parse.y"
+ {
+ yyerror("formal argument cannot be an instance variable");
+ }
+ break;
+
+ case 452:
+#line 2291 "parse.y"
+ {
+ yyerror("formal argument cannot be a global variable");
+ }
+ break;
+
+ case 453:
+#line 2295 "parse.y"
+ {
+ yyerror("formal argument cannot be a class variable");
+ }
+ break;
+
+ case 454:
+#line 2299 "parse.y"
+ {
+ if (!is_local_id((yyvsp[(1) - (1)].id)))
+ yyerror("formal argument must be local variable");
+ else if (local_id((yyvsp[(1) - (1)].id)))
+ yyerror("duplicate argument name");
+ local_cnt((yyvsp[(1) - (1)].id));
+ (yyval.num) = 1;
+ }
+ break;
+
+ case 456:
+#line 2311 "parse.y"
+ {
+ (yyval.num) += 1;
+ }
+ break;
+
+ case 457:
+#line 2317 "parse.y"
+ {
+ if (!is_local_id((yyvsp[(1) - (3)].id)))
+ yyerror("formal argument must be local variable");
+ else if (local_id((yyvsp[(1) - (3)].id)))
+ yyerror("duplicate optional argument name");
+ (yyval.node) = assignable((yyvsp[(1) - (3)].id), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 458:
+#line 2327 "parse.y"
+ {
+ (yyval.node) = NEW_BLOCK((yyvsp[(1) - (1)].node));
+ (yyval.node)->nd_end = (yyval.node);
+ }
+ break;
+
+ case 459:
+#line 2332 "parse.y"
+ {
+ (yyval.node) = block_append((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 462:
+#line 2342 "parse.y"
+ {
+ if (!is_local_id((yyvsp[(2) - (2)].id)))
+ yyerror("rest argument must be local variable");
+ else if (local_id((yyvsp[(2) - (2)].id)))
+ yyerror("duplicate rest argument name");
+ if (dyna_in_block()) {
+ rb_dvar_push((yyvsp[(2) - (2)].id), Qnil);
+ }
+ (yyval.node) = assignable((yyvsp[(2) - (2)].id), 0);
+ }
+ break;
+
+ case 463:
+#line 2353 "parse.y"
+ {
+ if (dyna_in_block()) {
+ (yyval.node) = NEW_DASGN_CURR(internal_id(), 0);
+ }
+ else {
+ (yyval.node) = NEW_NODE(NODE_LASGN,0,0,local_append(0));
+ }
+ }
+ break;
+
+ case 466:
+#line 2368 "parse.y"
+ {
+ if (!is_local_id((yyvsp[(2) - (2)].id)))
+ yyerror("block argument must be local variable");
+ else if (local_id((yyvsp[(2) - (2)].id)))
+ yyerror("duplicate block argument name");
+ (yyval.node) = NEW_BLOCK_ARG((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 467:
+#line 2378 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 469:
+#line 2385 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ value_expr((yyval.node));
+ }
+ break;
+
+ case 470:
+#line 2389 "parse.y"
+ {lex_state = EXPR_BEG;}
+ break;
+
+ case 471:
+#line 2390 "parse.y"
+ {
+ if ((yyvsp[(3) - (5)].node) == 0) {
+ yyerror("can't define singleton method for ().");
+ }
+ else {
+ switch (nd_type((yyvsp[(3) - (5)].node))) {
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_XSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_LIT:
+ case NODE_ARRAY:
+ case NODE_ZARRAY:
+ yyerror("can't define singleton method for literals");
+ default:
+ value_expr((yyvsp[(3) - (5)].node));
+ break;
+ }
+ }
+ (yyval.node) = (yyvsp[(3) - (5)].node);
+ }
+ break;
+
+ case 473:
+#line 2416 "parse.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ }
+ break;
+
+ case 474:
+#line 2420 "parse.y"
+ {
+ if ((yyvsp[(1) - (2)].node)->nd_alen%2 != 0) {
+ yyerror("odd number list for Hash");
+ }
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ }
+ break;
+
+ case 476:
+#line 2430 "parse.y"
+ {
+ (yyval.node) = list_concat((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 477:
+#line 2436 "parse.y"
+ {
+ (yyval.node) = list_append(NEW_LIST((yyvsp[(1) - (3)].node)), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 497:
+#line 2474 "parse.y"
+ {yyerrok;}
+ break;
+
+ case 500:
+#line 2479 "parse.y"
+ {yyerrok;}
+ break;
+
+ case 501:
+#line 2482 "parse.y"
+ {(yyval.node) = 0;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 7259 "parse.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 2484 "parse.y"
+
+#ifdef yystacksize
+#undef YYMALLOC
+#endif
+
+#include "regex.h"
+#include "util.h"
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+# define SIGN_EXTEND_CHAR(c) ((signed char)(c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
+#endif
+#define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_' || ismbchar(c)))
+
+static char *tokenbuf = NULL;
+static int tokidx, toksiz = 0;
+
+#define LEAVE_BS 1
+
+static VALUE (*lex_gets)(); /* gets function */
+static VALUE lex_input; /* non-nil if File */
+static VALUE lex_lastline; /* gc protect */
+static char *lex_pbeg;
+static char *lex_p;
+static char *lex_pend;
+
+static int
+yyerror(msg)
+ const char *msg;
+{
+ const int max_line_margin = 30;
+ const char *p, *pe;
+ char *buf;
+ int len, i;
+
+ rb_compile_error("%s", msg);
+ p = lex_p;
+ while (lex_pbeg <= p) {
+ if (*p == '\n') break;
+ p--;
+ }
+ p++;
+
+ pe = lex_p;
+ while (pe < lex_pend) {
+ if (*pe == '\n') break;
+ pe++;
+ }
+
+ len = pe - p;
+ if (len > 4) {
+ char *p2;
+ const char *pre = "", *post = "";
+
+ if (len > max_line_margin * 2 + 10) {
+ int re_mbc_startpos _((const char *, int, int, int));
+ if ((len = lex_p - p) > max_line_margin) {
+ p = p + re_mbc_startpos(p, len, len - max_line_margin, 0);
+ pre = "...";
+ }
+ if ((len = pe - lex_p) > max_line_margin) {
+ pe = lex_p + re_mbc_startpos(lex_p, len, max_line_margin, 1);
+ post = "...";
+ }
+ len = pe - p;
+ }
+ buf = ALLOCA_N(char, len+2);
+ MEMCPY(buf, p, char, len);
+ buf[len] = '\0';
+ rb_compile_error_append("%s%s%s", pre, buf, post);
+
+ i = lex_p - p;
+ p2 = buf; pe = buf + len;
+
+ while (p2 < pe) {
+ if (*p2 != '\t') *p2 = ' ';
+ p2++;
+ }
+ buf[i] = '^';
+ buf[i+1] = '\0';
+ rb_compile_error_append("%s", buf);
+ }
+
+ return 0;
+}
+
+static int heredoc_end;
+
+int ruby_in_compile = 0;
+int ruby__end__seen;
+
+static VALUE ruby_debug_lines;
+#ifdef YYMALLOC
+static NODE *parser_heap;
+#endif
+
+static NODE*
+yycompile(f, line)
+ char *f;
+ int line;
+{
+ int n;
+ NODE *node = 0;
+ struct RVarmap *vp, *vars = ruby_dyna_vars;
+
+ ruby_in_compile = 1;
+ if (!compile_for_eval && rb_safe_level() == 0 &&
+ rb_const_defined(rb_cObject, rb_intern("SCRIPT_LINES__"))) {
+ VALUE hash, fname;
+
+ hash = rb_const_get(rb_cObject, rb_intern("SCRIPT_LINES__"));
+ if (TYPE(hash) == T_HASH) {
+ fname = rb_str_new2(f);
+ ruby_debug_lines = rb_ary_new();
+ rb_hash_aset(hash, fname, ruby_debug_lines);
+ }
+ if (line > 1) {
+ VALUE str = rb_str_new(0,0);
+ while (line > 1) {
+ rb_ary_push(ruby_debug_lines, str);
+ line--;
+ }
+ }
+ }
+
+ ruby__end__seen = 0;
+ ruby_eval_tree = 0;
+ ruby_eval_tree_begin = 0;
+ heredoc_end = 0;
+ lex_strterm = 0;
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename(f);
+ deferred_nodes = 0;
+ n = yyparse();
+ ruby_debug_lines = 0;
+ compile_for_eval = 0;
+ ruby_in_compile = 0;
+ cond_stack = 0;
+ cmdarg_stack = 0;
+ command_start = 1;
+ class_nest = 0;
+ in_single = 0;
+ in_def = 0;
+ cur_mid = 0;
+ deferred_nodes = 0;
+
+ vp = ruby_dyna_vars;
+ ruby_dyna_vars = vars;
+ lex_strterm = 0;
+ while (vp && vp != vars) {
+ struct RVarmap *tmp = vp;
+ vp = vp->next;
+ rb_gc_force_recycle((VALUE)tmp);
+ }
+ if (n == 0) node = ruby_eval_tree;
+ if (ruby_nerrs) ruby_eval_tree_begin = 0;
+ return node;
+}
+
+static int lex_gets_ptr;
+
+static VALUE
+lex_get_str(s)
+ VALUE s;
+{
+ char *beg, *end, *pend;
+
+ beg = RSTRING(s)->ptr;
+ if (lex_gets_ptr) {
+ if (RSTRING(s)->len == lex_gets_ptr) return Qnil;
+ beg += lex_gets_ptr;
+ }
+ pend = RSTRING(s)->ptr + RSTRING(s)->len;
+ end = beg;
+ while (end < pend) {
+ if (*end++ == '\n') break;
+ }
+ lex_gets_ptr = end - RSTRING(s)->ptr;
+ return rb_str_new(beg, end - beg);
+}
+
+static VALUE
+lex_getline()
+{
+ VALUE line = (*lex_gets)(lex_input);
+ if (ruby_debug_lines && !NIL_P(line)) {
+ rb_ary_push(ruby_debug_lines, line);
+ }
+ return line;
+}
+
+NODE*
+rb_compile_string(f, s, line)
+ const char *f;
+ VALUE s;
+ int line;
+{
+ lex_gets = lex_get_str;
+ lex_gets_ptr = 0;
+ lex_input = s;
+ lex_pbeg = lex_p = lex_pend = 0;
+ ruby_sourceline = line - 1;
+ compile_for_eval = ruby_in_eval;
+
+ return yycompile(f, line);
+}
+
+NODE*
+rb_compile_cstr(f, s, len, line)
+ const char *f, *s;
+ int len, line;
+{
+ return rb_compile_string(f, rb_str_new(s, len), line);
+}
+
+NODE*
+rb_compile_file(f, file, start)
+ const char *f;
+ VALUE file;
+ int start;
+{
+ lex_gets = rb_io_gets;
+ lex_input = file;
+ lex_pbeg = lex_p = lex_pend = 0;
+ ruby_sourceline = start - 1;
+
+ return yycompile(f, start);
+}
+
+static inline int
+nextc()
+{
+ int c;
+
+ if (lex_p == lex_pend) {
+ if (lex_input) {
+ VALUE v = lex_getline();
+
+ if (NIL_P(v)) return -1;
+ if (heredoc_end > 0) {
+ ruby_sourceline = heredoc_end;
+ heredoc_end = 0;
+ }
+ ruby_sourceline++;
+ lex_pbeg = lex_p = RSTRING(v)->ptr;
+ lex_pend = lex_p + RSTRING(v)->len;
+ lex_lastline = v;
+ }
+ else {
+ lex_lastline = 0;
+ return -1;
+ }
+ }
+ c = (unsigned char)*lex_p++;
+ if (c == '\r' && lex_p < lex_pend && *lex_p == '\n') {
+ lex_p++;
+ c = '\n';
+ }
+
+ return c;
+}
+
+static void
+pushback(c)
+ int c;
+{
+ if (c == -1) return;
+ lex_p--;
+}
+
+#define was_bol() (lex_p == lex_pbeg + 1)
+#define peek(c) (lex_p != lex_pend && (c) == *lex_p)
+
+#define tokfix() (tokenbuf[tokidx]='\0')
+#define tok() tokenbuf
+#define toklen() tokidx
+#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0)
+
+static char*
+newtok()
+{
+ tokidx = 0;
+ if (!tokenbuf) {
+ toksiz = 60;
+ tokenbuf = ALLOC_N(char, 60);
+ }
+ if (toksiz > 4096) {
+ toksiz = 60;
+ REALLOC_N(tokenbuf, char, 60);
+ }
+ return tokenbuf;
+}
+
+static void
+tokadd(c)
+ char c;
+{
+ tokenbuf[tokidx++] = c;
+ if (tokidx >= toksiz) {
+ toksiz *= 2;
+ REALLOC_N(tokenbuf, char, toksiz);
+ }
+}
+
+static int
+read_escape()
+{
+ int c;
+
+ switch (c = nextc()) {
+ case '\\': /* Backslash */
+ return c;
+
+ case 'n': /* newline */
+ return '\n';
+
+ case 't': /* horizontal tab */
+ return '\t';
+
+ case 'r': /* carriage-return */
+ return '\r';
+
+ case 'f': /* form-feed */
+ return '\f';
+
+ case 'v': /* vertical tab */
+ return '\13';
+
+ case 'a': /* alarm(bell) */
+ return '\007';
+
+ case 'e': /* escape */
+ return 033;
+
+ case '0': case '1': case '2': case '3': /* octal constant */
+ case '4': case '5': case '6': case '7':
+ {
+ int numlen;
+
+ pushback(c);
+ c = scan_oct(lex_p, 3, &numlen);
+ lex_p += numlen;
+ }
+ return c;
+
+ case 'x': /* hex constant */
+ {
+ int numlen;
+
+ c = scan_hex(lex_p, 2, &numlen);
+ if (numlen == 0) {
+ yyerror("Invalid escape character syntax");
+ return 0;
+ }
+ lex_p += numlen;
+ }
+ return c;
+
+ case 'b': /* backspace */
+ return '\010';
+
+ case 's': /* space */
+ return ' ';
+
+ case 'M':
+ if ((c = nextc()) != '-') {
+ yyerror("Invalid escape character syntax");
+ pushback(c);
+ return '\0';
+ }
+ if ((c = nextc()) == '\\') {
+ return read_escape() | 0x80;
+ }
+ else if (c == -1) goto eof;
+ else {
+ return ((c & 0xff) | 0x80);
+ }
+
+ case 'C':
+ if ((c = nextc()) != '-') {
+ yyerror("Invalid escape character syntax");
+ pushback(c);
+ return '\0';
+ }
+ case 'c':
+ if ((c = nextc())== '\\') {
+ c = read_escape();
+ }
+ else if (c == '?')
+ return 0177;
+ else if (c == -1) goto eof;
+ return c & 0x9f;
+
+ eof:
+ case -1:
+ yyerror("Invalid escape character syntax");
+ return '\0';
+
+ default:
+ return c;
+ }
+}
+
+static int
+tokadd_escape(term)
+ int term;
+{
+ int c;
+
+ switch (c = nextc()) {
+ case '\n':
+ return 0; /* just ignore */
+
+ case '0': case '1': case '2': case '3': /* octal constant */
+ case '4': case '5': case '6': case '7':
+ {
+ int i;
+
+ tokadd('\\');
+ tokadd(c);
+ for (i=0; i<2; i++) {
+ c = nextc();
+ if (c == -1) goto eof;
+ if (c < '0' || '7' < c) {
+ pushback(c);
+ break;
+ }
+ tokadd(c);
+ }
+ }
+ return 0;
+
+ case 'x': /* hex constant */
+ {
+ int numlen;
+
+ tokadd('\\');
+ tokadd(c);
+ scan_hex(lex_p, 2, &numlen);
+ if (numlen == 0) {
+ yyerror("Invalid escape character syntax");
+ return -1;
+ }
+ while (numlen--)
+ tokadd(nextc());
+ }
+ return 0;
+
+ case 'M':
+ if ((c = nextc()) != '-') {
+ yyerror("Invalid escape character syntax");
+ pushback(c);
+ return 0;
+ }
+ tokadd('\\'); tokadd('M'); tokadd('-');
+ goto escaped;
+
+ case 'C':
+ if ((c = nextc()) != '-') {
+ yyerror("Invalid escape character syntax");
+ pushback(c);
+ return 0;
+ }
+ tokadd('\\'); tokadd('C'); tokadd('-');
+ goto escaped;
+
+ case 'c':
+ tokadd('\\'); tokadd('c');
+ escaped:
+ if ((c = nextc()) == '\\') {
+ return tokadd_escape(term);
+ }
+ else if (c == -1) goto eof;
+ tokadd(c);
+ return 0;
+
+ eof:
+ case -1:
+ yyerror("Invalid escape character syntax");
+ return -1;
+
+ default:
+ if (c != '\\' || c != term)
+ tokadd('\\');
+ tokadd(c);
+ }
+ return 0;
+}
+
+static int
+regx_options()
+{
+ char kcode = 0;
+ int options = 0;
+ int c;
+
+ newtok();
+ while (c = nextc(), ISALPHA(c)) {
+ switch (c) {
+ case 'i':
+ options |= RE_OPTION_IGNORECASE;
+ break;
+ case 'x':
+ options |= RE_OPTION_EXTENDED;
+ break;
+ case 'm':
+ options |= RE_OPTION_MULTILINE;
+ break;
+ case 'o':
+ options |= RE_OPTION_ONCE;
+ break;
+ case 'n':
+ kcode = 16;
+ break;
+ case 'e':
+ kcode = 32;
+ break;
+ case 's':
+ kcode = 48;
+ break;
+ case 'u':
+ kcode = 64;
+ break;
+ default:
+ tokadd(c);
+ break;
+ }
+ }
+ pushback(c);
+ if (toklen()) {
+ tokfix();
+ rb_compile_error("unknown regexp option%s - %s",
+ toklen() > 1 ? "s" : "", tok());
+ }
+ return options | kcode;
+}
+
+#define STR_FUNC_ESCAPE 0x01
+#define STR_FUNC_EXPAND 0x02
+#define STR_FUNC_REGEXP 0x04
+#define STR_FUNC_QWORDS 0x08
+#define STR_FUNC_SYMBOL 0x10
+#define STR_FUNC_INDENT 0x20
+
+enum string_type {
+ str_squote = (0),
+ str_dquote = (STR_FUNC_EXPAND),
+ str_xquote = (STR_FUNC_EXPAND),
+ str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND),
+ str_sword = (STR_FUNC_QWORDS),
+ str_dword = (STR_FUNC_QWORDS|STR_FUNC_EXPAND),
+ str_ssym = (STR_FUNC_SYMBOL),
+ str_dsym = (STR_FUNC_SYMBOL|STR_FUNC_EXPAND),
+};
+
+static void
+dispose_string(str)
+ VALUE str;
+{
+ xfree(RSTRING(str)->ptr);
+ rb_gc_force_recycle(str);
+}
+
+static int
+tokadd_string(func, term, paren, nest)
+ int func, term, paren, *nest;
+{
+ int c;
+
+ while ((c = nextc()) != -1) {
+ if (paren && c == paren) {
+ ++*nest;
+ }
+ else if (c == term) {
+ if (!nest || !*nest) {
+ pushback(c);
+ break;
+ }
+ --*nest;
+ }
+ else if ((func & STR_FUNC_EXPAND) && c == '#' && lex_p < lex_pend) {
+ int c2 = *lex_p;
+ if (c2 == '$' || c2 == '@' || c2 == '{') {
+ pushback(c);
+ break;
+ }
+ }
+ else if (c == '\\') {
+ c = nextc();
+ switch (c) {
+ case '\n':
+ if (func & STR_FUNC_QWORDS) break;
+ if (func & STR_FUNC_EXPAND) continue;
+ tokadd('\\');
+ break;
+
+ case '\\':
+ if (func & STR_FUNC_ESCAPE) tokadd(c);
+ break;
+
+ default:
+ if (func & STR_FUNC_REGEXP) {
+ pushback(c);
+ if (tokadd_escape(term) < 0)
+ return -1;
+ continue;
+ }
+ else if (func & STR_FUNC_EXPAND) {
+ pushback(c);
+ if (func & STR_FUNC_ESCAPE) tokadd('\\');
+ c = read_escape();
+ }
+ else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
+ /* ignore backslashed spaces in %w */
+ }
+ else if (c != term && !(paren && c == paren)) {
+ tokadd('\\');
+ }
+ }
+ }
+ else if (ismbchar(c)) {
+ int i, len = mbclen(c)-1;
+
+ for (i = 0; i < len; i++) {
+ tokadd(c);
+ c = nextc();
+ }
+ }
+ else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
+ pushback(c);
+ break;
+ }
+ if (!c && (func & STR_FUNC_SYMBOL)) {
+ func &= ~STR_FUNC_SYMBOL;
+ rb_compile_error("symbol cannot contain '\\0'");
+ continue;
+ }
+ tokadd(c);
+ }
+ return c;
+}
+
+#define NEW_STRTERM(func, term, paren) \
+ rb_node_newnode(NODE_STRTERM, (func), (term) | ((paren) << (CHAR_BIT * 2)), 0)
+
+static int
+parse_string(quote)
+ NODE *quote;
+{
+ int func = quote->nd_func;
+ int term = nd_term(quote);
+ int paren = nd_paren(quote);
+ int c, space = 0;
+
+ if (func == -1) return tSTRING_END;
+ c = nextc();
+ if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
+ do {c = nextc();} while (ISSPACE(c));
+ space = 1;
+ }
+ if (c == term && !quote->nd_nest) {
+ if (func & STR_FUNC_QWORDS) {
+ quote->nd_func = -1;
+ return ' ';
+ }
+ if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
+ yylval.num = regx_options();
+ return tREGEXP_END;
+ }
+ if (space) {
+ pushback(c);
+ return ' ';
+ }
+ newtok();
+ if ((func & STR_FUNC_EXPAND) && c == '#') {
+ switch (c = nextc()) {
+ case '$':
+ case '@':
+ pushback(c);
+ return tSTRING_DVAR;
+ case '{':
+ return tSTRING_DBEG;
+ }
+ tokadd('#');
+ }
+ pushback(c);
+ if (tokadd_string(func, term, paren, &quote->nd_nest) == -1) {
+ ruby_sourceline = nd_line(quote);
+ rb_compile_error("unterminated string meets end of file");
+ return tSTRING_END;
+ }
+
+ tokfix();
+ yylval.node = NEW_STR(rb_str_new(tok(), toklen()));
+ return tSTRING_CONTENT;
+}
+
+static int
+heredoc_identifier()
+{
+ int c = nextc(), term, func = 0, len;
+
+ if (c == '-') {
+ c = nextc();
+ func = STR_FUNC_INDENT;
+ }
+ switch (c) {
+ case '\'':
+ func |= str_squote; goto quoted;
+ case '"':
+ func |= str_dquote; goto quoted;
+ case '`':
+ func |= str_xquote;
+ quoted:
+ newtok();
+ tokadd(func);
+ term = c;
+ while ((c = nextc()) != -1 && c != term) {
+ len = mbclen(c);
+ do {tokadd(c);} while (--len > 0 && (c = nextc()) != -1);
+ }
+ if (c == -1) {
+ rb_compile_error("unterminated here document identifier");
+ return 0;
+ }
+ break;
+
+ default:
+ if (!is_identchar(c)) {
+ pushback(c);
+ if (func & STR_FUNC_INDENT) {
+ pushback('-');
+ }
+ return 0;
+ }
+ newtok();
+ term = '"';
+ tokadd(func |= str_dquote);
+ do {
+ len = mbclen(c);
+ do {tokadd(c);} while (--len > 0 && (c = nextc()) != -1);
+ } while ((c = nextc()) != -1 && is_identchar(c));
+ pushback(c);
+ break;
+ }
+
+ tokfix();
+ len = lex_p - lex_pbeg;
+ lex_p = lex_pend;
+ lex_strterm = rb_node_newnode(NODE_HEREDOC,
+ rb_str_new(tok(), toklen()), /* nd_lit */
+ len, /* nd_nth */
+ lex_lastline); /* nd_orig */
+ return term == '`' ? tXSTRING_BEG : tSTRING_BEG;
+}
+
+static void
+heredoc_restore(here)
+ NODE *here;
+{
+ VALUE line = here->nd_orig;
+ lex_lastline = line;
+ lex_pbeg = RSTRING(line)->ptr;
+ lex_pend = lex_pbeg + RSTRING(line)->len;
+ lex_p = lex_pbeg + here->nd_nth;
+ heredoc_end = ruby_sourceline;
+ ruby_sourceline = nd_line(here);
+ dispose_string(here->nd_lit);
+ rb_gc_force_recycle((VALUE)here);
+}
+
+static int
+whole_match_p(eos, len, indent)
+ char *eos;
+ int len, indent;
+{
+ char *p = lex_pbeg;
+ int n;
+
+ if (indent) {
+ while (*p && ISSPACE(*p)) p++;
+ }
+ n= lex_pend - (p + len);
+ if (n < 0 || (n > 0 && p[len] != '\n' && p[len] != '\r')) return Qfalse;
+ if (strncmp(eos, p, len) == 0) return Qtrue;
+ return Qfalse;
+}
+
+static int
+here_document(here)
+ NODE *here;
+{
+ int c, func, indent = 0;
+ char *eos, *p, *pend;
+ long len;
+ VALUE str = 0;
+
+ eos = RSTRING(here->nd_lit)->ptr;
+ len = RSTRING(here->nd_lit)->len - 1;
+ indent = (func = *eos++) & STR_FUNC_INDENT;
+
+ if ((c = nextc()) == -1) {
+ error:
+ rb_compile_error("can't find string \"%s\" anywhere before EOF", eos);
+ heredoc_restore(lex_strterm);
+ lex_strterm = 0;
+ return 0;
+ }
+ if (was_bol() && whole_match_p(eos, len, indent)) {
+ heredoc_restore(lex_strterm);
+ return tSTRING_END;
+ }
+
+ if (!(func & STR_FUNC_EXPAND)) {
+ do {
+ p = RSTRING(lex_lastline)->ptr;
+ pend = lex_pend;
+ if (pend > p) {
+ switch (pend[-1]) {
+ case '\n':
+ if (--pend == p || pend[-1] != '\r') {
+ pend++;
+ break;
+ }
+ case '\r':
+ --pend;
+ }
+ }
+ if (str)
+ rb_str_cat(str, p, pend - p);
+ else
+ str = rb_str_new(p, pend - p);
+ if (pend < lex_pend) rb_str_cat(str, "\n", 1);
+ lex_p = lex_pend;
+ if (nextc() == -1) {
+ if (str) dispose_string(str);
+ goto error;
+ }
+ } while (!whole_match_p(eos, len, indent));
+ }
+ else {
+ newtok();
+ if (c == '#') {
+ switch (c = nextc()) {
+ case '$':
+ case '@':
+ pushback(c);
+ return tSTRING_DVAR;
+ case '{':
+ return tSTRING_DBEG;
+ }
+ tokadd('#');
+ }
+ do {
+ pushback(c);
+ if ((c = tokadd_string(func, '\n', 0, NULL)) == -1) goto error;
+ if (c != '\n') {
+ yylval.node = NEW_STR(rb_str_new(tok(), toklen()));
+ return tSTRING_CONTENT;
+ }
+ tokadd(nextc());
+ if ((c = nextc()) == -1) goto error;
+ } while (!whole_match_p(eos, len, indent));
+ str = rb_str_new(tok(), toklen());
+ }
+ heredoc_restore(lex_strterm);
+ lex_strterm = NEW_STRTERM(-1, 0, 0);
+ yylval.node = NEW_STR(str);
+ return tSTRING_CONTENT;
+}
+
+#include "lex.c"
+
+static void
+arg_ambiguous()
+{
+ rb_warning("ambiguous first argument; put parentheses or even spaces");
+}
+
+#define IS_ARG() (lex_state == EXPR_ARG || lex_state == EXPR_CMDARG)
+#define IS_BEG() (lex_state == EXPR_BEG || lex_state == EXPR_MID || lex_state == EXPR_CLASS)
+
+static int
+yylex()
+{
+ register int c;
+ int space_seen = 0;
+ int cmd_state;
+ enum lex_state last_state;
+
+ if (lex_strterm) {
+ int token;
+ if (nd_type(lex_strterm) == NODE_HEREDOC) {
+ token = here_document(lex_strterm);
+ if (token == tSTRING_END) {
+ lex_strterm = 0;
+ lex_state = EXPR_END;
+ }
+ }
+ else {
+ token = parse_string(lex_strterm);
+ if (token == tSTRING_END || token == tREGEXP_END) {
+ rb_gc_force_recycle((VALUE)lex_strterm);
+ lex_strterm = 0;
+ lex_state = EXPR_END;
+ }
+ }
+ return token;
+ }
+ cmd_state = command_start;
+ command_start = Qfalse;
+ retry:
+ switch (c = nextc()) {
+ case '\0': /* NUL */
+ case '\004': /* ^D */
+ case '\032': /* ^Z */
+ case -1: /* end of script. */
+ return 0;
+
+ /* white spaces */
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen++;
+ goto retry;
+
+ case '#': /* it's a comment */
+ while ((c = nextc()) != '\n') {
+ if (c == -1)
+ return 0;
+ }
+ /* fall through */
+ case '\n':
+ switch (lex_state) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ goto retry;
+ default:
+ break;
+ }
+ command_start = Qtrue;
+ lex_state = EXPR_BEG;
+ return '\n';
+
+ case '*':
+ if ((c = nextc()) == '*') {
+ if ((c = nextc()) == '=') {
+ yylval.id = tPOW;
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ c = tPOW;
+ }
+ else {
+ if (c == '=') {
+ yylval.id = '*';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ if (IS_ARG() && space_seen && !ISSPACE(c)){
+ rb_warning("`*' interpreted as argument prefix");
+ c = tSTAR;
+ }
+ else if (IS_BEG()) {
+ c = tSTAR;
+ }
+ else {
+ c = '*';
+ }
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ return c;
+
+ case '!':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return tNEQ;
+ }
+ if (c == '~') {
+ return tNMATCH;
+ }
+ pushback(c);
+ return '!';
+
+ case '=':
+ if (was_bol()) {
+ /* skip embedded rd document */
+ if (strncmp(lex_p, "begin", 5) == 0 && ISSPACE(lex_p[5])) {
+ for (;;) {
+ lex_p = lex_pend;
+ c = nextc();
+ if (c == -1) {
+ rb_compile_error("embedded document meets end of file");
+ return 0;
+ }
+ if (c != '=') continue;
+ if (strncmp(lex_p, "end", 3) == 0 &&
+ (lex_p + 3 == lex_pend || ISSPACE(lex_p[3]))) {
+ break;
+ }
+ }
+ lex_p = lex_pend;
+ goto retry;
+ }
+ }
+
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ if ((c = nextc()) == '=') {
+ if ((c = nextc()) == '=') {
+ return tEQQ;
+ }
+ pushback(c);
+ return tEQ;
+ }
+ if (c == '~') {
+ return tMATCH;
+ }
+ else if (c == '>') {
+ return tASSOC;
+ }
+ pushback(c);
+ return '=';
+
+ case '<':
+ c = nextc();
+ if (c == '<' &&
+ lex_state != EXPR_END &&
+ lex_state != EXPR_DOT &&
+ lex_state != EXPR_ENDARG &&
+ lex_state != EXPR_CLASS &&
+ (!IS_ARG() || space_seen)) {
+ int token = heredoc_identifier();
+ if (token) return token;
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ if (c == '=') {
+ if ((c = nextc()) == '>') {
+ return tCMP;
+ }
+ pushback(c);
+ return tLEQ;
+ }
+ if (c == '<') {
+ if ((c = nextc()) == '=') {
+ yylval.id = tLSHFT;
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ return tLSHFT;
+ }
+ pushback(c);
+ return '<';
+
+ case '>':
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ if ((c = nextc()) == '=') {
+ return tGEQ;
+ }
+ if (c == '>') {
+ if ((c = nextc()) == '=') {
+ yylval.id = tRSHFT;
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ return tRSHFT;
+ }
+ pushback(c);
+ return '>';
+
+ case '"':
+ lex_strterm = NEW_STRTERM(str_dquote, '"', 0);
+ return tSTRING_BEG;
+
+ case '`':
+ if (lex_state == EXPR_FNAME) {
+ lex_state = EXPR_END;
+ return c;
+ }
+ if (lex_state == EXPR_DOT) {
+ if (cmd_state)
+ lex_state = EXPR_CMDARG;
+ else
+ lex_state = EXPR_ARG;
+ return c;
+ }
+ lex_strterm = NEW_STRTERM(str_xquote, '`', 0);
+ return tXSTRING_BEG;
+
+ case '\'':
+ lex_strterm = NEW_STRTERM(str_squote, '\'', 0);
+ return tSTRING_BEG;
+
+ case '?':
+ if (lex_state == EXPR_END || lex_state == EXPR_ENDARG) {
+ lex_state = EXPR_BEG;
+ return '?';
+ }
+ c = nextc();
+ if (c == -1) {
+ rb_compile_error("incomplete character syntax");
+ return 0;
+ }
+ if (ISSPACE(c)){
+ if (!IS_ARG()){
+ int c2 = 0;
+ switch (c) {
+ case ' ':
+ c2 = 's';
+ break;
+ case '\n':
+ c2 = 'n';
+ break;
+ case '\t':
+ c2 = 't';
+ break;
+ case '\v':
+ c2 = 'v';
+ break;
+ case '\r':
+ c2 = 'r';
+ break;
+ case '\f':
+ c2 = 'f';
+ break;
+ }
+ if (c2) {
+ rb_warn("invalid character syntax; use ?\\%c", c2);
+ }
+ }
+ ternary:
+ pushback(c);
+ lex_state = EXPR_BEG;
+ return '?';
+ }
+ else if (ismbchar(c)) {
+ rb_warn("multibyte character literal not supported yet; use ?\\%.3o", c);
+ goto ternary;
+ }
+ else if ((ISALNUM(c) || c == '_') && lex_p < lex_pend && is_identchar(*lex_p)) {
+ goto ternary;
+ }
+ else if (c == '\\') {
+ c = read_escape();
+ }
+ c &= 0xff;
+ lex_state = EXPR_END;
+ yylval.node = NEW_LIT(INT2FIX(c));
+ return tINTEGER;
+
+ case '&':
+ if ((c = nextc()) == '&') {
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = tANDOP;
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ return tANDOP;
+ }
+ else if (c == '=') {
+ yylval.id = '&';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ if (IS_ARG() && space_seen && !ISSPACE(c)){
+ rb_warning("`&' interpreted as argument prefix");
+ c = tAMPER;
+ }
+ else if (IS_BEG()) {
+ c = tAMPER;
+ }
+ else {
+ c = '&';
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG;
+ }
+ return c;
+
+ case '|':
+ if ((c = nextc()) == '|') {
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = tOROP;
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ return tOROP;
+ }
+ if (c == '=') {
+ yylval.id = '|';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
+ lex_state = EXPR_ARG;
+ }
+ else {
+ lex_state = EXPR_BEG;
+ }
+ pushback(c);
+ return '|';
+
+ case '+':
+ c = nextc();
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
+ lex_state = EXPR_ARG;
+ if (c == '@') {
+ return tUPLUS;
+ }
+ pushback(c);
+ return '+';
+ }
+ if (c == '=') {
+ yylval.id = '+';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (IS_BEG() ||
+ (IS_ARG() && space_seen && !ISSPACE(c))) {
+ if (IS_ARG()) arg_ambiguous();
+ lex_state = EXPR_BEG;
+ pushback(c);
+ if (ISDIGIT(c)) {
+ c = '+';
+ goto start_num;
+ }
+ return tUPLUS;
+ }
+ lex_state = EXPR_BEG;
+ pushback(c);
+ return '+';
+
+ case '-':
+ c = nextc();
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
+ lex_state = EXPR_ARG;
+ if (c == '@') {
+ return tUMINUS;
+ }
+ pushback(c);
+ return '-';
+ }
+ if (c == '=') {
+ yylval.id = '-';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (IS_BEG() ||
+ (IS_ARG() && space_seen && !ISSPACE(c))) {
+ if (IS_ARG()) arg_ambiguous();
+ lex_state = EXPR_BEG;
+ pushback(c);
+ if (ISDIGIT(c)) {
+ return tUMINUS_NUM;
+ }
+ return tUMINUS;
+ }
+ lex_state = EXPR_BEG;
+ pushback(c);
+ return '-';
+
+ case '.':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '.') {
+ if ((c = nextc()) == '.') {
+ return tDOT3;
+ }
+ pushback(c);
+ return tDOT2;
+ }
+ pushback(c);
+ if (ISDIGIT(c)) {
+ yyerror("no .<digit> floating literal anymore; put 0 before dot");
+ }
+ lex_state = EXPR_DOT;
+ return '.';
+
+ start_num:
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int is_float, seen_point, seen_e, nondigit;
+
+ is_float = seen_point = seen_e = nondigit = 0;
+ lex_state = EXPR_END;
+ newtok();
+ if (c == '-' || c == '+') {
+ tokadd(c);
+ c = nextc();
+ }
+ if (c == '0') {
+ int start = toklen();
+ c = nextc();
+ if (c == 'x' || c == 'X') {
+ /* hexadecimal */
+ c = nextc();
+ if (ISXDIGIT(c)) {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (!ISXDIGIT(c)) break;
+ nondigit = 0;
+ tokadd(c);
+ } while ((c = nextc()) != -1);
+ }
+ pushback(c);
+ tokfix();
+ if (toklen() == start) {
+ yyerror("numeric literal without digits");
+ }
+ else if (nondigit) goto trailing_uc;
+ yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 16, Qfalse));
+ return tINTEGER;
+ }
+ if (c == 'b' || c == 'B') {
+ /* binary */
+ c = nextc();
+ if (c == '0' || c == '1') {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (c != '0' && c != '1') break;
+ nondigit = 0;
+ tokadd(c);
+ } while ((c = nextc()) != -1);
+ }
+ pushback(c);
+ tokfix();
+ if (toklen() == start) {
+ yyerror("numeric literal without digits");
+ }
+ else if (nondigit) goto trailing_uc;
+ yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 2, Qfalse));
+ return tINTEGER;
+ }
+ if (c == 'd' || c == 'D') {
+ /* decimal */
+ c = nextc();
+ if (ISDIGIT(c)) {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (!ISDIGIT(c)) break;
+ nondigit = 0;
+ tokadd(c);
+ } while ((c = nextc()) != -1);
+ }
+ pushback(c);
+ tokfix();
+ if (toklen() == start) {
+ yyerror("numeric literal without digits");
+ }
+ else if (nondigit) goto trailing_uc;
+ yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 10, Qfalse));
+ return tINTEGER;
+ }
+ if (c == '_') {
+ /* 0_0 */
+ goto octal_number;
+ }
+ if (c == 'o' || c == 'O') {
+ /* prefixed octal */
+ c = nextc();
+ if (c == '_') {
+ yyerror("numeric literal without digits");
+ }
+ }
+ if (c >= '0' && c <= '7') {
+ /* octal */
+ octal_number:
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (c < '0' || c > '9') break;
+ if (c > '7') goto invalid_octal;
+ nondigit = 0;
+ tokadd(c);
+ } while ((c = nextc()) != -1);
+ if (toklen() > start) {
+ pushback(c);
+ tokfix();
+ if (nondigit) goto trailing_uc;
+ yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 8, Qfalse));
+ return tINTEGER;
+ }
+ if (nondigit) {
+ pushback(c);
+ goto trailing_uc;
+ }
+ }
+ if (c > '7' && c <= '9') {
+ invalid_octal:
+ yyerror("Illegal octal digit");
+ }
+ else if (c == '.' || c == 'e' || c == 'E') {
+ tokadd('0');
+ }
+ else {
+ pushback(c);
+ yylval.node = NEW_LIT(INT2FIX(0));
+ return tINTEGER;
+ }
+ }
+
+ for (;;) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ nondigit = 0;
+ tokadd(c);
+ break;
+
+ case '.':
+ if (nondigit) goto trailing_uc;
+ if (seen_point || seen_e) {
+ goto decode_num;
+ }
+ else {
+ int c0 = nextc();
+ if (!ISDIGIT(c0)) {
+ pushback(c0);
+ goto decode_num;
+ }
+ c = c0;
+ }
+ tokadd('.');
+ tokadd(c);
+ is_float++;
+ seen_point++;
+ nondigit = 0;
+ break;
+
+ case 'e':
+ case 'E':
+ if (nondigit) {
+ pushback(c);
+ c = nondigit;
+ goto decode_num;
+ }
+ if (seen_e) {
+ goto decode_num;
+ }
+ tokadd(c);
+ seen_e++;
+ is_float++;
+ nondigit = c;
+ c = nextc();
+ if (c != '-' && c != '+') continue;
+ tokadd(c);
+ nondigit = c;
+ break;
+
+ case '_': /* `_' in number just ignored */
+ if (nondigit) goto decode_num;
+ nondigit = c;
+ break;
+
+ default:
+ goto decode_num;
+ }
+ c = nextc();
+ }
+
+ decode_num:
+ pushback(c);
+ tokfix();
+ if (nondigit) {
+ char tmp[30];
+ trailing_uc:
+ sprintf(tmp, "trailing `%c' in number", nondigit);
+ yyerror(tmp);
+ }
+ if (is_float) {
+ double d = strtod(tok(), 0);
+ if (errno == ERANGE) {
+ rb_warn("Float %s out of range", tok());
+ errno = 0;
+ }
+ yylval.node = NEW_LIT(rb_float_new(d));
+ return tFLOAT;
+ }
+ yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 10, Qfalse));
+ return tINTEGER;
+ }
+
+ case ']':
+ case '}':
+ case ')':
+ COND_LEXPOP();
+ CMDARG_LEXPOP();
+ lex_state = EXPR_END;
+ return c;
+
+ case ':':
+ c = nextc();
+ if (c == ':') {
+ if (IS_BEG() || (IS_ARG() && space_seen)) {
+ lex_state = EXPR_BEG;
+ return tCOLON3;
+ }
+ lex_state = EXPR_DOT;
+ return tCOLON2;
+ }
+ if (lex_state == EXPR_END || lex_state == EXPR_ENDARG || ISSPACE(c)) {
+ pushback(c);
+ lex_state = EXPR_BEG;
+ return ':';
+ }
+ switch (c) {
+ case '\'':
+ lex_strterm = NEW_STRTERM(str_ssym, c, 0);
+ break;
+ case '"':
+ lex_strterm = NEW_STRTERM(str_dsym, c, 0);
+ break;
+ default:
+ pushback(c);
+ break;
+ }
+ lex_state = EXPR_FNAME;
+ return tSYMBEG;
+
+ case '/':
+ if (IS_BEG()) {
+ lex_strterm = NEW_STRTERM(str_regexp, '/', 0);
+ return tREGEXP_BEG;
+ }
+ if ((c = nextc()) == '=') {
+ yylval.id = '/';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(c);
+ if (IS_ARG() && space_seen) {
+ if (!ISSPACE(c)) {
+ arg_ambiguous();
+ lex_strterm = NEW_STRTERM(str_regexp, '/', 0);
+ return tREGEXP_BEG;
+ }
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ return '/';
+
+ case '^':
+ if ((c = nextc()) == '=') {
+ yylval.id = '^';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ pushback(c);
+ return '^';
+
+ case ';':
+ command_start = Qtrue;
+ case ',':
+ lex_state = EXPR_BEG;
+ return c;
+
+ case '~':
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
+ if ((c = nextc()) != '@') {
+ pushback(c);
+ }
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ return '~';
+
+ case '(':
+ command_start = Qtrue;
+ if (IS_BEG()) {
+ c = tLPAREN;
+ }
+ else if (space_seen) {
+ if (lex_state == EXPR_CMDARG) {
+ c = tLPAREN_ARG;
+ }
+ else if (lex_state == EXPR_ARG) {
+ rb_warn("don't put space before argument parentheses");
+ c = '(';
+ }
+ }
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ lex_state = EXPR_BEG;
+ return c;
+
+ case '[':
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
+ lex_state = EXPR_ARG;
+ if ((c = nextc()) == ']') {
+ if ((c = nextc()) == '=') {
+ return tASET;
+ }
+ pushback(c);
+ return tAREF;
+ }
+ pushback(c);
+ return '[';
+ }
+ else if (IS_BEG()) {
+ c = tLBRACK;
+ }
+ else if (IS_ARG() && space_seen) {
+ c = tLBRACK;
+ }
+ lex_state = EXPR_BEG;
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ return c;
+
+ case '{':
+ if (IS_ARG() || lex_state == EXPR_END)
+ c = '{'; /* block (primary) */
+ else if (lex_state == EXPR_ENDARG)
+ c = tLBRACE_ARG; /* block (expr) */
+ else
+ c = tLBRACE; /* hash */
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ lex_state = EXPR_BEG;
+ return c;
+
+ case '\\':
+ c = nextc();
+ if (c == '\n') {
+ space_seen = 1;
+ goto retry; /* skip \\n */
+ }
+ pushback(c);
+ return '\\';
+
+ case '%':
+ if (IS_BEG()) {
+ int term;
+ int paren;
+
+ c = nextc();
+ quotation:
+ if (!ISALNUM(c)) {
+ term = c;
+ c = 'Q';
+ }
+ else {
+ term = nextc();
+ if (ISALNUM(term) || ismbchar(term)) {
+ yyerror("unknown type of %string");
+ return 0;
+ }
+ }
+ if (c == -1 || term == -1) {
+ rb_compile_error("unterminated quoted string meets end of file");
+ return 0;
+ }
+ paren = term;
+ if (term == '(') term = ')';
+ else if (term == '[') term = ']';
+ else if (term == '{') term = '}';
+ else if (term == '<') term = '>';
+ else paren = 0;
+
+ switch (c) {
+ case 'Q':
+ lex_strterm = NEW_STRTERM(str_dquote, term, paren);
+ return tSTRING_BEG;
+
+ case 'q':
+ lex_strterm = NEW_STRTERM(str_squote, term, paren);
+ return tSTRING_BEG;
+
+ case 'W':
+ lex_strterm = NEW_STRTERM(str_dword, term, paren);
+ do {c = nextc();} while (ISSPACE(c));
+ pushback(c);
+ return tWORDS_BEG;
+
+ case 'w':
+ lex_strterm = NEW_STRTERM(str_sword, term, paren);
+ do {c = nextc();} while (ISSPACE(c));
+ pushback(c);
+ return tQWORDS_BEG;
+
+ case 'x':
+ lex_strterm = NEW_STRTERM(str_xquote, term, paren);
+ return tXSTRING_BEG;
+
+ case 'r':
+ lex_strterm = NEW_STRTERM(str_regexp, term, paren);
+ return tREGEXP_BEG;
+
+ case 's':
+ lex_strterm = NEW_STRTERM(str_ssym, term, paren);
+ lex_state = EXPR_FNAME;
+ return tSYMBEG;
+
+ default:
+ yyerror("unknown type of %string");
+ return 0;
+ }
+ }
+ if ((c = nextc()) == '=') {
+ yylval.id = '%';
+ lex_state = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (IS_ARG() && space_seen && !ISSPACE(c)) {
+ goto quotation;
+ }
+ switch (lex_state) {
+ case EXPR_FNAME: case EXPR_DOT:
+ lex_state = EXPR_ARG; break;
+ default:
+ lex_state = EXPR_BEG; break;
+ }
+ pushback(c);
+ return '%';
+
+ case '$':
+ last_state = lex_state;
+ lex_state = EXPR_END;
+ newtok();
+ c = nextc();
+ switch (c) {
+ case '_': /* $_: last read line string */
+ c = nextc();
+ if (is_identchar(c)) {
+ tokadd('$');
+ tokadd('_');
+ break;
+ }
+ pushback(c);
+ c = '_';
+ /* fall through */
+ case '~': /* $~: match-data */
+ local_cnt(c);
+ /* fall through */
+ case '*': /* $*: argv */
+ case '$': /* $$: pid */
+ case '?': /* $?: last status */
+ case '!': /* $!: error string */
+ case '@': /* $@: error position */
+ case '/': /* $/: input record separator */
+ case '\\': /* $\: output record separator */
+ case ';': /* $;: field separator */
+ case ',': /* $,: output field separator */
+ case '.': /* $.: last read line number */
+ case '=': /* $=: ignorecase */
+ case ':': /* $:: load path */
+ case '<': /* $<: reading filename */
+ case '>': /* $>: default output handle */
+ case '\"': /* $": already loaded files */
+ tokadd('$');
+ tokadd(c);
+ tokfix();
+ yylval.id = rb_intern(tok());
+ return tGVAR;
+
+ case '-':
+ tokadd('$');
+ tokadd(c);
+ c = nextc();
+ if (is_identchar(c)) {
+ tokadd(c);
+ }
+ else {
+ pushback(c);
+ }
+ gvar:
+ tokfix();
+ yylval.id = rb_intern(tok());
+ /* xxx shouldn't check if valid option variable */
+ return tGVAR;
+
+ case '&': /* $&: last match */
+ case '`': /* $`: string before last match */
+ case '\'': /* $': string after last match */
+ case '+': /* $+: string matches last paren. */
+ if (last_state == EXPR_FNAME) {
+ tokadd('$');
+ tokadd(c);
+ goto gvar;
+ }
+ yylval.node = NEW_BACK_REF(c);
+ return tBACK_REF;
+
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ tokadd('$');
+ do {
+ tokadd(c);
+ c = nextc();
+ } while (ISDIGIT(c));
+ pushback(c);
+ if (last_state == EXPR_FNAME) goto gvar;
+ tokfix();
+ yylval.node = NEW_NTH_REF(atoi(tok()+1));
+ return tNTH_REF;
+
+ default:
+ if (!is_identchar(c)) {
+ pushback(c);
+ return '$';
+ }
+ case '0':
+ tokadd('$');
+ }
+ break;
+
+ case '@':
+ c = nextc();
+ newtok();
+ tokadd('@');
+ if (c == '@') {
+ tokadd('@');
+ c = nextc();
+ }
+ if (ISDIGIT(c)) {
+ if (tokidx == 1) {
+ rb_compile_error("`@%c' is not allowed as an instance variable name", c);
+ }
+ else {
+ rb_compile_error("`@@%c' is not allowed as a class variable name", c);
+ }
+ return 0;
+ }
+ if (!is_identchar(c)) {
+ pushback(c);
+ return '@';
+ }
+ break;
+
+ case '_':
+ if (was_bol() && whole_match_p("__END__", 7, 0)) {
+ ruby__end__seen = 1;
+ lex_lastline = 0;
+ return -1;
+ }
+ newtok();
+ break;
+
+ default:
+ if (!is_identchar(c)) {
+ rb_compile_error("Invalid char `\\%03o' in expression", c);
+ goto retry;
+ }
+
+ newtok();
+ break;
+ }
+
+ do {
+ tokadd(c);
+ if (ismbchar(c)) {
+ int i, len = mbclen(c)-1;
+
+ for (i = 0; i < len; i++) {
+ c = nextc();
+ tokadd(c);
+ }
+ }
+ c = nextc();
+ } while (is_identchar(c));
+ if ((c == '!' || c == '?') && is_identchar(tok()[0]) && !peek('=')) {
+ tokadd(c);
+ }
+ else {
+ pushback(c);
+ }
+ tokfix();
+
+ {
+ int result = 0;
+
+ last_state = lex_state;
+ switch (tok()[0]) {
+ case '$':
+ lex_state = EXPR_END;
+ result = tGVAR;
+ break;
+ case '@':
+ lex_state = EXPR_END;
+ if (tok()[1] == '@')
+ result = tCVAR;
+ else
+ result = tIVAR;
+ break;
+
+ default:
+ if (toklast() == '!' || toklast() == '?') {
+ result = tFID;
+ }
+ else {
+ if (lex_state == EXPR_FNAME) {
+ if ((c = nextc()) == '=' && !peek('~') && !peek('>') &&
+ (!peek('=') || (lex_p + 1 < lex_pend && lex_p[1] == '>'))) {
+ result = tIDENTIFIER;
+ tokadd(c);
+ tokfix();
+ }
+ else {
+ pushback(c);
+ }
+ }
+ if (result == 0 && ISUPPER(tok()[0])) {
+ result = tCONSTANT;
+ }
+ else {
+ result = tIDENTIFIER;
+ }
+ }
+
+ if (lex_state != EXPR_DOT) {
+ struct kwtable *kw;
+
+ /* See if it is a reserved word. */
+ kw = rb_reserved_word(tok(), toklen());
+ if (kw) {
+ enum lex_state state = lex_state;
+ lex_state = kw->state;
+ if (state == EXPR_FNAME) {
+ yylval.id = rb_intern(kw->name);
+ return kw->id[0];
+ }
+ if (kw->id[0] == kDO) {
+ if (COND_P()) return kDO_COND;
+ if (CMDARG_P() && state != EXPR_CMDARG)
+ return kDO_BLOCK;
+ if (state == EXPR_ENDARG)
+ return kDO_BLOCK;
+ return kDO;
+ }
+ if (state == EXPR_BEG)
+ return kw->id[0];
+ else {
+ if (kw->id[0] != kw->id[1])
+ lex_state = EXPR_BEG;
+ return kw->id[1];
+ }
+ }
+ }
+
+ if (lex_state == EXPR_BEG ||
+ lex_state == EXPR_MID ||
+ lex_state == EXPR_DOT ||
+ lex_state == EXPR_ARG ||
+ lex_state == EXPR_CLASS ||
+ lex_state == EXPR_CMDARG) {
+ if (cmd_state) {
+ lex_state = EXPR_CMDARG;
+ }
+ else {
+ lex_state = EXPR_ARG;
+ }
+ }
+ else {
+ lex_state = EXPR_END;
+ }
+ }
+ yylval.id = rb_intern(tok());
+ if (is_local_id(yylval.id) &&
+ last_state != EXPR_DOT &&
+ ((dyna_in_block() && rb_dvar_defined(yylval.id)) || local_id(yylval.id))) {
+ lex_state = EXPR_END;
+ }
+ return result;
+ }
+}
+
+NODE*
+rb_node_newnode(type, a0, a1, a2)
+ enum node_type type;
+ VALUE a0, a1, a2;
+{
+ NODE *n = (NODE*)rb_newobj();
+
+ n->flags |= T_NODE;
+ nd_set_type(n, type);
+ nd_set_line(n, ruby_sourceline);
+ n->nd_file = ruby_sourcefile;
+
+ n->u1.value = a0;
+ n->u2.value = a1;
+ n->u3.value = a2;
+
+ return n;
+}
+
+static enum node_type
+nodetype(node) /* for debug */
+ NODE *node;
+{
+ return (enum node_type)nd_type(node);
+}
+
+static int
+nodeline(node)
+ NODE *node;
+{
+ return nd_line(node);
+}
+
+static NODE*
+newline_node(node)
+ NODE *node;
+{
+ NODE *nl = 0;
+ if (node) {
+ int line;
+ if (nd_type(node) == NODE_NEWLINE) return node;
+ line = nd_line(node);
+ node = remove_begin(node);
+ nl = NEW_NEWLINE(node);
+ nd_set_line(nl, line);
+ nl->nd_nth = line;
+ }
+ return nl;
+}
+
+static void
+fixpos(node, orig)
+ NODE *node, *orig;
+{
+ if (!node) return;
+ if (!orig) return;
+ if (orig == (NODE*)1) return;
+ node->nd_file = orig->nd_file;
+ nd_set_line(node, nd_line(orig));
+}
+
+static void
+parser_warning(node, mesg)
+ NODE *node;
+ const char *mesg;
+{
+ int line = ruby_sourceline;
+ ruby_sourceline = nd_line(node);
+ rb_warning("%s", mesg);
+ ruby_sourceline = line;
+}
+
+static void
+parser_warn(node, mesg)
+ NODE *node;
+ const char *mesg;
+{
+ int line = ruby_sourceline;
+ ruby_sourceline = nd_line(node);
+ rb_warn("%s", mesg);
+ ruby_sourceline = line;
+}
+
+static NODE*
+block_append(head, tail)
+ NODE *head, *tail;
+{
+ NODE *end, *h = head;
+
+ if (tail == 0) return head;
+
+ again:
+ if (h == 0) return tail;
+ switch (nd_type(h)) {
+ case NODE_NEWLINE:
+ h = h->nd_next;
+ goto again;
+ case NODE_LIT:
+ case NODE_STR:
+ parser_warning(h, "unused literal ignored");
+ return tail;
+ default:
+ h = end = NEW_BLOCK(head);
+ end->nd_end = end;
+ fixpos(end, head);
+ head = end;
+ break;
+ case NODE_BLOCK:
+ end = h->nd_end;
+ break;
+ }
+
+ if (RTEST(ruby_verbose)) {
+ NODE *nd = end->nd_head;
+ newline:
+ switch (nd_type(nd)) {
+ case NODE_RETURN:
+ case NODE_BREAK:
+ case NODE_NEXT:
+ case NODE_REDO:
+ case NODE_RETRY:
+ parser_warning(nd, "statement not reached");
+ break;
+
+ case NODE_NEWLINE:
+ nd = nd->nd_next;
+ goto newline;
+
+ default:
+ break;
+ }
+ }
+
+ if (nd_type(tail) != NODE_BLOCK) {
+ tail = NEW_BLOCK(tail);
+ tail->nd_end = tail;
+ }
+ end->nd_next = tail;
+ h->nd_end = tail->nd_end;
+ return head;
+}
+
+/* append item to the list */
+static NODE*
+list_append(list, item)
+ NODE *list, *item;
+{
+ NODE *last;
+
+ if (list == 0) return NEW_LIST(item);
+ if (list->nd_next) {
+ last = list->nd_next->nd_end;
+ }
+ else {
+ last = list;
+ }
+
+ list->nd_alen += 1;
+ last->nd_next = NEW_LIST(item);
+ list->nd_next->nd_end = last->nd_next;
+ return list;
+}
+
+/* concat two lists */
+static NODE*
+list_concat(head, tail)
+ NODE *head, *tail;
+{
+ NODE *last;
+
+ if (head->nd_next) {
+ last = head->nd_next->nd_end;
+ }
+ else {
+ last = head;
+ }
+
+ head->nd_alen += tail->nd_alen;
+ last->nd_next = tail;
+ if (tail->nd_next) {
+ head->nd_next->nd_end = tail->nd_next->nd_end;
+ }
+ else {
+ head->nd_next->nd_end = tail;
+ }
+
+ return head;
+}
+
+/* concat two string literals */
+static NODE *
+literal_concat(head, tail)
+ NODE *head, *tail;
+{
+ enum node_type htype;
+
+ if (!head) return tail;
+ if (!tail) return head;
+
+ htype = nd_type(head);
+ if (htype == NODE_EVSTR) {
+ NODE *node = NEW_DSTR(rb_str_new(0, 0));
+ head = list_append(node, head);
+ }
+ switch (nd_type(tail)) {
+ case NODE_STR:
+ if (htype == NODE_STR) {
+ rb_str_concat(head->nd_lit, tail->nd_lit);
+ rb_gc_force_recycle((VALUE)tail);
+ }
+ else {
+ list_append(head, tail);
+ }
+ break;
+
+ case NODE_DSTR:
+ if (htype == NODE_STR) {
+ rb_str_concat(head->nd_lit, tail->nd_lit);
+ tail->nd_lit = head->nd_lit;
+ rb_gc_force_recycle((VALUE)head);
+ head = tail;
+ }
+ else {
+ nd_set_type(tail, NODE_ARRAY);
+ tail->nd_head = NEW_STR(tail->nd_lit);
+ list_concat(head, tail);
+ }
+ break;
+
+ case NODE_EVSTR:
+ if (htype == NODE_STR) {
+ nd_set_type(head, NODE_DSTR);
+ head->nd_alen = 1;
+ }
+ list_append(head, tail);
+ break;
+ }
+ return head;
+}
+
+static NODE *
+evstr2dstr(node)
+ NODE *node;
+{
+ if (nd_type(node) == NODE_EVSTR) {
+ node = list_append(NEW_DSTR(rb_str_new(0, 0)), node);
+ }
+ return node;
+}
+
+static NODE *
+new_evstr(node)
+ NODE *node;
+{
+ NODE *head = node;
+
+ again:
+ if (node) {
+ switch (nd_type(node)) {
+ case NODE_STR: case NODE_DSTR: case NODE_EVSTR:
+ return node;
+ case NODE_NEWLINE:
+ node = node->nd_next;
+ goto again;
+ }
+ }
+ return NEW_EVSTR(head);
+}
+
+static NODE *
+call_op(recv, id, narg, arg1)
+ NODE *recv;
+ ID id;
+ int narg;
+ NODE *arg1;
+{
+ value_expr(recv);
+ if (narg == 1) {
+ value_expr(arg1);
+ arg1 = NEW_LIST(arg1);
+ }
+ else {
+ arg1 = 0;
+ }
+ return NEW_CALL(recv, id, arg1);
+}
+
+static NODE*
+match_gen(node1, node2)
+ NODE *node1;
+ NODE *node2;
+{
+ local_cnt('~');
+
+ value_expr(node1);
+ value_expr(node2);
+ if (node1) {
+ switch (nd_type(node1)) {
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ return NEW_MATCH2(node1, node2);
+
+ case NODE_LIT:
+ if (TYPE(node1->nd_lit) == T_REGEXP) {
+ return NEW_MATCH2(node1, node2);
+ }
+ }
+ }
+
+ if (node2) {
+ switch (nd_type(node2)) {
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ return NEW_MATCH3(node2, node1);
+
+ case NODE_LIT:
+ if (TYPE(node2->nd_lit) == T_REGEXP) {
+ return NEW_MATCH3(node2, node1);
+ }
+ }
+ }
+
+ return NEW_CALL(node1, tMATCH, NEW_LIST(node2));
+}
+
+static NODE*
+gettable(id)
+ ID id;
+{
+ if (id == kSELF) {
+ return NEW_SELF();
+ }
+ else if (id == kNIL) {
+ return NEW_NIL();
+ }
+ else if (id == kTRUE) {
+ return NEW_TRUE();
+ }
+ else if (id == kFALSE) {
+ return NEW_FALSE();
+ }
+ else if (id == k__FILE__) {
+ return NEW_STR(rb_str_new2(ruby_sourcefile));
+ }
+ else if (id == k__LINE__) {
+ return NEW_LIT(INT2FIX(ruby_sourceline));
+ }
+ else if (is_local_id(id)) {
+ if (dyna_in_block() && rb_dvar_defined(id)) return NEW_DVAR(id);
+ if (local_id(id)) return NEW_LVAR(id);
+ /* method call without arguments */
+#if 0
+ /* Rite will warn this */
+ rb_warn("ambiguous identifier; %s() or self.%s is better for method call",
+ rb_id2name(id), rb_id2name(id));
+#endif
+ return NEW_VCALL(id);
+ }
+ else if (is_global_id(id)) {
+ return NEW_GVAR(id);
+ }
+ else if (is_instance_id(id)) {
+ return NEW_IVAR(id);
+ }
+ else if (is_const_id(id)) {
+ return NEW_CONST(id);
+ }
+ else if (is_class_id(id)) {
+ return NEW_CVAR(id);
+ }
+ rb_compile_error("identifier %s is not valid", rb_id2name(id));
+ return 0;
+}
+
+static VALUE dyna_var_lookup _((ID id));
+
+static NODE*
+assignable(id, val)
+ ID id;
+ NODE *val;
+{
+ value_expr(val);
+ if (id == kSELF) {
+ yyerror("Can't change the value of self");
+ }
+ else if (id == kNIL) {
+ yyerror("Can't assign to nil");
+ }
+ else if (id == kTRUE) {
+ yyerror("Can't assign to true");
+ }
+ else if (id == kFALSE) {
+ yyerror("Can't assign to false");
+ }
+ else if (id == k__FILE__) {
+ yyerror("Can't assign to __FILE__");
+ }
+ else if (id == k__LINE__) {
+ yyerror("Can't assign to __LINE__");
+ }
+ else if (is_local_id(id)) {
+ if (rb_dvar_curr(id)) {
+ return NEW_DASGN_CURR(id, val);
+ }
+ else if (dyna_var_lookup(id)) {
+ return NEW_DASGN(id, val);
+ }
+ else if (local_id(id) || !dyna_in_block()) {
+ return NEW_LASGN(id, val);
+ }
+ else{
+ rb_dvar_push(id, Qnil);
+ return NEW_DASGN_CURR(id, val);
+ }
+ }
+ else if (is_global_id(id)) {
+ return NEW_GASGN(id, val);
+ }
+ else if (is_instance_id(id)) {
+ return NEW_IASGN(id, val);
+ }
+ else if (is_const_id(id)) {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ return NEW_CDECL(id, val, 0);
+ }
+ else if (is_class_id(id)) {
+ if (in_def || in_single) return NEW_CVASGN(id, val);
+ return NEW_CVDECL(id, val);
+ }
+ else {
+ rb_compile_error("identifier %s is not valid", rb_id2name(id));
+ }
+ return 0;
+}
+
+static NODE *
+aryset(recv, idx)
+ NODE *recv, *idx;
+{
+ if (recv && nd_type(recv) == NODE_SELF)
+ recv = (NODE *)1;
+ else
+ value_expr(recv);
+ return NEW_ATTRASGN(recv, tASET, idx);
+}
+
+ID
+rb_id_attrset(id)
+ ID id;
+{
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+ return id;
+}
+
+static NODE *
+attrset(recv, id)
+ NODE *recv;
+ ID id;
+{
+ if (recv && nd_type(recv) == NODE_SELF)
+ recv = (NODE *)1;
+ else
+ value_expr(recv);
+ return NEW_ATTRASGN(recv, rb_id_attrset(id), 0);
+}
+
+static void
+rb_backref_error(node)
+ NODE *node;
+{
+ switch (nd_type(node)) {
+ case NODE_NTH_REF:
+ rb_compile_error("Can't set variable $%d", node->nd_nth);
+ break;
+ case NODE_BACK_REF:
+ rb_compile_error("Can't set variable $%c", (int)node->nd_nth);
+ break;
+ }
+}
+
+static NODE *
+arg_concat(node1, node2)
+ NODE *node1;
+ NODE *node2;
+{
+ if (!node2) return node1;
+ return NEW_ARGSCAT(node1, node2);
+}
+
+static NODE *
+arg_add(node1, node2)
+ NODE *node1;
+ NODE *node2;
+{
+ if (!node1) return NEW_LIST(node2);
+ if (nd_type(node1) == NODE_ARRAY) {
+ return list_append(node1, node2);
+ }
+ else {
+ return NEW_ARGSPUSH(node1, node2);
+ }
+}
+
+static NODE*
+node_assign(lhs, rhs)
+ NODE *lhs, *rhs;
+{
+ if (!lhs) return 0;
+
+ value_expr(rhs);
+ switch (nd_type(lhs)) {
+ case NODE_GASGN:
+ case NODE_IASGN:
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:
+ case NODE_MASGN:
+ case NODE_CDECL:
+ case NODE_CVDECL:
+ case NODE_CVASGN:
+ lhs->nd_value = rhs;
+ break;
+
+ case NODE_ATTRASGN:
+ case NODE_CALL:
+ lhs->nd_args = arg_add(lhs->nd_args, rhs);
+ break;
+
+ default:
+ /* should not happen */
+ break;
+ }
+
+ return lhs;
+}
+
+static int
+value_expr0(node)
+ NODE *node;
+{
+ int cond = 0;
+
+ while (node) {
+ switch (nd_type(node)) {
+ case NODE_DEFN:
+ case NODE_DEFS:
+ parser_warning(node, "void value expression");
+ return Qfalse;
+
+ case NODE_RETURN:
+ case NODE_BREAK:
+ case NODE_NEXT:
+ case NODE_REDO:
+ case NODE_RETRY:
+ if (!cond) yyerror("void value expression");
+ /* or "control never reach"? */
+ return Qfalse;
+
+ case NODE_BLOCK:
+ while (node->nd_next) {
+ node = node->nd_next;
+ }
+ node = node->nd_head;
+ break;
+
+ case NODE_BEGIN:
+ node = node->nd_body;
+ break;
+
+ case NODE_IF:
+ if (!value_expr(node->nd_body)) return Qfalse;
+ node = node->nd_else;
+ break;
+
+ case NODE_AND:
+ case NODE_OR:
+ cond = 1;
+ node = node->nd_2nd;
+ break;
+
+ case NODE_NEWLINE:
+ node = node->nd_next;
+ break;
+
+ default:
+ return Qtrue;
+ }
+ }
+
+ return Qtrue;
+}
+
+static void
+void_expr0(node)
+ NODE *node;
+{
+ char *useless = 0;
+
+ if (!RTEST(ruby_verbose)) return;
+
+ again:
+ if (!node) return;
+ switch (nd_type(node)) {
+ case NODE_NEWLINE:
+ node = node->nd_next;
+ goto again;
+
+ case NODE_CALL:
+ switch (node->nd_mid) {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case tPOW:
+ case tUPLUS:
+ case tUMINUS:
+ case '|':
+ case '^':
+ case '&':
+ case tCMP:
+ case '>':
+ case tGEQ:
+ case '<':
+ case tLEQ:
+ case tEQ:
+ case tNEQ:
+ useless = rb_id2name(node->nd_mid);
+ break;
+ }
+ break;
+
+ case NODE_LVAR:
+ case NODE_DVAR:
+ case NODE_GVAR:
+ case NODE_IVAR:
+ case NODE_CVAR:
+ case NODE_NTH_REF:
+ case NODE_BACK_REF:
+ useless = "a variable";
+ break;
+ case NODE_CONST:
+ case NODE_CREF:
+ useless = "a constant";
+ break;
+ case NODE_LIT:
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ useless = "a literal";
+ break;
+ case NODE_COLON2:
+ case NODE_COLON3:
+ useless = "::";
+ break;
+ case NODE_DOT2:
+ useless = "..";
+ break;
+ case NODE_DOT3:
+ useless = "...";
+ break;
+ case NODE_SELF:
+ useless = "self";
+ break;
+ case NODE_NIL:
+ useless = "nil";
+ break;
+ case NODE_TRUE:
+ useless = "true";
+ break;
+ case NODE_FALSE:
+ useless = "false";
+ break;
+ case NODE_DEFINED:
+ useless = "defined?";
+ break;
+ }
+
+ if (useless) {
+ int line = ruby_sourceline;
+
+ ruby_sourceline = nd_line(node);
+ rb_warn("useless use of %s in void context", useless);
+ ruby_sourceline = line;
+ }
+}
+
+static void
+void_stmts(node)
+ NODE *node;
+{
+ if (!RTEST(ruby_verbose)) return;
+ if (!node) return;
+ if (nd_type(node) != NODE_BLOCK) return;
+
+ for (;;) {
+ if (!node->nd_next) return;
+ void_expr0(node->nd_head);
+ node = node->nd_next;
+ }
+}
+
+static NODE *
+remove_begin(node)
+ NODE *node;
+{
+ NODE **n = &node;
+ while (*n) {
+ switch (nd_type(*n)) {
+ case NODE_NEWLINE:
+ n = &(*n)->nd_next;
+ continue;
+ case NODE_BEGIN:
+ *n = (*n)->nd_body;
+ default:
+ return node;
+ }
+ }
+ return node;
+}
+
+static int
+assign_in_cond(node)
+ NODE *node;
+{
+ switch (nd_type(node)) {
+ case NODE_MASGN:
+ yyerror("multiple assignment in conditional");
+ return 1;
+
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ break;
+
+ case NODE_NEWLINE:
+ default:
+ return 0;
+ }
+
+ switch (nd_type(node->nd_value)) {
+ case NODE_LIT:
+ case NODE_STR:
+ case NODE_NIL:
+ case NODE_TRUE:
+ case NODE_FALSE:
+ /* reports always */
+ parser_warn(node->nd_value, "found = in conditional, should be ==");
+ return 1;
+
+ case NODE_DSTR:
+ case NODE_XSTR:
+ case NODE_DXSTR:
+ case NODE_EVSTR:
+ case NODE_DREGX:
+ default:
+ break;
+ }
+#if 0
+ if (assign_in_cond(node->nd_value) == 0) {
+ parser_warning(node->nd_value, "assignment in condition");
+ }
+#endif
+ return 1;
+}
+
+static int
+e_option_supplied()
+{
+ if (strcmp(ruby_sourcefile, "-e") == 0)
+ return Qtrue;
+ return Qfalse;
+}
+
+static void
+warn_unless_e_option(node, str)
+ NODE *node;
+ const char *str;
+{
+ if (!e_option_supplied()) parser_warn(node, str);
+}
+
+static void
+warning_unless_e_option(node, str)
+ NODE *node;
+ const char *str;
+{
+ if (!e_option_supplied()) parser_warning(node, str);
+}
+
+static void
+fixup_nodes(rootnode)
+ NODE **rootnode;
+{
+ NODE *node, *next, *head;
+
+ for (node = *rootnode; node; node = next) {
+ enum node_type type;
+ VALUE val;
+
+ next = node->nd_next;
+ head = node->nd_head;
+ rb_gc_force_recycle((VALUE)node);
+ *rootnode = next;
+ switch (type = nd_type(head)) {
+ case NODE_DOT2:
+ case NODE_DOT3:
+ val = rb_range_new(head->nd_beg->nd_lit, head->nd_end->nd_lit,
+ type == NODE_DOT3 ? Qtrue : Qfalse);
+ rb_gc_force_recycle((VALUE)head->nd_beg);
+ rb_gc_force_recycle((VALUE)head->nd_end);
+ nd_set_type(head, NODE_LIT);
+ head->nd_lit = val;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static NODE *cond0();
+
+static NODE*
+range_op(node)
+ NODE *node;
+{
+ enum node_type type;
+
+ if (node == 0) return 0;
+
+ type = nd_type(node);
+ if (type == NODE_NEWLINE) {
+ node = node->nd_next;
+ type = nd_type(node);
+ }
+ value_expr(node);
+ if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) {
+ warn_unless_e_option(node, "integer literal in conditional range");
+ return call_op(node,tEQ,1,NEW_GVAR(rb_intern("$.")));
+ }
+ return cond0(node);
+}
+
+static int
+literal_node(node)
+ NODE *node;
+{
+ if (!node) return 1; /* same as NODE_NIL */
+ switch (nd_type(node)) {
+ case NODE_LIT:
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_EVSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ case NODE_DSYM:
+ return 2;
+ case NODE_TRUE:
+ case NODE_FALSE:
+ case NODE_NIL:
+ return 1;
+ }
+ return 0;
+}
+
+static NODE*
+cond0(node)
+ NODE *node;
+{
+ if (node == 0) return 0;
+ assign_in_cond(node);
+
+ switch (nd_type(node)) {
+ case NODE_DSTR:
+ case NODE_EVSTR:
+ case NODE_STR:
+ rb_warn("string literal in condition");
+ break;
+
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ warning_unless_e_option(node, "regex literal in condition");
+ local_cnt('_');
+ local_cnt('~');
+ return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_")));
+
+ case NODE_AND:
+ case NODE_OR:
+ node->nd_1st = cond0(node->nd_1st);
+ node->nd_2nd = cond0(node->nd_2nd);
+ break;
+
+ case NODE_DOT2:
+ case NODE_DOT3:
+ node->nd_beg = range_op(node->nd_beg);
+ node->nd_end = range_op(node->nd_end);
+ if (nd_type(node) == NODE_DOT2) nd_set_type(node,NODE_FLIP2);
+ else if (nd_type(node) == NODE_DOT3) nd_set_type(node, NODE_FLIP3);
+ node->nd_cnt = local_append(internal_id());
+ if (!e_option_supplied()) {
+ int b = literal_node(node->nd_beg);
+ int e = literal_node(node->nd_end);
+ if ((b == 1 && e == 1) || (b + e >= 2 && RTEST(ruby_verbose))) {
+ parser_warn(node, "range literal in condition");
+ }
+ }
+ break;
+
+ case NODE_DSYM:
+ parser_warning(node, "literal in condition");
+ break;
+
+ case NODE_LIT:
+ if (TYPE(node->nd_lit) == T_REGEXP) {
+ warn_unless_e_option(node, "regex literal in condition");
+ nd_set_type(node, NODE_MATCH);
+ local_cnt('_');
+ local_cnt('~');
+ }
+ else {
+ parser_warning(node, "literal in condition");
+ }
+ default:
+ break;
+ }
+ return node;
+}
+
+static NODE*
+cond(node)
+ NODE *node;
+{
+ if (node == 0) return 0;
+ value_expr(node);
+ if (nd_type(node) == NODE_NEWLINE){
+ node->nd_next = cond0(node->nd_next);
+ return node;
+ }
+ return cond0(node);
+}
+
+static NODE*
+logop(type, left, right)
+ enum node_type type;
+ NODE *left, *right;
+{
+ value_expr(left);
+ if (left && nd_type(left) == type) {
+ NODE *node = left, *second;
+ while ((second = node->nd_2nd) != 0 && nd_type(second) == type) {
+ node = second;
+ }
+ node->nd_2nd = NEW_NODE(type, second, right, 0);
+ return left;
+ }
+ return NEW_NODE(type, left, right, 0);
+}
+
+static int
+cond_negative(nodep)
+ NODE **nodep;
+{
+ NODE *c = *nodep;
+
+ if (!c) return 0;
+ switch (nd_type(c)) {
+ case NODE_NOT:
+ *nodep = c->nd_body;
+ return 1;
+ case NODE_NEWLINE:
+ if (c->nd_next && nd_type(c->nd_next) == NODE_NOT) {
+ c->nd_next = c->nd_next->nd_body;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+no_blockarg(node)
+ NODE *node;
+{
+ if (node && nd_type(node) == NODE_BLOCK_PASS) {
+ rb_compile_error("block argument should not be given");
+ }
+}
+
+static NODE *
+ret_args(node)
+ NODE *node;
+{
+ if (node) {
+ no_blockarg(node);
+ if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) {
+ node = node->nd_head;
+ }
+ if (node && nd_type(node) == NODE_SPLAT) {
+ node = NEW_SVALUE(node);
+ }
+ }
+ return node;
+}
+
+static NODE *
+new_yield(node)
+ NODE *node;
+{
+ long state = Qtrue;
+
+ if (node) {
+ no_blockarg(node);
+ if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) {
+ node = node->nd_head;
+ state = Qfalse;
+ }
+ if (node && nd_type(node) == NODE_SPLAT) {
+ state = Qtrue;
+ }
+ }
+ else {
+ state = Qfalse;
+ }
+ return NEW_YIELD(node, state);
+}
+
+static NODE*
+negate_lit(node)
+ NODE *node;
+{
+ switch (TYPE(node->nd_lit)) {
+ case T_FIXNUM:
+ node->nd_lit = LONG2FIX(-FIX2LONG(node->nd_lit));
+ break;
+ case T_BIGNUM:
+ node->nd_lit = rb_funcall(node->nd_lit,tUMINUS,0,0);
+ break;
+ case T_FLOAT:
+ RFLOAT(node->nd_lit)->value = -RFLOAT(node->nd_lit)->value;
+ break;
+ default:
+ break;
+ }
+ return node;
+}
+
+static NODE *
+arg_blk_pass(node1, node2)
+ NODE *node1;
+ NODE *node2;
+{
+ if (node2) {
+ node2->nd_head = node1;
+ return node2;
+ }
+ return node1;
+}
+
+static NODE*
+arg_prepend(node1, node2)
+ NODE *node1, *node2;
+{
+ switch (nd_type(node2)) {
+ case NODE_ARRAY:
+ return list_concat(NEW_LIST(node1), node2);
+
+ case NODE_SPLAT:
+ return arg_concat(node1, node2->nd_head);
+
+ case NODE_BLOCK_PASS:
+ node2->nd_body = arg_prepend(node1, node2->nd_body);
+ return node2;
+
+ default:
+ rb_bug("unknown nodetype(%d) for arg_prepend", nd_type(node2));
+ }
+ return 0; /* not reached */
+}
+
+static NODE*
+new_call(r,m,a)
+ NODE *r;
+ ID m;
+ NODE *a;
+{
+ if (a && nd_type(a) == NODE_BLOCK_PASS) {
+ a->nd_iter = NEW_CALL(r,m,a->nd_head);
+ return a;
+ }
+ return NEW_CALL(r,m,a);
+}
+
+static NODE*
+new_fcall(m,a)
+ ID m;
+ NODE *a;
+{
+ if (a && nd_type(a) == NODE_BLOCK_PASS) {
+ a->nd_iter = NEW_FCALL(m,a->nd_head);
+ return a;
+ }
+ return NEW_FCALL(m,a);
+}
+
+static NODE*
+new_super(a)
+ NODE *a;
+{
+ if (a && nd_type(a) == NODE_BLOCK_PASS) {
+ a->nd_iter = NEW_SUPER(a->nd_head);
+ return a;
+ }
+ return NEW_SUPER(a);
+}
+
+static struct local_vars {
+ ID *tbl;
+ int nofree;
+ int cnt;
+ int dlev;
+ struct RVarmap* dyna_vars;
+ struct local_vars *prev;
+} *lvtbl;
+
+static void
+local_push(top)
+ int top;
+{
+ struct local_vars *local;
+
+ local = ALLOC(struct local_vars);
+ local->prev = lvtbl;
+ local->nofree = 0;
+ local->cnt = 0;
+ local->tbl = 0;
+ local->dlev = 0;
+ local->dyna_vars = ruby_dyna_vars;
+ lvtbl = local;
+ if (!top) {
+ /* preserve reference for GC, but link should be cut. */
+ rb_dvar_push(0, (VALUE)ruby_dyna_vars);
+ ruby_dyna_vars->next = 0;
+ }
+}
+
+static void
+local_pop()
+{
+ struct local_vars *local = lvtbl->prev;
+
+ if (lvtbl->tbl) {
+ if (!lvtbl->nofree) xfree(lvtbl->tbl);
+ else lvtbl->tbl[0] = lvtbl->cnt;
+ }
+ ruby_dyna_vars = lvtbl->dyna_vars;
+ xfree(lvtbl);
+ lvtbl = local;
+}
+
+static ID*
+local_tbl()
+{
+ lvtbl->nofree = 1;
+ return lvtbl->tbl;
+}
+
+static int
+local_append(id)
+ ID id;
+{
+ if (lvtbl->tbl == 0) {
+ lvtbl->tbl = ALLOC_N(ID, 4);
+ lvtbl->tbl[0] = 0;
+ lvtbl->tbl[1] = '_';
+ lvtbl->tbl[2] = '~';
+ lvtbl->cnt = 2;
+ if (id == '_') return 0;
+ if (id == '~') return 1;
+ }
+ else {
+ REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2);
+ }
+
+ lvtbl->tbl[lvtbl->cnt+1] = id;
+ return lvtbl->cnt++;
+}
+
+static int
+local_cnt(id)
+ ID id;
+{
+ int cnt, max;
+
+ if (id == 0) return lvtbl->cnt;
+
+ for (cnt=1, max=lvtbl->cnt+1; cnt<max;cnt++) {
+ if (lvtbl->tbl[cnt] == id) return cnt-1;
+ }
+ return local_append(id);
+}
+
+static int
+local_id(id)
+ ID id;
+{
+ int i, max;
+
+ if (lvtbl == 0) return Qfalse;
+ for (i=3, max=lvtbl->cnt+1; i<max; i++) {
+ if (lvtbl->tbl[i] == id) return Qtrue;
+ }
+ return Qfalse;
+}
+
+static void
+top_local_init()
+{
+ local_push(1);
+ lvtbl->cnt = ruby_scope->local_tbl?ruby_scope->local_tbl[0]:0;
+ if (lvtbl->cnt > 0) {
+ lvtbl->tbl = ALLOC_N(ID, lvtbl->cnt+3);
+ MEMCPY(lvtbl->tbl, ruby_scope->local_tbl, ID, lvtbl->cnt+1);
+ }
+ else {
+ lvtbl->tbl = 0;
+ }
+ if (ruby_dyna_vars)
+ lvtbl->dlev = 1;
+ else
+ lvtbl->dlev = 0;
+}
+
+static void
+top_local_setup()
+{
+ int len = lvtbl->cnt;
+ int i;
+
+ if (len > 0) {
+ i = ruby_scope->local_tbl?ruby_scope->local_tbl[0]:0;
+
+ if (i < len) {
+ if (i == 0 || (ruby_scope->flags & SCOPE_MALLOC) == 0) {
+ VALUE *vars = ALLOC_N(VALUE, len+1);
+ if (ruby_scope->local_vars) {
+ *vars++ = ruby_scope->local_vars[-1];
+ MEMCPY(vars, ruby_scope->local_vars, VALUE, i);
+ rb_mem_clear(vars+i, len-i);
+ }
+ else {
+ *vars++ = 0;
+ rb_mem_clear(vars, len);
+ }
+ ruby_scope->local_vars = vars;
+ ruby_scope->flags |= SCOPE_MALLOC;
+ }
+ else {
+ VALUE *vars = ruby_scope->local_vars-1;
+ REALLOC_N(vars, VALUE, len+1);
+ ruby_scope->local_vars = vars+1;
+ rb_mem_clear(ruby_scope->local_vars+i, len-i);
+ }
+ if (ruby_scope->local_tbl && ruby_scope->local_vars[-1] == 0) {
+ if (!(ruby_scope->flags & SCOPE_CLONE))
+ xfree(ruby_scope->local_tbl);
+ }
+ ruby_scope->local_vars[-1] = 0; /* no reference needed */
+ ruby_scope->local_tbl = local_tbl();
+ }
+ }
+ local_pop();
+}
+
+#define DVAR_USED FL_USER6
+
+static VALUE
+dyna_var_lookup(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) {
+ FL_SET(vars, DVAR_USED);
+ return Qtrue;
+ }
+ vars = vars->next;
+ }
+ return Qfalse;
+}
+
+static struct RVarmap*
+dyna_push()
+{
+ struct RVarmap* vars = ruby_dyna_vars;
+
+ rb_dvar_push(0, 0);
+ lvtbl->dlev++;
+ return vars;
+}
+
+static void
+dyna_pop(vars)
+ struct RVarmap* vars;
+{
+ lvtbl->dlev--;
+ ruby_dyna_vars = vars;
+}
+
+static int
+dyna_in_block()
+{
+ return (lvtbl->dlev > 0);
+}
+
+static NODE *
+dyna_init(node, pre)
+ NODE *node;
+ struct RVarmap *pre;
+{
+ struct RVarmap *post = ruby_dyna_vars;
+ NODE *var;
+
+ if (!node || !post || pre == post) return node;
+ for (var = 0; post != pre && post->id; post = post->next) {
+ if (FL_TEST(post, DVAR_USED)) {
+ var = NEW_DASGN_CURR(post->id, var);
+ }
+ }
+ return block_append(var, node);
+}
+
+int
+ruby_parser_stack_on_heap()
+{
+#if defined(YYMALLOC)
+ return Qfalse;
+#else
+ return Qtrue;
+#endif
+}
+
+void
+rb_gc_mark_parser()
+{
+#if defined YYMALLOC
+ rb_gc_mark((VALUE)parser_heap);
+#elif defined yystacksize
+ if (yyvsp) rb_gc_mark_locations((VALUE *)yyvs, (VALUE *)yyvsp);
+#endif
+
+ if (!ruby_in_compile) return;
+
+ rb_gc_mark_maybe((VALUE)yylval.node);
+ rb_gc_mark(ruby_debug_lines);
+ rb_gc_mark(lex_lastline);
+ rb_gc_mark(lex_input);
+ rb_gc_mark((VALUE)lex_strterm);
+ rb_gc_mark((VALUE)deferred_nodes);
+}
+
+void
+rb_parser_append_print()
+{
+ ruby_eval_tree =
+ block_append(ruby_eval_tree,
+ NEW_FCALL(rb_intern("print"),
+ NEW_ARRAY(NEW_GVAR(rb_intern("$_")))));
+}
+
+void
+rb_parser_while_loop(chop, split)
+ int chop, split;
+{
+ if (split) {
+ ruby_eval_tree =
+ block_append(NEW_GASGN(rb_intern("$F"),
+ NEW_CALL(NEW_GVAR(rb_intern("$_")),
+ rb_intern("split"), 0)),
+ ruby_eval_tree);
+ }
+ if (chop) {
+ ruby_eval_tree =
+ block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")),
+ rb_intern("chop!"), 0), ruby_eval_tree);
+ }
+ ruby_eval_tree = NEW_OPT_N(ruby_eval_tree);
+}
+
+static struct {
+ ID token;
+ char *name;
+} op_tbl[] = {
+ {tDOT2, ".."},
+ {tDOT3, "..."},
+ {'+', "+"},
+ {'-', "-"},
+ {'+', "+(binary)"},
+ {'-', "-(binary)"},
+ {'*', "*"},
+ {'/', "/"},
+ {'%', "%"},
+ {tPOW, "**"},
+ {tUPLUS, "+@"},
+ {tUMINUS, "-@"},
+ {tUPLUS, "+(unary)"},
+ {tUMINUS, "-(unary)"},
+ {'|', "|"},
+ {'^', "^"},
+ {'&', "&"},
+ {tCMP, "<=>"},
+ {'>', ">"},
+ {tGEQ, ">="},
+ {'<', "<"},
+ {tLEQ, "<="},
+ {tEQ, "=="},
+ {tEQQ, "==="},
+ {tNEQ, "!="},
+ {tMATCH, "=~"},
+ {tNMATCH, "!~"},
+ {'!', "!"},
+ {'~', "~"},
+ {'!', "!(unary)"},
+ {'~', "~(unary)"},
+ {'!', "!@"},
+ {'~', "~@"},
+ {tAREF, "[]"},
+ {tASET, "[]="},
+ {tLSHFT, "<<"},
+ {tRSHFT, ">>"},
+ {tCOLON2, "::"},
+ {'`', "`"},
+ {0, 0}
+};
+
+static st_table *sym_tbl;
+static st_table *sym_rev_tbl;
+
+void
+Init_sym()
+{
+ sym_tbl = st_init_strtable_with_size(200);
+ sym_rev_tbl = st_init_numtable_with_size(200);
+}
+
+static ID last_id = tLAST_TOKEN;
+
+static ID
+internal_id()
+{
+ return ID_INTERNAL | (++last_id << ID_SCOPE_SHIFT);
+}
+
+static int
+is_special_global_name(m)
+ const char *m;
+{
+ switch (*m) {
+ case '~': case '*': case '$': case '?': case '!': case '@':
+ case '/': case '\\': case ';': case ',': case '.': case '=':
+ case ':': case '<': case '>': case '\"':
+ case '&': case '`': case '\'': case '+':
+ case '0':
+ ++m;
+ break;
+ case '-':
+ ++m;
+ if (is_identchar(*m)) m += mbclen(*m);
+ break;
+ default:
+ if (!ISDIGIT(*m)) return 0;
+ do ++m; while (ISDIGIT(*m));
+ }
+ return !*m;
+}
+
+int
+rb_symname_p(name)
+ const char *name;
+{
+ const char *m = name;
+ int localid = Qfalse;
+
+ if (!m) return Qfalse;
+ switch (*m) {
+ case '\0':
+ return Qfalse;
+
+ case '$':
+ if (is_special_global_name(++m)) return Qtrue;
+ goto id;
+
+ case '@':
+ if (*++m == '@') ++m;
+ goto id;
+
+ case '<':
+ switch (*++m) {
+ case '<': ++m; break;
+ case '=': if (*++m == '>') ++m; break;
+ default: break;
+ }
+ break;
+
+ case '>':
+ switch (*++m) {
+ case '>': case '=': ++m; break;
+ }
+ break;
+
+ case '=':
+ switch (*++m) {
+ case '~': ++m; break;
+ case '=': if (*++m == '=') ++m; break;
+ default: return Qfalse;
+ }
+ break;
+
+ case '*':
+ if (*++m == '*') ++m;
+ break;
+
+ case '+': case '-':
+ if (*++m == '@') ++m;
+ break;
+
+ case '|': case '^': case '&': case '/': case '%': case '~': case '`':
+ ++m;
+ break;
+
+ case '[':
+ if (*++m != ']') return Qfalse;
+ if (*++m == '=') ++m;
+ break;
+
+ default:
+ localid = !ISUPPER(*m);
+ id:
+ if (*m != '_' && !ISALPHA(*m) && !ismbchar(*m)) return Qfalse;
+ while (is_identchar(*m)) m += mbclen(*m);
+ if (localid) {
+ switch (*m) {
+ case '!': case '?': case '=': ++m;
+ }
+ }
+ break;
+ }
+ return *m ? Qfalse : Qtrue;
+}
+
+int
+rb_sym_interned_p(str)
+ VALUE str;
+{
+ ID id;
+
+ if (st_lookup(sym_tbl, (st_data_t)RSTRING(str)->ptr, (st_data_t *)&id))
+ return Qtrue;
+ return Qfalse;
+}
+
+ID
+rb_intern(name)
+ const char *name;
+{
+ const char *m = name;
+ ID id;
+ int last;
+
+ if (st_lookup(sym_tbl, (st_data_t)name, (st_data_t *)&id))
+ return id;
+
+ last = strlen(name)-1;
+ id = 0;
+ switch (*name) {
+ case '$':
+ id |= ID_GLOBAL;
+ if (is_special_global_name(++m)) goto new_id;
+ break;
+ case '@':
+ if (name[1] == '@') {
+ m++;
+ id |= ID_CLASS;
+ }
+ else {
+ id |= ID_INSTANCE;
+ }
+ m++;
+ break;
+ default:
+ if (name[0] != '_' && ISASCII(name[0]) && !ISALNUM(name[0])) {
+ /* operators */
+ int i;
+
+ for (i=0; op_tbl[i].token; i++) {
+ if (*op_tbl[i].name == *name &&
+ strcmp(op_tbl[i].name, name) == 0) {
+ id = op_tbl[i].token;
+ goto id_regist;
+ }
+ }
+ }
+
+ if (name[last] == '=') {
+ /* attribute assignment */
+ char *buf = ALLOCA_N(char,last+1);
+
+ strncpy(buf, name, last);
+ buf[last] = '\0';
+ id = rb_intern(buf);
+ if (id > tLAST_TOKEN && !is_attrset_id(id)) {
+ id = rb_id_attrset(id);
+ goto id_regist;
+ }
+ id = ID_ATTRSET;
+ }
+ else if (ISUPPER(name[0])) {
+ id = ID_CONST;
+ }
+ else {
+ id = ID_LOCAL;
+ }
+ break;
+ }
+ if (!ISDIGIT(*m)) {
+ while (m <= name + last && is_identchar(*m)) {
+ m += mbclen(*m);
+ }
+ }
+ if (*m) id = ID_JUNK;
+ new_id:
+ id |= ++last_id << ID_SCOPE_SHIFT;
+ id_regist:
+ name = strdup(name);
+ st_add_direct(sym_tbl, (st_data_t)name, id);
+ st_add_direct(sym_rev_tbl, id, (st_data_t)name);
+ return id;
+}
+
+char *
+rb_id2name(id)
+ ID id;
+{
+ char *name;
+ st_data_t data;
+
+ if (id < tLAST_TOKEN) {
+ int i;
+
+ for (i=0; op_tbl[i].token; i++) {
+ if (op_tbl[i].token == id)
+ return op_tbl[i].name;
+ }
+ }
+
+ if (st_lookup(sym_rev_tbl, id, &data))
+ return (char *)data;
+
+ if (is_attrset_id(id)) {
+ ID id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL;
+
+ again:
+ name = rb_id2name(id2);
+ if (name) {
+ char *buf = ALLOCA_N(char, strlen(name)+2);
+
+ strcpy(buf, name);
+ strcat(buf, "=");
+ rb_intern(buf);
+ return rb_id2name(id);
+ }
+ if (is_local_id(id2)) {
+ id2 = (id & ~ID_SCOPE_MASK) | ID_CONST;
+ goto again;
+ }
+ }
+ return 0;
+}
+
+static int
+symbols_i(key, value, ary)
+ char *key;
+ ID value;
+ VALUE ary;
+{
+ rb_ary_push(ary, ID2SYM(value));
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * Symbol.all_symbols => array
+ *
+ * Returns an array of all the symbols currently in Ruby's symbol
+ * table.
+ *
+ * Symbol.all_symbols.size #=> 903
+ * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink,
+ * :chown, :EOFError, :$;, :String,
+ * :LOCK_SH, :"setuid?", :$<,
+ * :default_proc, :compact, :extend,
+ * :Tms, :getwd, :$=, :ThreadGroup,
+ * :wait2, :$>]
+ */
+
+VALUE
+rb_sym_all_symbols()
+{
+ VALUE ary = rb_ary_new2(sym_tbl->num_entries);
+
+ st_foreach(sym_tbl, symbols_i, ary);
+ return ary;
+}
+
+int
+rb_is_const_id(id)
+ ID id;
+{
+ if (is_const_id(id)) return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_is_class_id(id)
+ ID id;
+{
+ if (is_class_id(id)) return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_is_instance_id(id)
+ ID id;
+{
+ if (is_instance_id(id)) return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_is_local_id(id)
+ ID id;
+{
+ if (is_local_id(id)) return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_is_junk_id(id)
+ ID id;
+{
+ if (is_junk_id(id)) return Qtrue;
+ return Qfalse;
+}
+
+static void
+special_local_set(c, val)
+ char c;
+ VALUE val;
+{
+ int cnt;
+
+ top_local_init();
+ cnt = local_cnt(c);
+ top_local_setup();
+ ruby_scope->local_vars[cnt] = val;
+}
+
+VALUE
+rb_backref_get()
+{
+ VALUE *var = rb_svar(1);
+ if (var) {
+ return *var;
+ }
+ return Qnil;
+}
+
+void
+rb_backref_set(val)
+ VALUE val;
+{
+ VALUE *var = rb_svar(1);
+ if (var) {
+ *var = val;
+ }
+ else {
+ special_local_set('~', val);
+ }
+}
+
+VALUE
+rb_lastline_get()
+{
+ VALUE *var = rb_svar(0);
+ if (var) {
+ return *var;
+ }
+ return Qnil;
+}
+
+void
+rb_lastline_set(val)
+ VALUE val;
+{
+ VALUE *var = rb_svar(0);
+ if (var) {
+ *var = val;
+ }
+ else {
+ special_local_set('_', val);
+ }
+}
+
+#ifdef YYMALLOC
+#define HEAPCNT(n, size) ((n) * (size) / sizeof(YYSTYPE))
+#define NEWHEAP() rb_node_newnode(NODE_ALLOCA, 0, (VALUE)parser_heap, 0)
+#define ADD2HEAP(n, c, p) ((parser_heap = (n))->u1.node = (p), \
+ (n)->u3.cnt = (c), (p))
+
+static void *
+rb_parser_malloc(size)
+ size_t size;
+{
+ size_t cnt = HEAPCNT(1, size);
+ NODE *n = NEWHEAP();
+ void *ptr = xmalloc(size);
+
+ return ADD2HEAP(n, cnt, ptr);
+}
+
+static void *
+rb_parser_calloc(nelem, size)
+ size_t nelem, size;
+{
+ size_t cnt = HEAPCNT(nelem, size);
+ NODE *n = NEWHEAP();
+ void *ptr = xcalloc(nelem, size);
+
+ return ADD2HEAP(n, cnt, ptr);
+}
+
+static void *
+rb_parser_realloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ NODE *n;
+ size_t cnt = HEAPCNT(1, size);
+
+ if (ptr && (n = parser_heap) != NULL) {
+ do {
+ if (n->u1.node == ptr) {
+ n->u1.node = ptr = xrealloc(ptr, size);
+ if (n->u3.cnt) n->u3.cnt = cnt;
+ return ptr;
+ }
+ } while ((n = n->u2.node) != NULL);
+ }
+ n = NEWHEAP();
+ ptr = xrealloc(ptr, size);
+ return ADD2HEAP(n, cnt, ptr);
+}
+
+static void
+rb_parser_free(ptr)
+ void *ptr;
+{
+ NODE **prev = &parser_heap, *n;
+
+ while ((n = *prev) != 0) {
+ if (n->u1.node == ptr) {
+ *prev = n->u2.node;
+ rb_gc_force_recycle((VALUE)n);
+ break;
+ }
+ prev = &n->u2.node;
+ }
+ xfree(ptr);
+}
+#endif
+
+/**********************************************************************
+
+ prec.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Tue Jan 26 02:40:41 2000
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+VALUE rb_mPrecision;
+
+static ID prc_pr, prc_if;
+
+
+/*
+ * call-seq:
+ * num.prec(klass) => a_klass
+ *
+ * Converts _self_ into an instance of _klass_. By default,
+ * +prec+ invokes
+ *
+ * klass.induced_from(num)
+ *
+ * and returns its value. So, if <code>klass.induced_from</code>
+ * doesn't return an instance of _klass_, it will be necessary
+ * to reimplement +prec+.
+ */
+
+static VALUE
+prec_prec(x, klass)
+ VALUE x, klass;
+{
+ return rb_funcall(klass, prc_if, 1, x);
+}
+
+/*
+ * call-seq:
+ * num.prec_i => Integer
+ *
+ * Returns an +Integer+ converted from _num_. It is equivalent
+ * to <code>prec(Integer)</code>.
+ */
+
+static VALUE
+prec_prec_i(x)
+ VALUE x;
+{
+ VALUE klass = rb_cInteger;
+
+ return rb_funcall(x, prc_pr, 1, klass);
+}
+
+/*
+ * call-seq:
+ * num.prec_f => Float
+ *
+ * Returns a +Float+ converted from _num_. It is equivalent
+ * to <code>prec(Float)</code>.
+ */
+
+static VALUE
+prec_prec_f(x)
+ VALUE x;
+{
+ VALUE klass = rb_cFloat;
+
+ return rb_funcall(x, prc_pr, 1, klass);
+}
+
+/*
+ * call-seq:
+ * Mod.induced_from(number) => a_mod
+ *
+ * Creates an instance of mod from. This method is overridden
+ * by concrete +Numeric+ classes, so that (for example)
+ *
+ * Fixnum.induced_from(9.9) #=> 9
+ *
+ * Note that a use of +prec+ in a redefinition may cause
+ * an infinite loop.
+ */
+
+static VALUE
+prec_induced_from(module, x)
+ VALUE module, x;
+{
+ rb_raise(rb_eTypeError, "undefined conversion from %s into %s",
+ rb_obj_classname(x), rb_class2name(module));
+ return Qnil; /* not reached */
+}
+
+/*
+ * call_seq:
+ * included
+ *
+ * When the +Precision+ module is mixed-in to a class, this +included+
+ * method is used to add our default +induced_from+ implementation
+ * to the host class.
+ */
+
+static VALUE
+prec_included(module, include)
+ VALUE module, include;
+{
+ switch (TYPE(include)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ Check_Type(include, T_CLASS);
+ break;
+ }
+ rb_define_singleton_method(include, "induced_from", prec_induced_from, 1);
+ return module;
+}
+
+/*
+ * Precision is a mixin for concrete numeric classes with
+ * precision. Here, `precision' means the fineness of approximation
+ * of a real number, so, this module should not be included into
+ * anything which is not a subset of Real (so it should not be
+ * included in classes such as +Complex+ or +Matrix+).
+*/
+
+void
+Init_Precision()
+{
+ rb_mPrecision = rb_define_module("Precision");
+ rb_define_singleton_method(rb_mPrecision, "included", prec_included, 1);
+ rb_define_method(rb_mPrecision, "prec", prec_prec, 1);
+ rb_define_method(rb_mPrecision, "prec_i", prec_prec_i, 0);
+ rb_define_method(rb_mPrecision, "prec_f", prec_prec_f, 0);
+
+ prc_pr = rb_intern("prec");
+ prc_if = rb_intern("induced_from");
+}
+/**********************************************************************
+
+ process.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-29 11:33:11 +0200 (Sun, 29 Jun 2008) $
+ created at: Tue Aug 10 14:30:50 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __DJGPP__
+#include <process.h>
+#endif
+
+#include <time.h>
+#include <ctype.h>
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+struct timeval rb_time_interval _((VALUE));
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+#include "st.h"
+
+#ifdef __EMX__
+#undef HAVE_GETPGRP
+#endif
+
+#ifdef HAVE_SYS_TIMES_H
+#include <sys/times.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#if defined(HAVE_TIMES) || defined(_WIN32)
+static VALUE S_Tms;
+#endif
+
+#ifndef WIFEXITED
+#define WIFEXITED(w) (((w) & 0xff) == 0)
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(w) (((w) & 0x7f) > 0 && (((w) & 0x7f) < 0x7f))
+#endif
+#ifndef WIFSTOPPED
+#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(w) ((w) & 0x7f)
+#endif
+#ifndef WSTOPSIG
+#define WSTOPSIG WEXITSTATUS
+#endif
+
+#if defined(__APPLE__) && ( defined(__MACH__) || defined(__DARWIN__) ) && !defined(__MacOS_X__)
+#define __MacOS_X__ 1
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
+#define HAVE_44BSD_SETUID 1
+#define HAVE_44BSD_SETGID 1
+#endif
+
+#ifdef __NetBSD__
+#undef HAVE_SETRUID
+#undef HAVE_SETRGID
+#endif
+
+#if defined(__MacOS_X__) || defined(__bsdi__)
+#define BROKEN_SETREUID 1
+#define BROKEN_SETREGID 1
+#endif
+
+#ifdef BROKEN_SETREUID
+#define setreuid ruby_setreuid
+#endif
+#ifdef BROKEN_SETREGID
+#define setregid ruby_setregid
+#endif
+
+#if defined(HAVE_44BSD_SETUID) || defined(__MacOS_X__)
+#if !defined(USE_SETREUID) && !defined(BROKEN_SETREUID)
+#define OBSOLETE_SETREUID 1
+#endif
+#if !defined(USE_SETREGID) && !defined(BROKEN_SETREGID)
+#define OBSOLETE_SETREGID 1
+#endif
+#endif
+
+#define preserving_errno(stmts) \
+ do {int saved_errno = errno; stmts; errno = saved_errno;} while (0)
+
+
+/*
+ * call-seq:
+ * Process.pid => fixnum
+ *
+ * Returns the process id of this process. Not available on all
+ * platforms.
+ *
+ * Process.pid #=> 27415
+ */
+
+static VALUE
+get_pid()
+{
+ rb_secure(2);
+ return INT2FIX(getpid());
+}
+
+
+/*
+ * call-seq:
+ * Process.ppid => fixnum
+ *
+ * Returns the process id of the parent of this process. Always
+ * returns 0 on NT. Not available on all platforms.
+ *
+ * puts "I am #{Process.pid}"
+ * Process.fork { puts "Dad is #{Process.ppid}" }
+ *
+ * <em>produces:</em>
+ *
+ * I am 27417
+ * Dad is 27417
+ */
+
+static VALUE
+get_ppid()
+{
+ rb_secure(2);
+#ifdef _WIN32
+ return INT2FIX(0);
+#else
+ return INT2FIX(getppid());
+#endif
+}
+
+
+/*********************************************************************
+ *
+ * Document-class: Process::Status
+ *
+ * <code>Process::Status</code> encapsulates the information on the
+ * status of a running or terminated system process. The built-in
+ * variable <code>$?</code> is either +nil+ or a
+ * <code>Process::Status</code> object.
+ *
+ * fork { exit 99 } #=> 26557
+ * Process.wait #=> 26557
+ * $?.class #=> Process::Status
+ * $?.to_i #=> 25344
+ * $? >> 8 #=> 99
+ * $?.stopped? #=> false
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 99
+ *
+ * Posix systems record information on processes using a 16-bit
+ * integer. The lower bits record the process status (stopped,
+ * exited, signaled) and the upper bits possibly contain additional
+ * information (for example the program's return code in the case of
+ * exited processes). Pre Ruby 1.8, these bits were exposed directly
+ * to the Ruby program. Ruby now encapsulates these in a
+ * <code>Process::Status</code> object. To maximize compatibility,
+ * however, these objects retain a bit-oriented interface. In the
+ * descriptions that follow, when we talk about the integer value of
+ * _stat_, we're referring to this 16 bit value.
+ */
+
+static VALUE rb_cProcStatus;
+VALUE rb_last_status = Qnil;
+
+static void
+last_status_set(status, pid)
+ int status, pid;
+{
+ rb_last_status = rb_obj_alloc(rb_cProcStatus);
+ rb_iv_set(rb_last_status, "status", INT2FIX(status));
+ rb_iv_set(rb_last_status, "pid", INT2FIX(pid));
+}
+
+
+/*
+ * call-seq:
+ * stat.to_i => fixnum
+ * stat.to_int => fixnum
+ *
+ * Returns the bits in _stat_ as a <code>Fixnum</code>. Poking
+ * around in these bits is platform dependent.
+ *
+ * fork { exit 0xab } #=> 26566
+ * Process.wait #=> 26566
+ * sprintf('%04x', $?.to_i) #=> "ab00"
+ */
+
+static VALUE
+pst_to_i(st)
+ VALUE st;
+{
+ return rb_iv_get(st, "status");
+}
+
+
+/*
+ * call-seq:
+ * stat.to_s => string
+ *
+ * Equivalent to _stat_<code>.to_i.to_s</code>.
+ */
+
+static VALUE
+pst_to_s(st)
+ VALUE st;
+{
+ return rb_fix2str(pst_to_i(st), 10);
+}
+
+
+/*
+ * call-seq:
+ * stat.pid => fixnum
+ *
+ * Returns the process ID that this status object represents.
+ *
+ * fork { exit } #=> 26569
+ * Process.wait #=> 26569
+ * $?.pid #=> 26569
+ */
+
+static VALUE
+pst_pid(st)
+ VALUE st;
+{
+ return rb_iv_get(st, "pid");
+}
+
+
+/*
+ * call-seq:
+ * stat.inspect => string
+ *
+ * Override the inspection method.
+ */
+
+static VALUE
+pst_inspect(st)
+ VALUE st;
+{
+ VALUE pid;
+ int status;
+ VALUE str;
+ char buf[256];
+
+ pid = pst_pid(st);
+ status = NUM2INT(st);
+
+ snprintf(buf, sizeof(buf), "#<%s: pid=%ld", rb_class2name(CLASS_OF(st)), NUM2LONG(pid));
+ str = rb_str_new2(buf);
+ if (WIFSTOPPED(status)) {
+ int stopsig = WSTOPSIG(status);
+ const char *signame = ruby_signal_name(stopsig);
+ if (signame) {
+ snprintf(buf, sizeof(buf), ",stopped(SIG%s=%d)", signame, stopsig);
+ }
+ else {
+ snprintf(buf, sizeof(buf), ",stopped(%d)", stopsig);
+ }
+ rb_str_cat2(str, buf);
+ }
+ if (WIFSIGNALED(status)) {
+ int termsig = WTERMSIG(status);
+ const char *signame = ruby_signal_name(termsig);
+ if (signame) {
+ snprintf(buf, sizeof(buf), ",signaled(SIG%s=%d)", signame, termsig);
+ }
+ else {
+ snprintf(buf, sizeof(buf), ",signaled(%d)", termsig);
+ }
+ rb_str_cat2(str, buf);
+ }
+ if (WIFEXITED(status)) {
+ snprintf(buf, sizeof(buf), ",exited(%d)", WEXITSTATUS(status));
+ rb_str_cat2(str, buf);
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ rb_str_cat2(str, ",coredumped");
+ }
+#endif
+ rb_str_cat2(str, ">");
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * stat == other => true or false
+ *
+ * Returns +true+ if the integer value of _stat_
+ * equals <em>other</em>.
+ */
+
+static VALUE
+pst_equal(st1, st2)
+ VALUE st1, st2;
+{
+ if (st1 == st2) return Qtrue;
+ return rb_equal(pst_to_i(st1), st2);
+}
+
+
+/*
+ * call-seq:
+ * stat & num => fixnum
+ *
+ * Logical AND of the bits in _stat_ with <em>num</em>.
+ *
+ * fork { exit 0x37 }
+ * Process.wait
+ * sprintf('%04x', $?.to_i) #=> "3700"
+ * sprintf('%04x', $? & 0x1e00) #=> "1600"
+ */
+
+static VALUE
+pst_bitand(st1, st2)
+ VALUE st1, st2;
+{
+ int status = NUM2INT(st1) & NUM2INT(st2);
+
+ return INT2NUM(status);
+}
+
+
+/*
+ * call-seq:
+ * stat >> num => fixnum
+ *
+ * Shift the bits in _stat_ right <em>num</em> places.
+ *
+ * fork { exit 99 } #=> 26563
+ * Process.wait #=> 26563
+ * $?.to_i #=> 25344
+ * $? >> 8 #=> 99
+ */
+
+static VALUE
+pst_rshift(st1, st2)
+ VALUE st1, st2;
+{
+ int status = NUM2INT(st1) >> NUM2INT(st2);
+
+ return INT2NUM(status);
+}
+
+
+/*
+ * call-seq:
+ * stat.stopped? => true or false
+ *
+ * Returns +true+ if this process is stopped. This is only
+ * returned if the corresponding <code>wait</code> call had the
+ * <code>WUNTRACED</code> flag set.
+ */
+
+static VALUE
+pst_wifstopped(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFSTOPPED(status))
+ return Qtrue;
+ else
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * stat.stopsig => fixnum or nil
+ *
+ * Returns the number of the signal that caused _stat_ to stop
+ * (or +nil+ if self is not stopped).
+ */
+
+static VALUE
+pst_wstopsig(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFSTOPPED(status))
+ return INT2NUM(WSTOPSIG(status));
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * stat.signaled? => true or false
+ *
+ * Returns +true+ if _stat_ terminated because of
+ * an uncaught signal.
+ */
+
+static VALUE
+pst_wifsignaled(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFSIGNALED(status))
+ return Qtrue;
+ else
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * stat.termsig => fixnum or nil
+ *
+ * Returns the number of the signal that caused _stat_ to
+ * terminate (or +nil+ if self was not terminated by an
+ * uncaught signal).
+ */
+
+static VALUE
+pst_wtermsig(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFSIGNALED(status))
+ return INT2NUM(WTERMSIG(status));
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * stat.exited? => true or false
+ *
+ * Returns +true+ if _stat_ exited normally (for
+ * example using an <code>exit()</code> call or finishing the
+ * program).
+ */
+
+static VALUE
+pst_wifexited(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFEXITED(status))
+ return Qtrue;
+ else
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * stat.exitstatus => fixnum or nil
+ *
+ * Returns the least significant eight bits of the return code of
+ * _stat_. Only available if <code>exited?</code> is
+ * +true+.
+ *
+ * fork { } #=> 26572
+ * Process.wait #=> 26572
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 0
+ *
+ * fork { exit 99 } #=> 26573
+ * Process.wait #=> 26573
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 99
+ */
+
+static VALUE
+pst_wexitstatus(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (WIFEXITED(status))
+ return INT2NUM(WEXITSTATUS(status));
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * stat.success? => true, false or nil
+ *
+ * Returns +true+ if _stat_ is successful, +false+ if not.
+ * Returns +nil+ if <code>exited?</code> is not +true+.
+ */
+
+static VALUE
+pst_success_p(st)
+ VALUE st;
+{
+ int status = NUM2INT(st);
+
+ if (!WIFEXITED(status))
+ return Qnil;
+ return WEXITSTATUS(status) == EXIT_SUCCESS ? Qtrue : Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * stat.coredump? => true or false
+ *
+ * Returns +true+ if _stat_ generated a coredump
+ * when it terminated. Not available on all platforms.
+ */
+
+static VALUE
+pst_wcoredump(st)
+ VALUE st;
+{
+#ifdef WCOREDUMP
+ int status = NUM2INT(st);
+
+ if (WCOREDUMP(status))
+ return Qtrue;
+ else
+ return Qfalse;
+#else
+ return Qfalse;
+#endif
+}
+
+#if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4)
+#define NO_WAITPID
+static st_table *pid_tbl;
+#endif
+
+int
+rb_waitpid(pid, st, flags)
+ int pid;
+ int *st;
+ int flags;
+{
+ int result;
+#ifndef NO_WAITPID
+ int oflags = flags;
+ if (!rb_thread_alone()) { /* there're other threads to run */
+ flags |= WNOHANG;
+ }
+
+ retry:
+ TRAP_BEG;
+#ifdef HAVE_WAITPID
+ result = waitpid(pid, st, flags);
+#else /* HAVE_WAIT4 */
+ result = wait4(pid, st, flags, NULL);
+#endif
+ TRAP_END;
+ if (result < 0) {
+ if (errno == EINTR) {
+ rb_thread_polling();
+ goto retry;
+ }
+ return -1;
+ }
+ if (result == 0) {
+ if (oflags & WNOHANG) return 0;
+ rb_thread_polling();
+ if (rb_thread_alone()) flags = oflags;
+ goto retry;
+ }
+#else /* NO_WAITPID */
+ if (pid_tbl && st_lookup(pid_tbl, pid, st)) {
+ last_status_set(*st, pid);
+ st_delete(pid_tbl, (st_data_t*)&pid, NULL);
+ return pid;
+ }
+
+ if (flags) {
+ rb_raise(rb_eArgError, "can't do waitpid with flags");
+ }
+
+ for (;;) {
+ TRAP_BEG;
+ result = wait(st);
+ TRAP_END;
+ if (result < 0) {
+ if (errno == EINTR) {
+ rb_thread_schedule();
+ continue;
+ }
+ return -1;
+ }
+ if (result == pid) {
+ break;
+ }
+ if (!pid_tbl)
+ pid_tbl = st_init_numtable();
+ st_insert(pid_tbl, pid, st);
+ if (!rb_thread_alone()) rb_thread_schedule();
+ }
+#endif
+ if (result > 0) {
+ last_status_set(*st, result);
+ }
+ return result;
+}
+
+#ifdef NO_WAITPID
+struct wait_data {
+ int pid;
+ int status;
+};
+
+static int
+wait_each(pid, status, data)
+ int pid, status;
+ struct wait_data *data;
+{
+ if (data->status != -1) return ST_STOP;
+
+ data->pid = pid;
+ data->status = status;
+ return ST_DELETE;
+}
+
+static int
+waitall_each(pid, status, ary)
+ int pid, status;
+ VALUE ary;
+{
+ last_status_set(status, pid);
+ rb_ary_push(ary, rb_assoc_new(INT2NUM(pid), rb_last_status));
+ return ST_DELETE;
+}
+#endif
+
+
+/* [MG]:FIXME: I wasn't sure how this should be done, since ::wait()
+ has historically been documented as if it didn't take any arguments
+ despite the fact that it's just an alias for ::waitpid(). The way I
+ have it below is more truthful, but a little confusing.
+
+ I also took the liberty of putting in the pid values, as they're
+ pretty useful, and it looked as if the original 'ri' output was
+ supposed to contain them after "[...]depending on the value of
+ aPid:".
+
+ The 'ansi' and 'bs' formats of the ri output don't display the
+ definition list for some reason, but the plain text one does.
+ */
+
+/*
+ * call-seq:
+ * Process.wait() => fixnum
+ * Process.wait(pid=-1, flags=0) => fixnum
+ * Process.waitpid(pid=-1, flags=0) => fixnum
+ *
+ * Waits for a child process to exit, returns its process id, and
+ * sets <code>$?</code> to a <code>Process::Status</code> object
+ * containing information on that process. Which child it waits on
+ * depends on the value of _pid_:
+ *
+ * > 0:: Waits for the child whose process ID equals _pid_.
+ *
+ * 0:: Waits for any child whose process group ID equals that of the
+ * calling process.
+ *
+ * -1:: Waits for any child process (the default if no _pid_ is
+ * given).
+ *
+ * < -1:: Waits for any child whose process group ID equals the absolute
+ * value of _pid_.
+ *
+ * The _flags_ argument may be a logical or of the flag values
+ * <code>Process::WNOHANG</code> (do not block if no child available)
+ * or <code>Process::WUNTRACED</code> (return stopped children that
+ * haven't been reported). Not all flags are available on all
+ * platforms, but a flag value of zero will work on all platforms.
+ *
+ * Calling this method raises a <code>SystemError</code> if there are
+ * no child processes. Not available on all platforms.
+ *
+ * include Process
+ * fork { exit 99 } #=> 27429
+ * wait #=> 27429
+ * $?.exitstatus #=> 99
+ *
+ * pid = fork { sleep 3 } #=> 27440
+ * Time.now #=> Wed Apr 09 08:57:09 CDT 2003
+ * waitpid(pid, Process::WNOHANG) #=> nil
+ * Time.now #=> Wed Apr 09 08:57:09 CDT 2003
+ * waitpid(pid, 0) #=> 27440
+ * Time.now #=> Wed Apr 09 08:57:12 CDT 2003
+ */
+
+static VALUE
+proc_wait(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vpid, vflags;
+ int pid, flags, status;
+
+ rb_secure(2);
+ flags = 0;
+ rb_scan_args(argc, argv, "02", &vpid, &vflags);
+ if (argc == 0) {
+ pid = -1;
+ }
+ else {
+ pid = NUM2INT(vpid);
+ if (argc == 2 && !NIL_P(vflags)) {
+ flags = NUM2UINT(vflags);
+ }
+ }
+ if ((pid = rb_waitpid(pid, &status, flags)) < 0)
+ rb_sys_fail(0);
+ if (pid == 0) {
+ return rb_last_status = Qnil;
+ }
+ return INT2FIX(pid);
+}
+
+
+/*
+ * call-seq:
+ * Process.wait2(pid=-1, flags=0) => [pid, status]
+ * Process.waitpid2(pid=-1, flags=0) => [pid, status]
+ *
+ * Waits for a child process to exit (see Process::waitpid for exact
+ * semantics) and returns an array containing the process id and the
+ * exit status (a <code>Process::Status</code> object) of that
+ * child. Raises a <code>SystemError</code> if there are no child
+ * processes.
+ *
+ * Process.fork { exit 99 } #=> 27437
+ * pid, status = Process.wait2
+ * pid #=> 27437
+ * status.exitstatus #=> 99
+ */
+
+static VALUE
+proc_wait2(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE pid = proc_wait(argc, argv);
+ if (NIL_P(pid)) return Qnil;
+ return rb_assoc_new(pid, rb_last_status);
+}
+
+
+/*
+ * call-seq:
+ * Process.waitall => [ [pid1,status1], ...]
+ *
+ * Waits for all children, returning an array of
+ * _pid_/_status_ pairs (where _status_ is a
+ * <code>Process::Status</code> object).
+ *
+ * fork { sleep 0.2; exit 2 } #=> 27432
+ * fork { sleep 0.1; exit 1 } #=> 27433
+ * fork { exit 0 } #=> 27434
+ * p Process.waitall
+ *
+ * <em>produces</em>:
+ *
+ * [[27434, #<Process::Status: pid=27434,exited(0)>],
+ * [27433, #<Process::Status: pid=27433,exited(1)>],
+ * [27432, #<Process::Status: pid=27432,exited(2)>]]
+ */
+
+static VALUE
+proc_waitall()
+{
+ VALUE result;
+ int pid, status;
+
+ rb_secure(2);
+ result = rb_ary_new();
+#ifdef NO_WAITPID
+ if (pid_tbl) {
+ st_foreach(pid_tbl, waitall_each, result);
+ }
+
+ for (pid = -1;;) {
+ pid = wait(&status);
+ if (pid == -1) {
+ if (errno == ECHILD)
+ break;
+ if (errno == EINTR) {
+ rb_thread_schedule();
+ continue;
+ }
+ rb_sys_fail(0);
+ }
+ last_status_set(status, pid);
+ rb_ary_push(result, rb_assoc_new(INT2NUM(pid), rb_last_status));
+ }
+#else
+ rb_last_status = Qnil;
+ for (pid = -1;;) {
+ pid = rb_waitpid(-1, &status, 0);
+ if (pid == -1) {
+ if (errno == ECHILD)
+ break;
+ rb_sys_fail(0);
+ }
+ rb_ary_push(result, rb_assoc_new(INT2NUM(pid), rb_last_status));
+ }
+#endif
+ return result;
+}
+
+static VALUE
+detach_process_watcher(arg)
+ void *arg;
+{
+ int pid = (int)(VALUE)arg, status;
+
+ while (rb_waitpid(pid, &status, WNOHANG) == 0) {
+ rb_thread_sleep(1);
+ }
+ return rb_last_status;
+}
+
+VALUE
+rb_detach_process(pid)
+ int pid;
+{
+ return rb_thread_create(detach_process_watcher, (void*)(VALUE)pid);
+}
+
+
+/*
+ * call-seq:
+ * Process.detach(pid) => thread
+ *
+ * Some operating systems retain the status of terminated child
+ * processes until the parent collects that status (normally using
+ * some variant of <code>wait()</code>. If the parent never collects
+ * this status, the child stays around as a <em>zombie</em> process.
+ * <code>Process::detach</code> prevents this by setting up a
+ * separate Ruby thread whose sole job is to reap the status of the
+ * process _pid_ when it terminates. Use <code>detach</code>
+ * only when you do not intent to explicitly wait for the child to
+ * terminate. <code>detach</code> only checks the status
+ * periodically (currently once each second).
+ *
+ * The waiting thread returns the exit status of the detached process
+ * when it terminates, so you can use <code>Thread#join</code> to
+ * know the result. If specified _pid_ is not a valid child process
+ * ID, the thread returns +nil+ immediately.
+ *
+ * In this first example, we don't reap the first child process, so
+ * it appears as a zombie in the process status display.
+ *
+ * p1 = fork { sleep 0.1 }
+ * p2 = fork { sleep 0.2 }
+ * Process.waitpid(p2)
+ * sleep 2
+ * system("ps -ho pid,state -p #{p1}")
+ *
+ * <em>produces:</em>
+ *
+ * 27389 Z
+ *
+ * In the next example, <code>Process::detach</code> is used to reap
+ * the child automatically.
+ *
+ * p1 = fork { sleep 0.1 }
+ * p2 = fork { sleep 0.2 }
+ * Process.detach(p1)
+ * Process.waitpid(p2)
+ * sleep 2
+ * system("ps -ho pid,state -p #{p1}")
+ *
+ * <em>(produces no output)</em>
+ */
+
+static VALUE
+proc_detach(VALUE obj, VALUE pid)
+{
+ rb_secure(2);
+ return rb_detach_process(NUM2INT(pid));
+}
+
+#ifndef HAVE_STRING_H
+char *strtok();
+#endif
+
+#ifdef HAVE_SETITIMER
+#define before_exec() rb_thread_stop_timer()
+#define after_exec() rb_thread_start_timer()
+#else
+#define before_exec()
+#define after_exec()
+#endif
+
+extern char *dln_find_exe();
+
+static void
+security(str)
+ char *str;
+{
+ if (rb_env_path_tainted()) {
+ if (rb_safe_level() > 0) {
+ rb_raise(rb_eSecurityError, "Insecure PATH - %s", str);
+ }
+ }
+}
+
+static int
+proc_exec_v(argv, prog)
+ char **argv;
+ char *prog;
+{
+ if (!prog)
+ prog = argv[0];
+ security(prog);
+ prog = dln_find_exe(prog, 0);
+ if (!prog)
+ return -1;
+
+#if (defined(MSDOS) && !defined(DJGPP)) || defined(__human68k__) || defined(__EMX__) || defined(OS2)
+ {
+#if defined(__human68k__)
+#define COMMAND "command.x"
+#endif
+#if defined(__EMX__) || defined(OS2) /* OS/2 emx */
+#define COMMAND "cmd.exe"
+#endif
+#if (defined(MSDOS) && !defined(DJGPP))
+#define COMMAND "command.com"
+#endif
+ char *extension;
+
+ if ((extension = strrchr(prog, '.')) != NULL && strcasecmp(extension, ".bat") == 0) {
+ char **new_argv;
+ char *p;
+ int n;
+
+ for (n = 0; argv[n]; n++)
+ /* no-op */;
+ new_argv = ALLOCA_N(char*, n + 2);
+ for (; n > 0; n--)
+ new_argv[n + 1] = argv[n];
+ new_argv[1] = strcpy(ALLOCA_N(char, strlen(argv[0]) + 1), argv[0]);
+ for (p = new_argv[1]; *p != '\0'; p++)
+ if (*p == '/')
+ *p = '\\';
+ new_argv[0] = COMMAND;
+ argv = new_argv;
+ prog = dln_find_exe(argv[0], 0);
+ if (!prog) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ }
+#endif /* MSDOS or __human68k__ or __EMX__ */
+ before_exec();
+ rb_thread_cancel_timer();
+ execv(prog, argv);
+ preserving_errno(after_exec());
+ return -1;
+}
+
+static int
+proc_exec_n(argc, argv, progv)
+ int argc;
+ VALUE *argv;
+ VALUE progv;
+{
+ char *prog = 0;
+ char **args;
+ int i;
+
+ if (progv) {
+ prog = RSTRING(progv)->ptr;
+ }
+ args = ALLOCA_N(char*, argc+1);
+ for (i=0; i<argc; i++) {
+ SafeStringValue(argv[i]);
+ args[i] = RSTRING(argv[i])->ptr;
+ }
+ args[i] = 0;
+ if (args[0]) {
+ return proc_exec_v(args, prog);
+ }
+ return -1;
+}
+
+int
+rb_proc_exec(str)
+ const char *str;
+{
+ const char *s = str;
+ char *ss, *t;
+ char **argv, **a;
+
+ while (*str && ISSPACE(*str))
+ str++;
+
+#ifdef _WIN32
+ before_exec();
+ do_spawn(P_OVERLAY, (char *)str);
+ after_exec();
+#else
+ for (s=str; *s; s++) {
+ if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
+#if defined(MSDOS)
+ int status;
+ before_exec();
+ status = system(str);
+ after_exec();
+ if (status != -1)
+ exit(status);
+#else
+#if defined(__human68k__) || defined(__CYGWIN32__) || defined(__EMX__)
+ char *shell = dln_find_exe("sh", 0);
+ int status = -1;
+ before_exec();
+ if (shell)
+ execl(shell, "sh", "-c", str, (char *) NULL);
+ else
+ status = system(str);
+ after_exec();
+ if (status != -1)
+ exit(status);
+#else
+ before_exec();
+ execl("/bin/sh", "sh", "-c", str, (char *)NULL);
+ preserving_errno(after_exec());
+#endif
+#endif
+ return -1;
+ }
+ }
+ a = argv = ALLOCA_N(char*, (s-str)/2+2);
+ ss = ALLOCA_N(char, s-str+1);
+ strcpy(ss, str);
+ if ((*a++ = strtok(ss, " \t")) != 0) {
+ while ((t = strtok(NULL, " \t")) != 0) {
+ *a++ = t;
+ }
+ *a = NULL;
+ }
+ if (argv[0]) {
+ return proc_exec_v(argv, 0);
+ }
+ errno = ENOENT;
+#endif /* _WIN32 */
+ return -1;
+}
+
+#if defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
+static int
+proc_spawn_v(argv, prog)
+ char **argv;
+ char *prog;
+{
+ char *extension;
+ int status;
+
+ if (!prog)
+ prog = argv[0];
+ security(prog);
+ prog = dln_find_exe(prog, 0);
+ if (!prog)
+ return -1;
+
+#if defined(__human68k__)
+ if ((extension = strrchr(prog, '.')) != NULL && strcasecmp(extension, ".bat") == 0) {
+ char **new_argv;
+ char *p;
+ int n;
+
+ for (n = 0; argv[n]; n++)
+ /* no-op */;
+ new_argv = ALLOCA_N(char*, n + 2);
+ for (; n > 0; n--)
+ new_argv[n + 1] = argv[n];
+ new_argv[1] = strcpy(ALLOCA_N(char, strlen(argv[0]) + 1), argv[0]);
+ for (p = new_argv[1]; *p != '\0'; p++)
+ if (*p == '/')
+ *p = '\\';
+ new_argv[0] = COMMAND;
+ argv = new_argv;
+ prog = dln_find_exe(argv[0], 0);
+ if (!prog) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+#endif
+ before_exec();
+#if defined(_WIN32)
+ status = do_aspawn(P_WAIT, prog, argv);
+#else
+ status = spawnv(P_WAIT, prog, argv);
+#endif
+ after_exec();
+ return status;
+}
+
+static int
+proc_spawn_n(argc, argv, prog)
+ int argc;
+ VALUE *argv;
+ VALUE prog;
+{
+ char **args;
+ int i;
+
+ args = ALLOCA_N(char*, argc + 1);
+ for (i = 0; i < argc; i++) {
+ SafeStringValue(argv[i]);
+ args[i] = StringValueCStr(argv[i]);
+ }
+ if (prog)
+ SafeStringValue(prog);
+ args[i] = (char*) 0;
+ if (args[0])
+ return proc_spawn_v(args, prog ? StringValueCStr(prog) : 0);
+ return -1;
+}
+
+#if !defined(_WIN32)
+static int
+proc_spawn(sv)
+ VALUE sv;
+{
+ char *str;
+ char *s, *t;
+ char **argv, **a;
+ int status;
+
+ SafeStringValue(sv);
+ str = s = StringValueCStr(sv);
+ for (s = str; *s; s++) {
+ if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
+ char *shell = dln_find_exe("sh", 0);
+ before_exec();
+ status = shell?spawnl(P_WAIT,shell,"sh","-c",str,(char*)NULL):system(str);
+ after_exec();
+ return status;
+ }
+ }
+ a = argv = ALLOCA_N(char*, (s - str) / 2 + 2);
+ s = ALLOCA_N(char, s - str + 1);
+ strcpy(s, str);
+ if (*a++ = strtok(s, " \t")) {
+ while (t = strtok(NULL, " \t"))
+ *a++ = t;
+ *a = NULL;
+ }
+ return argv[0] ? proc_spawn_v(argv, 0) : -1;
+}
+#endif
+#endif
+
+struct rb_exec_arg {
+ int argc;
+ VALUE *argv;
+ VALUE prog;
+};
+
+static void
+proc_prepare_args(e, argc, argv, prog)
+ struct rb_exec_arg *e;
+ int argc;
+ VALUE *argv;
+ VALUE prog;
+{
+ int i;
+
+ MEMZERO(e, struct rb_exec_arg, 1);
+ if (prog) {
+ SafeStringValue(prog);
+ StringValueCStr(prog);
+ }
+ for (i = 0; i < argc; i++) {
+ SafeStringValue(argv[i]);
+ StringValueCStr(argv[i]);
+ }
+ security(RSTRING(prog ? prog : argv[0])->ptr);
+ e->prog = prog;
+ e->argc = argc;
+ e->argv = argv;
+}
+
+static VALUE
+proc_exec_args(earg)
+ VALUE earg;
+{
+ struct rb_exec_arg *e = (struct rb_exec_arg *)earg;
+ int argc = e->argc;
+ VALUE *argv = e->argv;
+ VALUE prog = e->prog;
+
+ if (argc == 1 && prog == 0) {
+ return (VALUE)rb_proc_exec(RSTRING(argv[0])->ptr);
+ }
+ else {
+ return (VALUE)proc_exec_n(argc, argv, prog);
+ }
+}
+
+/*
+ * call-seq:
+ * exec(command [, arg, ...])
+ *
+ * Replaces the current process by running the given external _command_.
+ * If +exec+ is given a single argument, that argument is
+ * taken as a line that is subject to shell expansion before being
+ * executed. If multiple arguments are given, the second and subsequent
+ * arguments are passed as parameters to _command_ with no shell
+ * expansion. If the first argument is a two-element array, the first
+ * element is the command to be executed, and the second argument is
+ * used as the <code>argv[0]</code> value, which may show up in process
+ * listings. In MSDOS environments, the command is executed in a
+ * subshell; otherwise, one of the <code>exec(2)</code> system calls is
+ * used, so the running command may inherit some of the environment of
+ * the original program (including open file descriptors).
+ *
+ * exec "echo *" # echoes list of files in current directory
+ * # never get here
+ *
+ *
+ * exec "echo", "*" # echoes an asterisk
+ * # never get here
+ */
+
+VALUE
+rb_f_exec(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE prog = 0;
+ VALUE tmp;
+ struct rb_exec_arg earg;
+
+ if (argc == 0) {
+ rb_last_status = Qnil;
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ tmp = rb_check_array_type(argv[0]);
+ if (!NIL_P(tmp)) {
+ if (RARRAY(tmp)->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ prog = RARRAY(tmp)->ptr[0];
+ argv[0] = RARRAY(tmp)->ptr[1];
+ SafeStringValue(prog);
+ }
+ proc_prepare_args(&earg, argc, argv, prog);
+ proc_exec_args((VALUE)&earg);
+ rb_sys_fail(RSTRING(argv[0])->ptr);
+ return Qnil; /* dummy */
+}
+
+
+/*
+ * call-seq:
+ * Kernel.fork [{ block }] => fixnum or nil
+ * Process.fork [{ block }] => fixnum or nil
+ *
+ * Creates a subprocess. If a block is specified, that block is run
+ * in the subprocess, and the subprocess terminates with a status of
+ * zero. Otherwise, the +fork+ call returns twice, once in
+ * the parent, returning the process ID of the child, and once in
+ * the child, returning _nil_. The child process can exit using
+ * <code>Kernel.exit!</code> to avoid running any
+ * <code>at_exit</code> functions. The parent process should
+ * use <code>Process.wait</code> to collect the termination statuses
+ * of its children or use <code>Process.detach</code> to register
+ * disinterest in their status; otherwise, the operating system
+ * may accumulate zombie processes.
+ *
+ * The thread calling fork is the only thread in the created child process.
+ * fork doesn't copy other threads.
+ */
+
+static VALUE
+rb_f_fork(obj)
+ VALUE obj;
+{
+#if !defined(__human68k__) && !defined(_WIN32) && !defined(__MACOS__) && !defined(__EMX__) && !defined(__VMS)
+ int pid;
+
+ rb_secure(2);
+
+#ifndef __VMS
+ fflush(stdout);
+ fflush(stderr);
+#endif
+
+ switch (pid = fork()) {
+ case 0:
+#ifdef linux
+ after_exec();
+#endif
+ rb_thread_atfork();
+ if (rb_block_given_p()) {
+ int status;
+
+ rb_protect(rb_yield, Qundef, &status);
+ ruby_stop(status);
+ }
+ return Qnil;
+
+ case -1:
+ rb_sys_fail("fork(2)");
+ return Qnil;
+
+ default:
+ return INT2FIX(pid);
+ }
+#else
+ rb_notimplement();
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.exit!(fixnum=-1)
+ *
+ * Exits the process immediately. No exit handlers are
+ * run. <em>fixnum</em> is returned to the underlying system as the
+ * exit status.
+ *
+ * Process.exit!(0)
+ */
+
+static VALUE
+rb_f_exit_bang(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE status;
+ int istatus;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "01", &status) == 1) {
+ switch (status) {
+ case Qtrue:
+ istatus = EXIT_SUCCESS;
+ break;
+ case Qfalse:
+ istatus = EXIT_FAILURE;
+ break;
+ default:
+ istatus = NUM2INT(status);
+ break;
+ }
+ }
+ else {
+ istatus = EXIT_FAILURE;
+ }
+ _exit(istatus);
+
+ return Qnil; /* not reached */
+}
+
+#if defined(sun)
+#define signal(a,b) sigset(a,b)
+#endif
+
+void
+rb_syswait(pid)
+ int pid;
+{
+ static int overriding;
+#ifdef SIGHUP
+ RETSIGTYPE (*hfunc)_((int));
+#endif
+#ifdef SIGQUIT
+ RETSIGTYPE (*qfunc)_((int));
+#endif
+ RETSIGTYPE (*ifunc)_((int));
+ int status;
+ int i, hooked = Qfalse;
+
+ if (!overriding) {
+#ifdef SIGHUP
+ hfunc = signal(SIGHUP, SIG_IGN);
+#endif
+#ifdef SIGQUIT
+ qfunc = signal(SIGQUIT, SIG_IGN);
+#endif
+ ifunc = signal(SIGINT, SIG_IGN);
+ overriding = Qtrue;
+ hooked = Qtrue;
+ }
+
+ do {
+ i = rb_waitpid(pid, &status, 0);
+ } while (i == -1 && errno == EINTR);
+
+ if (hooked) {
+#ifdef SIGHUP
+ signal(SIGHUP, hfunc);
+#endif
+#ifdef SIGQUIT
+ signal(SIGQUIT, qfunc);
+#endif
+ signal(SIGINT, ifunc);
+ overriding = Qfalse;
+ }
+}
+
+/*
+ * call-seq:
+ * system(cmd [, arg, ...]) => true or false
+ *
+ * Executes _cmd_ in a subshell, returning +true+ if
+ * the command was found and ran successfully, +false+
+ * otherwise. An error status is available in <code>$?</code>. The
+ * arguments are processed in the same way as for
+ * <code>Kernel::exec</code>.
+ *
+ * system("echo *")
+ * system("echo", "*")
+ *
+ * <em>produces:</em>
+ *
+ * config.h main.rb
+ * *
+ */
+
+static VALUE
+rb_f_system(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int status;
+#if defined(__EMX__)
+ VALUE cmd;
+
+ fflush(stdout);
+ fflush(stderr);
+ if (argc == 0) {
+ rb_last_status = Qnil;
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ if (TYPE(argv[0]) == T_ARRAY) {
+ if (RARRAY(argv[0])->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ argv[0] = RARRAY(argv[0])->ptr[0];
+ }
+ cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
+
+ SafeStringValue(cmd);
+ status = do_spawn(RSTRING(cmd)->ptr);
+ last_status_set(status, 0);
+#elif defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
+ volatile VALUE prog = 0;
+
+ fflush(stdout);
+ fflush(stderr);
+ if (argc == 0) {
+ rb_last_status = Qnil;
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ if (TYPE(argv[0]) == T_ARRAY) {
+ if (RARRAY(argv[0])->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ prog = RARRAY(argv[0])->ptr[0];
+ argv[0] = RARRAY(argv[0])->ptr[1];
+ }
+
+ if (argc == 1 && prog == 0) {
+#if defined(_WIN32)
+ SafeStringValue(argv[0]);
+ status = do_spawn(P_WAIT, StringValueCStr(argv[0]));
+#else
+ status = proc_spawn(argv[0]);
+#endif
+ }
+ else {
+ status = proc_spawn_n(argc, argv, prog);
+ }
+#if !defined(_WIN32)
+ last_status_set(status == -1 ? 127 : status, 0);
+#else
+ if (status == -1)
+ last_status_set(0x7f << 8, 0);
+#endif
+#elif defined(__VMS)
+ VALUE cmd;
+
+ if (argc == 0) {
+ rb_last_status = Qnil;
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ if (TYPE(argv[0]) == T_ARRAY) {
+ if (RARRAY(argv[0])->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ argv[0] = RARRAY(argv[0])->ptr[0];
+ }
+ cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
+
+ SafeStringValue(cmd);
+ status = system(StringValueCStr(cmd));
+ last_status_set((status & 0xff) << 8, 0);
+#else
+ volatile VALUE prog = 0;
+ int pid;
+ struct rb_exec_arg earg;
+ RETSIGTYPE (*chfunc)(int);
+
+ fflush(stdout);
+ fflush(stderr);
+ if (argc == 0) {
+ rb_last_status = Qnil;
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ if (TYPE(argv[0]) == T_ARRAY) {
+ if (RARRAY(argv[0])->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ prog = RARRAY(argv[0])->ptr[0];
+ argv[0] = RARRAY(argv[0])->ptr[1];
+ }
+ proc_prepare_args(&earg, argc, argv, prog);
+
+ chfunc = signal(SIGCHLD, SIG_DFL);
+ retry:
+ pid = fork();
+ if (pid == 0) {
+ /* child process */
+ rb_thread_atfork();
+ rb_protect(proc_exec_args, (VALUE)&earg, NULL);
+ _exit(127);
+ }
+ if (pid < 0) {
+ if (errno == EAGAIN) {
+ rb_thread_sleep(1);
+ goto retry;
+ }
+ }
+ else {
+ rb_syswait(pid);
+ }
+ signal(SIGCHLD, chfunc);
+ if (pid < 0) rb_sys_fail(0);
+ status = NUM2INT(rb_last_status);
+#endif
+
+ if (status == EXIT_SUCCESS) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * sleep([duration]) => fixnum
+ *
+ * Suspends the current thread for _duration_ seconds (which may be any number,
+ * including a +Float+ with fractional seconds). Returns the actual number of
+ * seconds slept (rounded), which may be less than that asked for if another
+ * thread calls <code>Thread#run</code>. Zero arguments causes +sleep+ to sleep
+ * forever.
+ *
+ * Time.new #=> Wed Apr 09 08:56:32 CDT 2003
+ * sleep 1.2 #=> 1
+ * Time.new #=> Wed Apr 09 08:56:33 CDT 2003
+ * sleep 1.9 #=> 2
+ * Time.new #=> Wed Apr 09 08:56:35 CDT 2003
+ */
+
+static VALUE
+rb_f_sleep(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int beg, end;
+
+ beg = time(0);
+ if (argc == 0) {
+ rb_thread_sleep_forever();
+ }
+ else if (argc == 1) {
+ rb_thread_wait_for(rb_time_interval(argv[0]));
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ end = time(0) - beg;
+
+ return INT2FIX(end);
+}
+
+
+/*
+ * call-seq:
+ * Process.getpgrp => integer
+ *
+ * Returns the process group ID for this process. Not available on
+ * all platforms.
+ *
+ * Process.getpgid(0) #=> 25527
+ * Process.getpgrp #=> 25527
+ */
+
+#if defined(SIGCLD) && !defined(SIGCHLD)
+# define SIGCHLD SIGCLD
+#endif
+
+static VALUE
+proc_getpgrp()
+{
+ int pgrp;
+
+ rb_secure(2);
+#if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
+ pgrp = getpgrp();
+ if (pgrp < 0) rb_sys_fail(0);
+ return INT2FIX(pgrp);
+#else
+# ifdef HAVE_GETPGID
+ pgrp = getpgid(0);
+ if (pgrp < 0) rb_sys_fail(0);
+ return INT2FIX(pgrp);
+# else
+ rb_notimplement();
+# endif
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.setpgrp => 0
+ *
+ * Equivalent to <code>setpgid(0,0)</code>. Not available on all
+ * platforms.
+ */
+
+static VALUE
+proc_setpgrp()
+{
+ rb_secure(2);
+ /* check for posix setpgid() first; this matches the posix */
+ /* getpgrp() above. It appears that configure will set SETPGRP_VOID */
+ /* even though setpgrp(0,0) would be prefered. The posix call avoids */
+ /* this confusion. */
+#ifdef HAVE_SETPGID
+ if (setpgid(0,0) < 0) rb_sys_fail(0);
+#elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
+ if (setpgrp() < 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(0);
+}
+
+
+/*
+ * call-seq:
+ * Process.getpgid(pid) => integer
+ *
+ * Returns the process group ID for the given process id. Not
+ * available on all platforms.
+ *
+ * Process.getpgid(Process.ppid()) #=> 25527
+ */
+
+static VALUE
+proc_getpgid(obj, pid)
+ VALUE obj, pid;
+{
+#if defined(HAVE_GETPGID) && !defined(__CHECKER__)
+ int i;
+
+ rb_secure(2);
+ i = getpgid(NUM2INT(pid));
+ if (i < 0) rb_sys_fail(0);
+ return INT2NUM(i);
+#else
+ rb_notimplement();
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.setpgid(pid, integer) => 0
+ *
+ * Sets the process group ID of _pid_ (0 indicates this
+ * process) to <em>integer</em>. Not available on all platforms.
+ */
+
+static VALUE
+proc_setpgid(obj, pid, pgrp)
+ VALUE obj, pid, pgrp;
+{
+#ifdef HAVE_SETPGID
+ int ipid, ipgrp;
+
+ rb_secure(2);
+ ipid = NUM2INT(pid);
+ ipgrp = NUM2INT(pgrp);
+
+ if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0);
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.setsid => fixnum
+ *
+ * Establishes this process as a new session and process group
+ * leader, with no controlling tty. Returns the session id. Not
+ * available on all platforms.
+ *
+ * Process.setsid #=> 27422
+ */
+
+static VALUE
+proc_setsid()
+{
+#if defined(HAVE_SETSID)
+ int pid;
+
+ rb_secure(2);
+ pid = setsid();
+ if (pid < 0) rb_sys_fail(0);
+ return INT2FIX(pid);
+#elif defined(HAVE_SETPGRP) && defined(TIOCNOTTY)
+ rb_pid_t pid;
+ int ret;
+
+ rb_secure(2);
+ pid = getpid();
+#if defined(SETPGRP_VOID)
+ ret = setpgrp();
+ /* If `pid_t setpgrp(void)' is equivalent to setsid(),
+ `ret' will be the same value as `pid', and following open() will fail.
+ In Linux, `int setpgrp(void)' is equivalent to setpgid(0, 0). */
+#else
+ ret = setpgrp(0, pid);
+#endif
+ if (ret == -1) rb_sys_fail(0);
+
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
+ ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
+ }
+ return INT2FIX(pid);
+#else
+ rb_notimplement();
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.getpriority(kind, integer) => fixnum
+ *
+ * Gets the scheduling priority for specified process, process group,
+ * or user. <em>kind</em> indicates the kind of entity to find: one
+ * of <code>Process::PRIO_PGRP</code>,
+ * <code>Process::PRIO_USER</code>, or
+ * <code>Process::PRIO_PROCESS</code>. _integer_ is an id
+ * indicating the particular process, process group, or user (an id
+ * of 0 means _current_). Lower priorities are more favorable
+ * for scheduling. Not available on all platforms.
+ *
+ * Process.getpriority(Process::PRIO_USER, 0) #=> 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ */
+
+static VALUE
+proc_getpriority(obj, which, who)
+ VALUE obj, which, who;
+{
+#ifdef HAVE_GETPRIORITY
+ int prio, iwhich, iwho;
+
+ rb_secure(2);
+ iwhich = NUM2INT(which);
+ iwho = NUM2INT(who);
+
+ errno = 0;
+ prio = getpriority(iwhich, iwho);
+ if (errno) rb_sys_fail(0);
+ return INT2FIX(prio);
+#else
+ rb_notimplement();
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.setpriority(kind, integer, priority) => 0
+ *
+ * See <code>Process#getpriority</code>.
+ *
+ * Process.setpriority(Process::PRIO_USER, 0, 19) #=> 0
+ * Process.setpriority(Process::PRIO_PROCESS, 0, 19) #=> 0
+ * Process.getpriority(Process::PRIO_USER, 0) #=> 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ */
+
+static VALUE
+proc_setpriority(obj, which, who, prio)
+ VALUE obj, which, who, prio;
+{
+#ifdef HAVE_GETPRIORITY
+ int iwhich, iwho, iprio;
+
+ rb_secure(2);
+ iwhich = NUM2INT(which);
+ iwho = NUM2INT(who);
+ iprio = NUM2INT(prio);
+
+ if (setpriority(iwhich, iwho, iprio) < 0)
+ rb_sys_fail(0);
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+#endif
+}
+
+#if SIZEOF_RLIM_T == SIZEOF_INT
+# define RLIM2NUM(v) UINT2NUM(v)
+# define NUM2RLIM(v) NUM2UINT(v)
+#elif SIZEOF_RLIM_T == SIZEOF_LONG
+# define RLIM2NUM(v) ULONG2NUM(v)
+# define NUM2RLIM(v) NUM2ULONG(v)
+#elif SIZEOF_RLIM_T == SIZEOF_LONG_LONG
+# define RLIM2NUM(v) ULL2NUM(v)
+# define NUM2RLIM(v) NUM2ULL(v)
+#endif
+
+/*
+ * call-seq:
+ * Process.getrlimit(resource) => [cur_limit, max_limit]
+ *
+ * Gets the resource limit of the process.
+ * _cur_limit_ means current (soft) limit and
+ * _max_limit_ means maximum (hard) limit.
+ *
+ * _resource_ indicates the kind of resource to limit:
+ * such as <code>Process::RLIMIT_CORE</code>,
+ * <code>Process::RLIMIT_CPU</code>, etc.
+ * See Process.setrlimit for details.
+ *
+ * _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>,
+ * <code>Process::RLIM_SAVED_MAX</code> or
+ * <code>Process::RLIM_SAVED_CUR</code>.
+ * See Process.setrlimit and the system getrlimit(2) manual for details.
+ */
+
+static VALUE
+proc_getrlimit(VALUE obj, VALUE resource)
+{
+#if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
+ struct rlimit rlim;
+
+ rb_secure(2);
+
+ if (getrlimit(NUM2INT(resource), &rlim) < 0) {
+ rb_sys_fail("getrlimit");
+ }
+ return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
+#else
+ rb_notimplement();
+#endif
+}
+
+/*
+ * call-seq:
+ * Process.setrlimit(resource, cur_limit, max_limit) => nil
+ * Process.setrlimit(resource, cur_limit) => nil
+ *
+ * Sets the resource limit of the process.
+ * _cur_limit_ means current (soft) limit and
+ * _max_limit_ means maximum (hard) limit.
+ *
+ * If _max_limit_ is not given, _cur_limit_ is used.
+ *
+ * _resource_ indicates the kind of resource to limit.
+ * The list of resources are OS dependent.
+ * Ruby may support following resources.
+ *
+ * [Process::RLIMIT_CORE] core size (bytes) (SUSv3)
+ * [Process::RLIMIT_CPU] CPU time (seconds) (SUSv3)
+ * [Process::RLIMIT_DATA] data segment (bytes) (SUSv3)
+ * [Process::RLIMIT_FSIZE] file size (bytes) (SUSv3)
+ * [Process::RLIMIT_NOFILE] file descriptors (number) (SUSv3)
+ * [Process::RLIMIT_STACK] stack size (bytes) (SUSv3)
+ * [Process::RLIMIT_AS] total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD but 4.4BSD-Lite)
+ * [Process::RLIMIT_MEMLOCK] total size for mlock(2) (bytes) (4.4BSD, GNU/Linux)
+ * [Process::RLIMIT_NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux)
+ * [Process::RLIMIT_RSS] resident memory size (bytes) (4.2BSD, GNU/Linux)
+ * [Process::RLIMIT_SBSIZE] all socket buffers (bytes) (NetBSD, FreeBSD)
+ *
+ * Other <code>Process::RLIMIT_???</code> constants may be defined.
+ *
+ * _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>,
+ * which means that the resource is not limited.
+ * They may be <code>Process::RLIM_SAVED_MAX</code> or
+ * <code>Process::RLIM_SAVED_CUR</code> too.
+ * See system setrlimit(2) manual for details.
+ *
+ */
+
+static VALUE
+proc_setrlimit(int argc, VALUE *argv, VALUE obj)
+{
+#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
+ VALUE resource, rlim_cur, rlim_max;
+ struct rlimit rlim;
+
+ rb_secure(2);
+
+ rb_scan_args(argc, argv, "21", &resource, &rlim_cur, &rlim_max);
+ if (rlim_max == Qnil)
+ rlim_max = rlim_cur;
+
+ rlim.rlim_cur = NUM2RLIM(rlim_cur);
+ rlim.rlim_max = NUM2RLIM(rlim_max);
+
+ if (setrlimit(NUM2INT(resource), &rlim) < 0) {
+ rb_sys_fail("setrlimit");
+ }
+ return Qnil;
+#else
+ rb_notimplement();
+#endif
+}
+
+static int under_uid_switch = 0;
+static void
+check_uid_switch()
+{
+ rb_secure(2);
+ if (under_uid_switch) {
+ rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
+ }
+}
+
+static int under_gid_switch = 0;
+static void
+check_gid_switch()
+{
+ rb_secure(2);
+ if (under_gid_switch) {
+ rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
+ }
+}
+
+
+/*********************************************************************
+ * Document-class: Process::Sys
+ *
+ * The <code>Process::Sys</code> module contains UID and GID
+ * functions which provide direct bindings to the system calls of the
+ * same names instead of the more-portable versions of the same
+ * functionality found in the <code>Process</code>,
+ * <code>Process::UID</code>, and <code>Process::GID</code> modules.
+ */
+
+
+/*
+ * call-seq:
+ * Process::Sys.setuid(integer) => nil
+ *
+ * Set the user ID of the current process to _integer_. Not
+ * available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setuid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETUID
+ check_uid_switch();
+ if (setuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+
+/*
+ * call-seq:
+ * Process::Sys.setruid(integer) => nil
+ *
+ * Set the real user ID of the calling process to _integer_.
+ * Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setruid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETRUID
+ check_uid_switch();
+ if (setruid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.seteuid(integer) => nil
+ *
+ * Set the effective user ID of the calling process to
+ * _integer_. Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_seteuid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETEUID
+ check_uid_switch();
+ if (seteuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setreuid(rid, eid) => nil
+ *
+ * Sets the (integer) real and/or effective user IDs of the current
+ * process to _rid_ and _eid_, respectively. A value of
+ * <code>-1</code> for either means to leave that ID unchanged. Not
+ * available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setreuid(obj, rid, eid)
+ VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREUID
+ check_uid_switch();
+ if (setreuid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setresuid(rid, eid, sid) => nil
+ *
+ * Sets the (integer) real, effective, and saved user IDs of the
+ * current process to _rid_, _eid_, and _sid_ respectively. A
+ * value of <code>-1</code> for any value means to
+ * leave that ID unchanged. Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setresuid(obj, rid, eid, sid)
+ VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESUID
+ check_uid_switch();
+ if (setresuid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process.uid => fixnum
+ * Process::UID.rid => fixnum
+ * Process::Sys.getuid => fixnum
+ *
+ * Returns the (real) user ID of this process.
+ *
+ * Process.uid #=> 501
+ */
+
+static VALUE
+proc_getuid(obj)
+ VALUE obj;
+{
+ int uid = getuid();
+ return INT2FIX(uid);
+}
+
+
+/*
+ * call-seq:
+ * Process.uid= integer => numeric
+ *
+ * Sets the (integer) user ID for this process. Not available on all
+ * platforms.
+ */
+
+static VALUE
+proc_setuid(obj, id)
+ VALUE obj, id;
+{
+ int uid = NUM2INT(id);
+
+ check_uid_switch();
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETREUID
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETRUID
+ if (setruid(uid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETUID
+ {
+ if (geteuid() == uid) {
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
+ }
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(uid);
+}
+
+
+/********************************************************************
+ *
+ * Document-class: Process::UID
+ *
+ * The <code>Process::UID</code> module contains a collection of
+ * module functions which can be used to portably get, set, and
+ * switch the current process's real, effective, and saved user IDs.
+ *
+ */
+
+static int SAVED_USER_ID = -1;
+
+#ifdef BROKEN_SETREUID
+int
+setreuid(ruid, euid)
+ rb_uid_t ruid, euid;
+{
+ if (ruid != -1 && ruid != getuid()) {
+ if (euid == -1) euid = geteuid();
+ if (setuid(ruid) < 0) return -1;
+ }
+ if (euid != -1 && euid != geteuid()) {
+ if (seteuid(euid) < 0) return -1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * call-seq:
+ * Process::UID.change_privilege(integer) => fixnum
+ *
+ * Change the current process's real and effective user ID to that
+ * specified by _integer_. Returns the new user ID. Not
+ * available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 0]
+ * Process::UID.change_privilege(31) #=> 31
+ * [Process.uid, Process.euid] #=> [31, 31]
+ */
+
+static VALUE
+p_uid_change_privilege(obj, id)
+ VALUE obj, id;
+{
+ int uid;
+
+ check_uid_switch();
+
+ uid = NUM2INT(id);
+
+ if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESUID)
+ if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETUID)
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (setreuid(-1, uid) < 0) rb_sys_fail(0);
+ } else {
+ if (uid == 0) { /* (r,e,s) == (root, root, x) */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ if (setreuid(0, -1) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ } else {
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ if (uid == 0) {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setruid(0) < 0) rb_sys_fail(0);
+ } else {
+ if (setruid(0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ } else {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+#else
+ rb_notimplement();
+#endif
+ } else { /* unprivileged user */
+#if defined(HAVE_SETRESUID)
+ if (setresuid((getuid() == uid)? -1: uid,
+ (geteuid() == uid)? -1: uid,
+ (SAVED_USER_ID == uid)? -1: uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (SAVED_USER_ID == uid) {
+ if (setreuid((getuid() == uid)? -1: uid,
+ (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+ } else if (getuid() != uid) {
+ if (setreuid(uid, (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else if (/* getuid() == uid && */ geteuid() != uid) {
+ if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ } else { /* getuid() == uid && geteuid() == uid */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
+ if (SAVED_USER_ID == uid) {
+ if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
+ if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
+ } else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
+ if (getuid() != uid) {
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ }
+ } else if (/* geteuid() != uid && */ getuid() == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_44BSD_SETUID
+ if (getuid() == uid) {
+ /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETEUID
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETUID
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ rb_notimplement();
+#endif
+ }
+ return INT2FIX(uid);
+}
+
+
+
+/*
+ * call-seq:
+ * Process::Sys.setgid(integer) => nil
+ *
+ * Set the group ID of the current process to _integer_. Not
+ * available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setgid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETGID
+ check_gid_switch();
+ if (setgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setrgid(integer) => nil
+ *
+ * Set the real group ID of the calling process to _integer_.
+ * Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setrgid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETRGID
+ check_gid_switch();
+ if (setrgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+
+/*
+ * call-seq:
+ * Process::Sys.setegid(integer) => nil
+ *
+ * Set the effective group ID of the calling process to
+ * _integer_. Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setegid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETEGID
+ check_gid_switch();
+ if (setegid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setregid(rid, eid) => nil
+ *
+ * Sets the (integer) real and/or effective group IDs of the current
+ * process to <em>rid</em> and <em>eid</em>, respectively. A value of
+ * <code>-1</code> for either means to leave that ID unchanged. Not
+ * available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setregid(obj, rid, eid)
+ VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREGID
+ check_gid_switch();
+ if (setregid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Process::Sys.setresgid(rid, eid, sid) => nil
+ *
+ * Sets the (integer) real, effective, and saved user IDs of the
+ * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
+ * respectively. A value of <code>-1</code> for any value means to
+ * leave that ID unchanged. Not available on all platforms.
+ *
+ */
+
+static VALUE
+p_sys_setresgid(obj, rid, eid, sid)
+ VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESGID
+ check_gid_switch();
+ if (setresgid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Process::Sys.issetugid => true or false
+ *
+ * Returns +true+ if the process was created as a result
+ * of an execve(2) system call which had either of the setuid or
+ * setgid bits set (and extra privileges were given as a result) or
+ * if it has changed any of its real, effective or saved user or
+ * group IDs since it began execution.
+ *
+ */
+
+static VALUE
+p_sys_issetugid(obj)
+ VALUE obj;
+{
+#if defined HAVE_ISSETUGID
+ rb_secure(2);
+ if (issetugid()) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.gid => fixnum
+ * Process::GID.rid => fixnum
+ * Process::Sys.getgid => fixnum
+ *
+ * Returns the (real) group ID for this process.
+ *
+ * Process.gid #=> 500
+ */
+
+static VALUE
+proc_getgid(obj)
+ VALUE obj;
+{
+ int gid = getgid();
+ return INT2FIX(gid);
+}
+
+
+/*
+ * call-seq:
+ * Process.gid= fixnum => fixnum
+ *
+ * Sets the group ID for this process.
+ */
+
+static VALUE
+proc_setgid(obj, id)
+ VALUE obj, id;
+{
+ int gid = NUM2INT(id);
+
+ check_gid_switch();
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETREGID
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETRGID
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETGID
+ {
+ if (getegid() == gid) {
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
+ }
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(gid);
+}
+
+
+static size_t maxgroups = 32;
+
+
+/*
+ * call-seq:
+ * Process.groups => array
+ *
+ * Get an <code>Array</code> of the gids of groups in the
+ * supplemental group access list for this process.
+ *
+ * Process.groups #=> [27, 6, 10, 11]
+ *
+ */
+
+static VALUE
+proc_getgroups(VALUE obj)
+{
+#ifdef HAVE_GETGROUPS
+ VALUE ary;
+ size_t ngroups;
+ rb_gid_t *groups;
+ int i;
+
+ groups = ALLOCA_N(rb_gid_t, maxgroups);
+
+ ngroups = getgroups(maxgroups, groups);
+ if (ngroups == -1)
+ rb_sys_fail(0);
+
+ ary = rb_ary_new();
+ for (i = 0; i < ngroups; i++)
+ rb_ary_push(ary, INT2NUM(groups[i]));
+
+ return ary;
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.groups= array => array
+ *
+ * Set the supplemental group access list to the given
+ * <code>Array</code> of group IDs.
+ *
+ * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.groups = [27, 6, 10, 11] #=> [27, 6, 10, 11]
+ * Process.groups #=> [27, 6, 10, 11]
+ *
+ */
+
+static VALUE
+proc_setgroups(VALUE obj, VALUE ary)
+{
+#ifdef HAVE_SETGROUPS
+ size_t ngroups;
+ rb_gid_t *groups;
+ int i;
+ struct group *gr;
+
+ Check_Type(ary, T_ARRAY);
+
+ ngroups = RARRAY(ary)->len;
+ if (ngroups > maxgroups)
+ rb_raise(rb_eArgError, "too many groups, %d max", maxgroups);
+
+ groups = ALLOCA_N(rb_gid_t, ngroups);
+
+ for (i = 0; i < ngroups && i < RARRAY(ary)->len; i++) {
+ VALUE g = RARRAY(ary)->ptr[i];
+
+ if (FIXNUM_P(g)) {
+ groups[i] = FIX2INT(g);
+ }
+ else {
+ VALUE tmp = rb_check_string_type(g);
+
+ if (NIL_P(tmp)) {
+ groups[i] = NUM2INT(g);
+ }
+ else {
+ gr = getgrnam(RSTRING(tmp)->ptr);
+ if (gr == NULL)
+ rb_raise(rb_eArgError,
+ "can't find group for %s", RSTRING(tmp)->ptr);
+ groups[i] = gr->gr_gid;
+ }
+ }
+ }
+
+ i = setgroups(ngroups, groups);
+ if (i == -1)
+ rb_sys_fail(0);
+
+ return proc_getgroups(obj);
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.initgroups(username, gid) => array
+ *
+ * Initializes the supplemental group access list by reading the
+ * system group database and using all groups of which the given user
+ * is a member. The group with the specified <em>gid</em> is also
+ * added to the list. Returns the resulting <code>Array</code> of the
+ * gids of all the groups in the supplementary group access list. Not
+ * available on all platforms.
+ *
+ * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.initgroups( "mgranger", 30 ) #=> [30, 6, 10, 11]
+ * Process.groups #=> [30, 6, 10, 11]
+ *
+ */
+
+static VALUE
+proc_initgroups(obj, uname, base_grp)
+ VALUE obj, uname, base_grp;
+{
+#ifdef HAVE_INITGROUPS
+ if (initgroups(StringValuePtr(uname), (rb_gid_t)NUM2INT(base_grp)) != 0) {
+ rb_sys_fail(0);
+ }
+ return proc_getgroups(obj);
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.maxgroups => fixnum
+ *
+ * Returns the maximum number of gids allowed in the supplemental
+ * group access list.
+ *
+ * Process.maxgroups #=> 32
+ */
+
+static VALUE
+proc_getmaxgroups(obj)
+ VALUE obj;
+{
+ return INT2FIX(maxgroups);
+}
+
+
+/*
+ * call-seq:
+ * Process.maxgroups= fixnum => fixnum
+ *
+ * Sets the maximum number of gids allowed in the supplemental group
+ * access list.
+ */
+
+static VALUE
+proc_setmaxgroups(VALUE obj, VALUE val)
+{
+ size_t ngroups = FIX2INT(val);
+
+ if (ngroups > 4096)
+ ngroups = 4096;
+
+ maxgroups = ngroups;
+
+ return INT2FIX(maxgroups);
+}
+
+
+/********************************************************************
+ *
+ * Document-class: Process::GID
+ *
+ * The <code>Process::GID</code> module contains a collection of
+ * module functions which can be used to portably get, set, and
+ * switch the current process's real, effective, and saved group IDs.
+ *
+ */
+
+static int SAVED_GROUP_ID = -1;
+
+#ifdef BROKEN_SETREGID
+int
+setregid(rgid, egid)
+ rb_gid_t rgid, egid;
+{
+ if (rgid != -1 && rgid != getgid()) {
+ if (egid == -1) egid = getegid();
+ if (setgid(rgid) < 0) return -1;
+ }
+ if (egid != -1 && egid != getegid()) {
+ if (setegid(egid) < 0) return -1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * call-seq:
+ * Process::GID.change_privilege(integer) => fixnum
+ *
+ * Change the current process's real and effective group ID to that
+ * specified by _integer_. Returns the new group ID. Not
+ * available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 0]
+ * Process::GID.change_privilege(33) #=> 33
+ * [Process.gid, Process.egid] #=> [33, 33]
+ */
+
+static VALUE
+p_gid_change_privilege(obj, id)
+ VALUE obj, id;
+{
+ int gid;
+
+ check_gid_switch();
+
+ gid = NUM2INT(id);
+
+ if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESGID)
+ if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETGID
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid(-1, gid) < 0) rb_sys_fail(0);
+ } else {
+ if (gid == 0) { /* (r,e,s) == (root, y, x) */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else { /* (r,e,s) == (z, y, x) */
+ if (setregid(0, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ } else {
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ } else {
+ if (gid == 0) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ } else {
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ } else {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+#else
+ rb_notimplement();
+#endif
+ } else { /* unprivileged user */
+#if defined(HAVE_SETRESGID)
+ if (setresgid((getgid() == gid)? -1: gid,
+ (getegid() == gid)? -1: gid,
+ (SAVED_GROUP_ID == gid)? -1: gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid((getgid() == gid)? -1: gid,
+ (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+ } else if (getgid() != gid) {
+ if (setregid(gid, (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else if (/* getgid() == gid && */ getegid() != gid) {
+ if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ } else { /* getgid() == gid && getegid() == gid */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
+ if (SAVED_GROUP_ID == gid) {
+ if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
+ if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
+ } else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
+ if (getgid() != gid) {
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else {
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ }
+ } else if (/* getegid() != gid && */ getgid() == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_44BSD_SETGID
+ if (getgid() == gid) {
+ /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETEGID
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETGID
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ rb_notimplement();
+#endif
+ }
+ return INT2FIX(gid);
+}
+
+
+/*
+ * call-seq:
+ * Process.euid => fixnum
+ * Process::UID.eid => fixnum
+ * Process::Sys.geteuid => fixnum
+ *
+ * Returns the effective user ID for this process.
+ *
+ * Process.euid #=> 501
+ */
+
+static VALUE
+proc_geteuid(obj)
+ VALUE obj;
+{
+ int euid = geteuid();
+ return INT2FIX(euid);
+}
+
+
+/*
+ * call-seq:
+ * Process.euid= integer
+ *
+ * Sets the effective user ID for this process. Not available on all
+ * platforms.
+ */
+
+static VALUE
+proc_seteuid(obj, euid)
+ VALUE obj, euid;
+{
+ check_uid_switch();
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (setresuid(-1, NUM2INT(euid), -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETREUID
+ if (setreuid(-1, NUM2INT(euid)) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETEUID
+ if (seteuid(NUM2INT(euid)) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETUID
+ euid = NUM2INT(euid);
+ if (euid == getuid()) {
+ if (setuid(euid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
+#else
+ rb_notimplement();
+#endif
+ return euid;
+}
+
+static VALUE
+rb_seteuid_core(euid)
+ int euid;
+{
+ int uid;
+
+ check_uid_switch();
+
+ uid = getuid();
+
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (uid != euid) {
+ if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
+ } else {
+ if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (setreuid(-1, euid) < 0) rb_sys_fail(0);
+ if (uid != euid) {
+ if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+ if (setreuid(uid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
+ }
+#elif defined HAVE_SETEUID
+ if (seteuid(euid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETUID
+ if (geteuid() == 0) rb_sys_fail(0);
+ if (setuid(euid) < 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(euid);
+}
+
+
+/*
+ * call-seq:
+ * Process::UID.grant_privilege(integer) => fixnum
+ * Process::UID.eid= integer => fixnum
+ *
+ * Set the effective user ID, and if possible, the saved user ID of
+ * the process to the given _integer_. Returns the new
+ * effective user ID. Not available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 0]
+ * Process::UID.grant_privilege(31) #=> 31
+ * [Process.uid, Process.euid] #=> [0, 31]
+ */
+
+static VALUE
+p_uid_grant_privilege(obj, id)
+ VALUE obj, id;
+{
+ return rb_seteuid_core(NUM2INT(id));
+}
+
+
+/*
+ * call-seq:
+ * Process.egid => fixnum
+ * Process::GID.eid => fixnum
+ * Process::Sys.geteid => fixnum
+ *
+ * Returns the effective group ID for this process. Not available on
+ * all platforms.
+ *
+ * Process.egid #=> 500
+ */
+
+static VALUE
+proc_getegid(obj)
+ VALUE obj;
+{
+ int egid = getegid();
+
+ return INT2FIX(egid);
+}
+
+
+/*
+ * call-seq:
+ * Process.egid = fixnum => fixnum
+ *
+ * Sets the effective group ID for this process. Not available on all
+ * platforms.
+ */
+
+static VALUE
+proc_setegid(obj, egid)
+ VALUE obj, egid;
+{
+ check_gid_switch();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (setresgid(-1, NUM2INT(egid), -1) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETREGID
+ if (setregid(-1, NUM2INT(egid)) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETEGID
+ if (setegid(NUM2INT(egid)) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETGID
+ egid = NUM2INT(egid);
+ if (egid == getgid()) {
+ if (setgid(egid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
+#else
+ rb_notimplement();
+#endif
+ return egid;
+}
+
+static VALUE
+rb_setegid_core(egid)
+ int egid;
+{
+ int gid;
+
+ check_gid_switch();
+
+ gid = getgid();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (gid != egid) {
+ if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
+ } else {
+ if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (setregid(-1, egid) < 0) rb_sys_fail(0);
+ if (gid != egid) {
+ if (setregid(egid,gid) < 0) rb_sys_fail(0);
+ if (setregid(gid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
+ }
+#elif defined HAVE_SETEGID
+ if (setegid(egid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETGID
+ if (geteuid() == 0 /* root user */) rb_sys_fail(0);
+ if (setgid(egid) < 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(egid);
+}
+
+
+/*
+ * call-seq:
+ * Process::GID.grant_privilege(integer) => fixnum
+ * Process::GID.eid = integer => fixnum
+ *
+ * Set the effective group ID, and if possible, the saved group ID of
+ * the process to the given _integer_. Returns the new
+ * effective group ID. Not available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 0]
+ * Process::GID.grant_privilege(31) #=> 33
+ * [Process.gid, Process.egid] #=> [0, 33]
+ */
+
+static VALUE
+p_gid_grant_privilege(obj, id)
+ VALUE obj, id;
+{
+ return rb_setegid_core(NUM2INT(id));
+}
+
+
+/*
+ * call-seq:
+ * Process::UID.re_exchangeable? => true or false
+ *
+ * Returns +true+ if the real and effective user IDs of a
+ * process may be exchanged on the current platform.
+ *
+ */
+
+static VALUE
+p_uid_exchangeable()
+{
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ return Qtrue;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process::UID.re_exchange => fixnum
+ *
+ * Exchange real and effective user IDs and return the new effective
+ * user ID. Not available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 31]
+ * Process::UID.re_exchange #=> 0
+ * [Process.uid, Process.euid] #=> [31, 0]
+ */
+
+static VALUE
+p_uid_exchange(obj)
+ VALUE obj;
+{
+ int uid, euid;
+
+ check_uid_switch();
+
+ uid = getuid();
+ euid = geteuid();
+
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(uid);
+}
+
+
+/*
+ * call-seq:
+ * Process::GID.re_exchangeable? => true or false
+ *
+ * Returns +true+ if the real and effective group IDs of a
+ * process may be exchanged on the current platform.
+ *
+ */
+
+static VALUE
+p_gid_exchangeable()
+{
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ return Qtrue;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process::GID.re_exchange => fixnum
+ *
+ * Exchange real and effective group IDs and return the new effective
+ * group ID. Not available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 33]
+ * Process::GID.re_exchange #=> 0
+ * [Process.gid, Process.egid] #=> [33, 0]
+ */
+
+static VALUE
+p_gid_exchange(obj)
+ VALUE obj;
+{
+ int gid, egid;
+
+ check_gid_switch();
+
+ gid = getgid();
+ egid = getegid();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (setregid(egid,gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(gid);
+}
+
+/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
+
+/*
+ * call-seq:
+ * Process::UID.sid_available? => true or false
+ *
+ * Returns +true+ if the current platform has saved user
+ * ID functionality.
+ *
+ */
+
+static VALUE
+p_uid_have_saved_id()
+{
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
+static VALUE
+p_uid_sw_ensure(id)
+ int id;
+{
+ under_uid_switch = 0;
+ return rb_seteuid_core(id);
+}
+
+
+/*
+ * call-seq:
+ * Process::UID.switch => fixnum
+ * Process::UID.switch {|| block} => object
+ *
+ * Switch the effective and real user IDs of the current process. If
+ * a <em>block</em> is given, the user IDs will be switched back
+ * after the block is executed. Returns the new effective user ID if
+ * called without a block, and the return value of the block if one
+ * is given.
+ *
+ */
+
+static VALUE
+p_uid_switch(obj)
+ VALUE obj;
+{
+ int uid, euid;
+
+ check_uid_switch();
+
+ uid = getuid();
+ euid = geteuid();
+
+ if (uid != euid) {
+ proc_seteuid(obj, INT2FIX(uid));
+ if (rb_block_given_p()) {
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
+ } else {
+ return INT2FIX(euid);
+ }
+ } else if (euid != SAVED_USER_ID) {
+ proc_seteuid(obj, INT2FIX(SAVED_USER_ID));
+ if (rb_block_given_p()) {
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
+ } else {
+ return INT2FIX(uid);
+ }
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+
+#else
+static VALUE
+p_uid_sw_ensure(obj)
+ VALUE obj;
+{
+ under_uid_switch = 0;
+ return p_uid_exchange(obj);
+}
+
+static VALUE
+p_uid_switch(obj)
+ VALUE obj;
+{
+ int uid, euid;
+
+ check_uid_switch();
+
+ uid = getuid();
+ euid = geteuid();
+
+ if (uid == euid) {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+ p_uid_exchange(obj);
+ if (rb_block_given_p()) {
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
+ } else {
+ return INT2FIX(euid);
+ }
+#endif
+}
+
+
+/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
+
+/*
+ * call-seq:
+ * Process::GID.sid_available? => true or false
+ *
+ * Returns +true+ if the current platform has saved group
+ * ID functionality.
+ *
+ */
+
+static VALUE
+p_gid_have_saved_id()
+{
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
+static VALUE
+p_gid_sw_ensure(id)
+ int id;
+{
+ under_gid_switch = 0;
+ return rb_setegid_core(id);
+}
+
+
+/*
+ * call-seq:
+ * Process::GID.switch => fixnum
+ * Process::GID.switch {|| block} => object
+ *
+ * Switch the effective and real group IDs of the current process. If
+ * a <em>block</em> is given, the group IDs will be switched back
+ * after the block is executed. Returns the new effective group ID if
+ * called without a block, and the return value of the block if one
+ * is given.
+ *
+ */
+
+static VALUE
+p_gid_switch(obj)
+ VALUE obj;
+{
+ int gid, egid;
+
+ check_gid_switch();
+
+ gid = getgid();
+ egid = getegid();
+
+ if (gid != egid) {
+ proc_setegid(obj, INT2FIX(gid));
+ if (rb_block_given_p()) {
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
+ } else {
+ return INT2FIX(egid);
+ }
+ } else if (egid != SAVED_GROUP_ID) {
+ proc_setegid(obj, INT2FIX(SAVED_GROUP_ID));
+ if (rb_block_given_p()) {
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
+ } else {
+ return INT2FIX(gid);
+ }
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+static VALUE
+p_gid_sw_ensure(obj)
+ VALUE obj;
+{
+ under_gid_switch = 0;
+ return p_gid_exchange(obj);
+}
+
+static VALUE
+p_gid_switch(obj)
+ VALUE obj;
+{
+ int gid, egid;
+
+ check_gid_switch();
+
+ gid = getgid();
+ egid = getegid();
+
+ if (gid == egid) {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+ p_gid_exchange(obj);
+ if (rb_block_given_p()) {
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
+ } else {
+ return INT2FIX(egid);
+ }
+#endif
+}
+
+
+/*
+ * call-seq:
+ * Process.times => aStructTms
+ *
+ * Returns a <code>Tms</code> structure (see <code>Struct::Tms</code>
+ * on page 388) that contains user and system CPU times for this
+ * process.
+ *
+ * t = Process.times
+ * [ t.utime, t.stime ] #=> [0.0, 0.02]
+ */
+
+VALUE
+rb_proc_times(obj)
+ VALUE obj;
+{
+#if defined(HAVE_TIMES) && !defined(__CHECKER__)
+ const double hertz =
+#ifdef HAVE__SC_CLK_TCK
+ (double)sysconf(_SC_CLK_TCK);
+#else
+#ifndef HZ
+# ifdef CLK_TCK
+# define HZ CLK_TCK
+# else
+# define HZ 60
+# endif
+#endif /* HZ */
+ HZ;
+#endif
+ struct tms buf;
+ volatile VALUE utime, stime, cutime, sctime;
+
+ times(&buf);
+ return rb_struct_new(S_Tms,
+ utime = rb_float_new(buf.tms_utime / hertz),
+ stime = rb_float_new(buf.tms_stime / hertz),
+ cutime = rb_float_new(buf.tms_cutime / hertz),
+ sctime = rb_float_new(buf.tms_cstime / hertz));
+#else
+ rb_notimplement();
+#endif
+}
+
+VALUE rb_mProcess;
+VALUE rb_mProcUID;
+VALUE rb_mProcGID;
+VALUE rb_mProcID_Syscall;
+
+
+/*
+ * The <code>Process</code> module is a collection of methods used to
+ * manipulate processes.
+ */
+
+void
+Init_process()
+{
+ rb_define_virtual_variable("$$", get_pid, 0);
+ rb_define_readonly_variable("$?", &rb_last_status);
+ rb_define_global_function("exec", rb_f_exec, -1);
+ rb_define_global_function("fork", rb_f_fork, 0);
+ rb_define_global_function("exit!", rb_f_exit_bang, -1);
+ rb_define_global_function("system", rb_f_system, -1);
+ rb_define_global_function("sleep", rb_f_sleep, -1);
+
+ rb_mProcess = rb_define_module("Process");
+
+#if !defined(_WIN32) && !defined(DJGPP)
+#ifdef WNOHANG
+ rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(WNOHANG));
+#else
+ rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(0));
+#endif
+#ifdef WUNTRACED
+ rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
+#else
+ rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(0));
+#endif
+#endif
+
+ rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
+ rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
+ rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1); /* in eval.c */
+ rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1); /* in eval.c */
+
+ rb_define_module_function(rb_mProcess, "kill", rb_f_kill, -1); /* in signal.c */
+ rb_define_module_function(rb_mProcess, "wait", proc_wait, -1);
+ rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
+ rb_define_module_function(rb_mProcess, "waitpid", proc_wait, -1);
+ rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
+ rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
+ rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
+
+ rb_cProcStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
+ rb_undef_method(CLASS_OF(rb_cProcStatus), "new");
+
+ rb_define_method(rb_cProcStatus, "==", pst_equal, 1);
+ rb_define_method(rb_cProcStatus, "&", pst_bitand, 1);
+ rb_define_method(rb_cProcStatus, ">>", pst_rshift, 1);
+ rb_define_method(rb_cProcStatus, "to_i", pst_to_i, 0);
+ rb_define_method(rb_cProcStatus, "to_int", pst_to_i, 0);
+ rb_define_method(rb_cProcStatus, "to_s", pst_to_s, 0);
+ rb_define_method(rb_cProcStatus, "inspect", pst_inspect, 0);
+
+ rb_define_method(rb_cProcStatus, "pid", pst_pid, 0);
+
+ rb_define_method(rb_cProcStatus, "stopped?", pst_wifstopped, 0);
+ rb_define_method(rb_cProcStatus, "stopsig", pst_wstopsig, 0);
+ rb_define_method(rb_cProcStatus, "signaled?", pst_wifsignaled, 0);
+ rb_define_method(rb_cProcStatus, "termsig", pst_wtermsig, 0);
+ rb_define_method(rb_cProcStatus, "exited?", pst_wifexited, 0);
+ rb_define_method(rb_cProcStatus, "exitstatus", pst_wexitstatus, 0);
+ rb_define_method(rb_cProcStatus, "success?", pst_success_p, 0);
+ rb_define_method(rb_cProcStatus, "coredump?", pst_wcoredump, 0);
+
+ rb_define_module_function(rb_mProcess, "pid", get_pid, 0);
+ rb_define_module_function(rb_mProcess, "ppid", get_ppid, 0);
+
+ rb_define_module_function(rb_mProcess, "getpgrp", proc_getpgrp, 0);
+ rb_define_module_function(rb_mProcess, "setpgrp", proc_setpgrp, 0);
+ rb_define_module_function(rb_mProcess, "getpgid", proc_getpgid, 1);
+ rb_define_module_function(rb_mProcess, "setpgid", proc_setpgid, 2);
+
+ rb_define_module_function(rb_mProcess, "setsid", proc_setsid, 0);
+
+ rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
+ rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
+
+#ifdef HAVE_GETPRIORITY
+ rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
+ rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
+ rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
+#endif
+
+ rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
+ rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, -1);
+#ifdef RLIM2NUM
+#ifdef RLIM_INFINITY
+ rb_define_const(rb_mProcess, "RLIM_INFINITY", RLIM2NUM(RLIM_INFINITY));
+#endif
+#ifdef RLIM_SAVED_MAX
+ rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", RLIM2NUM(RLIM_SAVED_MAX));
+#endif
+#ifdef RLIM_SAVED_CUR
+ rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", RLIM2NUM(RLIM_SAVED_CUR));
+#endif
+#ifdef RLIMIT_CORE
+ rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
+#endif
+#ifdef RLIMIT_CPU
+ rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
+#endif
+#ifdef RLIMIT_DATA
+ rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
+#endif
+#ifdef RLIMIT_FSIZE
+ rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
+#endif
+#ifdef RLIMIT_NOFILE
+ rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
+#endif
+#ifdef RLIMIT_STACK
+ rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
+#endif
+#ifdef RLIMIT_AS
+ rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
+#endif
+#ifdef RLIMIT_MEMLOCK
+ rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
+#endif
+#ifdef RLIMIT_NPROC
+ rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
+#endif
+#ifdef RLIMIT_RSS
+ rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
+#endif
+#ifdef RLIMIT_SBSIZE
+ rb_define_const(rb_mProcess, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE));
+#endif
+#endif
+
+ rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
+ rb_define_module_function(rb_mProcess, "uid=", proc_setuid, 1);
+ rb_define_module_function(rb_mProcess, "gid", proc_getgid, 0);
+ rb_define_module_function(rb_mProcess, "gid=", proc_setgid, 1);
+ rb_define_module_function(rb_mProcess, "euid", proc_geteuid, 0);
+ rb_define_module_function(rb_mProcess, "euid=", proc_seteuid, 1);
+ rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
+ rb_define_module_function(rb_mProcess, "egid=", proc_setegid, 1);
+ rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
+ rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
+ rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
+ rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
+ rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
+
+ rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
+
+#if defined(HAVE_TIMES) || defined(_WIN32)
+ S_Tms = rb_struct_define("Tms", "utime", "stime", "cutime", "cstime", NULL);
+#endif
+
+ SAVED_USER_ID = geteuid();
+ SAVED_GROUP_ID = getegid();
+
+ rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
+ rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
+
+ rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
+ rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
+ rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
+ rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
+ rb_define_module_function(rb_mProcUID, "change_privilege", p_uid_change_privilege, 1);
+ rb_define_module_function(rb_mProcGID, "change_privilege", p_gid_change_privilege, 1);
+ rb_define_module_function(rb_mProcUID, "grant_privilege", p_uid_grant_privilege, 1);
+ rb_define_module_function(rb_mProcGID, "grant_privilege", p_gid_grant_privilege, 1);
+ rb_define_alias(rb_singleton_class(rb_mProcUID), "eid=", "grant_privilege");
+ rb_define_alias(rb_singleton_class(rb_mProcGID), "eid=", "grant_privilege");
+ rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
+ rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
+ rb_define_module_function(rb_mProcUID, "re_exchangeable?", p_uid_exchangeable, 0);
+ rb_define_module_function(rb_mProcGID, "re_exchangeable?", p_gid_exchangeable, 0);
+ rb_define_module_function(rb_mProcUID, "sid_available?", p_uid_have_saved_id, 0);
+ rb_define_module_function(rb_mProcGID, "sid_available?", p_gid_have_saved_id, 0);
+ rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
+ rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
+
+ rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
+
+ rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setreuid", p_sys_setreuid, 2);
+ rb_define_module_function(rb_mProcID_Syscall, "setregid", p_sys_setregid, 2);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setresuid", p_sys_setresuid, 3);
+ rb_define_module_function(rb_mProcID_Syscall, "setresgid", p_sys_setresgid, 3);
+ rb_define_module_function(rb_mProcID_Syscall, "issetugid", p_sys_issetugid, 0);
+}
+/**********************************************************************
+
+ random.c -
+
+ $Author: shyouhei $
+ $Date: 2009-01-17 04:15:36 +0100 (Sat, 17 Jan 2009) $
+ created at: Fri Dec 24 16:39:21 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+/*
+This is based on trimmed version of MT19937. To get the original version,
+contact <http://www.math.keio.ac.jp/~matumoto/emt.html>.
+
+The original copyright notice follows.
+
+ A C-program for MT19937, with initialization improved 2002/2/10.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+ This is a faster version by taking Shawn Cokus's optimization,
+ Matthe Bellew's simplification, Isaku Wada's real version.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.keio.ac.jp/matumoto/emt.html
+ email: matumoto@math.keio.ac.jp
+*/
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UMASK 0x80000000UL /* most significant w-r bits */
+#define LMASK 0x7fffffffUL /* least significant r bits */
+#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
+#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
+
+static unsigned long state[N]; /* the array for the state vector */
+static int left = 1;
+static int initf = 0;
+static unsigned long *next;
+
+/* initializes state[N] with a seed */
+static void
+init_genrand(s)
+ unsigned long s;
+{
+ int j;
+ state[0]= s & 0xffffffffUL;
+ for (j=1; j<N; j++) {
+ state[j] = (1812433253UL * (state[j-1] ^ (state[j-1] >> 30)) + j);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array state[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ state[j] &= 0xffffffffUL; /* for >32 bit machines */
+ }
+ left = 1; initf = 1;
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+static void
+init_by_array(unsigned long init_key[], int key_length)
+{
+ int i, j, k;
+ init_genrand(19650218UL);
+ i=1; j=0;
+ k = (N>key_length ? N : key_length);
+ for (; k; k--) {
+ state[i] = (state[i] ^ ((state[i-1] ^ (state[i-1] >> 30)) * 1664525UL))
+ + init_key[j] + j; /* non linear */
+ state[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++; j++;
+ if (i>=N) { state[0] = state[N-1]; i=1; }
+ if (j>=key_length) j=0;
+ }
+ for (k=N-1; k; k--) {
+ state[i] = (state[i] ^ ((state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL))
+ - i; /* non linear */
+ state[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++;
+ if (i>=N) { state[0] = state[N-1]; i=1; }
+ }
+
+ state[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+ left = 1; initf = 1;
+}
+
+static void
+next_state()
+{
+ unsigned long *p=state;
+ int j;
+
+ /* if init_genrand() has not been called, */
+ /* a default initial seed is used */
+ if (initf==0) init_genrand(5489UL);
+
+ left = N;
+ next = state;
+
+ for (j=N-M+1; --j; p++)
+ *p = p[M] ^ TWIST(p[0], p[1]);
+
+ for (j=M; --j; p++)
+ *p = p[M-N] ^ TWIST(p[0], p[1]);
+
+ *p = p[M-N] ^ TWIST(p[0], state[0]);
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+static unsigned long
+genrand_int32(void)
+{
+ unsigned long y;
+
+ if (--left == 0) next_state();
+ y = *next++;
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+static double
+genrand_real(void)
+{
+ unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+ return(a*67108864.0+b)*(1.0/9007199254740992.0);
+}
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+
+#undef N
+#undef M
+
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+
+#include "ruby.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+static VALUE saved_seed = INT2FIX(0);
+
+static VALUE
+rand_init(vseed)
+ VALUE vseed;
+{
+ volatile VALUE seed;
+ VALUE old;
+ long len;
+ unsigned long *buf;
+
+ seed = rb_to_int(vseed);
+ switch (TYPE(seed)) {
+ case T_FIXNUM:
+ len = sizeof(VALUE);
+ break;
+ case T_BIGNUM:
+ len = RBIGNUM(seed)->len * SIZEOF_BDIGITS;
+ if (len == 0)
+ len = 4;
+ break;
+ default:
+ rb_raise(rb_eTypeError, "failed to convert %s into Integer",
+ rb_obj_classname(vseed));
+ }
+ len = (len + 3) / 4; /* number of 32bit words */
+ buf = ALLOC_N(unsigned long, len); /* allocate longs for init_by_array */
+ memset(buf, 0, len * sizeof(long));
+ if (FIXNUM_P(seed)) {
+ buf[0] = FIX2ULONG(seed) & 0xffffffff;
+#if SIZEOF_LONG > 4
+ buf[1] = FIX2ULONG(seed) >> 32;
+#endif
+ }
+ else {
+ int i, j;
+ for (i = RBIGNUM(seed)->len-1; 0 <= i; i--) {
+ j = i * SIZEOF_BDIGITS / 4;
+#if SIZEOF_BDIGITS < 4
+ buf[j] <<= SIZEOF_BDIGITS * 8;
+#endif
+ buf[j] |= ((BDIGIT *)RBIGNUM(seed)->digits)[i];
+ }
+ }
+ while (1 < len && buf[len-1] == 0) {
+ len--;
+ }
+ if (len <= 1) {
+ init_genrand(buf[0]);
+ }
+ else {
+ if (buf[len-1] == 1) /* remove leading-zero-guard */
+ len--;
+ init_by_array(buf, len);
+ }
+ old = saved_seed;
+ saved_seed = seed;
+ free(buf);
+ return old;
+}
+
+static VALUE
+random_seed()
+{
+ static int n = 0;
+ struct timeval tv;
+ int fd;
+ struct stat statbuf;
+
+ int seed_len;
+ BDIGIT *digits;
+ unsigned long *seed;
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, rb_cBignum, T_BIGNUM);
+
+ seed_len = 4 * sizeof(long);
+ big->sign = 1;
+ big->len = seed_len / SIZEOF_BDIGITS + 1;
+ digits = big->digits = ALLOC_N(BDIGIT, big->len);
+ seed = (unsigned long *)big->digits;
+
+ memset(digits, 0, big->len * SIZEOF_BDIGITS);
+
+#ifdef S_ISCHR
+ if ((fd = open("/dev/urandom", O_RDONLY
+#ifdef O_NONBLOCK
+ |O_NONBLOCK
+#endif
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+ |O_NOFOLLOW
+#endif
+ )) >= 0) {
+ if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
+ read(fd, seed, seed_len);
+ }
+ close(fd);
+ }
+#endif
+
+ gettimeofday(&tv, 0);
+ seed[0] ^= tv.tv_usec;
+ seed[1] ^= tv.tv_sec;
+ seed[2] ^= getpid() ^ (n++ << 16);
+ seed[3] ^= (unsigned long)&seed;
+
+ /* set leading-zero-guard if need. */
+ digits[big->len-1] = digits[big->len-2] <= 1 ? 1 : 0;
+
+ return rb_big_norm((VALUE)big);
+}
+
+/*
+ * call-seq:
+ * srand(number=0) => old_seed
+ *
+ * Seeds the pseudorandom number generator to the value of
+ * <i>number</i>.<code>to_i.abs</code>. If <i>number</i> is omitted,
+ * seeds the generator using a combination of the time, the
+ * process id, and a sequence number. (This is also the behavior if
+ * <code>Kernel::rand</code> is called without previously calling
+ * <code>srand</code>, but without the sequence.) By setting the seed
+ * to a known value, scripts can be made deterministic during testing.
+ * The previous seed value is returned. Also see <code>Kernel::rand</code>.
+ */
+
+static VALUE
+rb_f_srand(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE seed, old;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "01", &seed) == 0) {
+ seed = random_seed();
+ }
+ old = rand_init(seed);
+
+ return old;
+}
+
+static unsigned long
+make_mask(unsigned long x)
+{
+ x = x | x >> 1;
+ x = x | x >> 2;
+ x = x | x >> 4;
+ x = x | x >> 8;
+ x = x | x >> 16;
+#if 4 < SIZEOF_LONG
+ x = x | x >> 32;
+#endif
+ return x;
+}
+
+static unsigned long
+limited_rand(unsigned long limit)
+{
+ unsigned long mask = make_mask(limit);
+ int i;
+ unsigned long val;
+
+ retry:
+ val = 0;
+ for (i = SIZEOF_LONG/4-1; 0 <= i; i--) {
+ if (mask >> (i * 32)) {
+ val |= genrand_int32() << (i * 32);
+ val &= mask;
+ if (limit < val)
+ goto retry;
+ }
+ }
+ return val;
+}
+
+static VALUE
+limited_big_rand(struct RBignum *limit)
+{
+ unsigned long mask, lim, rnd;
+ struct RBignum *val;
+ int i, len, boundary;
+
+ len = (limit->len * SIZEOF_BDIGITS + 3) / 4;
+ val = (struct RBignum *)rb_big_clone((VALUE)limit);
+ val->sign = 1;
+#if SIZEOF_BDIGITS == 2
+# define BIG_GET32(big,i) (((BDIGIT *)(big)->digits)[(i)*2] | \
+ ((i)*2+1 < (big)->len ? (((BDIGIT *)(big)->digits)[(i)*2+1] << 16) \
+ : 0))
+# define BIG_SET32(big,i,d) ((((BDIGIT *)(big)->digits)[(i)*2] = (d) & 0xffff), \
+ ((i)*2+1 < (big)->len ? (((BDIGIT *)(big)->digits)[(i)*2+1] = (d) >> 16) \
+ : 0))
+#else
+ /* SIZEOF_BDIGITS == 4 */
+# define BIG_GET32(big,i) (((BDIGIT *)(big)->digits)[i])
+# define BIG_SET32(big,i,d) (((BDIGIT *)(big)->digits)[i] = (d))
+#endif
+ retry:
+ mask = 0;
+ boundary = 1;
+ for (i = len-1; 0 <= i; i--) {
+ lim = BIG_GET32(limit, i);
+ mask = mask ? 0xffffffff : make_mask(lim);
+ if (mask) {
+ rnd = genrand_int32() & mask;
+ if (boundary) {
+ if (lim < rnd)
+ goto retry;
+ if (rnd < lim)
+ boundary = 0;
+ }
+ }
+ else {
+ rnd = 0;
+ }
+ BIG_SET32(val, i, rnd);
+ }
+ return rb_big_norm((VALUE)val);
+}
+
+/*
+ * call-seq:
+ * rand(max=0) => number
+ *
+ * Converts <i>max</i> to an integer using max1 =
+ * max<code>.to_i.abs</code>. If the result is zero, returns a
+ * pseudorandom floating point number greater than or equal to 0.0 and
+ * less than 1.0. Otherwise, returns a pseudorandom integer greater
+ * than or equal to zero and less than max1. <code>Kernel::srand</code>
+ * may be used to ensure repeatable sequences of random numbers between
+ * different runs of the program. Ruby currently uses a modified
+ * Mersenne Twister with a period of 2**19937-1.
+ *
+ * srand 1234 #=> 0
+ * [ rand, rand ] #=> [0.191519450163469, 0.49766366626136]
+ * [ rand(10), rand(1000) ] #=> [6, 817]
+ * srand 1234 #=> 1234
+ * [ rand, rand ] #=> [0.191519450163469, 0.49766366626136]
+ */
+
+static VALUE
+rb_f_rand(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE vmax;
+ long val, max;
+
+ rb_scan_args(argc, argv, "01", &vmax);
+ switch (TYPE(vmax)) {
+ case T_FLOAT:
+ if (RFLOAT(vmax)->value <= LONG_MAX && RFLOAT(vmax)->value >= LONG_MIN) {
+ max = (long)RFLOAT(vmax)->value;
+ break;
+ }
+ if (RFLOAT(vmax)->value < 0)
+ vmax = rb_dbl2big(-RFLOAT(vmax)->value);
+ else
+ vmax = rb_dbl2big(RFLOAT(vmax)->value);
+ /* fall through */
+ case T_BIGNUM:
+ bignum:
+ {
+ struct RBignum *limit = (struct RBignum *)vmax;
+ if (!limit->sign) {
+ limit = (struct RBignum *)rb_big_clone(vmax);
+ limit->sign = 1;
+ }
+ limit = (struct RBignum *)rb_big_minus((VALUE)limit, INT2FIX(1));
+ if (FIXNUM_P((VALUE)limit)) {
+ if (FIX2LONG((VALUE)limit) == -1)
+ return rb_float_new(genrand_real());
+ return LONG2NUM(limited_rand(FIX2LONG((VALUE)limit)));
+ }
+ return limited_big_rand(limit);
+ }
+ case T_NIL:
+ max = 0;
+ break;
+ default:
+ vmax = rb_Integer(vmax);
+ if (TYPE(vmax) == T_BIGNUM) goto bignum;
+ /* fall through */
+ case T_FIXNUM:
+ max = FIX2LONG(vmax);
+ break;
+ }
+
+ if (max == 0) {
+ return rb_float_new(genrand_real());
+ }
+ if (max < 0) max = -max;
+ val = limited_rand(max-1);
+ return LONG2NUM(val);
+}
+
+void
+Init_Random()
+{
+ rand_init(random_seed());
+ rb_define_global_function("srand", rb_f_srand, -1);
+ rb_define_global_function("rand", rb_f_rand, -1);
+ rb_global_variable(&saved_seed);
+}
+/**********************************************************************
+
+ range.c -
+
+ $Author: shyouhei $
+ $Date: 2007-09-16 21:38:27 +0200 (Sun, 16 Sep 2007) $
+ created at: Thu Aug 19 17:46:47 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+VALUE rb_cRange;
+static ID id_cmp, id_succ, id_beg, id_end, id_excl;
+
+#define EXCL(r) RTEST(rb_ivar_get((r), id_excl))
+#define SET_EXCL(r,v) rb_ivar_set((r), id_excl, (v) ? Qtrue : Qfalse)
+
+static VALUE
+range_failed()
+{
+ rb_raise(rb_eArgError, "bad value for range");
+ return Qnil; /* dummy */
+}
+
+static VALUE
+range_check(args)
+ VALUE *args;
+{
+ return rb_funcall(args[0], id_cmp, 1, args[1]);
+}
+
+static void
+range_init(range, beg, end, exclude_end)
+ VALUE range, beg, end;
+ int exclude_end;
+{
+ VALUE args[2];
+
+ args[0] = beg;
+ args[1] = end;
+
+ if (!FIXNUM_P(beg) || !FIXNUM_P(end)) {
+ VALUE v;
+
+ v = rb_rescue(range_check, (VALUE)args, range_failed, 0);
+ if (NIL_P(v)) range_failed();
+ }
+
+ SET_EXCL(range, exclude_end);
+ rb_ivar_set(range, id_beg, beg);
+ rb_ivar_set(range, id_end, end);
+}
+
+VALUE
+rb_range_new(beg, end, exclude_end)
+ VALUE beg, end;
+ int exclude_end;
+{
+ VALUE range = rb_obj_alloc(rb_cRange);
+
+ range_init(range, beg, end, exclude_end);
+ return range;
+}
+
+/*
+ * call-seq:
+ * Range.new(start, end, exclusive=false) => range
+ *
+ * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
+ * parameter is omitted or is <code>false</code>, the <i>range</i> will include
+ * the end object; otherwise, it will be excluded.
+ */
+
+static VALUE
+range_initialize(argc, argv, range)
+ int argc;
+ VALUE *argv;
+ VALUE range;
+{
+ VALUE beg, end, flags;
+
+ rb_scan_args(argc, argv, "21", &beg, &end, &flags);
+ /* Ranges are immutable, so that they should be initialized only once. */
+ if (rb_ivar_defined(range, id_beg)) {
+ rb_name_error(rb_intern("initialize"), "`initialize' called twice");
+ }
+ range_init(range, beg, end, RTEST(flags));
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * rng.exclude_end? => true or false
+ *
+ * Returns <code>true</code> if <i>rng</i> excludes its end value.
+ */
+
+static VALUE
+range_exclude_end_p(range)
+ VALUE range;
+{
+ return EXCL(range) ? Qtrue : Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * rng == obj => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
+ * beginning and end items (by comparing them with <code>==</code>), and has
+ * the same #exclude_end? setting as <i>rng</t>.
+ *
+ * (0..2) == (0..2) #=> true
+ * (0..2) == Range.new(0,2) #=> true
+ * (0..2) == (0...2) #=> false
+ *
+ */
+
+static VALUE
+range_eq(range, obj)
+ VALUE range, obj;
+{
+ if (range == obj) return Qtrue;
+ if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
+ return Qfalse;
+
+ if (!rb_equal(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
+ return Qfalse;
+ if (!rb_equal(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
+ return Qfalse;
+
+ if (EXCL(range) != EXCL(obj)) return Qfalse;
+
+ return Qtrue;
+}
+
+static int
+r_lt(a, b)
+ VALUE a, b;
+{
+ VALUE r = rb_funcall(a, id_cmp, 1, b);
+
+ if (NIL_P(r)) return Qfalse;
+ if (rb_cmpint(r, a, b) < 0) return Qtrue;
+ return Qfalse;
+}
+
+static int
+r_le(a, b)
+ VALUE a, b;
+{
+ int c;
+ VALUE r = rb_funcall(a, id_cmp, 1, b);
+
+ if (NIL_P(r)) return Qfalse;
+ c = rb_cmpint(r, a, b);
+ if (c == 0) return INT2FIX(0);
+ if (c < 0) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * rng.eql?(obj) => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
+ * beginning and end items (by comparing them with #eql?), and has the same
+ * #exclude_end? setting as <i>rng</i>.
+ *
+ * (0..2) == (0..2) #=> true
+ * (0..2) == Range.new(0,2) #=> true
+ * (0..2) == (0...2) #=> false
+ *
+ */
+
+static VALUE
+range_eql(range, obj)
+ VALUE range, obj;
+{
+ if (range == obj) return Qtrue;
+ if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
+ return Qfalse;
+
+ if (!rb_eql(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
+ return Qfalse;
+ if (!rb_eql(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
+ return Qfalse;
+
+ if (EXCL(range) != EXCL(obj)) return Qfalse;
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * rng.hash => fixnum
+ *
+ * Generate a hash value such that two ranges with the same start and
+ * end points, and the same value for the "exclude end" flag, generate
+ * the same hash value.
+ */
+
+static VALUE
+range_hash(range)
+ VALUE range;
+{
+ long hash = EXCL(range);
+ VALUE v;
+
+ v = rb_hash(rb_ivar_get(range, id_beg));
+ hash ^= v << 1;
+ v = rb_hash(rb_ivar_get(range, id_end));
+ hash ^= v << 9;
+ hash ^= EXCL(range) << 24;
+
+ return LONG2FIX(hash);
+}
+
+static VALUE
+str_step(args)
+ VALUE *args;
+{
+ return rb_str_upto(args[0], args[1], EXCL(args[2]));
+}
+
+static void
+range_each_func(range, func, v, e, arg)
+ VALUE range;
+ void (*func) _((VALUE, void*));
+ VALUE v, e;
+ void *arg;
+{
+ int c;
+
+ if (EXCL(range)) {
+ while (r_lt(v, e)) {
+ (*func)(v, arg);
+ v = rb_funcall(v, id_succ, 0, 0);
+ }
+ }
+ else {
+ while (RTEST(c = r_le(v, e))) {
+ (*func)(v, arg);
+ if (c == INT2FIX(0)) break;
+ v = rb_funcall(v, id_succ, 0, 0);
+ }
+ }
+}
+
+static VALUE
+step_i(i, iter)
+ VALUE i;
+ long *iter;
+{
+ iter[0]--;
+ if (iter[0] == 0) {
+ rb_yield(i);
+ iter[0] = iter[1];
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * rng.step(n=1) {| obj | block } => rng
+ *
+ * Iterates over <i>rng</i>, passing each <i>n</i>th element to the block. If
+ * the range contains numbers or strings, natural ordering is used. Otherwise
+ * <code>step</code> invokes <code>succ</code> to iterate through range
+ * elements. The following code uses class <code>Xs</code>, which is defined
+ * in the class-level documentation.
+ *
+ * range = Xs.new(1)..Xs.new(10)
+ * range.step(2) {|x| puts x}
+ * range.step(3) {|x| puts x}
+ *
+ * <em>produces:</em>
+ *
+ * 1 x
+ * 3 xxx
+ * 5 xxxxx
+ * 7 xxxxxxx
+ * 9 xxxxxxxxx
+ * 1 x
+ * 4 xxxx
+ * 7 xxxxxxx
+ * 10 xxxxxxxxxx
+ */
+
+
+static VALUE
+range_step(argc, argv, range)
+ int argc;
+ VALUE *argv;
+ VALUE range;
+{
+ VALUE b, e, step;
+ long unit;
+
+ b = rb_ivar_get(range, id_beg);
+ e = rb_ivar_get(range, id_end);
+ if (rb_scan_args(argc, argv, "01", &step) == 0) {
+ step = INT2FIX(1);
+ }
+
+ unit = NUM2LONG(step);
+ if (unit < 0) {
+ rb_raise(rb_eArgError, "step can't be negative");
+ }
+ if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
+ long end = FIX2LONG(e);
+ long i;
+
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
+ if (!EXCL(range)) end += 1;
+ i = FIX2LONG(b);
+ while (i < end) {
+ rb_yield(LONG2NUM(i));
+ if (i + unit < i) break;
+ i += unit;
+ }
+ }
+ else {
+ VALUE tmp = rb_check_string_type(b);
+
+ if (!NIL_P(tmp)) {
+ VALUE args[5];
+ long iter[2];
+
+ b = tmp;
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
+ args[0] = b; args[1] = e; args[2] = range;
+ iter[0] = 1; iter[1] = unit;
+ rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i,
+ (VALUE)iter);
+ }
+ else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
+ ID c = rb_intern(EXCL(range) ? "<" : "<=");
+
+ if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0");
+ while (RTEST(rb_funcall(b, c, 1, e))) {
+ rb_yield(b);
+ b = rb_funcall(b, '+', 1, step);
+ }
+ }
+ else {
+ long args[2];
+
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
+ if (!rb_respond_to(b, id_succ)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(b));
+ }
+
+ args[0] = 1;
+ args[1] = unit;
+ range_each_func(range, step_i, b, e, args);
+ }
+ }
+ return range;
+}
+
+static void
+each_i(v, arg)
+ VALUE v;
+ void *arg;
+{
+ rb_yield(v);
+}
+
+/*
+ * call-seq:
+ * rng.each {| i | block } => rng
+ *
+ * Iterates over the elements <i>rng</i>, passing each in turn to the
+ * block. You can only iterate if the start object of the range
+ * supports the +succ+ method (which means that you can't iterate over
+ * ranges of +Float+ objects).
+ *
+ * (10..15).each do |n|
+ * print n, ' '
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 10 11 12 13 14 15
+ */
+
+static VALUE
+range_each(range)
+ VALUE range;
+{
+ VALUE beg, end;
+
+ beg = rb_ivar_get(range, id_beg);
+ end = rb_ivar_get(range, id_end);
+
+ if (!rb_respond_to(beg, id_succ)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(beg));
+ }
+ if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
+ long lim = FIX2LONG(end);
+ long i;
+
+ if (!EXCL(range)) lim += 1;
+ for (i=FIX2LONG(beg); i<lim; i++) {
+ rb_yield(LONG2NUM(i));
+ }
+ }
+ else if (TYPE(beg) == T_STRING) {
+ VALUE args[5];
+ long iter[2];
+
+ args[0] = beg; args[1] = end; args[2] = range;
+ iter[0] = 1; iter[1] = 1;
+ rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i,
+ (VALUE)iter);
+ }
+ else {
+ range_each_func(range, each_i, beg, end, NULL);
+ }
+ return range;
+}
+
+/*
+ * call-seq:
+ * rng.first => obj
+ * rng.begin => obj
+ *
+ * Returns the first object in <i>rng</i>.
+ */
+
+static VALUE
+range_first(range)
+ VALUE range;
+{
+ return rb_ivar_get(range, id_beg);
+}
+
+
+/*
+ * call-seq:
+ * rng.end => obj
+ * rng.last => obj
+ *
+ * Returns the object that defines the end of <i>rng</i>.
+ *
+ * (1..10).end #=> 10
+ * (1...10).end #=> 10
+ */
+
+
+static VALUE
+range_last(range)
+ VALUE range;
+{
+ return rb_ivar_get(range, id_end);
+}
+
+VALUE
+rb_range_beg_len(range, begp, lenp, len, err)
+ VALUE range;
+ long *begp, *lenp;
+ long len;
+ int err;
+{
+ long beg, end, b, e;
+
+ if (!rb_obj_is_kind_of(range, rb_cRange)) return Qfalse;
+
+ beg = b = NUM2LONG(rb_ivar_get(range, id_beg));
+ end = e = NUM2LONG(rb_ivar_get(range, id_end));
+
+ if (beg < 0) {
+ beg += len;
+ if (beg < 0) goto out_of_range;
+ }
+ if (err == 0 || err == 2) {
+ if (beg > len) goto out_of_range;
+ if (end > len) end = len;
+ }
+ if (end < 0) end += len;
+ if (!EXCL(range)) end++; /* include end point */
+ len = end - beg;
+ if (len < 0) len = 0;
+
+ *begp = beg;
+ *lenp = len;
+ return Qtrue;
+
+ out_of_range:
+ if (err) {
+ rb_raise(rb_eRangeError, "%ld..%s%ld out of range",
+ b, EXCL(range)? "." : "", e);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * rng.to_s => string
+ *
+ * Convert this range object to a printable form.
+ */
+
+static VALUE
+range_to_s(range)
+ VALUE range;
+{
+ VALUE str, str2;
+
+ str = rb_obj_as_string(rb_ivar_get(range, id_beg));
+ str2 = rb_obj_as_string(rb_ivar_get(range, id_end));
+ str = rb_str_dup(str);
+ rb_str_cat(str, "...", EXCL(range)?3:2);
+ rb_str_append(str, str2);
+ OBJ_INFECT(str, str2);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * rng.inspect => string
+ *
+ * Convert this range object to a printable form (using
+ * <code>inspect</code> to convert the start and end
+ * objects).
+ */
+
+
+static VALUE
+range_inspect(range)
+ VALUE range;
+{
+ VALUE str, str2;
+
+ str = rb_inspect(rb_ivar_get(range, id_beg));
+ str2 = rb_inspect(rb_ivar_get(range, id_end));
+ str = rb_str_dup(str);
+ rb_str_cat(str, "...", EXCL(range)?3:2);
+ rb_str_append(str, str2);
+ OBJ_INFECT(str, str2);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * rng === obj => true or false
+ * rng.member?(val) => true or false
+ * rng.include?(val) => true or false
+ *
+ * Returns <code>true</code> if <i>obj</i> is an element of
+ * <i>rng</i>, <code>false</code> otherwise. Conveniently,
+ * <code>===</code> is the comparison operator used by
+ * <code>case</code> statements.
+ *
+ * case 79
+ * when 1..50 then print "low\n"
+ * when 51..75 then print "medium\n"
+ * when 76..100 then print "high\n"
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * high
+ */
+
+static VALUE
+range_include(range, val)
+ VALUE range, val;
+{
+ VALUE beg, end;
+
+ beg = rb_ivar_get(range, id_beg);
+ end = rb_ivar_get(range, id_end);
+ if (r_le(beg, val)) {
+ if (EXCL(range)) {
+ if (r_lt(val, end)) return Qtrue;
+ }
+ else {
+ if (r_le(val, end)) return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+
+/* A <code>Range</code> represents an interval---a set of values with a
+ * start and an end. Ranges may be constructed using the
+ * <em>s</em><code>..</code><em>e</em> and
+ * <em>s</em><code>...</code><em>e</em> literals, or with
+ * <code>Range::new</code>. Ranges constructed using <code>..</code>
+ * run from the start to the end inclusively. Those created using
+ * <code>...</code> exclude the end value. When used as an iterator,
+ * ranges return each value in the sequence.
+ *
+ * (-1..-5).to_a #=> []
+ * (-5..-1).to_a #=> [-5, -4, -3, -2, -1]
+ * ('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
+ * ('a'...'e').to_a #=> ["a", "b", "c", "d"]
+ *
+ * Ranges can be constructed using objects of any type, as long as the
+ * objects can be compared using their <code><=></code> operator and
+ * they support the <code>succ</code> method to return the next object
+ * in sequence.
+ *
+ * class Xs # represent a string of 'x's
+ * include Comparable
+ * attr :length
+ * def initialize(n)
+ * @length = n
+ * end
+ * def succ
+ * Xs.new(@length + 1)
+ * end
+ * def <=>(other)
+ * @length <=> other.length
+ * end
+ * def to_s
+ * sprintf "%2d #{inspect}", @length
+ * end
+ * def inspect
+ * 'x' * @length
+ * end
+ * end
+ *
+ * r = Xs.new(3)..Xs.new(6) #=> xxx..xxxxxx
+ * r.to_a #=> [xxx, xxxx, xxxxx, xxxxxx]
+ * r.member?(Xs.new(5)) #=> true
+ *
+ * In the previous code example, class <code>Xs</code> includes the
+ * <code>Comparable</code> module. This is because
+ * <code>Enumerable#member?</code> checks for equality using
+ * <code>==</code>. Including <code>Comparable</code> ensures that the
+ * <code>==</code> method is defined in terms of the <code><=></code>
+ * method implemented in <code>Xs</code>.
+ *
+ */
+
+void
+Init_Range()
+{
+ rb_cRange = rb_define_class("Range", rb_cObject);
+ rb_include_module(rb_cRange, rb_mEnumerable);
+ rb_define_method(rb_cRange, "initialize", range_initialize, -1);
+ rb_define_method(rb_cRange, "==", range_eq, 1);
+ rb_define_method(rb_cRange, "===", range_include, 1);
+ rb_define_method(rb_cRange, "eql?", range_eql, 1);
+ rb_define_method(rb_cRange, "hash", range_hash, 0);
+ rb_define_method(rb_cRange, "each", range_each, 0);
+ rb_define_method(rb_cRange, "step", range_step, -1);
+ rb_define_method(rb_cRange, "first", range_first, 0);
+ rb_define_method(rb_cRange, "last", range_last, 0);
+ rb_define_method(rb_cRange, "begin", range_first, 0);
+ rb_define_method(rb_cRange, "end", range_last, 0);
+ rb_define_method(rb_cRange, "to_s", range_to_s, 0);
+ rb_define_method(rb_cRange, "inspect", range_inspect, 0);
+
+ rb_define_method(rb_cRange, "exclude_end?", range_exclude_end_p, 0);
+
+ rb_define_method(rb_cRange, "member?", range_include, 1);
+ rb_define_method(rb_cRange, "include?", range_include, 1);
+
+ id_cmp = rb_intern("<=>");
+ id_succ = rb_intern("succ");
+ id_beg = rb_intern("begin");
+ id_end = rb_intern("end");
+ id_excl = rb_intern("excl");
+}
+/**********************************************************************
+
+ re.c -
+
+ $Author: shyouhei $
+ created at: Mon Aug 9 18:24:49 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "re.h"
+#include <ctype.h>
+
+VALUE rb_eRegexpError;
+
+#define BEG(no) regs->beg[no]
+#define END(no) regs->end[no]
+
+#if 'a' == 97 /* it's ascii */
+static const char casetable[] = {
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ /* ' ' '!' '"' '#' '$' '%' '&' ''' */
+ '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+ /* '(' ')' '*' '+' ',' '-' '.' '/' */
+ '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+ /* '0' '1' '2' '3' '4' '5' '6' '7' */
+ '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+ /* '8' '9' ':' ';' '<' '=' '>' '?' */
+ '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+ /* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' */
+ '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */
+ '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+ /* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' */
+ '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'x' 'y' 'z' '{' '|' '}' '~' */
+ '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+ '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+ '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+ '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+ '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
+ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
+ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
+ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
+ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+#else
+# error >>> "You lose. You will need a translation table for your character set." <<<
+#endif
+
+int
+rb_memcicmp(x, y, len)
+ const void *x, *y;
+ long len;
+{
+ const unsigned char *p1 = x, *p2 = y;
+ int tmp;
+
+ while (len--) {
+ if ((tmp = casetable[(unsigned)*p1++] - casetable[(unsigned)*p2++]) != 0)
+ return tmp;
+ }
+ return 0;
+}
+
+int
+rb_memcmp(p1, p2, len)
+ const void *p1, *p2;
+ long len;
+{
+ if (!ruby_ignorecase) {
+ return memcmp(p1, p2, len);
+ }
+ return rb_memcicmp(p1, p2, len);
+}
+
+long
+rb_memsearch(x0, m, y0, n)
+ const void *x0, *y0;
+ long m, n;
+{
+ const unsigned char *x = (unsigned char *)x0, *y = (unsigned char *)y0;
+ const unsigned char *s, *e;
+ long i;
+ int d;
+ unsigned long hx, hy;
+
+#define KR_REHASH(a, b, h) (((h) << 1) - (((unsigned long)(a))<<d) + (b))
+
+ if (m > n) return -1;
+ s = y; e = s + n - m;
+
+ /* Preprocessing */
+ /* computes d = 2^(m-1) with
+ the left-shift operator */
+ d = sizeof(hx) * CHAR_BIT - 1;
+ if (d > m) d = m;
+
+ if (ruby_ignorecase) {
+ if (n == m) {
+ return rb_memcicmp(x, s, m) == 0 ? 0 : -1;
+ }
+ /* Prepare hash value */
+ for (hy = hx = i = 0; i < d; ++i) {
+ hx = KR_REHASH(0, casetable[x[i]], hx);
+ hy = KR_REHASH(0, casetable[s[i]], hy);
+ }
+ /* Searching */
+ while (hx != hy || rb_memcicmp(x, s, m)) {
+ if (s >= e) return -1;
+ hy = KR_REHASH(casetable[*s], casetable[*(s+d)], hy);
+ s++;
+ }
+ }
+ else {
+ if (n == m) {
+ return memcmp(x, s, m) == 0 ? 0 : -1;
+ }
+ /* Prepare hash value */
+ for (hy = hx = i = 0; i < d; ++i) {
+ hx = KR_REHASH(0, x[i], hx);
+ hy = KR_REHASH(0, s[i], hy);
+ }
+ /* Searching */
+ while (hx != hy || memcmp(x, s, m)) {
+ if (s >= e) return -1;
+ hy = KR_REHASH(*s, *(s+d), hy);
+ s++;
+ }
+ }
+ return s-y;
+}
+
+#define REG_LITERAL FL_USER5
+#define REG_CASESTATE FL_USER0
+#define KCODE_NONE 0
+#define KCODE_EUC FL_USER1
+#define KCODE_SJIS FL_USER2
+#define KCODE_UTF8 FL_USER3
+#define KCODE_FIXED FL_USER4
+#define KCODE_MASK (KCODE_EUC|KCODE_SJIS|KCODE_UTF8)
+
+static int reg_kcode = DEFAULT_KCODE;
+
+static void
+kcode_euc(re)
+ struct RRegexp *re;
+{
+ FL_UNSET(re, KCODE_MASK);
+ FL_SET(re, KCODE_EUC);
+ FL_SET(re, KCODE_FIXED);
+}
+
+static void
+kcode_sjis(re)
+ struct RRegexp *re;
+{
+ FL_UNSET(re, KCODE_MASK);
+ FL_SET(re, KCODE_SJIS);
+ FL_SET(re, KCODE_FIXED);
+}
+
+static void
+kcode_utf8(re)
+ struct RRegexp *re;
+{
+ FL_UNSET(re, KCODE_MASK);
+ FL_SET(re, KCODE_UTF8);
+ FL_SET(re, KCODE_FIXED);
+}
+
+static void
+kcode_none(re)
+ struct RRegexp *re;
+{
+ FL_UNSET(re, KCODE_MASK);
+ FL_SET(re, KCODE_FIXED);
+}
+
+static int curr_kcode;
+
+void
+rb_kcode_set_option(re)
+ VALUE re;
+{
+ if (!FL_TEST(re, KCODE_FIXED)) return;
+
+ curr_kcode = RBASIC(re)->flags & KCODE_MASK;
+ if (reg_kcode == curr_kcode) return;
+ switch (curr_kcode) {
+ case KCODE_NONE:
+ re_mbcinit(MBCTYPE_ASCII);
+ break;
+ case KCODE_EUC:
+ re_mbcinit(MBCTYPE_EUC);
+ break;
+ case KCODE_SJIS:
+ re_mbcinit(MBCTYPE_SJIS);
+ break;
+ case KCODE_UTF8:
+ re_mbcinit(MBCTYPE_UTF8);
+ break;
+ }
+}
+
+void
+rb_kcode_reset_option()
+{
+ if (reg_kcode == curr_kcode) return;
+ switch (reg_kcode) {
+ case KCODE_NONE:
+ re_mbcinit(MBCTYPE_ASCII);
+ break;
+ case KCODE_EUC:
+ re_mbcinit(MBCTYPE_EUC);
+ break;
+ case KCODE_SJIS:
+ re_mbcinit(MBCTYPE_SJIS);
+ break;
+ case KCODE_UTF8:
+ re_mbcinit(MBCTYPE_UTF8);
+ break;
+ }
+}
+
+int
+rb_reg_mbclen2(c, re)
+ unsigned int c;
+ VALUE re;
+{
+ int len;
+
+ if (!FL_TEST(re, KCODE_FIXED))
+ return mbclen(c);
+ rb_kcode_set_option(re);
+ len = mbclen(c);
+ rb_kcode_reset_option();
+ return len;
+}
+
+static void
+rb_reg_check(re)
+ VALUE re;
+{
+ if (!RREGEXP(re)->ptr || !RREGEXP(re)->str) {
+ rb_raise(rb_eTypeError, "uninitialized Regexp");
+ }
+}
+
+extern int ruby_in_compile;
+
+static void
+rb_reg_expr_str(str, s, len)
+ VALUE str;
+ const char *s;
+ long len;
+{
+ const char *p, *pend;
+ int need_escape = 0;
+
+ p = s; pend = p + len;
+ while (p<pend) {
+ if (*p == '/' || (!ISPRINT(*p) && !ismbchar(*p))) {
+ need_escape = 1;
+ break;
+ }
+ p += mbclen(*p);
+ }
+ if (!need_escape) {
+ rb_str_buf_cat(str, s, len);
+ }
+ else {
+ p = s;
+ while (p<pend) {
+ if (*p == '\\') {
+ int n = mbclen(p[1]) + 1;
+ rb_str_buf_cat(str, p, n);
+ p += n;
+ continue;
+ }
+ else if (*p == '/') {
+ char c = '\\';
+ rb_str_buf_cat(str, &c, 1);
+ rb_str_buf_cat(str, p, 1);
+ }
+ else if (ismbchar(*p)) {
+ rb_str_buf_cat(str, p, mbclen(*p));
+ p += mbclen(*p);
+ continue;
+ }
+ else if (ISPRINT(*p)) {
+ rb_str_buf_cat(str, p, 1);
+ }
+ else if (!ISSPACE(*p)) {
+ char b[8];
+
+ sprintf(b, "\\%03o", *p & 0377);
+ rb_str_buf_cat(str, b, 4);
+ }
+ else {
+ rb_str_buf_cat(str, p, 1);
+ }
+ p++;
+ }
+ }
+}
+
+static VALUE
+rb_reg_desc(s, len, re)
+ const char *s;
+ long len;
+ VALUE re;
+{
+ VALUE str = rb_str_buf_new2("/");
+
+ rb_reg_expr_str(str, s, len);
+ rb_str_buf_cat2(str, "/");
+ if (re) {
+ rb_reg_check(re);
+ if (RREGEXP(re)->ptr->options & RE_OPTION_MULTILINE)
+ rb_str_buf_cat2(str, "m");
+ if (RREGEXP(re)->ptr->options & RE_OPTION_IGNORECASE)
+ rb_str_buf_cat2(str, "i");
+ if (RREGEXP(re)->ptr->options & RE_OPTION_EXTENDED)
+ rb_str_buf_cat2(str, "x");
+
+ if (FL_TEST(re, KCODE_FIXED)) {
+ switch ((RBASIC(re)->flags & KCODE_MASK)) {
+ case KCODE_NONE:
+ rb_str_buf_cat2(str, "n");
+ break;
+ case KCODE_EUC:
+ rb_str_buf_cat2(str, "e");
+ break;
+ case KCODE_SJIS:
+ rb_str_buf_cat2(str, "s");
+ break;
+ case KCODE_UTF8:
+ rb_str_buf_cat2(str, "u");
+ break;
+ }
+ }
+ }
+ OBJ_INFECT(str, re);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * rxp.source => str
+ *
+ * Returns the original string of the pattern.
+ *
+ * /ab+c/ix.source #=> "ab+c"
+ */
+
+static VALUE
+rb_reg_source(re)
+ VALUE re;
+{
+ VALUE str;
+
+ rb_reg_check(re);
+ str = rb_str_new(RREGEXP(re)->str,RREGEXP(re)->len);
+ if (OBJ_TAINTED(re)) OBJ_TAINT(str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * rxp.inspect => string
+ *
+ * Produce a nicely formatted string-version of _rxp_. Perhaps surprisingly,
+ * <code>#inspect</code> actually produces the more natural version of
+ * the string than <code>#to_s</code>.
+ *
+ * /ab+c/ix.to_s #=> /ab+c/ix
+*/
+
+static VALUE
+rb_reg_inspect(re)
+ VALUE re;
+{
+ rb_reg_check(re);
+ return rb_reg_desc(RREGEXP(re)->str, RREGEXP(re)->len, re);
+}
+
+
+/*
+ * call-seq:
+ * rxp.to_s => str
+ *
+ * Returns a string containing the regular expression and its options (using the
+ * <code>(?xxx:yyy)</code> notation. This string can be fed back in to
+ * <code>Regexp::new</code> to a regular expression with the same semantics as
+ * the original. (However, <code>Regexp#==</code> may not return true when
+ * comparing the two, as the source of the regular expression itself may
+ * differ, as the example shows). <code>Regexp#inspect</code> produces a
+ * generally more readable version of <i>rxp</i>.
+ *
+ * r1 = /ab+c/ix #=> /ab+c/ix
+ * s1 = r1.to_s #=> "(?ix-m:ab+c)"
+ * r2 = Regexp.new(s1) #=> /(?ix-m:ab+c)/
+ * r1 == r2 #=> false
+ * r1.source #=> "ab+c"
+ * r2.source #=> "(?ix-m:ab+c)"
+ */
+
+static VALUE
+rb_reg_to_s(re)
+ VALUE re;
+{
+ int options;
+ const int embeddable = RE_OPTION_MULTILINE|RE_OPTION_IGNORECASE|RE_OPTION_EXTENDED;
+ long len;
+ const char* ptr;
+ VALUE str = rb_str_buf_new2("(?");
+
+ rb_reg_check(re);
+
+ options = RREGEXP(re)->ptr->options;
+ ptr = RREGEXP(re)->str;
+ len = RREGEXP(re)->len;
+ again:
+ if (len >= 4 && ptr[0] == '(' && ptr[1] == '?') {
+ int err = 1;
+ ptr += 2;
+ if ((len -= 2) > 0) {
+ do {
+ if (*ptr == 'm') {
+ options |= RE_OPTION_MULTILINE;
+ }
+ else if (*ptr == 'i') {
+ options |= RE_OPTION_IGNORECASE;
+ }
+ else if (*ptr == 'x') {
+ options |= RE_OPTION_EXTENDED;
+ }
+ else break;
+ ++ptr;
+ } while (--len > 0);
+ }
+ if (len > 1 && *ptr == '-') {
+ ++ptr;
+ --len;
+ do {
+ if (*ptr == 'm') {
+ options &= ~RE_OPTION_MULTILINE;
+ }
+ else if (*ptr == 'i') {
+ options &= ~RE_OPTION_IGNORECASE;
+ }
+ else if (*ptr == 'x') {
+ options &= ~RE_OPTION_EXTENDED;
+ }
+ else break;
+ ++ptr;
+ } while (--len > 0);
+ }
+ if (*ptr == ')') {
+ --len;
+ ++ptr;
+ goto again;
+ }
+ if (*ptr == ':' && ptr[len-1] == ')') {
+ Regexp *rp;
+ rb_kcode_set_option(re);
+ rp = ALLOC(Regexp);
+ MEMZERO((char *)rp, Regexp, 1);
+ err = re_compile_pattern(++ptr, len -= 2, rp) != 0;
+ rb_kcode_reset_option();
+ re_free_pattern(rp);
+ }
+ if (err) {
+ options = RREGEXP(re)->ptr->options;
+ ptr = RREGEXP(re)->str;
+ len = RREGEXP(re)->len;
+ }
+ }
+
+ if (options & RE_OPTION_MULTILINE) rb_str_buf_cat2(str, "m");
+ if (options & RE_OPTION_IGNORECASE) rb_str_buf_cat2(str, "i");
+ if (options & RE_OPTION_EXTENDED) rb_str_buf_cat2(str, "x");
+
+ if ((options & embeddable) != embeddable) {
+ rb_str_buf_cat2(str, "-");
+ if (!(options & RE_OPTION_MULTILINE)) rb_str_buf_cat2(str, "m");
+ if (!(options & RE_OPTION_IGNORECASE)) rb_str_buf_cat2(str, "i");
+ if (!(options & RE_OPTION_EXTENDED)) rb_str_buf_cat2(str, "x");
+ }
+
+ rb_str_buf_cat2(str, ":");
+ rb_reg_expr_str(str, ptr, len);
+ rb_str_buf_cat2(str, ")");
+
+ OBJ_INFECT(str, re);
+ return str;
+}
+
+static void
+rb_reg_raise(s, len, err, re)
+ const char *s;
+ long len;
+ const char *err;
+ VALUE re;
+{
+ VALUE desc = rb_reg_desc(s, len, re);
+
+ if (ruby_in_compile)
+ rb_compile_error("%s: %s", err, RSTRING(desc)->ptr);
+ else
+ rb_raise(rb_eRegexpError, "%s: %s", err, RSTRING(desc)->ptr);
+}
+
+
+/*
+ * call-seq:
+ * rxp.casefold? => true or false
+ *
+ * Returns the value of the case-insensitive flag.
+ */
+
+static VALUE
+rb_reg_casefold_p(re)
+ VALUE re;
+{
+ rb_reg_check(re);
+ if (RREGEXP(re)->ptr->options & RE_OPTION_IGNORECASE) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * rxp.options => fixnum
+ *
+ * Returns the set of bits corresponding to the options used when creating this
+ * Regexp (see <code>Regexp::new</code> for details. Note that additional bits
+ * may be set in the returned options: these are used internally by the regular
+ * expression code. These extra bits are ignored if the options are passed to
+ * <code>Regexp::new</code>.
+ *
+ * Regexp::IGNORECASE #=> 1
+ * Regexp::EXTENDED #=> 2
+ * Regexp::MULTILINE #=> 4
+ *
+ * /cat/.options #=> 128
+ * /cat/ix.options #=> 131
+ * Regexp.new('cat', true).options #=> 129
+ * Regexp.new('cat', 0, 's').options #=> 384
+ *
+ * r = /cat/ix
+ * Regexp.new(r.source, r.options) #=> /cat/ix
+ */
+
+static VALUE
+rb_reg_options_m(re)
+ VALUE re;
+{
+ int options = rb_reg_options(re);
+ return INT2NUM(options);
+}
+
+
+/*
+ * call-seq:
+ * rxp.kcode => str
+ *
+ * Returns the character set code for the regexp.
+ */
+
+static VALUE
+rb_reg_kcode_m(re)
+ VALUE re;
+{
+ char *kcode;
+
+ if (FL_TEST(re, KCODE_FIXED)) {
+ switch (RBASIC(re)->flags & KCODE_MASK) {
+ case KCODE_NONE:
+ kcode = "none"; break;
+ case KCODE_EUC:
+ kcode = "euc"; break;
+ case KCODE_SJIS:
+ kcode = "sjis"; break;
+ case KCODE_UTF8:
+ kcode = "utf8"; break;
+ default:
+ rb_bug("unknown kcode - should not happen");
+ break;
+ }
+ return rb_str_new2(kcode);
+ }
+ return Qnil;
+}
+
+static Regexp*
+make_regexp(s, len, flags)
+ const char *s;
+ long len;
+ int flags;
+{
+ Regexp *rp;
+ char *err;
+
+ /* Handle escaped characters first. */
+
+ /* Build a copy of the string (in dest) with the
+ escaped characters translated, and generate the regex
+ from that.
+ */
+
+ rp = ALLOC(Regexp);
+ MEMZERO((char *)rp, Regexp, 1);
+ rp->buffer = ALLOC_N(char, 16);
+ rp->allocated = 16;
+ rp->fastmap = ALLOC_N(char, 256);
+ if (flags) {
+ rp->options = flags;
+ }
+ err = re_compile_pattern(s, len, rp);
+
+ if (err != NULL) {
+ re_free_pattern(rp);
+ rb_reg_raise(s, len, err, 0);
+ return 0;
+ }
+ return rp;
+}
+
+
+/*
+ * Document-class: MatchData
+ *
+ * <code>MatchData</code> is the type of the special variable <code>$~</code>,
+ * and is the type of the object returned by <code>Regexp#match</code> and
+ * <code>Regexp#last_match</code>. It encapsulates all the results of a pattern
+ * match, results normally accessed through the special variables
+ * <code>$&</code>, <code>$'</code>, <code>$`</code>, <code>$1</code>,
+ * <code>$2</code>, and so on. <code>Matchdata</code> is also known as
+ * <code>MatchingData</code>.
+ *
+ */
+
+VALUE rb_cMatch;
+
+static VALUE match_alloc _((VALUE));
+static VALUE
+match_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(match, struct RMatch);
+ OBJSETUP(match, klass, T_MATCH);
+
+ match->str = 0;
+ match->regs = 0;
+ match->regs = ALLOC(struct re_registers);
+ MEMZERO(match->regs, struct re_registers, 1);
+
+ return (VALUE)match;
+}
+
+static void
+match_check(VALUE match)
+{
+ if (!RMATCH(match)->str) {
+ rb_raise(rb_eTypeError, "uninitialized Match");
+ }
+}
+
+/* :nodoc: */
+static VALUE
+match_init_copy(obj, orig)
+ VALUE obj, orig;
+{
+ if (obj == orig) return obj;
+
+ if (!rb_obj_is_instance_of(orig, rb_obj_class(obj))) {
+ rb_raise(rb_eTypeError, "wrong argument class");
+ }
+ RMATCH(obj)->str = RMATCH(orig)->str;
+ re_free_registers(RMATCH(obj)->regs);
+ RMATCH(obj)->regs->allocated = 0;
+ re_copy_registers(RMATCH(obj)->regs, RMATCH(orig)->regs);
+
+ return obj;
+}
+
+
+/*
+ * call-seq:
+ * mtch.length => integer
+ * mtch.size => integer
+ *
+ * Returns the number of elements in the match array.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.length #=> 5
+ * m.size #=> 5
+ */
+
+static VALUE
+match_size(match)
+ VALUE match;
+{
+ match_check(match);
+ return INT2FIX(RMATCH(match)->regs->num_regs);
+}
+
+
+/*
+ * call-seq:
+ * mtch.offset(n) => array
+ *
+ * Returns a two-element array containing the beginning and ending offsets of
+ * the <em>n</em>th match.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.offset(0) #=> [1, 7]
+ * m.offset(4) #=> [6, 7]
+ */
+
+static VALUE
+match_offset(match, n)
+ VALUE match, n;
+{
+ int i = NUM2INT(n);
+
+ match_check(match);
+ if (i < 0 || RMATCH(match)->regs->num_regs <= i)
+ rb_raise(rb_eIndexError, "index %d out of matches", i);
+
+ if (RMATCH(match)->regs->beg[i] < 0)
+ return rb_assoc_new(Qnil, Qnil);
+
+ return rb_assoc_new(INT2FIX(RMATCH(match)->regs->beg[i]),
+ INT2FIX(RMATCH(match)->regs->end[i]));
+}
+
+
+/*
+ * call-seq:
+ * mtch.begin(n) => integer
+ *
+ * Returns the offset of the start of the <em>n</em>th element of the match
+ * array in the string.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.begin(0) #=> 1
+ * m.begin(2) #=> 2
+ */
+
+static VALUE
+match_begin(match, n)
+ VALUE match, n;
+{
+ int i = NUM2INT(n);
+
+ match_check(match);
+ if (i < 0 || RMATCH(match)->regs->num_regs <= i)
+ rb_raise(rb_eIndexError, "index %d out of matches", i);
+
+ if (RMATCH(match)->regs->beg[i] < 0)
+ return Qnil;
+
+ return INT2FIX(RMATCH(match)->regs->beg[i]);
+}
+
+
+/*
+ * call-seq:
+ * mtch.end(n) => integer
+ *
+ * Returns the offset of the character immediately following the end of the
+ * <em>n</em>th element of the match array in the string.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.end(0) #=> 7
+ * m.end(2) #=> 3
+ */
+
+static VALUE
+match_end(match, n)
+ VALUE match, n;
+{
+ int i = NUM2INT(n);
+
+ match_check(match);
+ if (i < 0 || RMATCH(match)->regs->num_regs <= i)
+ rb_raise(rb_eIndexError, "index %d out of matches", i);
+
+ if (RMATCH(match)->regs->beg[i] < 0)
+ return Qnil;
+
+ return INT2FIX(RMATCH(match)->regs->end[i]);
+}
+
+#define MATCH_BUSY FL_USER2
+
+void
+rb_match_busy(match)
+ VALUE match;
+{
+ FL_SET(match, MATCH_BUSY);
+}
+
+int ruby_ignorecase;
+static int may_need_recompile;
+
+static void
+rb_reg_prepare_re(re)
+ VALUE re;
+{
+ int need_recompile = 0;
+ int state;
+
+ rb_reg_check(re);
+ state = FL_TEST(re, REG_CASESTATE);
+ /* ignorecase status */
+ if (ruby_ignorecase && !state) {
+ FL_SET(re, REG_CASESTATE);
+ RREGEXP(re)->ptr->options |= RE_OPTION_IGNORECASE;
+ need_recompile = 1;
+ }
+ if (!ruby_ignorecase && state) {
+ FL_UNSET(re, REG_CASESTATE);
+ RREGEXP(re)->ptr->options &= ~RE_OPTION_IGNORECASE;
+ need_recompile = 1;
+ }
+
+ if (!FL_TEST(re, KCODE_FIXED) &&
+ (RBASIC(re)->flags & KCODE_MASK) != reg_kcode) {
+ need_recompile = 1;
+ RBASIC(re)->flags &= ~KCODE_MASK;
+ RBASIC(re)->flags |= reg_kcode;
+ }
+
+ if (need_recompile) {
+ char *err;
+
+ if (FL_TEST(re, KCODE_FIXED))
+ rb_kcode_set_option(re);
+ rb_reg_check(re);
+ RREGEXP(re)->ptr->fastmap_accurate = 0;
+ err = re_compile_pattern(RREGEXP(re)->str, RREGEXP(re)->len, RREGEXP(re)->ptr);
+ if (err != NULL) {
+ rb_reg_raise(RREGEXP(re)->str, RREGEXP(re)->len, err, re);
+ }
+ }
+}
+
+long
+rb_reg_adjust_startpos(re, str, pos, reverse)
+ VALUE re, str;
+ long pos, reverse;
+{
+ long range;
+
+ rb_reg_check(re);
+ if (may_need_recompile) rb_reg_prepare_re(re);
+
+ if (FL_TEST(re, KCODE_FIXED))
+ rb_kcode_set_option(re);
+ else if (reg_kcode != curr_kcode)
+ rb_kcode_reset_option();
+
+ if (reverse) {
+ range = -pos;
+ }
+ else {
+ range = RSTRING(str)->len - pos;
+ }
+ return re_adjust_startpos(RREGEXP(re)->ptr,
+ RSTRING(str)->ptr, RSTRING(str)->len,
+ pos, range);
+}
+
+long
+rb_reg_search(re, str, pos, reverse)
+ VALUE re, str;
+ long pos, reverse;
+{
+ long result;
+ VALUE match;
+ struct re_registers regs;
+ long range;
+
+ if (pos > RSTRING(str)->len || pos < 0) {
+ rb_backref_set(Qnil);
+ return -1;
+ }
+
+ rb_reg_check(re);
+ if (may_need_recompile) rb_reg_prepare_re(re);
+
+ if (FL_TEST(re, KCODE_FIXED))
+ rb_kcode_set_option(re);
+ else if (reg_kcode != curr_kcode)
+ rb_kcode_reset_option();
+
+ if (reverse) {
+ range = -pos;
+ }
+ else {
+ range = RSTRING(str)->len - pos;
+ }
+ MEMZERO(&regs, struct re_registers, 1);
+ result = re_search(RREGEXP(re)->ptr,RSTRING(str)->ptr,RSTRING(str)->len,
+ pos, range, &regs);
+
+ if (FL_TEST(re, KCODE_FIXED))
+ rb_kcode_reset_option();
+
+ if (result == -2) {
+ rb_reg_raise(RREGEXP(re)->str, RREGEXP(re)->len,
+ "Stack overflow in regexp matcher", re);
+ }
+
+ if (result < 0) {
+ re_free_registers(&regs);
+ rb_backref_set(Qnil);
+ return result;
+ }
+
+ match = rb_backref_get();
+ if (NIL_P(match) || FL_TEST(match, MATCH_BUSY)) {
+ match = match_alloc(rb_cMatch);
+ }
+ else {
+ if (rb_safe_level() >= 3)
+ OBJ_TAINT(match);
+ else
+ FL_UNSET(match, FL_TAINT);
+ }
+
+ re_copy_registers(RMATCH(match)->regs, &regs);
+ re_free_registers(&regs);
+ RMATCH(match)->str = rb_str_new4(str);
+ rb_backref_set(match);
+
+ OBJ_INFECT(match, re);
+ OBJ_INFECT(match, str);
+ return result;
+}
+
+VALUE
+rb_reg_nth_defined(nth, match)
+ int nth;
+ VALUE match;
+{
+ if (NIL_P(match)) return Qnil;
+ match_check(match);
+ if (nth >= RMATCH(match)->regs->num_regs) {
+ return Qnil;
+ }
+ if (nth < 0) {
+ nth += RMATCH(match)->regs->num_regs;
+ if (nth <= 0) return Qnil;
+ }
+ if (RMATCH(match)->BEG(nth) == -1) return Qfalse;
+ return Qtrue;
+}
+
+VALUE
+rb_reg_nth_match(nth, match)
+ int nth;
+ VALUE match;
+{
+ VALUE str;
+ long start, end, len;
+
+ if (NIL_P(match)) return Qnil;
+ match_check(match);
+ if (nth >= RMATCH(match)->regs->num_regs) {
+ return Qnil;
+ }
+ if (nth < 0) {
+ nth += RMATCH(match)->regs->num_regs;
+ if (nth <= 0) return Qnil;
+ }
+ start = RMATCH(match)->BEG(nth);
+ if (start == -1) return Qnil;
+ end = RMATCH(match)->END(nth);
+ len = end - start;
+ str = rb_str_substr(RMATCH(match)->str, start, len);
+ OBJ_INFECT(str, match);
+ return str;
+}
+
+VALUE
+rb_reg_last_match(match)
+ VALUE match;
+{
+ return rb_reg_nth_match(0, match);
+}
+
+
+/*
+ * call-seq:
+ * mtch.pre_match => str
+ *
+ * Returns the portion of the original string before the current match.
+ * Equivalent to the special variable <code>$`</code>.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.pre_match #=> "T"
+ */
+
+VALUE
+rb_reg_match_pre(match)
+ VALUE match;
+{
+ VALUE str;
+
+ if (NIL_P(match)) return Qnil;
+ match_check(match);
+ if (RMATCH(match)->BEG(0) == -1) return Qnil;
+ str = rb_str_substr(RMATCH(match)->str, 0, RMATCH(match)->BEG(0));
+ if (OBJ_TAINTED(match)) OBJ_TAINT(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * mtch.post_match => str
+ *
+ * Returns the portion of the original string after the current match.
+ * Equivalent to the special variable <code>$'</code>.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138: The Movie")
+ * m.post_match #=> ": The Movie"
+ */
+
+VALUE
+rb_reg_match_post(match)
+ VALUE match;
+{
+ VALUE str;
+ long pos;
+
+ if (NIL_P(match)) return Qnil;
+ match_check(match);
+ if (RMATCH(match)->BEG(0) == -1) return Qnil;
+ str = RMATCH(match)->str;
+ pos = RMATCH(match)->END(0);
+ str = rb_str_substr(str, pos, RSTRING(str)->len - pos);
+ if (OBJ_TAINTED(match)) OBJ_TAINT(str);
+ return str;
+}
+
+VALUE
+rb_reg_match_last(match)
+ VALUE match;
+{
+ int i;
+
+ if (NIL_P(match)) return Qnil;
+ match_check(match);
+ if (RMATCH(match)->BEG(0) == -1) return Qnil;
+
+ for (i=RMATCH(match)->regs->num_regs-1; RMATCH(match)->BEG(i) == -1 && i > 0; i--)
+ ;
+ if (i == 0) return Qnil;
+ return rb_reg_nth_match(i, match);
+}
+
+static VALUE
+last_match_getter()
+{
+ return rb_reg_last_match(rb_backref_get());
+}
+
+static VALUE
+prematch_getter()
+{
+ return rb_reg_match_pre(rb_backref_get());
+}
+
+static VALUE
+postmatch_getter()
+{
+ return rb_reg_match_post(rb_backref_get());
+}
+
+static VALUE
+last_paren_match_getter()
+{
+ return rb_reg_match_last(rb_backref_get());
+}
+
+static VALUE
+match_array(match, start)
+ VALUE match;
+ int start;
+{
+ struct re_registers *regs;
+ VALUE ary;
+ VALUE target;
+ int i;
+ int taint = OBJ_TAINTED(match);
+
+ match_check(match);
+ regs = RMATCH(match)->regs;
+ ary = rb_ary_new2(regs->num_regs);
+ target = RMATCH(match)->str;
+
+ for (i=start; i<regs->num_regs; i++) {
+ if (regs->beg[i] == -1) {
+ rb_ary_push(ary, Qnil);
+ }
+ else {
+ VALUE str = rb_str_substr(target, regs->beg[i], regs->end[i]-regs->beg[i]);
+ if (taint) OBJ_TAINT(str);
+ rb_ary_push(ary, str);
+ }
+ }
+ return ary;
+}
+
+
+/* [MG]:FIXME: I put parens around the /.../.match() in the first line of the
+ second example to prevent the '*' followed by a '/' from ending the
+ comment. */
+
+/*
+ * call-seq:
+ * mtch.to_a => anArray
+ *
+ * Returns the array of matches.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.to_a #=> ["HX1138", "H", "X", "113", "8"]
+ *
+ * Because <code>to_a</code> is called when expanding
+ * <code>*</code><em>variable</em>, there's a useful assignment
+ * shortcut for extracting matched fields. This is slightly slower than
+ * accessing the fields directly (as an intermediate array is
+ * generated).
+ *
+ * all,f1,f2,f3 = *(/(.)(.)(\d+)(\d)/.match("THX1138."))
+ * all #=> "HX1138"
+ * f1 #=> "H"
+ * f2 #=> "X"
+ * f3 #=> "113"
+ */
+
+static VALUE
+match_to_a(match)
+ VALUE match;
+{
+ return match_array(match, 0);
+}
+
+
+/*
+ * call-seq:
+ * mtch.captures => array
+ *
+ * Returns the array of captures; equivalent to <code>mtch.to_a[1..-1]</code>.
+ *
+ * f1,f2,f3,f4 = /(.)(.)(\d+)(\d)/.match("THX1138.").captures
+ * f1 #=> "H"
+ * f2 #=> "X"
+ * f3 #=> "113"
+ * f4 #=> "8"
+ */
+static VALUE
+match_captures(match)
+ VALUE match;
+{
+ return match_array(match, 1);
+}
+
+
+/*
+ * call-seq:
+ * mtch[i] => obj
+ * mtch[start, length] => array
+ * mtch[range] => array
+ *
+ * Match Reference---<code>MatchData</code> acts as an array, and may be
+ * accessed using the normal array indexing techniques. <i>mtch</i>[0] is
+ * equivalent to the special variable <code>$&</code>, and returns the entire
+ * matched string. <i>mtch</i>[1], <i>mtch</i>[2], and so on return the values
+ * of the matched backreferences (portions of the pattern between parentheses).
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m[0] #=> "HX1138"
+ * m[1, 2] #=> ["H", "X"]
+ * m[1..3] #=> ["H", "X", "113"]
+ * m[-3, 2] #=> ["X", "113"]
+ */
+
+static VALUE
+match_aref(argc, argv, match)
+ int argc;
+ VALUE *argv;
+ VALUE match;
+{
+ VALUE idx, rest;
+
+ rb_scan_args(argc, argv, "11", &idx, &rest);
+
+ if (!NIL_P(rest) || !FIXNUM_P(idx) || FIX2INT(idx) < 0) {
+ return rb_ary_aref(argc, argv, match_to_a(match));
+ }
+ return rb_reg_nth_match(FIX2INT(idx), match);
+}
+
+static VALUE match_entry _((VALUE, long));
+static VALUE
+match_entry(match, n)
+ VALUE match;
+ long n;
+{
+ return rb_reg_nth_match(n, match);
+}
+
+
+/*
+ * call-seq:
+ * mtch.select([index]*) => array
+ *
+ * Uses each <i>index</i> to access the matching values, returning an array of
+ * the corresponding matches.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138: The Movie")
+ * m.to_a #=> ["HX1138", "H", "X", "113", "8"]
+ * m.select(0, 2, -2) #=> ["HX1138", "X", "113"]
+ */
+
+static VALUE
+match_values_at(argc, argv, match)
+ int argc;
+ VALUE *argv;
+ VALUE match;
+{
+ match_check(match);
+ return rb_values_at(match, RMATCH(match)->regs->num_regs, argc, argv, match_entry);
+}
+
+
+/*
+ * call-seq:
+ * mtch.select([index]*) => array
+ *
+ * Uses each <i>index</i> to access the matching values, returning an
+ * array of the corresponding matches.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138: The Movie")
+ * m.to_a #=> ["HX1138", "H", "X", "113", "8"]
+ * m.select(0, 2, -2) #=> ["HX1138", "X", "113"]
+ */
+
+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;
+ VALUE target;
+ VALUE result = rb_ary_new();
+ int i;
+ int taint = OBJ_TAINTED(match);
+
+ match_check(match);
+ regs = RMATCH(match)->regs;
+ target = RMATCH(match)->str;
+
+ 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
+ *
+ * Returns the entire matched string.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.to_s #=> "HX1138"
+ */
+
+static VALUE
+match_to_s(match)
+ VALUE match;
+{
+ VALUE str = rb_reg_last_match(match);
+
+ if (NIL_P(str)) str = rb_str_new(0,0);
+ if (OBJ_TAINTED(match)) OBJ_TAINT(str);
+ if (OBJ_TAINTED(RMATCH(match)->str)) OBJ_TAINT(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * mtch.string => str
+ *
+ * Returns a frozen copy of the string passed in to <code>match</code>.
+ *
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ * m.string #=> "THX1138."
+ */
+
+static VALUE
+match_string(match)
+ VALUE match;
+{
+ match_check(match);
+ return RMATCH(match)->str; /* str is frozen */
+}
+
+VALUE rb_cRegexp;
+
+static void
+rb_reg_initialize(obj, s, len, options)
+ VALUE obj;
+ const char *s;
+ long len;
+ int options; /* CASEFOLD = 1 */
+ /* EXTENDED = 2 */
+ /* MULTILINE = 4 */
+ /* CODE_NONE = 16 */
+ /* CODE_EUC = 32 */
+ /* CODE_SJIS = 48 */
+ /* CODE_UTF8 = 64 */
+{
+ struct RRegexp *re = RREGEXP(obj);
+
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify regexp");
+ rb_check_frozen(obj);
+ if (FL_TEST(obj, REG_LITERAL))
+ rb_raise(rb_eSecurityError, "can't modify literal regexp");
+ if (re->ptr) re_free_pattern(re->ptr);
+ if (re->str) free(re->str);
+ re->ptr = 0;
+ re->str = 0;
+
+ switch (options & ~0xf) {
+ case 0:
+ default:
+ FL_SET(re, reg_kcode);
+ break;
+ case 16:
+ kcode_none(re);
+ break;
+ case 32:
+ kcode_euc(re);
+ break;
+ case 48:
+ kcode_sjis(re);
+ break;
+ case 64:
+ kcode_utf8(re);
+ break;
+ }
+
+ if (options & ~0xf) {
+ rb_kcode_set_option((VALUE)re);
+ }
+ if (ruby_ignorecase) {
+ options |= RE_OPTION_IGNORECASE;
+ FL_SET(re, REG_CASESTATE);
+ }
+ re->ptr = make_regexp(s, len, options & 0xf);
+ re->str = ALLOC_N(char, len+1);
+ memcpy(re->str, s, len);
+ re->str[len] = '\0';
+ re->len = len;
+ if (options & ~0xf) {
+ rb_kcode_reset_option();
+ }
+ if (ruby_in_compile) FL_SET(obj, REG_LITERAL);
+}
+
+static VALUE rb_reg_s_alloc _((VALUE));
+static VALUE
+rb_reg_s_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(re, struct RRegexp);
+ OBJSETUP(re, klass, T_REGEXP);
+
+ re->ptr = 0;
+ re->len = 0;
+ re->str = 0;
+
+ return (VALUE)re;
+}
+
+VALUE
+rb_reg_new(s, len, options)
+ const char *s;
+ long len;
+ int options;
+{
+ VALUE re = rb_reg_s_alloc(rb_cRegexp);
+
+ rb_reg_initialize(re, s, len, options);
+ return (VALUE)re;
+}
+
+static int case_cache;
+static int kcode_cache;
+static VALUE reg_cache;
+
+VALUE
+rb_reg_regcomp(str)
+ VALUE str;
+{
+ volatile VALUE save_str = str;
+ if (reg_cache && RREGEXP(reg_cache)->len == RSTRING(str)->len
+ && case_cache == ruby_ignorecase
+ && kcode_cache == reg_kcode
+ && memcmp(RREGEXP(reg_cache)->str, RSTRING(str)->ptr, RSTRING(str)->len) == 0)
+ return reg_cache;
+
+ case_cache = ruby_ignorecase;
+ kcode_cache = reg_kcode;
+ return reg_cache = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ ruby_ignorecase);
+}
+
+static int
+rb_reg_cur_kcode(re)
+ VALUE re;
+{
+ if (FL_TEST(re, KCODE_FIXED)) {
+ return RBASIC(re)->flags & KCODE_MASK;
+ }
+ return 0;
+}
+
+/*
+ * call-seq:
+ * rxp.hash => fixnum
+ *
+ * Produce a hash based on the text and options of this regular expression.
+ */
+
+static VALUE
+rb_reg_hash(re)
+ VALUE re;
+{
+ int hashval, len;
+ char *p;
+
+ rb_reg_check(re);
+ hashval = RREGEXP(re)->ptr->options;
+ len = RREGEXP(re)->len;
+ p = RREGEXP(re)->str;
+ while (len--) {
+ hashval = hashval * 33 + *p++;
+ }
+ hashval = hashval + (hashval>>5);
+
+ return INT2FIX(hashval);
+}
+
+
+/*
+ * call-seq:
+ * rxp == other_rxp => true or false
+ * rxp.eql?(other_rxp) => true or false
+ *
+ * Equality---Two regexps are equal if their patterns are identical, they have
+ * the same character set code, and their <code>casefold?</code> values are the
+ * same.
+ *
+ * /abc/ == /abc/x #=> false
+ * /abc/ == /abc/i #=> false
+ * /abc/u == /abc/n #=> false
+ */
+
+static VALUE
+rb_reg_equal(re1, re2)
+ VALUE re1, re2;
+{
+ if (re1 == re2) return Qtrue;
+ if (TYPE(re2) != T_REGEXP) return Qfalse;
+ rb_reg_check(re1); rb_reg_check(re2);
+ if (RREGEXP(re1)->len != RREGEXP(re2)->len) return Qfalse;
+ if (memcmp(RREGEXP(re1)->str, RREGEXP(re2)->str, RREGEXP(re1)->len) == 0 &&
+ rb_reg_cur_kcode(re1) == rb_reg_cur_kcode(re2) &&
+ RREGEXP(re1)->ptr->options == RREGEXP(re2)->ptr->options) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * rxp.match(str) => matchdata or nil
+ *
+ * Returns a <code>MatchData</code> object describing the match, or
+ * <code>nil</code> if there was no match. This is equivalent to retrieving the
+ * value of the special variable <code>$~</code> following a normal match.
+ *
+ * /(.)(.)(.)/.match("abc")[2] #=> "b"
+ */
+
+VALUE
+rb_reg_match(re, str)
+ VALUE re, str;
+{
+ long start;
+
+ if (NIL_P(str)) {
+ rb_backref_set(Qnil);
+ return Qnil;
+ }
+ StringValue(str);
+ start = rb_reg_search(re, str, 0, 0);
+ if (start < 0) {
+ return Qnil;
+ }
+ return LONG2FIX(start);
+}
+
+
+/*
+ * call-seq:
+ * rxp === str => true or false
+ *
+ * Case Equality---Synonym for <code>Regexp#=~</code> used in case statements.
+ *
+ * a = "HELLO"
+ * case a
+ * when /^[a-z]*$/; print "Lower case\n"
+ * when /^[A-Z]*$/; print "Upper case\n"
+ * else; print "Mixed case\n"
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * Upper case
+ */
+
+VALUE
+rb_reg_eqq(re, str)
+ VALUE re, str;
+{
+ long start;
+
+ if (TYPE(str) != T_STRING) {
+ str = rb_check_string_type(str);
+ if (NIL_P(str)) {
+ rb_backref_set(Qnil);
+ return Qfalse;
+ }
+ }
+ StringValue(str);
+ start = rb_reg_search(re, str, 0, 0);
+ if (start < 0) {
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * ~ rxp => integer or nil
+ *
+ * Match---Matches <i>rxp</i> against the contents of <code>$_</code>.
+ * Equivalent to <code><i>rxp</i> =~ $_</code>.
+ *
+ * $_ = "input data"
+ * ~ /at/ #=> 7
+ */
+
+VALUE
+rb_reg_match2(re)
+ VALUE re;
+{
+ long start;
+ VALUE line = rb_lastline_get();
+
+ if (TYPE(line) != T_STRING) {
+ rb_backref_set(Qnil);
+ return Qnil;
+ }
+
+ start = rb_reg_search(re, line, 0, 0);
+ if (start < 0) {
+ return Qnil;
+ }
+ return LONG2FIX(start);
+}
+
+
+/*
+ * call-seq:
+ * rxp.match(str) => matchdata or nil
+ *
+ * Returns a <code>MatchData</code> object describing the match, or
+ * <code>nil</code> if there was no match. This is equivalent to retrieving the
+ * value of the special variable <code>$~</code> following a normal match.
+ *
+ * /(.)(.)(.)/.match("abc")[2] #=> "b"
+ */
+
+static VALUE
+rb_reg_match_m(re, str)
+ VALUE re, str;
+{
+ VALUE result = rb_reg_match(re, str);
+
+ if (NIL_P(result)) return Qnil;
+ result = rb_backref_get();
+ rb_match_busy(result);
+ return result;
+}
+
+/*
+ * Document-method: compile
+ *
+ * Synonym for <code>Regexp.new</code>
+ */
+
+/*
+ * call-seq:
+ * Regexp.new(string [, options [, lang]]) => regexp
+ * Regexp.new(regexp) => regexp
+ * Regexp.compile(string [, options [, lang]]) => regexp
+ * Regexp.compile(regexp) => regexp
+ *
+ * Constructs a new regular expression from <i>pattern</i>, which can be either
+ * a <code>String</code> or a <code>Regexp</code> (in which case that regexp's
+ * options are propagated, and new options may not be specified (a change as of
+ * Ruby 1.8). If <i>options</i> is a <code>Fixnum</code>, it should be one or
+ * more of the constants <code>Regexp::EXTENDED</code>,
+ * <code>Regexp::IGNORECASE</code>, and <code>Regexp::MULTILINE</code>,
+ * <em>or</em>-ed together. Otherwise, if <i>options</i> is not
+ * <code>nil</code>, the regexp will be case insensitive. The <i>lang</i>
+ * parameter enables multibyte support for the regexp: `n', `N' = none, `e',
+ * `E' = EUC, `s', `S' = SJIS, `u', `U' = UTF-8.
+ *
+ * r1 = Regexp.new('^a-z+:\\s+\w+') #=> /^a-z+:\s+\w+/
+ * r2 = Regexp.new('cat', true) #=> /cat/i
+ * r3 = Regexp.new('dog', Regexp::EXTENDED) #=> /dog/x
+ * r4 = Regexp.new(r2) #=> /cat/i
+ */
+
+static VALUE
+rb_reg_initialize_m(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ const char *s;
+ long len;
+ int flags = 0;
+
+ if (argc == 0 || argc > 3) {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ if (TYPE(argv[0]) == T_REGEXP) {
+ if (argc > 1) {
+ rb_warn("flags%s ignored", (argc == 3) ? " and encoding": "");
+ }
+ rb_reg_check(argv[0]);
+ flags = RREGEXP(argv[0])->ptr->options & 0xf;
+ if (FL_TEST(argv[0], KCODE_FIXED)) {
+ switch (RBASIC(argv[0])->flags & KCODE_MASK) {
+ case KCODE_NONE:
+ flags |= 16;
+ break;
+ case KCODE_EUC:
+ flags |= 32;
+ break;
+ case KCODE_SJIS:
+ flags |= 48;
+ break;
+ case KCODE_UTF8:
+ flags |= 64;
+ break;
+ default:
+ break;
+ }
+ }
+ s = RREGEXP(argv[0])->str;
+ len = RREGEXP(argv[0])->len;
+ }
+ else {
+ if (argc >= 2) {
+ if (FIXNUM_P(argv[1])) flags = FIX2INT(argv[1]);
+ else if (RTEST(argv[1])) flags = RE_OPTION_IGNORECASE;
+ }
+ if (argc == 3 && !NIL_P(argv[2])) {
+ char *kcode = StringValuePtr(argv[2]);
+
+ flags &= ~0x70;
+ switch (kcode[0]) {
+ case 'n': case 'N':
+ flags |= 16;
+ break;
+ case 'e': case 'E':
+ flags |= 32;
+ break;
+ case 's': case 'S':
+ flags |= 48;
+ break;
+ case 'u': case 'U':
+ flags |= 64;
+ break;
+ default:
+ break;
+ }
+ }
+ s = StringValuePtr(argv[0]);
+ len = RSTRING(argv[0])->len;
+ }
+ rb_reg_initialize(self, s, len, flags);
+ return self;
+}
+
+VALUE
+rb_reg_quote(str)
+ VALUE str;
+{
+ char *s, *send, *t;
+ VALUE tmp;
+ int c;
+
+ s = RSTRING(str)->ptr;
+ send = s + RSTRING(str)->len;
+ for (; s < send; s++) {
+ c = *s;
+ if (ismbchar(c)) {
+ int n = mbclen(c);
+
+ while (n-- && s < send)
+ s++;
+ s--;
+ continue;
+ }
+ switch (c) {
+ case '[': case ']': case '{': case '}':
+ case '(': case ')': case '|': case '-':
+ case '*': case '.': case '\\':
+ case '?': case '+': case '^': case '$':
+ case ' ': case '#':
+ case '\t': case '\f': case '\n': case '\r':
+ goto meta_found;
+ }
+ }
+ return rb_str_new3(str);
+
+ meta_found:
+ tmp = rb_str_new(0, RSTRING(str)->len*2);
+ t = RSTRING(tmp)->ptr;
+ /* copy upto metacharacter */
+ memcpy(t, RSTRING(str)->ptr, s - RSTRING(str)->ptr);
+ t += s - RSTRING(str)->ptr;
+
+ for (; s < send; s++) {
+ c = *s;
+ if (ismbchar(c)) {
+ int n = mbclen(c);
+
+ while (n-- && s < send)
+ *t++ = *s++;
+ s--;
+ continue;
+ }
+ switch (c) {
+ case '[': case ']': case '{': case '}':
+ case '(': case ')': case '|': case '-':
+ case '*': case '.': case '\\':
+ case '?': case '+': case '^': case '$':
+ case '#':
+ *t++ = '\\';
+ break;
+ case ' ':
+ *t++ = '\\';
+ *t++ = ' ';
+ continue;
+ case '\t':
+ *t++ = '\\';
+ *t++ = 't';
+ continue;
+ case '\n':
+ *t++ = '\\';
+ *t++ = 'n';
+ continue;
+ case '\r':
+ *t++ = '\\';
+ *t++ = 'r';
+ continue;
+ case '\f':
+ *t++ = '\\';
+ *t++ = 'f';
+ continue;
+ }
+ *t++ = c;
+ }
+ rb_str_resize(tmp, t - RSTRING(tmp)->ptr);
+ OBJ_INFECT(tmp, str);
+ return tmp;
+}
+
+
+/*
+ * call-seq:
+ * Regexp.escape(str) => a_str
+ * Regexp.quote(str) => a_str
+ *
+ * Escapes any characters that would have special meaning in a regular
+ * expression. Returns a new escaped string, or self if no characters are
+ * escaped. For any string,
+ * <code>Regexp.escape(<i>str</i>)=~<i>str</i></code> will be true.
+ *
+ * Regexp.escape('\\*?{}.') #=> \\\\\*\?\{\}\.
+ */
+
+static VALUE
+rb_reg_s_quote(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE str, kcode;
+ int kcode_saved = reg_kcode;
+
+ rb_scan_args(argc, argv, "11", &str, &kcode);
+ if (!NIL_P(kcode)) {
+ rb_set_kcode(StringValuePtr(kcode));
+ curr_kcode = reg_kcode;
+ reg_kcode = kcode_saved;
+ }
+ StringValue(str);
+ str = rb_reg_quote(str);
+ rb_kcode_reset_option();
+ return str;
+}
+
+int
+rb_kcode()
+{
+ switch (reg_kcode) {
+ case KCODE_EUC:
+ return MBCTYPE_EUC;
+ case KCODE_SJIS:
+ return MBCTYPE_SJIS;
+ case KCODE_UTF8:
+ return MBCTYPE_UTF8;
+ case KCODE_NONE:
+ return MBCTYPE_ASCII;
+ }
+ rb_bug("wrong reg_kcode value (0x%x)", reg_kcode);
+}
+
+static int
+rb_reg_get_kcode(re)
+ VALUE re;
+{
+ switch (RBASIC(re)->flags & KCODE_MASK) {
+ case KCODE_NONE:
+ return 16;
+ case KCODE_EUC:
+ return 32;
+ case KCODE_SJIS:
+ return 48;
+ case KCODE_UTF8:
+ return 64;
+ default:
+ return 0;
+ }
+}
+
+int
+rb_reg_options(re)
+ VALUE re;
+{
+ int options;
+
+ rb_reg_check(re);
+ options = RREGEXP(re)->ptr->options &
+ (RE_OPTION_IGNORECASE|RE_OPTION_MULTILINE|RE_OPTION_EXTENDED);
+ if (FL_TEST(re, KCODE_FIXED)) {
+ options |= rb_reg_get_kcode(re);
+ }
+ return options;
+}
+
+
+/*
+ * call-seq:
+ * Regexp.union([pattern]*) => new_str
+ *
+ * Return a <code>Regexp</code> object that is the union of the given
+ * <em>pattern</em>s, i.e., will match any of its parts. The <em>pattern</em>s
+ * can be Regexp objects, in which case their options will be preserved, or
+ * Strings. If no arguments are given, returns <code>/(?!)/</code>.
+ *
+ * Regexp.union #=> /(?!)/
+ * Regexp.union("penzance") #=> /penzance/
+ * Regexp.union("skiing", "sledding") #=> /skiing|sledding/
+ * Regexp.union(/dogs/, /cats/i) #=> /(?-mix:dogs)|(?i-mx:cats)/
+ */
+static VALUE
+rb_reg_s_union(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ if (argc == 0) {
+ VALUE args[1];
+ args[0] = rb_str_new2("(?!)");
+ return rb_class_new_instance(1, args, rb_cRegexp);
+ }
+ else if (argc == 1) {
+ VALUE v;
+ v = rb_check_convert_type(argv[0], T_REGEXP, "Regexp", "to_regexp");
+ if (!NIL_P(v))
+ return v;
+ else {
+ VALUE args[1];
+ args[0] = rb_reg_s_quote(argc, argv);
+ return rb_class_new_instance(1, args, rb_cRegexp);
+ }
+ }
+ else {
+ int i, kcode = -1;
+ VALUE kcode_re = Qnil;
+ VALUE source = rb_str_buf_new(0);
+ VALUE args[3];
+ for (i = 0; i < argc; i++) {
+ volatile VALUE v;
+ if (0 < i)
+ rb_str_buf_cat2(source, "|");
+ v = rb_check_convert_type(argv[i], T_REGEXP, "Regexp", "to_regexp");
+ if (!NIL_P(v)) {
+ if (FL_TEST(v, KCODE_FIXED)) {
+ if (kcode == -1) {
+ kcode_re = v;
+ kcode = RBASIC(v)->flags & KCODE_MASK;
+ }
+ else if ((RBASIC(v)->flags & KCODE_MASK) != kcode) {
+ volatile VALUE str1, str2;
+ str1 = rb_inspect(kcode_re);
+ str2 = rb_inspect(v);
+ rb_raise(rb_eArgError, "mixed kcode: %s and %s",
+ RSTRING(str1)->ptr, RSTRING(str2)->ptr);
+ }
+ }
+ v = rb_reg_to_s(v);
+ }
+ else {
+ args[0] = argv[i];
+ v = rb_reg_s_quote(1, args);
+ }
+ rb_str_buf_append(source, v);
+ }
+ args[0] = source;
+ args[1] = Qnil;
+ switch (kcode) {
+ case -1:
+ args[2] = Qnil;
+ break;
+ case KCODE_NONE:
+ args[2] = rb_str_new2("n");
+ break;
+ case KCODE_EUC:
+ args[2] = rb_str_new2("e");
+ break;
+ case KCODE_SJIS:
+ args[2] = rb_str_new2("s");
+ break;
+ case KCODE_UTF8:
+ args[2] = rb_str_new2("u");
+ break;
+ }
+ return rb_class_new_instance(3, args, rb_cRegexp);
+ }
+}
+
+/* :nodoc: */
+static VALUE
+rb_reg_init_copy(copy, re)
+ VALUE copy, re;
+{
+ if (copy == re) return copy;
+ rb_check_frozen(copy);
+ /* need better argument type check */
+ if (!rb_obj_is_instance_of(re, rb_obj_class(copy))) {
+ rb_raise(rb_eTypeError, "wrong argument type");
+ }
+ rb_reg_check(re);
+ rb_reg_initialize(copy, RREGEXP(re)->str, RREGEXP(re)->len,
+ rb_reg_options(re));
+ return copy;
+}
+
+VALUE
+rb_reg_regsub(str, src, regs)
+ VALUE str, src;
+ struct re_registers *regs;
+{
+ VALUE val = 0;
+ char *p, *s, *e, c;
+ int no;
+
+ p = s = RSTRING(str)->ptr;
+ e = s + RSTRING(str)->len;
+
+ while (s < e) {
+ char *ss = s;
+
+ c = *s++;
+ if (ismbchar(c)) {
+ s += mbclen(c) - 1;
+ continue;
+ }
+ if (c != '\\' || s == e) continue;
+
+ if (!val) {
+ val = rb_str_buf_new(ss-p);
+ rb_str_buf_cat(val, p, ss-p);
+ }
+ else {
+ rb_str_buf_cat(val, p, ss-p);
+ }
+
+ c = *s++;
+ p = s;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ no = c - '0';
+ break;
+ case '&':
+ no = 0;
+ break;
+
+ case '`':
+ rb_str_buf_cat(val, RSTRING(src)->ptr, BEG(0));
+ continue;
+
+ case '\'':
+ rb_str_buf_cat(val, RSTRING(src)->ptr+END(0), RSTRING(src)->len-END(0));
+ continue;
+
+ case '+':
+ no = regs->num_regs-1;
+ while (BEG(no) == -1 && no > 0) no--;
+ if (no == 0) continue;
+ break;
+
+ case '\\':
+ rb_str_buf_cat(val, s-1, 1);
+ continue;
+
+ default:
+ rb_str_buf_cat(val, s-2, 2);
+ continue;
+ }
+
+ if (no >= 0) {
+ if (no >= regs->num_regs) continue;
+ if (BEG(no) == -1) continue;
+ rb_str_buf_cat(val, RSTRING(src)->ptr+BEG(no), END(no)-BEG(no));
+ }
+ }
+
+ if (p < e) {
+ if (!val) {
+ val = rb_str_buf_new(e-p);
+ rb_str_buf_cat(val, p, e-p);
+ }
+ else {
+ rb_str_buf_cat(val, p, e-p);
+ }
+ }
+ if (!val) return str;
+
+ return val;
+}
+
+const char*
+rb_get_kcode()
+{
+ switch (reg_kcode) {
+ case KCODE_SJIS:
+ return "SJIS";
+ case KCODE_EUC:
+ return "EUC";
+ case KCODE_UTF8:
+ return "UTF8";
+ default:
+ return "NONE";
+ }
+}
+
+static VALUE
+kcode_getter()
+{
+ return rb_str_new2(rb_get_kcode());
+}
+
+void
+rb_set_kcode(code)
+ const char *code;
+{
+ if (code == 0) goto set_no_conversion;
+
+ switch (code[0]) {
+ case 'E':
+ case 'e':
+ reg_kcode = KCODE_EUC;
+ re_mbcinit(MBCTYPE_EUC);
+ break;
+ case 'S':
+ case 's':
+ reg_kcode = KCODE_SJIS;
+ re_mbcinit(MBCTYPE_SJIS);
+ break;
+ case 'U':
+ case 'u':
+ reg_kcode = KCODE_UTF8;
+ re_mbcinit(MBCTYPE_UTF8);
+ break;
+ default:
+ case 'N':
+ case 'n':
+ case 'A':
+ case 'a':
+ set_no_conversion:
+ reg_kcode = KCODE_NONE;
+ re_mbcinit(MBCTYPE_ASCII);
+ break;
+ }
+}
+
+static void
+kcode_setter(val)
+ VALUE val;
+{
+ may_need_recompile = 1;
+ rb_set_kcode(StringValuePtr(val));
+}
+
+static VALUE
+ignorecase_getter()
+{
+ return ruby_ignorecase?Qtrue:Qfalse;
+}
+
+static void
+ignorecase_setter(val, id)
+ VALUE val;
+ ID id;
+{
+ rb_warn("modifying %s is deprecated", rb_id2name(id));
+ may_need_recompile = 1;
+ ruby_ignorecase = RTEST(val);
+}
+
+static VALUE
+match_getter()
+{
+ VALUE match = rb_backref_get();
+
+ if (NIL_P(match)) return Qnil;
+ rb_match_busy(match);
+ return match;
+}
+
+static void
+match_setter(val)
+ VALUE val;
+{
+ if (!NIL_P(val)) {
+ Check_Type(val, T_MATCH);
+ }
+ rb_backref_set(val);
+}
+
+/*
+ * call-seq:
+ * Regexp.last_match => matchdata
+ * Regexp.last_match(fixnum) => str
+ *
+ * The first form returns the <code>MatchData</code> object generated by the
+ * last successful pattern match. Equivalent to reading the global variable
+ * <code>$~</code>. The second form returns the nth field in this
+ * <code>MatchData</code> object.
+ *
+ * /c(.)t/ =~ 'cat' #=> 0
+ * Regexp.last_match #=> #<MatchData:0x401b3d30>
+ * Regexp.last_match(0) #=> "cat"
+ * Regexp.last_match(1) #=> "a"
+ * Regexp.last_match(2) #=> nil
+ */
+
+static VALUE
+rb_reg_s_last_match(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE nth;
+
+ if (rb_scan_args(argc, argv, "01", &nth) == 1) {
+ return rb_reg_nth_match(NUM2INT(nth), rb_backref_get());
+ }
+ return match_getter();
+}
+
+
+/*
+ * Document-class: Regexp
+ *
+ * A <code>Regexp</code> holds a regular expression, used to match a pattern
+ * against strings. Regexps are created using the <code>/.../</code> and
+ * <code>%r{...}</code> literals, and by the <code>Regexp::new</code>
+ * constructor.
+ *
+ */
+
+void
+Init_Regexp()
+{
+ rb_eRegexpError = rb_define_class("RegexpError", rb_eStandardError);
+
+ re_set_casetable(casetable);
+#if DEFAULT_KCODE == KCODE_EUC
+ re_mbcinit(MBCTYPE_EUC);
+#else
+#if DEFAULT_KCODE == KCODE_SJIS
+ re_mbcinit(MBCTYPE_SJIS);
+#else
+#if DEFAULT_KCODE == KCODE_UTF8
+ re_mbcinit(MBCTYPE_UTF8);
+#else
+ re_mbcinit(MBCTYPE_ASCII);
+#endif
+#endif
+#endif
+
+ rb_define_virtual_variable("$~", match_getter, match_setter);
+ rb_define_virtual_variable("$&", last_match_getter, 0);
+ rb_define_virtual_variable("$`", prematch_getter, 0);
+ rb_define_virtual_variable("$'", postmatch_getter, 0);
+ rb_define_virtual_variable("$+", last_paren_match_getter, 0);
+
+ rb_define_virtual_variable("$=", ignorecase_getter, ignorecase_setter);
+ rb_define_virtual_variable("$KCODE", kcode_getter, kcode_setter);
+ rb_define_virtual_variable("$-K", kcode_getter, kcode_setter);
+
+ rb_cRegexp = rb_define_class("Regexp", rb_cObject);
+ rb_define_alloc_func(rb_cRegexp, rb_reg_s_alloc);
+ rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_cRegexp, "quote", rb_reg_s_quote, -1);
+ rb_define_singleton_method(rb_cRegexp, "escape", rb_reg_s_quote, -1);
+ rb_define_singleton_method(rb_cRegexp, "union", rb_reg_s_union, -1);
+ rb_define_singleton_method(rb_cRegexp, "last_match", rb_reg_s_last_match, -1);
+
+ rb_define_method(rb_cRegexp, "initialize", rb_reg_initialize_m, -1);
+ rb_define_method(rb_cRegexp, "initialize_copy", rb_reg_init_copy, 1);
+ rb_define_method(rb_cRegexp, "hash", rb_reg_hash, 0);
+ rb_define_method(rb_cRegexp, "eql?", rb_reg_equal, 1);
+ rb_define_method(rb_cRegexp, "==", rb_reg_equal, 1);
+ rb_define_method(rb_cRegexp, "=~", rb_reg_match, 1);
+ rb_define_method(rb_cRegexp, "===", rb_reg_eqq, 1);
+ rb_define_method(rb_cRegexp, "~", rb_reg_match2, 0);
+ rb_define_method(rb_cRegexp, "match", rb_reg_match_m, 1);
+ rb_define_method(rb_cRegexp, "to_s", rb_reg_to_s, 0);
+ rb_define_method(rb_cRegexp, "inspect", rb_reg_inspect, 0);
+ rb_define_method(rb_cRegexp, "source", rb_reg_source, 0);
+ rb_define_method(rb_cRegexp, "casefold?", rb_reg_casefold_p, 0);
+ rb_define_method(rb_cRegexp, "options", rb_reg_options_m, 0);
+ rb_define_method(rb_cRegexp, "kcode", rb_reg_kcode_m, 0);
+
+ rb_define_const(rb_cRegexp, "IGNORECASE", INT2FIX(RE_OPTION_IGNORECASE));
+ rb_define_const(rb_cRegexp, "EXTENDED", INT2FIX(RE_OPTION_EXTENDED));
+ rb_define_const(rb_cRegexp, "MULTILINE", INT2FIX(RE_OPTION_MULTILINE));
+
+ rb_global_variable(&reg_cache);
+
+ rb_cMatch = rb_define_class("MatchData", rb_cObject);
+ rb_define_global_const("MatchingData", rb_cMatch);
+ rb_define_alloc_func(rb_cMatch, match_alloc);
+ rb_undef_method(CLASS_OF(rb_cMatch), "new");
+
+ rb_define_method(rb_cMatch, "initialize_copy", match_init_copy, 1);
+ rb_define_method(rb_cMatch, "size", match_size, 0);
+ rb_define_method(rb_cMatch, "length", match_size, 0);
+ rb_define_method(rb_cMatch, "offset", match_offset, 1);
+ rb_define_method(rb_cMatch, "begin", match_begin, 1);
+ rb_define_method(rb_cMatch, "end", match_end, 1);
+ rb_define_method(rb_cMatch, "to_a", match_to_a, 0);
+ rb_define_method(rb_cMatch, "[]", match_aref, -1);
+ rb_define_method(rb_cMatch, "captures", match_captures, 0);
+ rb_define_method(rb_cMatch, "select", match_select, -1);
+ rb_define_method(rb_cMatch, "values_at", match_values_at, -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);
+ rb_define_method(rb_cMatch, "inspect", rb_any_to_s, 0); /* in object.c */
+ rb_define_method(rb_cMatch, "string", match_string, 0);
+}
+/* Extended regular expression matching and search library.
+ Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file LGPL. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto)
+ Last change: May 21, 1993 by t^2 */
+/* removed gapped buffer support, multiple syntax support by matz <matz@nts.co.jp> */
+/* Perl5 extension added by matz <matz@caelum.co.jp> */
+/* UTF-8 extension added Jan 16 1999 by Yoshida Masato <yoshidam@tau.bekkoame.ne.jp> */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+/* We write fatal error messages on standard error. */
+#include <stdio.h>
+
+/* isalpha(3) etc. are used for the character classes. */
+#include <ctype.h>
+#include <sys/types.h>
+
+#ifndef PARAMS
+# if defined __GNUC__ || (defined __STDC__ && __STDC__)
+# define PARAMS(args) args
+# else
+# define PARAMS(args) ()
+# endif /* GCC. */
+#endif /* Not PARAMS. */
+
+#if defined(STDC_HEADERS)
+# include <stddef.h>
+#else
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+# include <sys/types.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#if !defined(__STDC__) && !defined(_MSC_VER)
+# define volatile
+#endif
+
+#ifdef HAVE_PROTOTYPES
+# define _(args) args
+#else
+# define _(args) ()
+#endif
+
+#ifdef RUBY_PLATFORM
+#include "defines.h"
+#undef xmalloc
+#undef xrealloc
+#undef xcalloc
+#undef xfree
+
+# define RUBY
+extern int rb_prohibit_interrupt;
+extern int rb_trap_pending;
+void rb_trap_exec _((void));
+
+# define CHECK_INTS do {\
+ if (!rb_prohibit_interrupt) {\
+ if (rb_trap_pending) rb_trap_exec();\
+ }\
+} while (0)
+#endif
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+# ifndef atarist
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# endif /* atarist */
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+void *alloca ();
+# endif
+# endif /* AIX */
+# endif /* HAVE_ALLOCA_H */
+
+#endif /* __GNUC__ */
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#define xmalloc malloc
+#define xrealloc realloc
+#define xcalloc calloc
+#define xfree free
+
+#ifdef C_ALLOCA
+#define FREE_VARIABLES() alloca(0)
+#else
+#define FREE_VARIABLES()
+#endif
+
+#define FREE_AND_RETURN_VOID(stackb) do { \
+ FREE_VARIABLES(); \
+ if (stackb != stacka) xfree(stackb); \
+ return; \
+} while(0)
+
+#define FREE_AND_RETURN(stackb,val) do { \
+ FREE_VARIABLES(); \
+ if (stackb != stacka) xfree(stackb); \
+ return(val); \
+} while(0)
+
+#define DOUBLE_STACK(type) do { \
+ type *stackx; \
+ unsigned int xlen = stacke - stackb; \
+ if (stackb == stacka) { \
+ stackx = (type*)xmalloc(2 * xlen * sizeof(type)); \
+ if (!stackx) goto memory_exhausted; \
+ memcpy(stackx, stackb, xlen * sizeof (type)); \
+ } \
+ else { \
+ stackx = (type*)xrealloc(stackb, 2 * xlen * sizeof(type)); \
+ if (!stackx) goto memory_exhausted; \
+ } \
+ /* Rearrange the pointers. */ \
+ stackp = stackx + (stackp - stackb); \
+ stackb = stackx; \
+ stacke = stackb + 2 * xlen; \
+} while (0)
+
+#define RE_TALLOC(n,t) ((t*)alloca((n)*sizeof(t)))
+#define TMALLOC(n,t) ((t*)xmalloc((n)*sizeof(t)))
+#define TREALLOC(s,n,t) (s=((t*)xrealloc(s,(n)*sizeof(t))))
+
+#define EXPAND_FAIL_STACK() DOUBLE_STACK(unsigned char*)
+#define ENSURE_FAIL_STACK(n) \
+ do { \
+ if (stacke - stackp <= (n)) { \
+ /* if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \
+ { \
+ FREE_AND_RETURN(stackb,(-2)); \
+ }*/ \
+ \
+ /* Roughly double the size of the stack. */ \
+ EXPAND_FAIL_STACK(); \
+ } \
+ } while (0)
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+/* Subroutines for re_compile_pattern. */
+static void store_jump _((char*, int, char*));
+static void insert_jump _((int, char*, char*, char*));
+static void store_jump_n _((char*, int, char*, unsigned));
+static void insert_jump_n _((int, char*, char*, char*, unsigned));
+static void insert_op _((int, char*, char*));
+static void insert_op_2 _((int, char*, char*, int, int));
+static int memcmp_translate _((unsigned char*, unsigned char*, int));
+
+/* Define the syntax stuff, so we can do the \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match. */
+#define Sword 1
+#define Sword2 2
+
+#define SYNTAX(c) re_syntax_table[c]
+
+static char re_syntax_table[256];
+static void init_syntax_once _((void));
+static const unsigned char *translate = 0;
+static void init_regs _((struct re_registers*, unsigned int));
+static void bm_init_skip _((int *, unsigned char*, int, const unsigned char*));
+static int current_mbctype = MBCTYPE_ASCII;
+
+#undef P
+
+#ifdef RUBY
+#include "util.h"
+void rb_warn _((const char*, ...));
+# define re_warning(x) rb_warn(x)
+#endif
+
+#ifndef re_warning
+# define re_warning(x)
+#endif
+
+static void
+init_syntax_once()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ memset(re_syntax_table, 0, sizeof re_syntax_table);
+
+ for (c=0; c<=0x7f; c++)
+ if (isalnum(c))
+ re_syntax_table[c] = Sword;
+ re_syntax_table['_'] = Sword;
+
+ for (c=0x80; c<=0xff; c++)
+ if (isalnum(c))
+ re_syntax_table[c] = Sword2;
+ done = 1;
+}
+
+void
+re_set_casetable(table)
+ const char *table;
+{
+ translate = (const unsigned char*)table;
+}
+
+/* Jim Meyering writes:
+
+ "... Some ctype macros are valid only for character codes that
+ isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+ using /bin/cc or gcc but without giving an ansi option). So, all
+ ctype uses should be through macros like ISPRINT... If
+ STDC_HEADERS is defined, then autoconf has verified that the ctype
+ macros don't need to be guarded with references to isascii. ...
+ Defining isascii to 1 should let any compiler worth its salt
+ eliminate the && through constant folding."
+ Solaris defines some of these symbols so we must undefine them first. */
+
+#undef ISASCII
+#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII)
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#undef ISPRINT
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifndef NULL
+# define NULL (void *)0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+# define SIGN_EXTEND_CHAR(c) ((signed char)(c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
+#endif
+
+/* These are the command codes that appear in compiled regular
+ expressions, one per byte. Some command codes are followed by
+ argument bytes. A command code can specify any interpretation
+ whatsoever for its arguments. Zero-bytes may appear in the compiled
+ regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+enum regexpcode
+ {
+ unused=0,
+ exactn=1, /* Followed by one byte giving n, then by n literal bytes. */
+ begline, /* Fail unless at beginning of line. */
+ endline, /* Fail unless at end of line. */
+ begbuf, /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ endbuf, /* Analogously, for end of buffer/string. */
+ endbuf2, /* End of buffer/string, or newline just before it. */
+ begpos, /* Matches where last scan//gsub left off. */
+ jump, /* Followed by two bytes giving relative address to jump to. */
+ jump_past_alt,/* Same as jump, but marks the end of an alternative. */
+ on_failure_jump, /* Followed by two bytes giving relative address of
+ place to resume at in case of failure. */
+ finalize_jump, /* Throw away latest failure point and then jump to
+ address. */
+ maybe_finalize_jump, /* Like jump but finalize if safe to do so.
+ This is used to jump back to the beginning
+ of a repeat. If the command that follows
+ this jump is clearly incompatible with the
+ one at the beginning of the repeat, such that
+ we can be sure that there is no use backtracking
+ out of repetitions already completed,
+ then we finalize. */
+ dummy_failure_jump, /* Jump, and push a dummy failure point. This
+ failure point will be thrown away if an attempt
+ is made to use it for a failure. A + construct
+ makes this before the first repeat. Also
+ use it as an intermediary kind of jump when
+ compiling an or construct. */
+ push_dummy_failure, /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ succeed_n, /* Used like on_failure_jump except has to succeed n times;
+ then gets turned into an on_failure_jump. The relative
+ address following it is useless until then. The
+ address is followed by two bytes containing n. */
+ jump_n, /* Similar to jump, but jump n times only; also the relative
+ address following is in turn followed by yet two more bytes
+ containing n. */
+ try_next, /* Jump to next pattern for the first time,
+ leaving this pattern on the failure stack. */
+ finalize_push, /* Finalize stack and push the beginning of the pattern
+ on the stack to retry (used for non-greedy match) */
+ finalize_push_n, /* Similar to finalize_push, buf finalize n time only */
+ set_number_at, /* Set the following relative location to the
+ subsequent number. */
+ anychar, /* Matches any (more or less) one character excluding newlines. */
+ anychar_repeat, /* Matches sequence of characters excluding newlines. */
+ charset, /* Matches any one char belonging to specified set.
+ First following byte is number of bitmap bytes.
+ Then come bytes for a bitmap saying which chars are in.
+ Bits in each byte are ordered low-bit-first.
+ A character is in the set if its bit is 1.
+ A character too large to have a bit in the map
+ is automatically not in the set. */
+ charset_not, /* Same parameters as charset, but match any character
+ that is not one of those specified. */
+ start_memory, /* Start remembering the text that is matched, for
+ storing in a memory register. Followed by one
+ byte containing the register number. Register numbers
+ must be in the range 0 through RE_NREGS. */
+ stop_memory, /* Stop remembering the text that is matched
+ and store it in a memory register. Followed by
+ one byte containing the register number. Register
+ numbers must be in the range 0 through RE_NREGS. */
+ start_paren, /* Place holder at the start of (?:..). */
+ stop_paren, /* Place holder at the end of (?:..). */
+ casefold_on, /* Turn on casefold flag. */
+ casefold_off, /* Turn off casefold flag. */
+ option_set, /* Turn on multi line match (match with newlines). */
+ start_nowidth, /* Save string point to the stack. */
+ stop_nowidth, /* Restore string place at the point start_nowidth. */
+ pop_and_fail, /* Fail after popping nowidth entry from stack. */
+ stop_backtrack, /* Restore backtrack stack at the point start_nowidth. */
+ duplicate, /* Match a duplicate of something remembered.
+ Followed by one byte containing the index of the memory
+ register. */
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+ };
+
+
+/* Number of failure points to allocate space for initially,
+ when matching. If this number is exceeded, more space is allocated,
+ so it is not a hard limit. */
+
+#ifndef NFAILURES
+#define NFAILURES 160
+#endif
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+#define STORE_NUMBER(destination, number) \
+ do { (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; } while (0)
+
+/* Same as STORE_NUMBER, except increment the destination pointer to
+ the byte after where the number is stored. Watch out that values for
+ DESTINATION such as p + 1 won't work, whereas p will. */
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { STORE_NUMBER(destination, number); \
+ (destination) += 2; } while (0)
+
+
+/* Put into DESTINATION a number stored in two contingous bytes starting
+ at SOURCE. */
+#define EXTRACT_NUMBER(destination, source) \
+ do { (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR(*(char*)((source) + 1)) << 8; } while (0)
+
+/* Same as EXTRACT_NUMBER, except increment the pointer for source to
+ point to second byte of SOURCE. Note that SOURCE has to be a value
+ such as p, not, e.g., p + 1. */
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { EXTRACT_NUMBER(destination, source); \
+ (source) += 2; } while (0)
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit-mask comprised of the various bits
+ defined in regex.h. */
+
+long
+re_set_syntax(syntax)
+ long syntax;
+{
+ /* obsolete */
+ return 0;
+}
+
+/* Macros for re_compile_pattern, which is found below these definitions. */
+
+#define TRANSLATE_P() ((options&RE_OPTION_IGNORECASE) && translate)
+#define MAY_TRANSLATE() ((bufp->options&(RE_OPTION_IGNORECASE|RE_MAY_IGNORECASE)) && translate)
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) goto end_of_pattern; \
+ c = (unsigned char) *p++; \
+ if (TRANSLATE_P()) c = (unsigned char)translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) goto end_of_pattern; \
+ c = (unsigned char)*p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+#define MBC2WC(c, p) \
+ do { \
+ if (current_mbctype == MBCTYPE_UTF8) { \
+ int n = mbclen(c) - 1; \
+ c &= (1<<(BYTEWIDTH-2-n)) - 1; \
+ while (n--) { \
+ c = c << 6 | (*p++ & ((1<<6)-1)); \
+ } \
+ } \
+ else { \
+ c <<= 8; \
+ c |= (unsigned char)*(p)++; \
+ } \
+ } while (0)
+
+#define PATFETCH_MBC(c) \
+ do { \
+ if (p + mbclen(c) - 1 >= pend) goto end_of_pattern; \
+ MBC2WC(c, p); \
+ } while(0)
+
+#define WC2MBC1ST(c) \
+ ((current_mbctype != MBCTYPE_UTF8) ? ((c<0x100) ? (c) : (((c)>>8)&0xff)) : utf8_firstbyte(c))
+
+typedef unsigned int (*mbc_startpos_func_t) _((const char *string, unsigned int pos));
+
+static unsigned int asc_startpos _((const char *string, unsigned int pos));
+static unsigned int euc_startpos _((const char *string, unsigned int pos));
+static unsigned int sjis_startpos _((const char *string, unsigned int pos));
+static unsigned int utf8_startpos _((const char *string, unsigned int pos));
+
+static const mbc_startpos_func_t mbc_startpos_func[4] = {
+ asc_startpos, euc_startpos, sjis_startpos, utf8_startpos
+};
+
+#define mbc_startpos(start, pos) (*mbc_startpos_func[current_mbctype])((start), (pos))
+
+static unsigned int
+utf8_firstbyte(c)
+ unsigned long c;
+{
+ if (c < 0x80) return c;
+ if (c <= 0x7ff) return ((c>>6)&0xff)|0xc0;
+ if (c <= 0xffff) return ((c>>12)&0xff)|0xe0;
+ if (c <= 0x1fffff) return ((c>>18)&0xff)|0xf0;
+ if (c <= 0x3ffffff) return ((c>>24)&0xff)|0xf8;
+ if (c <= 0x7fffffff) return ((c>>30)&0xff)|0xfc;
+#if SIZEOF_INT > 4
+ if (c <= 0xfffffffff) return 0xfe;
+#else
+ return 0xfe;
+#endif
+}
+
+static void
+print_mbc(c)
+ unsigned int c;
+{
+ if (current_mbctype == MBCTYPE_UTF8) {
+ if (c < 0x80)
+ printf("%c", (int)c);
+ else if (c <= 0x7ff)
+ printf("%c%c", (int)utf8_firstbyte(c), (int)(c & 0x3f));
+ else if (c <= 0xffff)
+ printf("%c%c%c", (int)utf8_firstbyte(c), (int)((c >> 6) & 0x3f),
+ (int)(c & 0x3f));
+ else if (c <= 0x1fffff)
+ printf("%c%c%c%c", (int)utf8_firstbyte(c), (int)((c >> 12) & 0x3f),
+ (int)((c >> 6) & 0x3f), (int)(c & 0x3f));
+ else if (c <= 0x3ffffff)
+ printf("%c%c%c%c%c", (int)utf8_firstbyte(c), (int)((c >> 18) & 0x3f),
+ (int)((c >> 12) & 0x3f), (int)((c >> 6) & 0x3f), (int)(c & 0x3f));
+ else if (c <= 0x7fffffff)
+ printf("%c%c%c%c%c%c", (int)utf8_firstbyte(c), (int)((c >> 24) & 0x3f),
+ (int)((c >> 18) & 0x3f), (int)((c >> 12) & 0x3f),
+ (int)((c >> 6) & 0x3f), (int)(c & 0x3f));
+ }
+ else if (c < 0xff) {
+ printf("\\%o", (int)c);
+ }
+ else {
+ printf("%c%c", (int)(c >> BYTEWIDTH), (int)(c &0xff));
+ }
+}
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 28
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ do { \
+ while (b - bufp->buffer + (n) >= bufp->allocated) \
+ EXTEND_BUFFER; \
+ } while (0)
+
+/* Make sure we have one more byte of buffer space and then add CH to it. */
+#define BUFPUSH(ch) \
+ do { \
+ GET_BUFFER_SPACE(1); \
+ *b++ = (char)(ch); \
+ } while (0)
+
+/* Extend the buffer by twice its current size via reallociation and
+ reset the pointers that pointed into the old allocation to point to
+ the correct places in the new allocation. If extending the buffer
+ results in it being larger than 1 << 16, then flag memory exhausted. */
+#define EXTEND_BUFFER \
+ do { char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == (1L<<16)) goto too_big; \
+ bufp->allocated *= 2; \
+ if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \
+ bufp->buffer = (char*)xrealloc(bufp->buffer, bufp->allocated); \
+ if (bufp->buffer == 0) \
+ goto memory_exhausted; \
+ b = (b - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer; \
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } while (0)
+
+
+/* Set the bit for character C in a character set list. */
+#define SET_LIST_BIT(c) \
+ (b[(unsigned char)(c) / BYTEWIDTH] \
+ |= 1 << ((unsigned char)(c) % BYTEWIDTH))
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ do { if (p != pend) { \
+ PATFETCH(c); \
+ while (ISDIGIT(c)) { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH(c); \
+ } \
+ } \
+ } while (0)
+
+#define STREQ(s1, s2) ((strcmp(s1, s2) == 0))
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ(string, "alpha") || STREQ(string, "upper") \
+ || STREQ(string, "lower") || STREQ(string, "digit") \
+ || STREQ(string, "alnum") || STREQ(string, "xdigit") \
+ || STREQ(string, "space") || STREQ(string, "print") \
+ || STREQ(string, "punct") || STREQ(string, "graph") \
+ || STREQ(string, "cntrl") || STREQ(string, "blank"))
+
+#define STORE_MBC(p, c) \
+ do { \
+ (p)[0] = (unsigned char)(((c) >>24) & 0xff); \
+ (p)[1] = (unsigned char)(((c) >>16) & 0xff); \
+ (p)[2] = (unsigned char)(((c) >> 8) & 0xff); \
+ (p)[3] = (unsigned char)(((c) >> 0) & 0xff); \
+ } while (0)
+
+#define STORE_MBC_AND_INCR(p, c) \
+ do { \
+ *(p)++ = (unsigned char)(((c) >>24) & 0xff); \
+ *(p)++ = (unsigned char)(((c) >>16) & 0xff); \
+ *(p)++ = (unsigned char)(((c) >> 8) & 0xff); \
+ *(p)++ = (unsigned char)(((c) >> 0) & 0xff); \
+ } while (0)
+
+#define EXTRACT_MBC(p) \
+ ((unsigned int)((unsigned char)(p)[0] << 24 | \
+ (unsigned char)(p)[1] << 16 | \
+ (unsigned char)(p)[2] << 8 | \
+ (unsigned char)(p)[3]))
+
+#define EXTRACT_MBC_AND_INCR(p) \
+ ((unsigned int)((p) += 4, \
+ (unsigned char)(p)[-4] << 24 | \
+ (unsigned char)(p)[-3] << 16 | \
+ (unsigned char)(p)[-2] << 8 | \
+ (unsigned char)(p)[-1]))
+
+#define EXTRACT_UNSIGNED(p) \
+ ((unsigned char)(p)[0] | (unsigned char)(p)[1] << 8)
+#define EXTRACT_UNSIGNED_AND_INCR(p) \
+ ((p) += 2, (unsigned char)(p)[-2] | (unsigned char)(p)[-1] << 8)
+
+/* Handle (mb)?charset(_not)?.
+
+ Structure of mbcharset(_not)? in compiled pattern.
+
+ struct {
+ unsinged char id; mbcharset(_not)?
+ unsigned char sbc_size;
+ unsigned char sbc_map[sbc_size]; same as charset(_not)? up to here.
+ unsigned short mbc_size; number of intervals.
+ struct {
+ unsigned long beg; beginning of interval.
+ unsigned long end; end of interval.
+ } intervals[mbc_size];
+ }; */
+
+static void
+set_list_bits(c1, c2, b)
+ unsigned long c1, c2;
+ unsigned char *b;
+{
+ unsigned char sbc_size = b[-1];
+ unsigned short mbc_size = EXTRACT_UNSIGNED(&b[sbc_size]);
+ unsigned short beg, end, upb;
+
+ if (c1 > c2)
+ return;
+ b = &b[sbc_size + 2];
+
+ for (beg = 0, upb = mbc_size; beg < upb; ) {
+ unsigned short mid = (unsigned short)(beg + upb) >> 1;
+
+ if ((int)c1 - 1 > (int)EXTRACT_MBC(&b[mid*8+4]))
+ beg = mid + 1;
+ else
+ upb = mid;
+ }
+
+ for (end = beg, upb = mbc_size; end < upb; ) {
+ unsigned short mid = (unsigned short)(end + upb) >> 1;
+
+ if ((int)c2 >= (int)EXTRACT_MBC(&b[mid*8]) - 1)
+ end = mid + 1;
+ else
+ upb = mid;
+ }
+
+ if (beg != end) {
+ if (c1 > EXTRACT_MBC(&b[beg*8]))
+ c1 = EXTRACT_MBC(&b[beg*8]);
+ if (c2 < EXTRACT_MBC(&b[(end - 1)*8+4]))
+ c2 = EXTRACT_MBC(&b[(end - 1)*8+4]);
+ }
+ if (end < mbc_size && end != beg + 1)
+ /* NOTE: memcpy() would not work here. */
+ memmove(&b[(beg + 1)*8], &b[end*8], (mbc_size - end)*8);
+ STORE_MBC(&b[beg*8 + 0], c1);
+ STORE_MBC(&b[beg*8 + 4], c2);
+ mbc_size += beg - end + 1;
+ STORE_NUMBER(&b[-2], mbc_size);
+}
+
+static int
+is_in_list_sbc(c, b)
+ unsigned long c;
+ const unsigned char *b;
+{
+ unsigned short size;
+
+ size = *b++;
+ return ((int)c / BYTEWIDTH < (int)size && b[c / BYTEWIDTH] & 1 << c % BYTEWIDTH);
+}
+
+static int
+is_in_list_mbc(c, b)
+ unsigned long c;
+ const unsigned char *b;
+{
+ unsigned short size;
+ unsigned short i, j;
+
+ size = *b++;
+ b += size + 2;
+ size = EXTRACT_UNSIGNED(&b[-2]);
+ if (size == 0) return 0;
+
+ for (i = 0, j = size; i < j; ) {
+ unsigned short k = (unsigned short)(i + j) >> 1;
+
+ if (c > EXTRACT_MBC(&b[k*8+4]))
+ i = k + 1;
+ else
+ j = k;
+ }
+ if (i < size && EXTRACT_MBC(&b[i*8]) <= c)
+ return 1;
+
+ return 0;
+}
+
+static int
+is_in_list(c, b)
+ unsigned long c;
+ const unsigned char *b;
+{
+ return is_in_list_sbc(c, b) || (current_mbctype ? is_in_list_mbc(c, b) : 0);
+}
+
+static void
+print_partial_compiled_pattern(start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL) {
+ printf("(null)\n");
+ return;
+ }
+
+ /* Loop over pattern commands. */
+ while (p < pend) {
+ switch ((enum regexpcode)*p++) {
+ case unused:
+ printf("/unused");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf("/exactn/%d", mcnt);
+ do {
+ putchar('/');
+ printf("%c", *p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case start_paren:
+ printf("/start_paren");
+ break;
+
+ case stop_paren:
+ printf("/stop_paren");
+ break;
+
+ case casefold_on:
+ printf("/casefold_on");
+ break;
+
+ case casefold_off:
+ printf("/casefold_off");
+ break;
+
+ case option_set:
+ printf("/option_set/%d", *p++);
+ break;
+
+ case start_nowidth:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/start_nowidth//%d", mcnt);
+ break;
+
+ case stop_nowidth:
+ printf("/stop_nowidth//");
+ p += 2;
+ break;
+
+ case pop_and_fail:
+ printf("/pop_and_fail");
+ break;
+
+ case stop_backtrack:
+ printf("/stop_backtrack//");
+ p += 2;
+ break;
+
+ case duplicate:
+ printf("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf("/anychar");
+ break;
+
+ case anychar_repeat:
+ printf("/anychar_repeat");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c;
+
+ printf("/charset%s",
+ (enum regexpcode)*(p - 1) == charset_not ? "_not" : "");
+
+ mcnt = *p++;
+ printf("/%d", mcnt);
+ for (c = 0; c < mcnt; c++) {
+ unsigned bit;
+ unsigned char map_byte = p[c];
+
+ putchar('/');
+
+ for (bit = 0; bit < BYTEWIDTH; bit++)
+ if (map_byte & (1 << bit))
+ printf("%c", c * BYTEWIDTH + bit);
+ }
+ p += mcnt;
+ mcnt = EXTRACT_UNSIGNED_AND_INCR(p);
+ putchar('/');
+ while (mcnt--) {
+ print_mbc(EXTRACT_MBC_AND_INCR(p));
+ putchar('-');
+ print_mbc(EXTRACT_MBC_AND_INCR(p));
+ }
+ break;
+ }
+
+ case begline:
+ printf("/begline");
+ break;
+
+ case endline:
+ printf("/endline");
+ break;
+
+ case on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/on_failure_jump//%d", mcnt);
+ break;
+
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/dummy_failure_jump//%d", mcnt);
+ break;
+
+ case push_dummy_failure:
+ printf("/push_dummy_failure");
+ break;
+
+ case finalize_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/finalize_jump//%d", mcnt);
+ break;
+
+ case maybe_finalize_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/maybe_finalize_jump//%d", mcnt);
+ break;
+
+ case jump_past_alt:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/jump_past_alt//%d", mcnt);
+ break;
+
+ case jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/jump//%d", mcnt);
+ break;
+
+ case succeed_n:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ EXTRACT_NUMBER_AND_INCR(mcnt2, p);
+ printf("/succeed_n//%d//%d", mcnt, mcnt2);
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ EXTRACT_NUMBER_AND_INCR(mcnt2, p);
+ printf("/jump_n//%d//%d", mcnt, mcnt2);
+ break;
+
+ case set_number_at:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ EXTRACT_NUMBER_AND_INCR(mcnt2, p);
+ printf("/set_number_at//%d//%d", mcnt, mcnt2);
+ break;
+
+ case try_next:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/try_next//%d", mcnt);
+ break;
+
+ case finalize_push:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ printf("/finalize_push//%d", mcnt);
+ break;
+
+ case finalize_push_n:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ EXTRACT_NUMBER_AND_INCR(mcnt2, p);
+ printf("/finalize_push_n//%d//%d", mcnt, mcnt2);
+ break;
+
+ case wordbound:
+ printf("/wordbound");
+ break;
+
+ case notwordbound:
+ printf("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf("/wordbeg");
+ break;
+
+ case wordend:
+ printf("/wordend");
+
+ case wordchar:
+ printf("/wordchar");
+ break;
+
+ case notwordchar:
+ printf("/notwordchar");
+ break;
+
+ case begbuf:
+ printf("/begbuf");
+ break;
+
+ case endbuf:
+ printf("/endbuf");
+ break;
+
+ case endbuf2:
+ printf("/endbuf2");
+ break;
+
+ case begpos:
+ printf("/begpos");
+ break;
+
+ default:
+ printf("?%d", *(p-1));
+ }
+ }
+ printf("/\n");
+}
+
+
+static void
+print_compiled_pattern(bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = (unsigned char*)bufp->buffer;
+
+ print_partial_compiled_pattern(buffer, buffer + bufp->used);
+}
+
+static char*
+calculate_must_string(start, end)
+ char *start;
+ char *end;
+{
+ int mcnt;
+ int max = 0;
+ unsigned char *p = (unsigned char *)start;
+ unsigned char *pend = (unsigned char *)end;
+ char *must = 0;
+
+ if (start == NULL) return 0;
+
+ /* Loop over pattern commands. */
+ while (p < pend) {
+ switch ((enum regexpcode)*p++) {
+ case unused:
+ break;
+
+ case exactn:
+ mcnt = *p;
+ if (mcnt > max) {
+ must = (char *)p;
+ max = mcnt;
+ }
+ p += mcnt+1;
+ break;
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ break;
+
+ case duplicate:
+ case option_set:
+ p++;
+ break;
+
+ case casefold_on:
+ case casefold_off:
+ return 0; /* should not check must_string */
+
+ case pop_and_fail:
+ case anychar:
+ case anychar_repeat:
+ case begline:
+ case endline:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case wordchar:
+ case notwordchar:
+ case begbuf:
+ case endbuf:
+ case endbuf2:
+ case begpos:
+ case push_dummy_failure:
+ case start_paren:
+ case stop_paren:
+ break;
+
+ case charset:
+ case charset_not:
+ mcnt = *p++;
+ p += mcnt;
+ mcnt = EXTRACT_UNSIGNED_AND_INCR(p);
+ while (mcnt--) {
+ p += 8;
+ }
+ break;
+
+ case on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (mcnt > 0) p += mcnt;
+ if ((enum regexpcode)p[-3] == jump) {
+ p -= 2;
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (mcnt > 0) p += mcnt;
+ }
+ break;
+
+ case dummy_failure_jump:
+ case succeed_n:
+ case try_next:
+ case jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (mcnt > 0) p += mcnt;
+ break;
+
+ case start_nowidth:
+ case stop_nowidth:
+ case stop_backtrack:
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case finalize_push:
+ p += 2;
+ break;
+
+ case jump_n:
+ case set_number_at:
+ case finalize_push_n:
+ p += 4;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return must;
+}
+
+static unsigned int
+read_backslash(c)
+ int c;
+{
+ switch (c) {
+ case 'n':
+ return '\n';
+
+ case 't':
+ return '\t';
+
+ case 'r':
+ return '\r';
+
+ case 'f':
+ return '\f';
+
+ case 'v':
+ return '\v';
+
+ case 'a':
+ return '\007';
+
+ case 'b':
+ return '\010';
+
+ case 'e':
+ return '\033';
+ }
+ return c;
+}
+
+static unsigned int
+read_special(p, pend, pp)
+ const char *p, *pend, **pp;
+{
+ int c;
+
+ PATFETCH_RAW(c);
+ switch (c) {
+ case 'M':
+ PATFETCH_RAW(c);
+ if (c != '-') return -1;
+ PATFETCH_RAW(c);
+ *pp = p;
+ if (c == '\\') {
+ return read_special(--p, pend, pp) | 0x80;
+ }
+ else if (c == -1) return ~0;
+ else {
+ return ((c & 0xff) | 0x80);
+ }
+
+ case 'C':
+ PATFETCH_RAW(c);
+ if (c != '-') return ~0;
+ case 'c':
+ PATFETCH_RAW(c);
+ *pp = p;
+ if (c == '\\') {
+ c = read_special(--p, pend, pp);
+ }
+ else if (c == '?') return 0177;
+ else if (c == -1) return ~0;
+ return c & 0x9f;
+ default:
+ *pp = p + 1;
+ return read_backslash(c);
+ }
+
+ end_of_pattern:
+ return ~0;
+}
+
+/* re_compile_pattern takes a regular-expression string
+ and converts it into a buffer full of byte commands for matching.
+
+ PATTERN is the address of the pattern string
+ SIZE is the length of it.
+ BUFP is a struct re_pattern_buffer * which points to the info
+ on where to store the byte commands.
+ This structure contains a char * which points to the
+ actual space, which should have been obtained with malloc.
+ re_compile_pattern may use realloc to grow the buffer space.
+
+ The number of bytes of commands can be found out by looking in
+ the `struct re_pattern_buffer' that bufp pointed to, after
+ re_compile_pattern returns. */
+
+char *
+re_compile_pattern(pattern, size, bufp)
+ const char *pattern;
+ int size;
+ struct re_pattern_buffer *bufp;
+{
+ register char *b = bufp->buffer;
+ register const char *p = pattern;
+ const char *nextp;
+ const char *pend = pattern + size;
+ register unsigned int c, c1 = 0;
+ const char *p0;
+ int numlen;
+#define ERROR_MSG_MAX_SIZE 200
+ static char error_msg[ERROR_MSG_MAX_SIZE+1];
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell whether a new exact-match
+ character can be added to that command or requires a new `exactn'
+ command. */
+
+ char *pending_exact = 0;
+
+ /* Address of the place where a forward-jump should go to the end of
+ the containing expression. Each alternative of an `or', except the
+ last, ends with a forward-jump of this sort. */
+
+ char *fixup_alt_jump = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells postfix * where to find the start of its operand. */
+
+ char *laststart = 0;
+
+ /* In processing a repeat, 1 means zero matches is allowed. */
+
+ char zero_times_ok;
+
+ /* In processing a repeat, 1 means many matches is allowed. */
+
+ char many_times_ok;
+
+ /* In processing a repeat, 1 means non-greedy matches. */
+
+ char greedy;
+
+ /* Address of beginning of regexp, or inside of last (. */
+
+ char *begalt = b;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* In processing an interval, at least this many matches must be made. */
+ int lower_bound;
+
+ /* In processing an interval, at most this many matches can be made. */
+ int upper_bound;
+
+ /* Stack of information saved by ( and restored by ).
+ Five stack elements are pushed by each (:
+ First, the value of b.
+ Second, the value of fixup_alt_jump.
+ Third, the value of begalt.
+ Fourth, the value of regnum.
+ Fifth, the type of the paren. */
+
+ int stacka[40];
+ int *stackb = stacka;
+ int *stackp = stackb;
+ int *stacke = stackb + 40;
+
+ /* Counts ('s as they are encountered. Remembered for the matching ),
+ where it becomes the register number to put in the stop_memory
+ command. */
+
+ int regnum = 1;
+
+ int range = 0;
+ int had_mbchar = 0;
+ int had_num_literal = 0;
+ int had_char_class = 0;
+
+ int options = bufp->options;
+
+ bufp->fastmap_accurate = 0;
+ bufp->must = 0;
+ bufp->must_skip = 0;
+
+ /* Initialize the syntax table. */
+ init_syntax_once();
+
+ if (bufp->allocated == 0) {
+ bufp->allocated = INIT_BUF_SIZE;
+ /* EXTEND_BUFFER loses when bufp->allocated is 0. */
+ bufp->buffer = (char*)xrealloc(bufp->buffer, INIT_BUF_SIZE);
+ if (!bufp->buffer) goto memory_exhausted; /* this not happen */
+ begalt = b = bufp->buffer;
+ }
+
+ while (p != pend) {
+ PATFETCH(c);
+
+ switch (c) {
+ case '$':
+ if (bufp->options & RE_OPTION_SINGLELINE) {
+ BUFPUSH(endbuf);
+ }
+ else {
+ p0 = p;
+ /* When testing what follows the $,
+ look past the \-constructs that don't consume anything. */
+
+ while (p0 != pend) {
+ if (*p0 == '\\' && p0 + 1 != pend
+ && (p0[1] == 'b' || p0[1] == 'B'))
+ p0 += 2;
+ else
+ break;
+ }
+ BUFPUSH(endline);
+ }
+ break;
+
+ case '^':
+ if (bufp->options & RE_OPTION_SINGLELINE)
+ BUFPUSH(begbuf);
+ else
+ BUFPUSH(begline);
+ break;
+
+ case '+':
+ case '?':
+ case '*':
+ /* If there is no previous pattern, char not special. */
+ if (!laststart) {
+ snprintf(error_msg, ERROR_MSG_MAX_SIZE,
+ "invalid regular expression; there's no previous pattern, to which '%c' would define cardinality at %d",
+ c, p-pattern);
+ FREE_AND_RETURN(stackb, error_msg);
+ }
+ /* If there is a sequence of repetition chars,
+ collapse it down to just one. */
+ zero_times_ok = c != '+';
+ many_times_ok = c != '?';
+ greedy = 1;
+ if (p != pend) {
+ PATFETCH(c);
+ switch (c) {
+ case '?':
+ greedy = 0;
+ break;
+ case '*':
+ case '+':
+ goto nested_meta;
+ default:
+ PATUNFETCH;
+ break;
+ }
+ }
+
+ repeat:
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ if (greedy && many_times_ok && *laststart == anychar && b - laststart <= 2) {
+ if (b[-1] == stop_paren)
+ b--;
+ if (zero_times_ok)
+ *laststart = anychar_repeat;
+ else {
+ BUFPUSH(anychar_repeat);
+ }
+ break;
+ }
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok) {
+ /* If more than one repetition is allowed, put in at the
+ end a backward relative jump from b to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump). */
+ GET_BUFFER_SPACE(3);
+ store_jump(b,greedy?maybe_finalize_jump:finalize_push,laststart-3);
+ b += 3; /* Because store_jump put stuff here. */
+ }
+
+ /* On failure, jump from laststart to next pattern, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE(3);
+ insert_jump(on_failure_jump, laststart, b + 3, b);
+ b += 3;
+
+ if (zero_times_ok) {
+ if (greedy == 0) {
+ GET_BUFFER_SPACE(3);
+ insert_jump(try_next, laststart, b + 3, b);
+ b += 3;
+ }
+ }
+ else {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE(3);
+ insert_jump(dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ break;
+
+ case '.':
+ laststart = b;
+ BUFPUSH(anychar);
+ break;
+
+ case '[':
+ if (p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; '[' can't be the last character ie. can't start range at the end of pattern");
+ while ((b - bufp->buffer + 9 + (1 << BYTEWIDTH) / BYTEWIDTH)
+ > bufp->allocated)
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (*p == '^') {
+ BUFPUSH(charset_not);
+ p++;
+ }
+ else
+ BUFPUSH(charset);
+ p0 = p;
+
+ BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Clear the whole map */
+ memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);
+
+ had_mbchar = 0;
+ had_num_literal = 0;
+ had_char_class = 0;
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;) {
+ int size;
+ unsigned last = (unsigned)-1;
+
+ if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])) || current_mbctype) {
+ /* Ensure the space is enough to hold another interval
+ of multi-byte chars in charset(_not)?. */
+ size = (1 << BYTEWIDTH) / BYTEWIDTH + 2 + size*8 + 8;
+ while (b + size + 1 > bufp->buffer + bufp->allocated)
+ EXTEND_BUFFER;
+ }
+ range_retry:
+ if (range && had_char_class) {
+ FREE_AND_RETURN(stackb, "invalid regular expression; can't use character class as an end value of range");
+ }
+ PATFETCH_RAW(c);
+
+ if (c == ']') {
+ if (p == p0 + 1) {
+ if (p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; empty character class");
+ re_warning("character class has `]' without escape");
+ }
+ else
+ /* Stop if this isn't merely a ] inside a bracket
+ expression, but rather the end of a bracket
+ expression. */
+ break;
+ }
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ FREE_AND_RETURN(stackb, "invalid regular expression; can't use character class as a start value of range");
+ if (ismbchar(c)) {
+ PATFETCH_MBC(c);
+ had_mbchar++;
+ }
+ had_char_class = 0;
+
+ if (c == '-' && ((p != p0 + 1 && *p != ']') ||
+ (p[0] == '-' && p[1] != ']') ||
+ range))
+ re_warning("character class has `-' without escape");
+ if (c == '[' && *p != ':')
+ re_warning("character class has `[' without escape");
+
+ /* \ escapes characters when inside [...]. */
+ if (c == '\\') {
+ PATFETCH_RAW(c);
+ switch (c) {
+ case 'w':
+ for (c = 0; c < (1 << BYTEWIDTH); c++) {
+ if (SYNTAX(c) == Sword ||
+ (!current_mbctype && SYNTAX(c) == Sword2))
+ SET_LIST_BIT(c);
+ }
+ if (current_mbctype) {
+ set_list_bits(0x80, 0xffffffff, b);
+ }
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 'W':
+ for (c = 0; c < (1 << BYTEWIDTH); c++) {
+ if (SYNTAX(c) != Sword &&
+ ((current_mbctype && !re_mbctab[c]) ||
+ (!current_mbctype && SYNTAX(c) != Sword2)))
+ SET_LIST_BIT(c);
+ }
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 's':
+ for (c = 0; c < 256; c++)
+ if (ISSPACE(c))
+ SET_LIST_BIT(c);
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 'S':
+ for (c = 0; c < 256; c++)
+ if (!ISSPACE(c))
+ SET_LIST_BIT(c);
+ if (current_mbctype)
+ set_list_bits(0x80, 0xffffffff, b);
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 'd':
+ for (c = '0'; c <= '9'; c++)
+ SET_LIST_BIT(c);
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 'D':
+ for (c = 0; c < 256; c++)
+ if (!ISDIGIT(c))
+ SET_LIST_BIT(c);
+ if (current_mbctype)
+ set_list_bits(0x80, 0xffffffff, b);
+ had_char_class = 1;
+ last = -1;
+ continue;
+
+ case 'x':
+ c = scan_hex(p, 2, &numlen);
+ if (numlen == 0) goto invalid_escape;
+ p += numlen;
+ had_num_literal = 1;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ PATUNFETCH;
+ c = scan_oct(p, 3, &numlen);
+ p += numlen;
+ had_num_literal = 1;
+ break;
+
+ case 'M':
+ case 'C':
+ case 'c':
+ {
+ const char *pp;
+
+ --p;
+ c = read_special(p, pend, &pp);
+ if (c > 255) goto invalid_escape;
+ p = pp;
+ had_num_literal = 1;
+ }
+ break;
+
+ default:
+ c = read_backslash(c);
+ if (ismbchar(c)) {
+ PATFETCH_MBC(c);
+ had_mbchar++;
+ }
+ break;
+ }
+ }
+ else if (c == '[' && *p == ':') { /* [:...:] */
+ /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH_RAW(c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; re can't end '[[:'");
+
+ for (;;) {
+ PATFETCH_RAW(c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and `:]':
+ undo the ending character, the letters, and
+ the leading `:' and `['. */
+ if (c == ':' && *p == ']') {
+ int ch;
+ char is_alnum = STREQ(str, "alnum");
+ char is_alpha = STREQ(str, "alpha");
+ char is_blank = STREQ(str, "blank");
+ char is_cntrl = STREQ(str, "cntrl");
+ char is_digit = STREQ(str, "digit");
+ char is_graph = STREQ(str, "graph");
+ char is_lower = STREQ(str, "lower");
+ char is_print = STREQ(str, "print");
+ char is_punct = STREQ(str, "punct");
+ char is_space = STREQ(str, "space");
+ char is_upper = STREQ(str, "upper");
+ char is_xdigit = STREQ(str, "xdigit");
+
+ if (!IS_CHAR_CLASS(str)){
+ snprintf(error_msg, ERROR_MSG_MAX_SIZE,
+ "invalid regular expression; [:%s:] is not a character class", str);
+ FREE_AND_RETURN(stackb, error_msg);
+ }
+
+ /* Throw away the ] at the end of the character class. */
+ PATFETCH(c);
+
+ if (p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; range doesn't have ending ']' after a character class");
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++) {
+ if ( (is_alnum && ISALNUM(ch))
+ || (is_alpha && ISALPHA(ch))
+ || (is_blank && ISBLANK(ch))
+ || (is_cntrl && ISCNTRL(ch))
+ || (is_digit && ISDIGIT(ch))
+ || (is_graph && ISGRAPH(ch))
+ || (is_lower && ISLOWER(ch))
+ || (is_print && ISPRINT(ch))
+ || (is_punct && ISPUNCT(ch))
+ || (is_space && ISSPACE(ch))
+ || (is_upper && ISUPPER(ch))
+ || (is_xdigit && ISXDIGIT(ch)))
+ SET_LIST_BIT(ch);
+ }
+ had_char_class = 1;
+ continue;
+ }
+ else {
+ c1 += 2;
+ while (c1--)
+ PATUNFETCH;
+ re_warning("character class has `[' without escape");
+ c = '[';
+ }
+ }
+
+ /* Get a range. */
+ if (range) {
+ if (last > c)
+ goto invalid_pattern;
+
+ range = 0;
+ if (had_mbchar == 0) {
+ if (TRANSLATE_P()) {
+ for (;last<=c;last++)
+ SET_LIST_BIT(translate[last]);
+ }
+ else {
+ for (;last<=c;last++)
+ SET_LIST_BIT(last);
+ }
+ }
+ else if (had_mbchar == 2) {
+ set_list_bits(last, c, b);
+ }
+ else {
+ /* restriction: range between sbc and mbc */
+ goto invalid_pattern;
+ }
+ }
+ else if (p[0] == '-' && p[1] != ']') {
+ last = c;
+ PATFETCH_RAW(c1);
+ range = 1;
+ goto range_retry;
+ }
+ else {
+ if (TRANSLATE_P() && c < 0x100) c = (unsigned char)translate[c];
+ if (had_mbchar == 0 && (!current_mbctype || !had_num_literal)) {
+ SET_LIST_BIT(c);
+ had_num_literal = 0;
+ }
+ else {
+ set_list_bits(c, c, b);
+ }
+ }
+ had_mbchar = 0;
+ }
+
+ /* Discard any character set/class bitmap bytes that are all
+ 0 at the end of the map. Decrement the map-length byte too. */
+ while ((int)b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
+ memmove(&b[(unsigned char)b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
+ 2 + EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])*8);
+ b += b[-1] + 2 + EXTRACT_UNSIGNED(&b[(unsigned char)b[-1]])*8;
+ had_num_literal = 0;
+ break;
+
+ case '(':
+ {
+ int old_options = options;
+ int push_option = 0;
+ int casefold = 0;
+
+ PATFETCH(c);
+ if (c == '?') {
+ int negative = 0;
+
+ PATFETCH_RAW(c);
+ switch (c) {
+ case 'x': case 'm': case 'i': case '-':
+ for (;;) {
+ switch (c) {
+ case '-':
+ negative = 1;
+ break;
+
+ case ':':
+ case ')':
+ break;
+
+ case 'x':
+ if (negative)
+ options &= ~RE_OPTION_EXTENDED;
+ else
+ options |= RE_OPTION_EXTENDED;
+ break;
+
+ case 'm':
+ if (negative) {
+ if (options&RE_OPTION_MULTILINE) {
+ options &= ~RE_OPTION_MULTILINE;
+ }
+ }
+ else if (!(options&RE_OPTION_MULTILINE)) {
+ options |= RE_OPTION_MULTILINE;
+ }
+ push_option = 1;
+ break;
+
+ case 'i':
+ if (negative) {
+ if (options&RE_OPTION_IGNORECASE) {
+ options &= ~RE_OPTION_IGNORECASE;
+ }
+ }
+ else if (!(options&RE_OPTION_IGNORECASE)) {
+ options |= RE_OPTION_IGNORECASE;
+ }
+ casefold = 1;
+ break;
+
+ default:
+ FREE_AND_RETURN(stackb, "undefined (?...) inline option");
+ }
+ if (c == ')') {
+ c = '#'; /* read whole in-line options */
+ break;
+ }
+ if (c == ':') break;
+ PATFETCH_RAW(c);
+ }
+ break;
+
+ case '#':
+ for (;;) {
+ PATFETCH(c);
+ if (c == ')') break;
+ }
+ c = '#';
+ break;
+
+ case ':':
+ case '=':
+ case '!':
+ case '>':
+ break;
+
+ default:
+ FREE_AND_RETURN(stackb, "undefined (?...) sequence");
+ }
+ }
+ else {
+ PATUNFETCH;
+ c = '(';
+ }
+ if (c == '#') {
+ if (push_option) {
+ BUFPUSH(option_set);
+ BUFPUSH(options);
+ }
+ if (casefold) {
+ if (options & RE_OPTION_IGNORECASE)
+ BUFPUSH(casefold_on);
+ else
+ BUFPUSH(casefold_off);
+ }
+ break;
+ }
+ if (stackp+8 >= stacke) {
+ DOUBLE_STACK(int);
+ }
+
+ /* Laststart should point to the start_memory that we are about
+ to push (unless the pattern has RE_NREGS or more ('s). */
+ /* obsolete: now RE_NREGS is just a default register size. */
+ *stackp++ = b - bufp->buffer;
+ *stackp++ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ *stackp++ = begalt - bufp->buffer;
+ switch (c) {
+ case '(':
+ BUFPUSH(start_memory);
+ BUFPUSH(regnum);
+ *stackp++ = regnum++;
+ *stackp++ = b - bufp->buffer;
+ BUFPUSH(0);
+ /* too many ()'s to fit in a byte. (max 254) */
+ if (regnum >= RE_REG_MAX) goto too_big;
+ break;
+
+ case '=':
+ case '!':
+ case '>':
+ BUFPUSH(start_nowidth);
+ *stackp++ = b - bufp->buffer;
+ BUFPUSH(0); /* temporary value */
+ BUFPUSH(0);
+ if (c != '!') break;
+
+ BUFPUSH(on_failure_jump);
+ *stackp++ = b - bufp->buffer;
+ BUFPUSH(0); /* temporary value */
+ BUFPUSH(0);
+ break;
+
+ case ':':
+ BUFPUSH(start_paren);
+ pending_exact = 0;
+ default:
+ break;
+ }
+ if (push_option) {
+ BUFPUSH(option_set);
+ BUFPUSH(options);
+ }
+ if (casefold) {
+ if (options & RE_OPTION_IGNORECASE)
+ BUFPUSH(casefold_on);
+ else
+ BUFPUSH(casefold_off);
+ }
+ *stackp++ = c;
+ *stackp++ = old_options;
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ }
+ break;
+
+ case ')':
+ if (stackp == stackb)
+ FREE_AND_RETURN(stackb, "unmatched )");
+
+ pending_exact = 0;
+ if (fixup_alt_jump) {
+ /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `finalize_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match'. */
+ BUFPUSH(push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ store_jump(fixup_alt_jump, jump, b);
+ }
+ if (options != stackp[-1]) {
+ if ((options ^ stackp[-1]) & RE_OPTION_IGNORECASE) {
+ BUFPUSH((options&RE_OPTION_IGNORECASE)?casefold_off:casefold_on);
+ }
+ if ((options ^ stackp[-1]) != RE_OPTION_IGNORECASE) {
+ BUFPUSH(option_set);
+ BUFPUSH(stackp[-1]);
+ }
+ }
+ p0 = b;
+ options = *--stackp;
+ switch (c = *--stackp) {
+ case '(':
+ {
+ char *loc = bufp->buffer + *--stackp;
+ *loc = regnum - stackp[-1];
+ BUFPUSH(stop_memory);
+ BUFPUSH(stackp[-1]);
+ BUFPUSH(regnum - stackp[-1]);
+ stackp--;
+ }
+ break;
+
+ case '!':
+ BUFPUSH(pop_and_fail);
+ /* back patch */
+ STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
+ stackp--;
+ /* fall through */
+ case '=':
+ BUFPUSH(stop_nowidth);
+ /* tell stack-pos place to start_nowidth */
+ STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
+ BUFPUSH(0); /* space to hold stack pos */
+ BUFPUSH(0);
+ stackp--;
+ break;
+
+ case '>':
+ BUFPUSH(stop_backtrack);
+ /* tell stack-pos place to start_nowidth */
+ STORE_NUMBER(bufp->buffer+stackp[-1], b - bufp->buffer - stackp[-1] - 2);
+ BUFPUSH(0); /* space to hold stack pos */
+ BUFPUSH(0);
+ stackp--;
+ break;
+
+ case ':':
+ BUFPUSH(stop_paren);
+ break;
+
+ default:
+ break;
+ }
+ begalt = *--stackp + bufp->buffer;
+ stackp--;
+ fixup_alt_jump = *stackp ? *stackp + bufp->buffer - 1 : 0;
+ laststart = *--stackp + bufp->buffer;
+ if (c == '!' || c == '=') laststart = b;
+ break;
+
+ case '|':
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE(3);
+ insert_jump(on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ store_jump(fixup_alt_jump, jump_past_alt, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE(3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case '{':
+ /* If there is no previous pattern, this is an invalid pattern. */
+ if (!laststart) {
+ snprintf(error_msg, ERROR_MSG_MAX_SIZE,
+ "invalid regular expression; there's no previous pattern, to which '{' would define cardinality at %d",
+ p-pattern);
+ FREE_AND_RETURN(stackb, error_msg);
+ }
+ if( p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; '{' can't be last character" );
+
+ beg_interval = p - 1;
+
+ lower_bound = -1; /* So can see if are set. */
+ upper_bound = -1;
+ GET_UNSIGNED_NUMBER(lower_bound);
+ if (c == ',') {
+ GET_UNSIGNED_NUMBER(upper_bound);
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || c != '}')
+ goto unfetch_interval;
+
+ if (lower_bound >= RE_DUP_MAX || upper_bound >= RE_DUP_MAX)
+ FREE_AND_RETURN(stackb, "too big quantifier in {,}");
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ if (lower_bound > upper_bound)
+ FREE_AND_RETURN(stackb, "can't do {n,m} with n > m");
+
+ beg_interval = 0;
+ pending_exact = 0;
+
+ greedy = 1;
+ if (p != pend) {
+ PATFETCH(c);
+ if (c == '?') greedy = 0;
+ else PATUNFETCH;
+ }
+
+ if (lower_bound == 0) {
+ zero_times_ok = 1;
+ if (upper_bound == RE_DUP_MAX) {
+ many_times_ok = 1;
+ goto repeat;
+ }
+ if (upper_bound == 1) {
+ many_times_ok = 0;
+ goto repeat;
+ }
+ }
+ if (lower_bound == 1) {
+ if (upper_bound == 1) {
+ /* No need to repeat */
+ break;
+ }
+ if (upper_bound == RE_DUP_MAX) {
+ many_times_ok = 1;
+ zero_times_ok = 0;
+ goto repeat;
+ }
+ }
+
+ /* If upper_bound is zero, don't want to succeed at all;
+ jump from laststart to b + 3, which will be the end of
+ the buffer after this jump is inserted. */
+
+ if (upper_bound == 0) {
+ GET_BUFFER_SPACE(3);
+ insert_jump(jump, laststart, b + 3, b);
+ b += 3;
+ break;
+ }
+
+ /* If lower_bound == upper_bound, repeat count can be removed */
+ if (lower_bound == upper_bound) {
+ int mcnt;
+ int skip_stop_paren = 0;
+
+ if (b[-1] == stop_paren) {
+ skip_stop_paren = 1;
+ b--;
+ }
+
+ if (*laststart == exactn && laststart[1]+2 == b - laststart
+ && laststart[1]*lower_bound < 256) {
+ mcnt = laststart[1];
+ GET_BUFFER_SPACE((lower_bound-1)*mcnt);
+ laststart[1] = lower_bound*mcnt;
+ while (--lower_bound) {
+ memcpy(b, laststart+2, mcnt);
+ b += mcnt;
+ }
+ if (skip_stop_paren) BUFPUSH(stop_paren);
+ break;
+ }
+
+ if (lower_bound < 5 && b - laststart < 10) {
+ /* 5 and 10 are the magic numbers */
+
+ mcnt = b - laststart;
+ GET_BUFFER_SPACE((lower_bound-1)*mcnt);
+ while (--lower_bound) {
+ memcpy(b, laststart, mcnt);
+ b += mcnt;
+ }
+ if (skip_stop_paren) BUFPUSH(stop_paren);
+ break;
+ }
+ if (skip_stop_paren) b++; /* push back stop_paren */
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = upper_bound == 1 ? 10 : 20;
+
+ GET_BUFFER_SPACE(nbytes);
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ insert_jump_n(succeed_n, laststart, b + (nbytes/2),
+ b, lower_bound);
+ b += 5; /* Just increment for the succeed_n here. */
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op_2(set_number_at, laststart, b, 5, lower_bound);
+ b += 5;
+
+ if (upper_bound > 1) {
+ /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ GET_BUFFER_SPACE(5);
+ store_jump_n(b, greedy?jump_n:finalize_push_n, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op_2(set_number_at, laststart, b, b - laststart,
+ upper_bound - 1);
+ b += 5;
+ }
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ re_warning("regexp has invalid interval");
+ p = beg_interval;
+ beg_interval = 0;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH(c);
+ goto normal_char;
+
+ case '\\':
+ if (p == pend)
+ FREE_AND_RETURN(stackb, "invalid regular expression; '\\' can't be last character");
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW(c);
+ switch (c) {
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ while (b - bufp->buffer + 9 + (1 << BYTEWIDTH) / BYTEWIDTH
+ > bufp->allocated)
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (c == 's' || c == 'd') {
+ BUFPUSH(charset);
+ }
+ else {
+ BUFPUSH(charset_not);
+ }
+
+ BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH);
+ memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2);
+ if (c == 's' || c == 'S') {
+ SET_LIST_BIT(' ');
+ SET_LIST_BIT('\t');
+ SET_LIST_BIT('\n');
+ SET_LIST_BIT('\r');
+ SET_LIST_BIT('\f');
+ }
+ else {
+ char cc;
+
+ for (cc = '0'; cc <= '9'; cc++) {
+ SET_LIST_BIT(cc);
+ }
+ }
+
+ while ((int)b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH)
+ memmove(&b[(unsigned char)b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH],
+ 2 + EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])*8);
+ b += b[-1] + 2 + EXTRACT_UNSIGNED(&b[(unsigned char)b[-1]])*8;
+ break;
+
+ case 'w':
+ laststart = b;
+ BUFPUSH(wordchar);
+ break;
+
+ case 'W':
+ laststart = b;
+ BUFPUSH(notwordchar);
+ break;
+
+#ifndef RUBY
+ case '<':
+ BUFPUSH(wordbeg);
+ break;
+
+ case '>':
+ BUFPUSH(wordend);
+ break;
+#endif
+
+ case 'b':
+ BUFPUSH(wordbound);
+ break;
+
+ case 'B':
+ BUFPUSH(notwordbound);
+ break;
+
+ case 'A':
+ BUFPUSH(begbuf);
+ break;
+
+ case 'Z':
+ if ((bufp->options & RE_OPTION_SINGLELINE) == 0) {
+ BUFPUSH(endbuf2);
+ break;
+ }
+ /* fall through */
+ case 'z':
+ BUFPUSH(endbuf);
+ break;
+
+ case 'G':
+ BUFPUSH(begpos);
+ break;
+
+ /* hex */
+ case 'x':
+ had_mbchar = 0;
+ c = scan_hex(p, 2, &numlen);
+ if (numlen == 0) goto invalid_escape;
+ p += numlen;
+ had_num_literal = 1;
+ goto numeric_char;
+
+ /* octal */
+ case '0':
+ had_mbchar = 0;
+ c = scan_oct(p, 2, &numlen);
+ p += numlen;
+ had_num_literal = 1;
+ goto numeric_char;
+
+ /* back-ref or octal */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ PATUNFETCH;
+ p0 = p;
+
+ had_mbchar = 0;
+ c1 = 0;
+ GET_UNSIGNED_NUMBER(c1);
+ if (!ISDIGIT(c)) PATUNFETCH;
+
+ if (9 < c1 && c1 >= regnum) {
+ /* need to get octal */
+ c = scan_oct(p0, 3, &numlen) & 0xff;
+ p = p0 + numlen;
+ c1 = 0;
+ had_num_literal = 1;
+ goto numeric_char;
+ }
+
+ laststart = b;
+ BUFPUSH(duplicate);
+ BUFPUSH(c1);
+ break;
+
+ case 'M':
+ case 'C':
+ case 'c':
+ p0 = --p;
+ c = read_special(p, pend, &p0);
+ if (c > 255) goto invalid_escape;
+ p = p0;
+ had_num_literal = 1;
+ goto numeric_char;
+
+ default:
+ c = read_backslash(c);
+ goto normal_char;
+ }
+ break;
+
+ case '#':
+ if (options & RE_OPTION_EXTENDED) {
+ while (p != pend) {
+ PATFETCH(c);
+ if (c == '\n') break;
+ }
+ break;
+ }
+ goto normal_char;
+
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\r':
+ case '\n':
+ if (options & RE_OPTION_EXTENDED)
+ break;
+
+ default:
+ if (c == ']')
+ re_warning("regexp has `]' without escape");
+ else if (c == '}')
+ re_warning("regexp has `}' without escape");
+ normal_char: /* Expects the character in `c'. */
+ had_mbchar = 0;
+ if (ismbchar(c)) {
+ had_mbchar = 1;
+ c1 = p - pattern;
+ }
+ numeric_char:
+ nextp = p + mbclen(c) - 1;
+ if (!pending_exact || pending_exact + *pending_exact + 1 != b
+ || *pending_exact >= (c1 ? 0176 : 0177)
+ || (nextp < pend &&
+ ( *nextp == '+' || *nextp == '?'
+ || *nextp == '*' || *nextp == '^'
+ || *nextp == '{'))) {
+ laststart = b;
+ BUFPUSH(exactn);
+ pending_exact = b;
+ BUFPUSH(0);
+ }
+ if (had_num_literal || c == 0xff) {
+ BUFPUSH(0xff);
+ (*pending_exact)++;
+ had_num_literal = 0;
+ }
+ BUFPUSH(c);
+ (*pending_exact)++;
+ if (had_mbchar) {
+ int len = mbclen(c) - 1;
+ while (len--) {
+ PATFETCH_RAW(c);
+ BUFPUSH(c);
+ (*pending_exact)++;
+ }
+ }
+ }
+ }
+
+ if (fixup_alt_jump)
+ store_jump(fixup_alt_jump, jump, b);
+
+ if (stackp != stackb)
+ FREE_AND_RETURN(stackb, "unmatched (");
+
+ /* set optimize flags */
+ laststart = bufp->buffer;
+ if (laststart != b) {
+ if (*laststart == dummy_failure_jump) laststart += 3;
+ else if (*laststart == try_next) laststart += 3;
+ if (*laststart == anychar_repeat) {
+ bufp->options |= RE_OPTIMIZE_ANCHOR;
+ }
+ }
+
+ bufp->used = b - bufp->buffer;
+ bufp->re_nsub = regnum;
+ laststart = bufp->buffer;
+ if (laststart != b) {
+ if (*laststart == start_memory) laststart += 3;
+ if (*laststart == exactn) {
+ bufp->options |= RE_OPTIMIZE_EXACTN;
+ bufp->must = laststart+1;
+ }
+ }
+ if (!bufp->must) {
+ bufp->must = calculate_must_string(bufp->buffer, b);
+ }
+ if (current_mbctype == MBCTYPE_SJIS) bufp->options |= RE_OPTIMIZE_NO_BM;
+ else if (bufp->must) {
+ int i;
+ int len = (unsigned char)bufp->must[0];
+
+ for (i=1; i<len; i++) {
+ if ((unsigned char)bufp->must[i] == 0xff ||
+ (current_mbctype && ismbchar(bufp->must[i]))) {
+ bufp->options |= RE_OPTIMIZE_NO_BM;
+ break;
+ }
+ }
+ if (!(bufp->options & RE_OPTIMIZE_NO_BM)) {
+ bufp->must_skip = (int *) xmalloc((1 << BYTEWIDTH)*sizeof(int));
+ bm_init_skip(bufp->must_skip, (unsigned char*)bufp->must+1,
+ (unsigned char)bufp->must[0],
+ (unsigned char*)(MAY_TRANSLATE()?translate:0));
+ }
+ }
+
+ bufp->regstart = TMALLOC(regnum, unsigned char*);
+ bufp->regend = TMALLOC(regnum, unsigned char*);
+ bufp->old_regstart = TMALLOC(regnum, unsigned char*);
+ bufp->old_regend = TMALLOC(regnum, unsigned char*);
+ bufp->reg_info = TMALLOC(regnum, register_info_type);
+ bufp->best_regstart = TMALLOC(regnum, unsigned char*);
+ bufp->best_regend = TMALLOC(regnum, unsigned char*);
+ FREE_AND_RETURN(stackb, 0);
+
+ invalid_pattern:
+ FREE_AND_RETURN(stackb, "invalid regular expression");
+
+ end_of_pattern:
+ FREE_AND_RETURN(stackb, "premature end of regular expression");
+
+ too_big:
+ FREE_AND_RETURN(stackb, "regular expression too big");
+
+ memory_exhausted:
+ FREE_AND_RETURN(stackb, "memory exhausted");
+
+ nested_meta:
+ FREE_AND_RETURN(stackb, "nested *?+ in regexp");
+
+ invalid_escape:
+ FREE_AND_RETURN(stackb, "Invalid escape character syntax");
+}
+
+void
+re_free_pattern(bufp)
+ struct re_pattern_buffer *bufp;
+{
+ xfree(bufp->buffer);
+ xfree(bufp->fastmap);
+ if (bufp->must_skip) xfree(bufp->must_skip);
+
+ xfree(bufp->regstart);
+ xfree(bufp->regend);
+ xfree(bufp->old_regstart);
+ xfree(bufp->old_regend);
+ xfree(bufp->best_regstart);
+ xfree(bufp->best_regend);
+ xfree(bufp->reg_info);
+ xfree(bufp);
+}
+
+/* Store a jump of the form <OPCODE> <relative address>.
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store. */
+
+static void
+store_jump(from, opcode, to)
+ char *from, *to;
+ int opcode;
+{
+ from[0] = (char)opcode;
+ STORE_NUMBER(from + 1, to - (from + 3));
+}
+
+
+/* Open up space before char FROM, and insert there a jump to TO.
+ CURRENT_END gives the end of the storage not in use, so we know
+ how much data to copy up. OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump(op, from, to, current_end)
+ int op;
+ char *from, *to, *current_end;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 3; /* ...to here. */
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump(from, op, to);
+}
+
+
+/* Store a jump of the form <opcode> <relative address> <n> .
+
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store, N is a number the
+ jump uses, say, to decide how many times to jump.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+store_jump_n(from, opcode, to, n)
+ char *from, *to;
+ int opcode;
+ unsigned n;
+{
+ from[0] = (char)opcode;
+ STORE_NUMBER(from + 1, to - (from + 3));
+ STORE_NUMBER(from + 3, n);
+}
+
+
+/* Similar to insert_jump, but handles a jump which needs an extra
+ number to handle minimum and maximum cases. Open up space at
+ location FROM, and insert there a jump to TO. CURRENT_END gives the
+ end of the storage in use, so we know how much data to copy up. OP is
+ the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump_n(op, from, to, current_end, n)
+ int op;
+ char *from, *to, *current_end;
+ unsigned n;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 5; /* ...to here. */
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump_n(from, op, to, n);
+}
+
+
+/* Open up space at location THERE, and insert operation OP.
+ CURRENT_END gives the end of the storage in use, so
+ we know how much data to copy up.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_op(op, there, current_end)
+ int op;
+ char *there, *current_end;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 1; /* ...to here. */
+
+ while (pfrom != there)
+ *--pto = *--pfrom;
+
+ there[0] = (char)op;
+}
+
+
+/* Open up space at location THERE, and insert operation OP followed by
+ NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so
+ we know how much data to copy up.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_op_2(op, there, current_end, num_1, num_2)
+ int op;
+ char *there, *current_end;
+ int num_1, num_2;
+{
+ register char *pfrom = current_end; /* Copy from here... */
+ register char *pto = current_end + 5; /* ...to here. */
+
+ while (pfrom != there)
+ *--pto = *--pfrom;
+
+ there[0] = (char)op;
+ STORE_NUMBER(there + 1, num_1);
+ STORE_NUMBER(there + 3, num_2);
+}
+
+
+#define trans_eq(c1, c2, translate) (translate?(translate[c1]==translate[c2]):((c1)==(c2)))
+static int
+slow_match(little, lend, big, bend, translate)
+ const unsigned char *little, *lend;
+ const unsigned char *big, *bend;
+ const unsigned char *translate;
+{
+ int c;
+
+ while (little < lend && big < bend) {
+ c = *little++;
+ if (c == 0xff)
+ c = *little++;
+ if (!trans_eq(*big++, c, translate)) break;
+ }
+ if (little == lend) return 1;
+ return 0;
+}
+
+static int
+slow_search(little, llen, big, blen, translate)
+ const unsigned char *little;
+ int llen;
+ const unsigned char *big;
+ int blen;
+ const char *translate;
+{
+ const unsigned char *bsave = big;
+ const unsigned char *bend = big + blen;
+ register int c;
+ int fescape = 0;
+
+ c = *little;
+ if (c == 0xff) {
+ c = little[1];
+ fescape = 1;
+ }
+ else if (translate && !ismbchar(c)) {
+ c = translate[c];
+ }
+
+ while (big < bend) {
+ /* look for first character */
+ if (fescape) {
+ while (big < bend) {
+ if (*big == c) break;
+ big++;
+ }
+ }
+ else if (translate && !ismbchar(c)) {
+ while (big < bend) {
+ if (ismbchar(*big)) big+=mbclen(*big)-1;
+ else if (translate[*big] == c) break;
+ big++;
+ }
+ }
+ else {
+ while (big < bend) {
+ if (*big == c) break;
+ if (ismbchar(*big)) big+=mbclen(*big)-1;
+ big++;
+ }
+ }
+
+ if (slow_match(little, little+llen, big, bend, (unsigned char *)translate))
+ return big - bsave;
+
+ big+=mbclen(*big);
+ }
+ return -1;
+}
+
+static void
+bm_init_skip(skip, pat, m, translate)
+ int *skip;
+ unsigned char *pat;
+ int m;
+ const unsigned char *translate;
+{
+ int j, c;
+
+ for (c=0; c<256; c++) {
+ skip[c] = m;
+ }
+ if (translate) {
+ for (j=0; j<m-1; j++) {
+ skip[translate[pat[j]]] = m-1-j;
+ }
+ }
+ else {
+ for (j=0; j<m-1; j++) {
+ skip[pat[j]] = m-1-j;
+ }
+ }
+}
+
+static int
+bm_search(little, llen, big, blen, skip, translate)
+ const unsigned char *little;
+ int llen;
+ const unsigned char *big;
+ int blen;
+ int *skip;
+ const unsigned char *translate;
+{
+ int i, j, k;
+
+ i = llen-1;
+ if (translate) {
+ while (i < blen) {
+ k = i;
+ j = llen-1;
+ while (j >= 0 && translate[big[k]] == translate[little[j]]) {
+ k--;
+ j--;
+ }
+ if (j < 0) return k+1;
+
+ i += skip[translate[big[i]]];
+ }
+ return -1;
+ }
+ while (i < blen) {
+ k = i;
+ j = llen-1;
+ while (j >= 0 && big[k] == little[j]) {
+ k--;
+ j--;
+ }
+ if (j < 0) return k+1;
+
+ i += skip[big[i]];
+ }
+ return -1;
+}
+
+/* Given a pattern, compute a fastmap from it. The fastmap records
+ which of the (1 << BYTEWIDTH) possible characters can start a string
+ that matches the pattern. This fastmap is used by re_search to skip
+ quickly over totally implausible text.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as bufp->fastmap.
+ The other components of bufp describe the pattern to be used. */
+static int
+re_compile_fastmap0(bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *pattern = (unsigned char*)bufp->buffer;
+ int size = bufp->used;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+ register int j, k;
+ unsigned is_a_succeed_n;
+
+
+ unsigned char *stacka[NFAILURES];
+ unsigned char **stackb = stacka;
+ unsigned char **stackp = stackb;
+ unsigned char **stacke = stackb + NFAILURES;
+ int options = bufp->options;
+
+ memset(fastmap, 0, (1 << BYTEWIDTH));
+ bufp->fastmap_accurate = 1;
+ bufp->can_be_null = 0;
+
+ while (p) {
+ is_a_succeed_n = 0;
+ if (p == pend) {
+ bufp->can_be_null = 1;
+ break;
+ }
+#ifdef SWITCH_ENUM_BUG
+ switch ((int)((enum regexpcode)*p++))
+#else
+ switch ((enum regexpcode)*p++)
+#endif
+ {
+ case exactn:
+ if (p[1] == 0xff) {
+ if (TRANSLATE_P())
+ fastmap[translate[p[2]]] = 2;
+ else
+ fastmap[p[2]] = 2;
+ bufp->options |= RE_OPTIMIZE_BMATCH;
+ }
+ else if (TRANSLATE_P())
+ fastmap[translate[p[1]]] = 1;
+ else
+ fastmap[p[1]] = 1;
+ break;
+
+ case begline:
+ case begbuf:
+ case begpos:
+ case endbuf:
+ case endbuf2:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case pop_and_fail:
+ case push_dummy_failure:
+ case start_paren:
+ case stop_paren:
+ continue;
+
+ case casefold_on:
+ bufp->options |= RE_MAY_IGNORECASE;
+ options |= RE_OPTION_IGNORECASE;
+ continue;
+
+ case casefold_off:
+ options &= ~RE_OPTION_IGNORECASE;
+ continue;
+
+ case option_set:
+ options = *p++;
+ continue;
+
+ case endline:
+ if (TRANSLATE_P())
+ fastmap[translate['\n']] = 1;
+ else
+ fastmap['\n'] = 1;
+ if ((options & RE_OPTION_SINGLELINE) == 0 && bufp->can_be_null == 0)
+ bufp->can_be_null = 2;
+ break;
+
+ case jump_n:
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ case finalize_push:
+ case finalize_push_n:
+ EXTRACT_NUMBER_AND_INCR(j, p);
+ p += j;
+ if (j > 0)
+ continue;
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing.
+ Opcode jumped to should be an on_failure_jump.
+ Just treat it like an ordinary jump.
+ For a * loop, it has pushed its failure point already;
+ If so, discard that as redundant. */
+
+ if ((enum regexpcode)*p != on_failure_jump
+ && (enum regexpcode)*p != try_next
+ && (enum regexpcode)*p != succeed_n)
+ continue;
+ p++;
+ EXTRACT_NUMBER_AND_INCR(j, p);
+ p += j;
+ if (stackp != stackb && *stackp == p)
+ stackp--; /* pop */
+ continue;
+
+ case try_next:
+ case start_nowidth:
+ case stop_nowidth:
+ case stop_backtrack:
+ p += 2;
+ continue;
+
+ case succeed_n:
+ is_a_succeed_n = 1;
+ /* Get to the number of times to succeed. */
+ EXTRACT_NUMBER(k, p + 2);
+ /* Increment p past the n for when k != 0. */
+ if (k != 0) {
+ p += 4;
+ continue;
+ }
+ /* fall through */
+
+ case on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR(j, p);
+ if (p + j < pend) {
+ if (stackp == stacke) {
+ EXPAND_FAIL_STACK();
+ }
+ *++stackp = p + j; /* push */
+ }
+ else {
+ bufp->can_be_null = 1;
+ }
+ if (is_a_succeed_n)
+ EXTRACT_NUMBER_AND_INCR(k, p); /* Skip the n. */
+ continue;
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+ case duplicate:
+ bufp->can_be_null = 1;
+ if (*p >= bufp->re_nsub) break;
+ fastmap['\n'] = 1;
+ case anychar_repeat:
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++) {
+ if (j != '\n' || (options & RE_OPTION_MULTILINE))
+ fastmap[j] = 1;
+ }
+ if (bufp->can_be_null) {
+ FREE_AND_RETURN(stackb, 0);
+ }
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ if ((enum regexpcode)p[-1] == anychar_repeat) {
+ continue;
+ }
+ break;
+
+ case wordchar:
+ for (j = 0; j < 0x80; j++) {
+ if (SYNTAX(j) == Sword)
+ fastmap[j] = 1;
+ }
+ switch (current_mbctype) {
+ case MBCTYPE_ASCII:
+ for (j = 0x80; j < (1 << BYTEWIDTH); j++) {
+ if (SYNTAX(j) == Sword2)
+ fastmap[j] = 1;
+ }
+ break;
+ case MBCTYPE_EUC:
+ case MBCTYPE_SJIS:
+ case MBCTYPE_UTF8:
+ for (j = 0x80; j < (1 << BYTEWIDTH); j++) {
+ if (re_mbctab[j])
+ fastmap[j] = 1;
+ }
+ break;
+ }
+ break;
+
+ case notwordchar:
+ for (j = 0; j < 0x80; j++)
+ if (SYNTAX(j) != Sword)
+ fastmap[j] = 1;
+ switch (current_mbctype) {
+ case MBCTYPE_ASCII:
+ for (j = 0x80; j < (1 << BYTEWIDTH); j++) {
+ if (SYNTAX(j) != Sword2)
+ fastmap[j] = 1;
+ }
+ break;
+ case MBCTYPE_EUC:
+ case MBCTYPE_SJIS:
+ case MBCTYPE_UTF8:
+ for (j = 0x80; j < (1 << BYTEWIDTH); j++) {
+ if (!re_mbctab[j])
+ fastmap[j] = 1;
+ }
+ break;
+ }
+ break;
+
+ case charset:
+ /* NOTE: Charset for single-byte chars never contain
+ multi-byte char. See set_list_bits(). */
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) {
+ int tmp = TRANSLATE_P()?translate[j]:j;
+ fastmap[tmp] = 1;
+ }
+ {
+ unsigned short size;
+ unsigned long c, beg, end;
+
+ p += p[-1] + 2;
+ size = EXTRACT_UNSIGNED(&p[-2]);
+ for (j = 0; j < (int)size; j++) {
+ c = EXTRACT_MBC(&p[j*8]);
+ beg = WC2MBC1ST(c);
+ c = EXTRACT_MBC(&p[j*8+4]);
+ end = WC2MBC1ST(c);
+ /* set bits for 1st bytes of multi-byte chars. */
+ while (beg <= end) {
+ /* NOTE: Charset for multi-byte chars might contain
+ single-byte chars. We must reject them. */
+ if (c < 0x100) {
+ fastmap[beg] = 2;
+ bufp->options |= RE_OPTIMIZE_BMATCH;
+ }
+ else if (ismbchar(beg))
+ fastmap[beg] = 1;
+ beg++;
+ }
+ }
+ }
+ break;
+
+ case charset_not:
+ /* S: set of all single-byte chars.
+ M: set of all first bytes that can start multi-byte chars.
+ s: any set of single-byte chars.
+ m: any set of first bytes that can start multi-byte chars.
+
+ We assume S+M = U.
+ ___ _ _
+ s+m = (S*s+M*m). */
+ /* Chars beyond end of map must be allowed */
+ /* NOTE: Charset_not for single-byte chars might contain
+ multi-byte chars. See set_list_bits(). */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ if (!ismbchar(j))
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) {
+ if (!ismbchar(j))
+ fastmap[j] = 1;
+ }
+ {
+ unsigned short size;
+ unsigned long c, beg;
+ int num_literal = 0;
+
+ p += p[-1] + 2;
+ size = EXTRACT_UNSIGNED(&p[-2]);
+ if (size == 0) {
+ for (j = 0x80; j < (1 << BYTEWIDTH); j++)
+ if (ismbchar(j))
+ fastmap[j] = 1;
+ break;
+ }
+ for (j = 0,c = 0;j < (int)size; j++) {
+ unsigned int cc = EXTRACT_MBC(&p[j*8]);
+ beg = WC2MBC1ST(cc);
+ while (c <= beg) {
+ if (ismbchar(c))
+ fastmap[c] = 1;
+ c++;
+ }
+
+ cc = EXTRACT_MBC(&p[j*8+4]);
+ if (cc < 0xff) {
+ num_literal = 1;
+ while (c <= cc) {
+ if (ismbchar(c))
+ fastmap[c] = 1;
+ c++;
+ }
+ }
+ c = WC2MBC1ST(cc);
+ }
+
+ for (j = c; j < (1 << BYTEWIDTH); j++) {
+ if (num_literal)
+ fastmap[j] = 1;
+ if (ismbchar(j))
+ fastmap[j] = 1;
+ }
+ }
+ break;
+
+ case unused: /* pacify gcc -Wall */
+ break;
+ }
+
+ /* Get here means we have successfully found the possible starting
+ characters of one path of the pattern. We need not follow this
+ path any farther. Instead, look at the next alternative
+ remembered in the stack. */
+ if (stackp != stackb)
+ p = *stackp--; /* pop */
+ else
+ break;
+ }
+ FREE_AND_RETURN(stackb, 0);
+ memory_exhausted:
+ FREE_AND_RETURN(stackb, -2);
+}
+
+void
+re_compile_fastmap(bufp)
+ struct re_pattern_buffer *bufp;
+{
+ (void)re_compile_fastmap0(bufp);
+}
+
+/* adjust startpos value to the position between characters. */
+int
+re_mbc_startpos(string, size, startpos, range)
+ const char *string;
+ int size, startpos, range;
+{
+ int i = mbc_startpos(string, startpos);
+
+ if (i < startpos) {
+ if (range > 0) {
+ startpos = i + mbclen(string[i]);
+ }
+ else {
+ int len = mbclen(string[i]);
+ if (i + len <= startpos)
+ startpos = i + len;
+ else
+ startpos = i;
+ }
+ }
+ return startpos;
+}
+
+int
+re_adjust_startpos(bufp, string, size, startpos, range)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+{
+ /* Update the fastmap now if not correct already. */
+ if (!bufp->fastmap_accurate) {
+ int ret = re_compile_fastmap0(bufp);
+ if (ret) return ret;
+ }
+
+ /* Adjust startpos for mbc string */
+ if (current_mbctype && startpos>0 && !(bufp->options&RE_OPTIMIZE_BMATCH)) {
+ startpos = re_mbc_startpos(string, size, startpos, range);
+ }
+ return startpos;
+}
+
+
+static int re_match_exec _((struct re_pattern_buffer *, const char *, int, int, int,
+ struct re_registers *));
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match
+ STRING, starting first at index STARTPOS, then at STARTPOS + 1, and
+ so on. RANGE is the number of places to try before giving up. If
+ RANGE is negative, it searches backwards, i.e., the starting
+ positions tried are STARTPOS, STARTPOS - 1, etc. STRING is of SIZE.
+ In REGS, return the indices of STRING that matched the entire
+ BUFP->buffer and its contained subexpressions.
+
+ The value returned is the position in the strings at which the match
+ was found, or -1 if no match was found, or -2 if error (such as
+ failure stack overflow). */
+
+int
+re_search(bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ register char *fastmap = bufp->fastmap;
+ int val, anchor = 0, initpos = startpos;
+
+ /* Check for out-of-range starting position. */
+ if (startpos < 0 || startpos > size)
+ return -1;
+ if (!string) {
+ if (size == 0) string = "";
+ else return -1;
+ }
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate) {
+ int ret = re_compile_fastmap0(bufp);
+ if (ret) return ret;
+ }
+
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0) {
+ switch ((enum regexpcode)bufp->buffer[0]) {
+ case begbuf:
+ begbuf_match:
+ if (range > 0) {
+ if (startpos > 0) return -1;
+ else {
+ val = re_match(bufp, string, size, 0, regs);
+ if (val >= 0) return 0;
+ return val;
+ }
+ }
+ break;
+
+ case begline:
+ anchor = 1;
+ break;
+
+ case begpos:
+ val = re_match(bufp, string, size, startpos, regs);
+ if (val >= 0) return startpos;
+ return val;
+
+ default:
+ break;
+ }
+ }
+ if (bufp->options & RE_OPTIMIZE_ANCHOR) {
+ if (bufp->options&RE_OPTION_MULTILINE && range > 0) {
+ goto begbuf_match;
+ }
+ anchor = 1;
+ }
+
+ if (bufp->must) {
+ int len = ((unsigned char*)bufp->must)[0];
+ int pos, pbeg, pend;
+
+ pbeg = startpos;
+ pend = startpos + range;
+ if (pbeg > pend) { /* swap pbeg,pend */
+ pos = pend; pend = pbeg; pbeg = pos;
+ }
+ pend = size;
+ if (bufp->options & RE_OPTIMIZE_NO_BM) {
+ pos = slow_search((unsigned char *)(bufp->must+1), len,
+ (unsigned char*)(string+pbeg), pend-pbeg,
+ (char *)(MAY_TRANSLATE()?translate:0));
+ }
+ else {
+ pos = bm_search((unsigned char *)(bufp->must+1), len,
+ (unsigned char *)(string+pbeg), pend-pbeg,
+ bufp->must_skip,
+ MAY_TRANSLATE()?translate:0);
+ }
+ if (pos == -1) return -1;
+ if (range > 0 && (bufp->options & RE_OPTIMIZE_EXACTN)) {
+ startpos += pos;
+ range -= pos;
+ if (range < 0) return -1;
+ }
+ }
+
+ for (;;) {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot possibly be the start of a match. Note, however, that
+ if the pattern can possibly match the null string, we must
+ test it at each starting point so that we take the first null
+ string we get. */
+
+ if (fastmap && startpos < size
+ && bufp->can_be_null != 1 && !(anchor && startpos == 0)) {
+ if (range > 0) { /* Searching forwards. */
+ register unsigned char *p, c;
+ int irange = range;
+
+ p = (unsigned char*)string+startpos;
+
+ while (range > 0) {
+ c = *p++;
+ if (ismbchar(c)) {
+ int len;
+
+ if (fastmap[c])
+ break;
+ len = mbclen(c) - 1;
+ while (len--) {
+ c = *p++;
+ range--;
+ if (fastmap[c] == 2)
+ goto startpos_adjust;
+ }
+ }
+ else {
+ if (fastmap[MAY_TRANSLATE() ? translate[c] : c])
+ break;
+ }
+ range--;
+ }
+ startpos_adjust:
+ startpos += irange - range;
+ }
+ else { /* Searching backwards. */
+ register unsigned char c;
+
+ c = string[startpos];
+ c &= 0xff;
+ if (MAY_TRANSLATE() ? !fastmap[translate[c]] : !fastmap[c])
+ goto advance;
+ }
+ }
+
+ if (startpos > size) return -1;
+ if ((anchor || !bufp->can_be_null) && range > 0 && size > 0 && startpos == size)
+ return -1;
+ val = re_match_exec(bufp, string, size, startpos, initpos, regs);
+ if (val >= 0) return startpos;
+ if (val == -2) return -2;
+
+#ifndef NO_ALLOCA
+#ifdef C_ALLOCA
+ alloca(0);
+#endif /* C_ALLOCA */
+#endif /* NO_ALLOCA */
+
+ if (range > 0) {
+ if (anchor && startpos < size &&
+ (startpos < 1 || string[startpos-1] != '\n')) {
+ while (range > 0 && string[startpos] != '\n') {
+ range--;
+ startpos++;
+ }
+ }
+ }
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0) {
+ const char *d = string + startpos;
+
+ if (ismbchar(*d)) {
+ int len = mbclen(*d) - 1;
+ range-=len, startpos+=len;
+ if (!range)
+ break;
+ }
+ range--, startpos++;
+ }
+ else {
+ range++, startpos--;
+ {
+ const char *s, *d, *p;
+
+ s = string; d = string + startpos;
+ for (p = d; p-- > s && ismbchar(*p); )
+ /* --p >= s would not work on 80[12]?86.
+ (when the offset of s equals 0 other than huge model.) */
+ ;
+ if (!((d - p) & 1)) {
+ if (!range)
+ break;
+ range++, startpos--;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+
+
+
+/* The following are used for re_match, defined below: */
+
+/* Accessing macros used in re_match: */
+
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+
+
+/* Macros used by re_match: */
+
+/* I.e., regstart, regend, and reg_info. */
+#define NUM_REG_ITEMS 3
+
+/* I.e., ptr and count. */
+#define NUM_COUNT_ITEMS 2
+
+/* Individual items aside from the registers. */
+#define NUM_NONREG_ITEMS 4
+
+/* We push at most this many things on the stack whenever we
+ fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are
+ arguments to the PUSH_FAILURE_POINT macro. */
+#define MAX_NUM_FAILURE_ITEMS (num_regs * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We push this many things on the stack whenever we fail. */
+#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + NUM_NONREG_ITEMS + 1)
+
+/* This pushes counter information for succeed_n and jump_n */
+#define PUSH_FAILURE_COUNT(ptr) \
+ do { \
+ int c; \
+ EXTRACT_NUMBER(c, ptr); \
+ ENSURE_FAIL_STACK(NUM_COUNT_ITEMS); \
+ *stackp++ = (unsigned char*)(long)c; \
+ *stackp++ = (ptr); \
+ num_failure_counts++; \
+ } while (0)
+
+/* This pushes most of the information about the current state we will want
+ if we ever fail back to it. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place) \
+ do { \
+ long last_used_reg, this_reg; \
+ \
+ /* Find out how many registers are active or have been matched. \
+ (Aside from register zero, which is only set at the end.) */ \
+ for (last_used_reg = num_regs-1; last_used_reg > 0; last_used_reg--)\
+ if (!REG_UNSET(regstart[last_used_reg])) \
+ break; \
+ \
+ ENSURE_FAIL_STACK(NUM_FAILURE_ITEMS); \
+ *stackp++ = (unsigned char*)(long)num_failure_counts; \
+ num_failure_counts = 0; \
+ \
+ /* Now push the info for each of those registers. */ \
+ for (this_reg = 1; this_reg <= last_used_reg; this_reg++) { \
+ *stackp++ = regstart[this_reg]; \
+ *stackp++ = regend[this_reg]; \
+ *stackp++ = reg_info[this_reg].word; \
+ } \
+ \
+ /* Push how many registers we saved. */ \
+ *stackp++ = (unsigned char*)last_used_reg; \
+ \
+ *stackp++ = pattern_place; \
+ *stackp++ = string_place; \
+ *stackp++ = (unsigned char*)(long)options; /* current option status */ \
+ *stackp++ = (unsigned char*)0; /* non-greedy flag */ \
+ } while(0)
+
+#define NON_GREEDY ((unsigned char*)1)
+
+#define POP_FAILURE_COUNT() \
+ do { \
+ unsigned char *ptr = *--stackp; \
+ int count = (long)*--stackp; \
+ STORE_NUMBER(ptr, count); \
+ } while (0)
+
+/* This pops what PUSH_FAILURE_POINT pushes. */
+
+#define POP_FAILURE_POINT() \
+ do { \
+ long temp; \
+ stackp -= NUM_NONREG_ITEMS; /* Remove failure points (and flag). */ \
+ temp = (long)*--stackp; /* How many regs pushed. */ \
+ temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \
+ stackp -= temp; /* Remove the register info. */ \
+ temp = (long)*--stackp; /* How many counters pushed. */ \
+ while (temp--) { \
+ POP_FAILURE_COUNT(); /* Remove the counter info. */ \
+ } \
+ num_failure_counts = 0; /* Reset num_failure_counts. */ \
+ } while(0)
+
+ /* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((unsigned char*)-1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+#define PREFETCH if (d == dend) goto fail
+
+ /* Call this when have matched something; it sets `matched' flags for the
+ registers corresponding to the subexpressions of which we currently
+ are inside. */
+#define SET_REGS_MATCHED \
+ do { unsigned this_reg; \
+ for (this_reg = 0; this_reg < num_regs; this_reg++) { \
+ if (IS_ACTIVE(reg_info[this_reg])) \
+ MATCHED_SOMETHING(reg_info[this_reg]) = 1; \
+ else \
+ MATCHED_SOMETHING(reg_info[this_reg]) = 0; \
+ } \
+ } while(0)
+
+#define AT_STRINGS_BEG(d) ((d) == string)
+#define AT_STRINGS_END(d) ((d) == dend)
+
+#define IS_A_LETTER(d) (SYNTAX(*(d)) == Sword || \
+ (current_mbctype ? \
+ (re_mbctab[*(d)] && ((d)+mbclen(*(d)))<=dend): \
+ SYNTAX(*(d)) == Sword2))
+
+#define PREV_IS_A_LETTER(d) ((current_mbctype == MBCTYPE_SJIS)? \
+ IS_A_LETTER((d)-(!AT_STRINGS_BEG((d)-1)&& \
+ ismbchar((d)[-2])?2:1)): \
+ ((current_mbctype && ((d)[-1] >= 0x80)) || \
+ IS_A_LETTER((d)-1)))
+
+static void
+init_regs(regs, num_regs)
+ struct re_registers *regs;
+ unsigned int num_regs;
+{
+ int i;
+
+ regs->num_regs = num_regs;
+ if (num_regs < RE_NREGS)
+ num_regs = RE_NREGS;
+
+ if (regs->allocated == 0) {
+ regs->beg = TMALLOC(num_regs, int);
+ regs->end = TMALLOC(num_regs, int);
+ regs->allocated = num_regs;
+ }
+ else if (regs->allocated < num_regs) {
+ TREALLOC(regs->beg, num_regs, int);
+ TREALLOC(regs->end, num_regs, int);
+ regs->allocated = num_regs;
+ }
+ for (i=0; i<num_regs; i++) {
+ regs->beg[i] = regs->end[i] = -1;
+ }
+}
+
+/* Match the pattern described by BUFP against STRING, which is of
+ SIZE. Start the match at index POS in STRING. In REGS, return the
+ indices of STRING that matched the entire BUFP->buffer and its
+ contained subexpressions.
+
+ If bufp->fastmap is nonzero, then it had better be up to date.
+
+ The reason that the data to match are specified as two components
+ which are to be regarded as concatenated is so this function can be
+ used directly on the contents of an Emacs buffer.
+
+ -1 is returned if there is no match. -2 is returned if there is an
+ error (such as match stack overflow). Otherwise the value is the
+ length of the substring which was matched. */
+
+int
+re_match(bufp, string_arg, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string_arg;
+ int size, pos;
+ struct re_registers *regs;
+{
+ return re_match_exec(bufp, string_arg, size, pos, pos, regs);
+}
+
+static int
+re_match_exec(bufp, string_arg, size, pos, beg, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string_arg;
+ int size, pos, beg;
+ struct re_registers *regs;
+{
+ register unsigned char *p = (unsigned char*)bufp->buffer;
+ unsigned char *p1;
+
+ /* Pointer to beyond end of buffer. */
+ register unsigned char *pend = p + bufp->used;
+
+ unsigned num_regs = bufp->re_nsub;
+
+ unsigned char *string = (unsigned char*)string_arg;
+
+ register unsigned char *d, *dend;
+ register int mcnt; /* Multipurpose. */
+ int options = bufp->options;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to the
+ subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where to
+ resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is a
+ ``dummy''; if a failure happens and the failure point is a dummy, it
+ gets discarded and the next next one is tried. */
+
+ unsigned char **const stacka = 0;
+ unsigned char **stackb;
+ unsigned char **stackp;
+ unsigned char **stacke;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+
+ unsigned char **regstart = bufp->regstart;
+ unsigned char **regend = bufp->regend;
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ unsigned char **old_regstart = bufp->old_regstart;
+ unsigned char **old_regend = bufp->old_regend;
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+
+ register_info_type *reg_info = bufp->reg_info;
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+
+ unsigned best_regs_set = 0;
+ unsigned char **best_regstart = bufp->best_regstart;
+ unsigned char **best_regend = bufp->best_regend;
+
+ int num_failure_counts = 0;
+
+ if (regs) {
+ init_regs(regs, num_regs);
+ }
+
+ /* Initialize the stack. */
+ stackb = TMALLOC(MAX_NUM_FAILURE_ITEMS * NFAILURES, unsigned char*);
+ stackp = stackb;
+ stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES];
+
+#ifdef DEBUG_REGEX
+ fprintf(stderr, "Entering re_match(%s)\n", string_arg);
+#endif
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ ( or ( and ) or ) has been seen for. Also set all registers to
+ inactive and mark them as not having matched anything or ever
+ failed. */
+ for (mcnt = 0; mcnt < num_regs; mcnt++) {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt]
+ = best_regstart[mcnt] = best_regend[mcnt] = REG_UNSET_VALUE;
+#ifdef __CHECKER__
+ reg_info[mcnt].word = 0;
+#endif
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* Set up pointers to ends of strings.
+ Don't allow the second string to be empty unless both are empty. */
+
+
+ /* `p' scans through the pattern as `d' scans through the data. `dend'
+ is the end of the input string that `d' points within. `d' is
+ advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal string2. */
+
+ d = string + pos, dend = string + size;
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if match is complete, or it drops through if match fails
+ at this starting point in the input data. */
+
+ for (;;) {
+#ifdef DEBUG_REGEX
+ fprintf(stderr,
+ "regex loop(%d): matching 0x%02d\n",
+ p - (unsigned char*)bufp->buffer,
+ *p);
+#endif
+ /* End of pattern means we might have succeeded. */
+ if (p == pend) {
+ /* If not end of string, try backtracking. Otherwise done. */
+ if ((bufp->options & RE_OPTION_LONGEST) && d != dend) {
+ if (best_regs_set) /* non-greedy, no need to backtrack */
+ goto restore_best_regs;
+ while (stackp != stackb && stackp[-1] == NON_GREEDY) {
+ if (best_regs_set) /* non-greedy, no need to backtrack */
+ goto restore_best_regs;
+ POP_FAILURE_POINT();
+ }
+ if (stackp != stackb) {
+ /* More failure points to try. */
+
+ /* If exceeds best match so far, save it. */
+ if (! best_regs_set || (d > best_regend[0])) {
+ best_regs_set = 1;
+ best_regend[0] = d; /* Never use regstart[0]. */
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++) {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set) {
+ restore_best_regs:
+ /* Restore best match. */
+ d = best_regend[0];
+
+ for (mcnt = 0; mcnt < num_regs; mcnt++) {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ }
+
+ /* If caller wants register contents data back, convert it
+ to indices. */
+ if (regs) {
+ regs->beg[0] = pos;
+ regs->end[0] = d - string;
+ for (mcnt = 1; mcnt < num_regs; mcnt++) {
+ if (REG_UNSET(regend[mcnt])) {
+ regs->beg[mcnt] = -1;
+ regs->end[mcnt] = -1;
+ continue;
+ }
+ regs->beg[mcnt] = regstart[mcnt] - string;
+ regs->end[mcnt] = regend[mcnt] - string;
+ }
+ }
+ FREE_AND_RETURN(stackb, (d - pos - string));
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int)((enum regexpcode)*p++))
+#else
+ switch ((enum regexpcode)*p++)
+#endif
+ {
+ /* ( [or `(', as appropriate] is represented by start_memory,
+ ) by stop_memory. Both of those commands are followed by
+ a register number in the next byte. The text matched
+ within the ( and ) is recorded under that number. */
+ case start_memory:
+ old_regstart[*p] = regstart[*p];
+ regstart[*p] = d;
+ IS_ACTIVE(reg_info[*p]) = 1;
+ MATCHED_SOMETHING(reg_info[*p]) = 0;
+ p += 2;
+ continue;
+
+ case stop_memory:
+ old_regend[*p] = regend[*p];
+ regend[*p] = d;
+ IS_ACTIVE(reg_info[*p]) = 0;
+ p += 2;
+ continue;
+
+ case start_paren:
+ case stop_paren:
+ break;
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ int regno = *p++; /* Get which register to match against */
+ register unsigned char *d2, *dend2;
+
+ /* Check if there's corresponding group */
+ if (regno >= num_regs) goto fail;
+ /* Check if corresponding group is still open */
+ if (IS_ACTIVE(reg_info[regno])) goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+ if (REG_UNSET(d2)) goto fail;
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = regend[regno];
+ if (REG_UNSET(dend2)) goto fail;
+ for (;;) {
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH;
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if ((options & RE_OPTION_IGNORECASE)
+ ? memcmp_translate(d, d2, mcnt)
+ : memcmp((char*)d, (char*)d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+ case start_nowidth:
+ PUSH_FAILURE_POINT(0, d);
+ if (stackp - stackb > RE_DUP_MAX) {
+ FREE_AND_RETURN(stackb,(-2));
+ }
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ STORE_NUMBER(p+mcnt, stackp - stackb);
+ continue;
+
+ case stop_nowidth:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ stackp = stackb + mcnt;
+ d = stackp[-3];
+ POP_FAILURE_POINT();
+ continue;
+
+ case stop_backtrack:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ stackp = stackb + mcnt;
+ POP_FAILURE_POINT();
+ continue;
+
+ case pop_and_fail:
+ EXTRACT_NUMBER(mcnt, p+1);
+ stackp = stackb + mcnt;
+ POP_FAILURE_POINT();
+ goto fail;
+
+ case anychar:
+ PREFETCH;
+ if (ismbchar(*d)) {
+ if (d + mbclen(*d) > dend)
+ goto fail;
+ SET_REGS_MATCHED;
+ d += mbclen(*d);
+ break;
+ }
+ if (!(options&RE_OPTION_MULTILINE)
+ && (TRANSLATE_P() ? translate[*d] : *d) == '\n')
+ goto fail;
+ SET_REGS_MATCHED;
+ d++;
+ break;
+
+ case anychar_repeat:
+ for (;;) {
+ PUSH_FAILURE_POINT(p, d);
+ PREFETCH;
+ if (ismbchar(*d)) {
+ if (d + mbclen(*d) > dend)
+ goto fail;
+ SET_REGS_MATCHED;
+ d += mbclen(*d);
+ continue;
+ }
+ if (!(options&RE_OPTION_MULTILINE) &&
+ (TRANSLATE_P() ? translate[*d] : *d) == '\n')
+ goto fail;
+ SET_REGS_MATCHED;
+ d++;
+ }
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ int not; /* Nonzero for charset_not. */
+ int part = 0; /* true if matched part of mbc */
+ unsigned char *dsave = d + 1;
+ int cc, c;
+
+ PREFETCH;
+ c = (unsigned char)*d++;
+ if (ismbchar(c)) {
+ if (d + mbclen(c) - 1 <= dend) {
+ cc = c;
+ MBC2WC(c, d);
+ not = is_in_list_mbc(c, p);
+ if (!not) {
+ part = not = is_in_list_sbc(cc, p);
+ }
+ } else {
+ not = is_in_list(c, p);
+ }
+ }
+ else {
+ if (TRANSLATE_P())
+ c = (unsigned char)translate[c];
+ not = is_in_list(c, p);
+ }
+
+ if (*(p - 1) == (unsigned char)charset_not) {
+ not = !not;
+ }
+ if (!not) goto fail;
+
+ p += 1 + *p + 2 + EXTRACT_UNSIGNED(&p[1 + *p])*8;
+ SET_REGS_MATCHED;
+
+ if (part) d = dsave;
+ break;
+ }
+
+ case begline:
+ if (size == 0 || AT_STRINGS_BEG(d))
+ break;
+ if (d[-1] == '\n' && !AT_STRINGS_END(d))
+ break;
+ goto fail;
+
+ case endline:
+ if (AT_STRINGS_END(d)) {
+ break;
+ }
+ else if (*d == '\n')
+ break;
+ goto fail;
+
+ /* Match at the very beginning of the string. */
+ case begbuf:
+ if (AT_STRINGS_BEG(d))
+ break;
+ goto fail;
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ if (AT_STRINGS_END(d))
+ break;
+ goto fail;
+
+ /* Match at the very end of the data. */
+ case endbuf2:
+ if (AT_STRINGS_END(d)) {
+ break;
+ }
+ /* .. or newline just before the end of the data. */
+ if (*d == '\n' && AT_STRINGS_END(d+1))
+ break;
+ goto fail;
+
+ /* `or' constructs are handled by starting each alternative with
+ an on_failure_jump that points to the start of the next
+ alternative. Each alternative except the last ends with a
+ jump to the joining point. (Actually, each jump except for
+ the last one really jumps to the following jump, because
+ tensioning the jumps is a hassle.) */
+
+ /* The start of a stupid repeat has an on_failure_jump that points
+ past the end of the repeat text. This makes a failure point so
+ that on failure to match a repetition, matching restarts past
+ as many repetitions have been found with no way to fail and
+ look for another one. */
+
+ /* A smart repeat is similar but loops back to the on_failure_jump
+ so that each repetition makes another failure point. */
+
+ /* Match at the starting position. */
+ case begpos:
+ if (d - string == beg)
+ break;
+ goto fail;
+
+ case on_failure_jump:
+ on_failure:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ PUSH_FAILURE_POINT(p + mcnt, d);
+ continue;
+
+ /* The end of a smart repeat has a maybe_finalize_jump back.
+ Change it either to a finalize_jump or an ordinary jump. */
+ case maybe_finalize_jump:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ p1 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to finalize_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands. */
+ while (p1 + 2 < pend) {
+ if ((enum regexpcode)*p1 == stop_memory ||
+ (enum regexpcode)*p1 == start_memory)
+ p1 += 3; /* Skip over args, too. */
+ else if (/*(enum regexpcode)*p1 == start_paren ||*/
+ (enum regexpcode)*p1 == stop_paren)
+ p1 += 1;
+ else
+ break;
+ }
+
+ if (p1 == pend)
+ p[-3] = (unsigned char)finalize_jump;
+ else if (*p1 == (unsigned char)exactn ||
+ *p1 == (unsigned char)endline) {
+ register int c = *p1 == (unsigned char)endline ? '\n' : p1[2];
+ register unsigned char *p2 = p + mcnt;
+ /* p2[0] ... p2[2] are an on_failure_jump.
+ Examine what follows that. */
+ if (p2[3] == (unsigned char)exactn && p2[5] != c)
+ p[-3] = (unsigned char)finalize_jump;
+ else if (p2[3] == (unsigned char)charset ||
+ p2[3] == (unsigned char)charset_not) {
+ int not;
+ if (ismbchar(c)) {
+ unsigned char *pp = p1+3;
+ MBC2WC(c, pp);
+ }
+ /* `is_in_list()' is TRUE if c would match */
+ /* That means it is not safe to finalize. */
+ not = is_in_list(c, p2 + 4);
+ if (p2[3] == (unsigned char)charset_not)
+ not = !not;
+ if (!not)
+ p[-3] = (unsigned char)finalize_jump;
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if (p[-1] != (unsigned char)finalize_jump) {
+ p[-1] = (unsigned char)jump;
+ goto nofinalize;
+ }
+ /* Note fall through. */
+
+ /* The end of a stupid repeat has a finalize_jump back to the
+ start, where another failure point will be made which will
+ point to after all the repetitions found so far. */
+
+ /* Take off failure points put on by matching on_failure_jump
+ because didn't fail. Also remove the register information
+ put on by the on_failure_jump. */
+ case finalize_jump:
+ if (stackp > stackb && stackp[-3] == d) {
+ p = stackp[-4];
+ POP_FAILURE_POINT();
+ continue;
+ }
+ POP_FAILURE_POINT();
+ /* Note fall through. */
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ /* fall through */
+
+ /* Jump without taking off any failure points. */
+ case jump:
+ nofinalize:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (mcnt < 0 && stackp > stackb && stackp[-3] == d) /* avoid infinite loop */
+ goto fail;
+ p += mcnt;
+ continue;
+
+ case dummy_failure_jump:
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at finalize_jump. We will end up at
+ finalize_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for finalize_jump to pop. */
+ PUSH_FAILURE_POINT(0, 0);
+ goto nofinalize;
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `finalize_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ p1 = p;
+ /* Skip over open/close-group commands. */
+ while (p1 + 2 < pend) {
+ if ((enum regexpcode)*p1 == stop_memory ||
+ (enum regexpcode)*p1 == start_memory)
+ p1 += 3; /* Skip over args, too. */
+ else if (/*(enum regexpcode)*p1 == start_paren ||*/
+ (enum regexpcode)*p1 == stop_paren)
+ p1 += 1;
+ else
+ break;
+ }
+ if (p1 < pend && (enum regexpcode)*p1 == jump)
+ p[-1] = unused;
+ else
+ PUSH_FAILURE_POINT(0, 0);
+ break;
+
+ /* Have to succeed matching what follows at least n times. Then
+ just handle like an on_failure_jump. */
+ case succeed_n:
+ EXTRACT_NUMBER(mcnt, p + 2);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt != 0) {
+ mcnt--;
+ p += 2;
+ PUSH_FAILURE_COUNT(p);
+ STORE_NUMBER_AND_INCR(p, mcnt);
+ PUSH_FAILURE_POINT(0, 0);
+ }
+ else {
+ goto on_failure;
+ }
+ continue;
+
+ case jump_n:
+ EXTRACT_NUMBER(mcnt, p + 2);
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt) {
+ mcnt--;
+ PUSH_FAILURE_COUNT(p + 2);
+ STORE_NUMBER(p + 2, mcnt);
+ goto nofinalize; /* Do the jump without taking off
+ any failure points. */
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ continue;
+
+ case set_number_at:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ STORE_NUMBER(p1, mcnt);
+ continue;
+
+ case try_next:
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (p + mcnt < pend) {
+ PUSH_FAILURE_POINT(p, d);
+ stackp[-1] = NON_GREEDY;
+ }
+ p += mcnt;
+ continue;
+
+ case finalize_push:
+ POP_FAILURE_POINT();
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ if (mcnt < 0 && stackp > stackb && stackp[-3] == d) /* avoid infinite loop */
+ goto fail;
+ PUSH_FAILURE_POINT(p + mcnt, d);
+ stackp[-1] = NON_GREEDY;
+ continue;
+
+ case finalize_push_n:
+ EXTRACT_NUMBER(mcnt, p + 2);
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt) {
+ int pos, i;
+
+ mcnt--;
+ STORE_NUMBER(p + 2, mcnt);
+ EXTRACT_NUMBER(pos, p);
+ EXTRACT_NUMBER(i, p+pos+5);
+ if (i > 0) goto nofinalize;
+ POP_FAILURE_POINT();
+ EXTRACT_NUMBER_AND_INCR(mcnt, p);
+ PUSH_FAILURE_POINT(p + mcnt, d);
+ stackp[-1] = NON_GREEDY;
+ p += 2; /* skip n */
+ }
+ /* If don't have to push any more, skip over the rest of command. */
+ else
+ p += 4;
+ continue;
+
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case unused:
+ continue;
+
+ case casefold_on:
+ options |= RE_OPTION_IGNORECASE;
+ continue;
+
+ case casefold_off:
+ options &= ~RE_OPTION_IGNORECASE;
+ continue;
+
+ case option_set:
+ options = *p++;
+ continue;
+
+ case wordbound:
+ if (AT_STRINGS_BEG(d)) {
+ if (AT_STRINGS_END(d)) goto fail;
+ if (IS_A_LETTER(d)) break;
+ else goto fail;
+ }
+ if (AT_STRINGS_END(d)) {
+ if (PREV_IS_A_LETTER(d)) break;
+ else goto fail;
+ }
+ if (PREV_IS_A_LETTER(d) != IS_A_LETTER(d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ if (AT_STRINGS_BEG(d)) {
+ if (IS_A_LETTER(d)) goto fail;
+ else break;
+ }
+ if (AT_STRINGS_END(d)) {
+ if (PREV_IS_A_LETTER(d)) goto fail;
+ else break;
+ }
+ if (PREV_IS_A_LETTER(d) != IS_A_LETTER(d))
+ goto fail;
+ break;
+
+ case wordbeg:
+ if (IS_A_LETTER(d) && (AT_STRINGS_BEG(d) || !PREV_IS_A_LETTER(d)))
+ break;
+ goto fail;
+
+ case wordend:
+ if (!AT_STRINGS_BEG(d) && PREV_IS_A_LETTER(d)
+ && (!IS_A_LETTER(d) || AT_STRINGS_END(d)))
+ break;
+ goto fail;
+
+ case wordchar:
+ PREFETCH;
+ if (!IS_A_LETTER(d))
+ goto fail;
+ if (ismbchar(*d) && d + mbclen(*d) - 1 < dend)
+ d += mbclen(*d) - 1;
+ d++;
+ SET_REGS_MATCHED;
+ break;
+
+ case notwordchar:
+ PREFETCH;
+ if (IS_A_LETTER(d))
+ goto fail;
+ if (ismbchar(*d) && d + mbclen(*d) - 1 < dend)
+ d += mbclen(*d) - 1;
+ d++;
+ SET_REGS_MATCHED;
+ break;
+
+ case exactn:
+ /* Match the next few pattern characters exactly.
+ mcnt is how many characters to match. */
+ mcnt = *p++;
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (TRANSLATE_P()) {
+ do {
+ unsigned char c;
+
+ PREFETCH;
+ if (*p == 0xff) {
+ p++;
+ if (!--mcnt
+ || AT_STRINGS_END(d)
+ || (unsigned char)*d++ != (unsigned char)*p++)
+ goto fail;
+ continue;
+ }
+ c = *d++;
+ if (ismbchar(c)) {
+ int n;
+
+ if (c != (unsigned char)*p++)
+ goto fail;
+ for (n = mbclen(c) - 1; n > 0; n--)
+ if (!--mcnt /* redundant check if pattern was
+ compiled properly. */
+ || AT_STRINGS_END(d)
+ || (unsigned char)*d++ != (unsigned char)*p++)
+ goto fail;
+ continue;
+ }
+ /* compiled code translation needed for ruby */
+ if ((unsigned char)translate[c] != (unsigned char)translate[*p++])
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else {
+ do {
+ PREFETCH;
+ if (*p == 0xff) {p++; mcnt--;}
+ if (*d++ != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED;
+ break;
+ }
+#ifdef RUBY
+ CHECK_INTS;
+#endif
+ continue; /* Successfully executed one pattern command; keep going. */
+
+ /* Jump here if any matching operation fails. */
+ fail:
+ if (stackp != stackb) {
+ /* A restart point is known. Restart there and pop it. */
+ short last_used_reg, this_reg;
+
+ /* If this failure point is from a dummy_failure_point, just
+ skip it. */
+ if (stackp[-4] == 0 || (best_regs_set && stackp[-1] == NON_GREEDY)) {
+ POP_FAILURE_POINT();
+ goto fail;
+ }
+ stackp--; /* discard greedy flag */
+ options = (long)*--stackp;
+ d = *--stackp;
+ p = *--stackp;
+ /* Restore register info. */
+ last_used_reg = (long)*--stackp;
+
+ /* Make the ones that weren't saved -1 or 0 again. */
+ for (this_reg = num_regs - 1; this_reg > last_used_reg; this_reg--) {
+ regend[this_reg] = REG_UNSET_VALUE;
+ regstart[this_reg] = REG_UNSET_VALUE;
+ IS_ACTIVE(reg_info[this_reg]) = 0;
+ MATCHED_SOMETHING(reg_info[this_reg]) = 0;
+ }
+
+ /* And restore the rest from the stack. */
+ for ( ; this_reg > 0; this_reg--) {
+ reg_info[this_reg].word = *--stackp;
+ regend[this_reg] = *--stackp;
+ regstart[this_reg] = *--stackp;
+ }
+ mcnt = (long)*--stackp;
+ while (mcnt--) {
+ POP_FAILURE_COUNT();
+ }
+ if (p < pend) {
+ int is_a_jump_n = 0;
+ int failed_paren = 0;
+
+ p1 = p;
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((enum regexpcode)*p1) {
+ case jump_n:
+ case finalize_push_n:
+ is_a_jump_n = 1;
+ case maybe_finalize_jump:
+ case finalize_jump:
+ case finalize_push:
+ case jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR(mcnt, p1);
+
+ if (mcnt >= 0) break; /* should be backward jump */
+ p1 += mcnt;
+
+ if (( is_a_jump_n && (enum regexpcode)*p1 == succeed_n) ||
+ (!is_a_jump_n && (enum regexpcode)*p1 == on_failure_jump)) {
+ if (failed_paren) {
+ p1++;
+ EXTRACT_NUMBER_AND_INCR(mcnt, p1);
+ PUSH_FAILURE_POINT(p1 + mcnt, d);
+ }
+ goto fail;
+ }
+ break;
+ default:
+ /* do nothing */;
+ }
+ }
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ }
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_AND_RETURN(stackb,(-1)); /* Failure to match. */
+ memory_exhausted:
+ FREE_AND_RETURN(stackb,(-2));
+}
+
+
+static int
+memcmp_translate(s1, s2, len)
+ unsigned char *s1, *s2;
+ register int len;
+{
+ register unsigned char *p1 = s1, *p2 = s2, c;
+ while (len) {
+ c = *p1++;
+ if (ismbchar(c)) {
+ int n;
+
+ if (c != *p2++) return 1;
+ for (n = mbclen(c) - 1; n > 0; n--)
+ if (!--len || *p1++ != *p2++)
+ return 1;
+ }
+ else
+ if (translate[c] != translate[*p2++])
+ return 1;
+ len--;
+ }
+ return 0;
+}
+
+void
+re_copy_registers(regs1, regs2)
+ struct re_registers *regs1, *regs2;
+{
+ int i;
+
+ if (regs1 == regs2) return;
+ if (regs1->allocated == 0) {
+ regs1->beg = TMALLOC(regs2->num_regs, int);
+ regs1->end = TMALLOC(regs2->num_regs, int);
+ regs1->allocated = regs2->num_regs;
+ }
+ else if (regs1->allocated < regs2->num_regs) {
+ TREALLOC(regs1->beg, regs2->num_regs, int);
+ TREALLOC(regs1->end, regs2->num_regs, int);
+ regs1->allocated = regs2->num_regs;
+ }
+ for (i=0; i<regs2->num_regs; i++) {
+ regs1->beg[i] = regs2->beg[i];
+ regs1->end[i] = regs2->end[i];
+ }
+ regs1->num_regs = regs2->num_regs;
+}
+
+void
+re_free_registers(regs)
+ struct re_registers *regs;
+{
+ if (regs->allocated == 0) return;
+ if (regs->beg) xfree(regs->beg);
+ if (regs->end) xfree(regs->end);
+}
+
+/* Functions for multi-byte support.
+ Created for grep multi-byte extension Jul., 1993 by t^2 (Takahiro Tanimoto)
+ Last change: Jul. 9, 1993 by t^2 */
+static const unsigned char mbctab_ascii[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static const unsigned char mbctab_euc[] = { /* 0xA1-0xFE */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+};
+
+static const unsigned char mbctab_sjis[] = { /* 0x81-0x9F,0xE0-0xFC */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
+};
+
+static const unsigned char mbctab_sjis_trail[] = { /* 0x40-0x7E,0x80-0xFC */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
+};
+
+static const unsigned char mbctab_utf8[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0,
+};
+
+const unsigned char *re_mbctab = mbctab_ascii;
+
+void
+re_mbcinit(mbctype)
+ int mbctype;
+{
+ switch (mbctype) {
+ case MBCTYPE_ASCII:
+ re_mbctab = mbctab_ascii;
+ current_mbctype = MBCTYPE_ASCII;
+ break;
+ case MBCTYPE_EUC:
+ re_mbctab = mbctab_euc;
+ current_mbctype = MBCTYPE_EUC;
+ break;
+ case MBCTYPE_SJIS:
+ re_mbctab = mbctab_sjis;
+ current_mbctype = MBCTYPE_SJIS;
+ break;
+ case MBCTYPE_UTF8:
+ re_mbctab = mbctab_utf8;
+ current_mbctype = MBCTYPE_UTF8;
+ break;
+ }
+}
+
+#define mbc_isfirst(t, c) (t)[(unsigned char)(c)]
+#define mbc_len(t, c) ((t)[(unsigned char)(c)]+1)
+
+static unsigned int
+asc_startpos(string, pos)
+ const char *string;
+ unsigned int pos;
+{
+ return pos;
+}
+
+#define euc_islead(c) ((unsigned char)((c) - 0xa1) > 0xfe - 0xa1)
+#define euc_mbclen(c) mbc_len(mbctab_euc, (c))
+static unsigned int
+euc_startpos(string, pos)
+ const char *string;
+ unsigned int pos;
+{
+ unsigned int i = pos, w;
+
+ while (i > 0 && !euc_islead(string[i])) {
+ --i;
+ }
+ if (i == pos || i + (w = euc_mbclen(string[i])) > pos) {
+ return i;
+ }
+ i += w;
+ return i + ((pos - i) & ~1);
+}
+
+#define sjis_isfirst(c) mbc_isfirst(mbctab_sjis, (c))
+#define sjis_istrail(c) mbctab_sjis_trail[(unsigned char)(c)]
+#define sjis_mbclen(c) mbc_len(mbctab_sjis, (c))
+static unsigned int
+sjis_startpos(string, pos)
+ const char *string;
+ unsigned int pos;
+{
+ unsigned int i = pos, w;
+
+ if (i > 0 && sjis_istrail(string[i])) {
+ do {
+ if (!sjis_isfirst(string[--i])) {
+ ++i;
+ break;
+ }
+ } while (i > 0);
+ }
+ if (i == pos || i + (w = sjis_mbclen(string[i])) > pos) {
+ return i;
+ }
+ i += w;
+ return i + ((pos - i) & ~1);
+}
+
+#define utf8_islead(c) ((unsigned char)((c) & 0xc0) != 0x80)
+#define utf8_mbclen(c) mbc_len(mbctab_utf8, (c))
+static unsigned int
+utf8_startpos(string, pos)
+ const char *string;
+ unsigned int pos;
+{
+ unsigned int i = pos, w;
+
+ while (i > 0 && !utf8_islead(string[i])) {
+ --i;
+ }
+ if (i == pos || i + (w = utf8_mbclen(string[i])) > pos) {
+ return i;
+ }
+ return i + w;
+}
+
+/*
+ vi: sw=2 ts=8
+ Local variables:
+ mode : C
+ c-file-style : "gnu"
+ tab-width : 8
+ End
+*/
+/**********************************************************************
+
+ ruby.c -
+
+ $Author: shyouhei $
+ $Date: 2008-07-10 11:36:08 +0200 (Thu, 10 Jul 2008) $
+ created at: Tue Aug 10 12:47:31 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#if defined _WIN32 || defined __CYGWIN__
+#include <windows.h>
+#endif
+#if defined __CYGWIN__
+#include <sys/cygwin.h>
+#endif
+#ifdef _WIN32_WCE
+#include <winsock.h>
+#include "wince.h"
+#endif
+#include "ruby.h"
+#include "dln.h"
+#include "node.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#ifdef __hpux
+#include <sys/pstat.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef HAVE_STRING_H
+char *strchr _((const char*,const char));
+char *strrchr _((const char*,const char));
+char *strstr _((const char*,const char*));
+#endif
+
+#include "util.h"
+
+#ifndef HAVE_STDLIB_H
+char *getenv();
+#endif
+
+VALUE ruby_debug = Qfalse;
+VALUE ruby_verbose = Qfalse;
+static int sflag = 0;
+static int xflag = 0;
+extern int ruby_yydebug;
+
+char *ruby_inplace_mode = Qfalse;
+
+static void load_stdin _((void));
+static void load_file _((const char *, int));
+static void forbid_setid _((const char *));
+
+static VALUE do_loop = Qfalse, do_print = Qfalse;
+static VALUE do_check = Qfalse, do_line = Qfalse;
+static VALUE do_split = Qfalse;
+
+static char *script;
+
+static int origargc;
+static char **origargv;
+
+static void
+usage(name)
+ const char *name;
+{
+ /* This message really ought to be max 23 lines.
+ * Removed -h because the user already knows that option. Others? */
+
+ static char *usage_msg[] = {
+"-0[octal] specify record separator (\\0, if no argument)",
+"-a autosplit mode with -n or -p (splits $_ into $F)",
+"-c check syntax only",
+"-Cdirectory cd to directory, before executing your script",
+"-d set debugging flags (set $DEBUG to true)",
+"-e 'command' one line of script. Several -e's allowed. Omit [programfile]",
+"-Fpattern split() pattern for autosplit (-a)",
+"-i[extension] edit ARGV files in place (make backup if extension supplied)",
+"-Idirectory specify $LOAD_PATH directory (may be used more than once)",
+"-Kkcode specifies KANJI (Japanese) code-set",
+"-l enable line ending processing",
+"-n assume 'while gets(); ... end' loop around your script",
+"-p assume loop like -n but print line also like sed",
+"-rlibrary require the library, before executing your script",
+"-s enable some switch parsing for switches after script name",
+"-S look for the script using PATH environment variable",
+"-T[level] turn on tainting checks",
+"-v print version number, then turn on verbose mode",
+"-w turn warnings on for your script",
+"-W[level] set warning level; 0=silence, 1=medium, 2=verbose (default)",
+"-x[directory] strip off text before #!ruby line and perhaps cd to directory",
+"--copyright print the copyright",
+"--version print the version",
+NULL
+};
+ char **p = usage_msg;
+
+ printf("Usage: %s [switches] [--] [programfile] [arguments]\n", name);
+ while (*p)
+ printf(" %s\n", *p++);
+}
+
+extern VALUE rb_load_path;
+
+#ifndef CharNext /* defined as CharNext[AW] on Windows. */
+#define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
+#endif
+
+#if defined DOSISH || defined __CYGWIN__
+static inline void
+translate_char(char *p, int from, int to)
+{
+ while (*p) {
+ if ((unsigned char)*p == from)
+ *p = to;
+ p = CharNext(p);
+ }
+}
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__ || defined __DJGPP__
+static VALUE
+rubylib_mangled_path(const char *s, unsigned int l)
+{
+ static char *newp, *oldp;
+ static int newl, oldl, notfound;
+ char *ptr;
+ VALUE ret;
+
+ if (!newp && !notfound) {
+ newp = getenv("RUBYLIB_PREFIX");
+ if (newp) {
+ oldp = newp = strdup(newp);
+ while (*newp && !ISSPACE(*newp) && *newp != ';') {
+ newp = CharNext(newp); /* Skip digits. */
+ }
+ oldl = newp - oldp;
+ while (*newp && (ISSPACE(*newp) || *newp == ';')) {
+ newp = CharNext(newp); /* Skip whitespace. */
+ }
+ newl = strlen(newp);
+ if (newl == 0 || oldl == 0) {
+ rb_fatal("malformed RUBYLIB_PREFIX");
+ }
+ translate_char(newp, '\\', '/');
+ }
+ else {
+ notfound = 1;
+ }
+ }
+ if (!newp || l < oldl || strncasecmp(oldp, s, oldl) != 0) {
+ return rb_str_new(s, l);
+ }
+ ret = rb_str_new(0, l + newl - oldl);
+ ptr = RSTRING_PTR(ret);
+ memcpy(ptr, newp, newl);
+ memcpy(ptr + newl, s + oldl, l - oldl);
+ ptr[l + newl - oldl] = 0;
+ return ret;
+}
+
+static VALUE
+rubylib_mangled_path2(const char *s)
+{
+ return rubylib_mangled_path(s, strlen(s));
+}
+#else
+#define rubylib_mangled_path rb_str_new
+#define rubylib_mangled_path2 rb_str_new2
+#endif
+
+static void push_include _((const char *path));
+
+static void
+push_include(path)
+ const char *path;
+{
+ const char sep = PATH_SEP_CHAR;
+ const char *p, *s;
+
+ p = path;
+ while (*p) {
+ while (*p == sep)
+ p++;
+ if (!*p) break;
+ for (s = p; *s && *s != sep; s = CharNext(s));
+ rb_ary_push(rb_load_path, rubylib_mangled_path(p, s - p));
+ p = s;
+ }
+}
+
+#ifdef __CYGWIN__
+static void
+push_include_cygwin(const char *path)
+{
+ const char *p, *s;
+ char rubylib[FILENAME_MAX];
+ VALUE buf = 0;
+
+ p = path;
+ while (*p) {
+ unsigned int len;
+ while (*p == ';')
+ p++;
+ if (!*p) break;
+ for (s = p; *s && *s != ';'; s = CharNext(s));
+ len = s - p;
+ if (*s) {
+ if (!buf) {
+ buf = rb_str_new(p, len);
+ p = RSTRING_PTR(buf);
+ }
+ else {
+ rb_str_resize(buf, len);
+ p = strncpy(RSTRING_PTR(buf), p, len);
+ }
+ }
+ if (cygwin_conv_to_posix_path(p, rubylib) == 0)
+ p = rubylib;
+ push_include(p);
+ if (!*s) break;
+ p = s + 1;
+ }
+}
+
+#define push_include push_include_cygwin
+#endif
+
+void
+ruby_incpush(path)
+ const char *path;
+{
+ if (path == 0)
+ return;
+ push_include(path);
+}
+
+#if defined DOSISH || defined __CYGWIN__
+#define LOAD_RELATIVE 1
+#endif
+
+void
+ruby_init_loadpath()
+{
+#if defined LOAD_RELATIVE
+ char libpath[FILENAME_MAX+1];
+ char *p;
+ int rest;
+#if defined _WIN32 || defined __CYGWIN__
+ HMODULE libruby = NULL;
+ MEMORY_BASIC_INFORMATION m;
+
+#ifndef _WIN32_WCE
+ memset(&m, 0, sizeof(m));
+ if (VirtualQuery(ruby_init_loadpath, &m, sizeof(m)) && m.State == MEM_COMMIT)
+ libruby = (HMODULE)m.AllocationBase;
+#endif
+ GetModuleFileName(libruby, libpath, sizeof libpath);
+#elif defined(DJGPP)
+ extern char *__dos_argv0;
+ strncpy(libpath, __dos_argv0, FILENAME_MAX);
+#elif defined(__human68k__)
+ extern char **_argv;
+ strncpy(libpath, _argv[0], FILENAME_MAX);
+#elif defined(__EMX__)
+ _execname(libpath, FILENAME_MAX);
+#endif
+
+ libpath[FILENAME_MAX] = '\0';
+#if defined DOSISH
+ translate_char(libpath, '\\', '/');
+#elif defined __CYGWIN__
+ {
+ char rubylib[FILENAME_MAX];
+ cygwin_conv_to_posix_path(libpath, rubylib);
+ strncpy(libpath, rubylib, sizeof(libpath));
+ }
+#endif
+ p = strrchr(libpath, '/');
+ if (p) {
+ *p = 0;
+ if (p - libpath > 3 && !strcasecmp(p - 4, "/bin")) {
+ p -= 4;
+ *p = 0;
+ }
+ }
+ else {
+ strcpy(libpath, ".");
+ p = libpath + 1;
+ }
+
+ rest = FILENAME_MAX - (p - libpath);
+
+#define RUBY_RELATIVE(path) (strncpy(p, (path), rest), libpath)
+#else
+#define RUBY_RELATIVE(path) (path)
+#endif
+#define incpush(path) rb_ary_push(rb_load_path, rubylib_mangled_path2(path))
+
+ if (rb_safe_level() == 0) {
+ ruby_incpush(getenv("RUBYLIB"));
+ }
+
+#ifdef RUBY_SEARCH_PATH
+ incpush(RUBY_RELATIVE(RUBY_SEARCH_PATH));
+#endif
+
+ incpush(RUBY_RELATIVE(RUBY_SITE_LIB2));
+#ifdef RUBY_SITE_THIN_ARCHLIB
+ incpush(RUBY_RELATIVE(RUBY_SITE_THIN_ARCHLIB));
+#endif
+ incpush(RUBY_RELATIVE(RUBY_SITE_ARCHLIB));
+ incpush(RUBY_RELATIVE(RUBY_SITE_LIB));
+
+ incpush(RUBY_RELATIVE(RUBY_LIB));
+#ifdef RUBY_THIN_ARCHLIB
+ incpush(RUBY_RELATIVE(RUBY_THIN_ARCHLIB));
+#endif
+ incpush(RUBY_RELATIVE(RUBY_ARCHLIB));
+
+ if (rb_safe_level() == 0) {
+ incpush(".");
+ }
+}
+
+struct req_list {
+ char *name;
+ struct req_list *next;
+};
+static struct req_list req_list_head, *req_list_last = &req_list_head;
+
+static void
+add_modules(mod)
+ const char *mod;
+{
+ struct req_list *list;
+
+ list = ALLOC(struct req_list);
+ list->name = ALLOC_N(char, strlen(mod)+1);
+ strcpy(list->name, mod);
+ list->next = 0;
+ req_list_last->next = list;
+ req_list_last = list;
+}
+
+extern void Init_ext _((void));
+
+static void
+require_libraries()
+{
+ extern NODE *ruby_eval_tree;
+ extern NODE *ruby_eval_tree_begin;
+ NODE *save[3];
+ struct req_list *list = req_list_head.next;
+ struct req_list *tmp;
+
+ save[0] = ruby_eval_tree;
+ save[1] = ruby_eval_tree_begin;
+ save[2] = NEW_NEWLINE(0);
+ ruby_eval_tree = ruby_eval_tree_begin = 0;
+ ruby_current_node = 0;
+ Init_ext(); /* should be called here for some reason :-( */
+ ruby_current_node = save[2];
+ ruby_set_current_source();
+ req_list_last = 0;
+ while (list) {
+ int state;
+
+ ruby_current_node = 0;
+ rb_protect((VALUE (*)(VALUE))rb_require, (VALUE)list->name, &state);
+ if (state) rb_jump_tag(state);
+ tmp = list->next;
+ free(list->name);
+ free(list);
+ list = tmp;
+ ruby_current_node = save[2];
+ ruby_set_current_source();
+ }
+ req_list_head.next = 0;
+ ruby_eval_tree = save[0];
+ ruby_eval_tree_begin = save[1];
+ rb_gc_force_recycle((VALUE)save[2]);
+ ruby_current_node = 0;
+}
+
+static void
+process_sflag()
+{
+ if (sflag) {
+ long n;
+ VALUE *args;
+
+ n = RARRAY(rb_argv)->len;
+ args = RARRAY(rb_argv)->ptr;
+ while (n > 0) {
+ VALUE v = *args++;
+ char *s = StringValuePtr(v);
+ char *p;
+ int hyphen = Qfalse;
+
+ if (s[0] != '-') break;
+ n--;
+ if (s[1] == '-' && s[2] == '\0') break;
+
+ v = Qtrue;
+ /* check if valid name before replacing - with _ */
+ for (p = s + 1; *p; p++) {
+ if (*p == '=') {
+ *p++ = '\0';
+ v = rb_str_new2(p);
+ break;
+ }
+ if (*p == '-') {
+ hyphen = Qtrue;
+ }
+ else if (*p != '_' && !ISALNUM(*p)) {
+ VALUE name_error[2];
+ name_error[0] = rb_str_new2("invalid name for global variable - ");
+ if (!(p = strchr(p, '='))) {
+ rb_str_cat2(name_error[0], s);
+ }
+ else {
+ rb_str_cat(name_error[0], s, p - s);
+ }
+ name_error[1] = args[-1];
+ rb_exc_raise(rb_class_new_instance(2, name_error, rb_eNameError));
+ }
+ }
+ s[0] = '$';
+ if (hyphen) {
+ for (p = s + 1; *p; ++p) {
+ if (*p == '-') *p = '_';
+ }
+ }
+ rb_gv_set(s, v);
+ }
+ n = RARRAY(rb_argv)->len - n;
+ while (n--) {
+ rb_ary_shift(rb_argv);
+ }
+ }
+ sflag = 0;
+}
+
+static void proc_options _((int argc, char **argv));
+
+static char*
+moreswitches(s)
+ char *s;
+{
+ int argc; char *argv[3];
+ char *p = s;
+
+ argc = 2; argv[0] = argv[2] = 0;
+ while (*s && !ISSPACE(*s))
+ s++;
+ argv[1] = ALLOCA_N(char, s-p+2);
+ argv[1][0] = '-';
+ strncpy(argv[1]+1, p, s-p);
+ argv[1][s-p+1] = '\0';
+ proc_options(argc, argv);
+ while (*s && ISSPACE(*s))
+ s++;
+ return s;
+}
+
+static void
+proc_options(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *argv0 = argv[0];
+ int do_search;
+ char *s;
+ NODE *volatile script_node = 0;
+
+ int version = 0;
+ int copyright = 0;
+ int verbose = 0;
+ VALUE e_script = Qfalse;
+
+ if (argc == 0) return;
+
+ do_search = Qfalse;
+
+ for (argc--,argv++; argc > 0; argc--,argv++) {
+ if (argv[0][0] != '-' || !argv[0][1]) break;
+
+ s = argv[0]+1;
+ reswitch:
+ switch (*s) {
+ case 'a':
+ do_split = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'p':
+ do_print = Qtrue;
+ /* through */
+ case 'n':
+ do_loop = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'd':
+ ruby_debug = Qtrue;
+ ruby_verbose = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'y':
+ ruby_yydebug = 1;
+ s++;
+ goto reswitch;
+
+ case 'v':
+ if (argv0 == 0 || verbose) {
+ s++;
+ goto reswitch;
+ }
+ ruby_show_version();
+ verbose = 1;
+ case 'w':
+ ruby_verbose = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'W':
+ {
+ int numlen;
+ int v = 2; /* -W as -W2 */
+
+ if (*++s) {
+ v = scan_oct(s, 1, &numlen);
+ if (numlen == 0) v = 1;
+ s += numlen;
+ }
+ switch (v) {
+ case 0:
+ ruby_verbose = Qnil; break;
+ case 1:
+ ruby_verbose = Qfalse; break;
+ default:
+ ruby_verbose = Qtrue; break;
+ }
+ }
+ goto reswitch;
+
+ case 'c':
+ do_check = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 's':
+ forbid_setid("-s");
+ sflag = 1;
+ s++;
+ goto reswitch;
+
+ case 'h':
+ usage(origargv[0]);
+ exit(0);
+
+ case 'l':
+ do_line = Qtrue;
+ rb_output_rs = rb_rs;
+ s++;
+ goto reswitch;
+
+ case 'S':
+ forbid_setid("-S");
+ do_search = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'e':
+ forbid_setid("-e");
+ if (!*++s) {
+ s = argv[1];
+ argc--,argv++;
+ }
+ if (!s) {
+ fprintf(stderr, "%s: no code specified for -e\n", origargv[0]);
+ exit(2);
+ }
+ if (!e_script) {
+ e_script = rb_str_new(0,0);
+ if (script == 0) script = "-e";
+ }
+ rb_str_cat2(e_script, s);
+ rb_str_cat2(e_script, "\n");
+ break;
+
+ case 'r':
+ forbid_setid("-r");
+ if (*++s) {
+ add_modules(s);
+ }
+ else if (argv[1]) {
+ add_modules(argv[1]);
+ argc--,argv++;
+ }
+ break;
+
+ case 'i':
+ forbid_setid("-i");
+ if (ruby_inplace_mode) free(ruby_inplace_mode);
+ ruby_inplace_mode = strdup(s+1);
+ break;
+
+ case 'x':
+ xflag = Qtrue;
+ s++;
+ if (*s && chdir(s) < 0) {
+ rb_fatal("Can't chdir to %s", s);
+ }
+ break;
+
+ case 'C':
+ case 'X':
+ s++;
+ if (!*s) {
+ s = argv[1];
+ argc--,argv++;
+ }
+ if (!s || !*s) {
+ rb_fatal("Can't chdir");
+ }
+ if (chdir(s) < 0) {
+ rb_fatal("Can't chdir to %s", s);
+ }
+ break;
+
+ case 'F':
+ if (*++s) {
+ rb_fs = rb_reg_new(s, strlen(s), 0);
+ }
+ break;
+
+ case 'K':
+ if (*++s) {
+ rb_set_kcode(s);
+ s++;
+ }
+ goto reswitch;
+
+ case 'T':
+ {
+ int numlen;
+ int v = 1;
+
+ if (*++s) {
+ v = scan_oct(s, 2, &numlen);
+ if (numlen == 0) v = 1;
+ s += numlen;
+ }
+ rb_set_safe_level(v);
+ }
+ goto reswitch;
+
+ case 'I':
+ forbid_setid("-I");
+ if (*++s)
+ ruby_incpush(s);
+ else if (argv[1]) {
+ ruby_incpush(argv[1]);
+ argc--,argv++;
+ }
+ break;
+
+ case '0':
+ {
+ int numlen;
+ int v;
+ char c;
+
+ v = scan_oct(s, 4, &numlen);
+ s += numlen;
+ if (v > 0377) rb_rs = Qnil;
+ else if (v == 0 && numlen >= 2) {
+ rb_rs = rb_str_new2("\n\n");
+ }
+ else {
+ c = v & 0xff;
+ rb_rs = rb_str_new(&c, 1);
+ }
+ }
+ goto reswitch;
+
+ case '-':
+ if (!s[1] || (s[1] == '\r' && !s[2])) {
+ argc--,argv++;
+ goto switch_end;
+ }
+ s++;
+ if (strcmp("copyright", s) == 0)
+ copyright = 1;
+ else if (strcmp("debug", s) == 0) {
+ ruby_debug = Qtrue;
+ ruby_verbose = Qtrue;
+ }
+ else if (strcmp("version", s) == 0)
+ version = 1;
+ else if (strcmp("verbose", s) == 0) {
+ verbose = 1;
+ ruby_verbose = Qtrue;
+ }
+ else if (strcmp("yydebug", s) == 0)
+ ruby_yydebug = 1;
+ else if (strcmp("help", s) == 0) {
+ usage(origargv[0]);
+ exit(0);
+ }
+ else {
+ fprintf(stderr, "%s: invalid option --%s (-h will show valid options)\n",
+ origargv[0], s);
+ exit(2);
+ }
+ break;
+
+ case '\r':
+ if (!s[1]) break;
+
+ default:
+ {
+ const char *format;
+ if (ISPRINT(*s)) {
+ format = "%s: invalid option -%c (-h will show valid options)\n";
+ }
+ else {
+ format = "%s: invalid option -\\%03o (-h will show valid options)\n";
+ }
+ fprintf(stderr, format, origargv[0], (int)(unsigned char)*s);
+ }
+ exit(2);
+
+ case 0:
+ break;
+ }
+ }
+
+ switch_end:
+ if (argv0 == 0) return;
+
+ if (rb_safe_level() == 0 && (s = getenv("RUBYOPT"))) {
+ while (ISSPACE(*s)) s++;
+ if (*s == 'T' || (*s == '-' && *(s+1) == 'T')) {
+ int numlen;
+ int v = 1;
+
+ if (*s != 'T') ++s;
+ if (*++s) {
+ v = scan_oct(s, 2, &numlen);
+ if (numlen == 0) v = 1;
+ }
+ rb_set_safe_level(v);
+ }
+ else {
+ while (s && *s) {
+ if (*s == '-') {
+ s++;
+ if (ISSPACE(*s)) {
+ do {s++;} while (ISSPACE(*s));
+ continue;
+ }
+ }
+ if (!*s) break;
+ if (!strchr("IdvwWrK", *s))
+ rb_raise(rb_eRuntimeError, "illegal switch in RUBYOPT: -%c", *s);
+ s = moreswitches(s);
+ }
+ }
+ }
+
+ if (version) {
+ ruby_show_version();
+ exit(0);
+ }
+ if (copyright) {
+ ruby_show_copyright();
+ }
+
+ if (rb_safe_level() >= 4) {
+ OBJ_TAINT(rb_argv);
+ OBJ_TAINT(rb_load_path);
+ }
+
+ if (!e_script) {
+ if (argc == 0) { /* no more args */
+ if (verbose) exit(0);
+ script = "-";
+ }
+ else {
+ script = argv[0];
+ if (script[0] == '\0') {
+ script = "-";
+ }
+ else if (do_search) {
+ char *path = getenv("RUBYPATH");
+
+ script = 0;
+ if (path) {
+ script = dln_find_file(argv[0], path);
+ }
+ if (!script) {
+ script = dln_find_file(argv[0], getenv(PATH_ENV));
+ }
+ if (!script) script = argv[0];
+ script = ruby_sourcefile = rb_source_filename(script);
+ script_node = NEW_NEWLINE(0);
+ }
+#if defined DOSISH || defined __CYGWIN__
+ translate_char(script, '\\', '/');
+#endif
+ argc--; argv++;
+ }
+ }
+
+ ruby_script(script);
+ ruby_set_argv(argc, argv);
+ process_sflag();
+
+ ruby_init_loadpath();
+ ruby_sourcefile = rb_source_filename(argv0);
+ if (e_script) {
+ require_libraries();
+ rb_compile_string(script, e_script, 1);
+ }
+ else if (strlen(script) == 1 && script[0] == '-') {
+ load_stdin();
+ }
+ else {
+ load_file(script, 1);
+ }
+
+ process_sflag();
+ xflag = 0;
+
+ if (rb_safe_level() >= 4) {
+ FL_UNSET(rb_argv, FL_TAINT);
+ FL_UNSET(rb_load_path, FL_TAINT);
+ }
+}
+
+extern int ruby__end__seen;
+
+static void
+load_file(fname, script)
+ const char *fname;
+ int script;
+{
+ extern VALUE rb_stdin;
+ VALUE f;
+ int line_start = 1;
+
+ if (!fname) rb_load_fail(fname);
+ if (strcmp(fname, "-") == 0) {
+ f = rb_stdin;
+ }
+ else {
+ FILE *fp = fopen(fname, "r");
+
+ if (fp == NULL) {
+ rb_load_fail(fname);
+ }
+ fclose(fp);
+
+ f = rb_file_open(fname, "r");
+#if defined DOSISH || defined __CYGWIN__
+ {
+ char *ext = strrchr(fname, '.');
+ if (ext && strcasecmp(ext, ".exe") == 0)
+ rb_io_binmode(f);
+ }
+#endif
+ }
+
+ if (script) {
+ VALUE c = 1; /* something not nil */
+ VALUE line;
+ char *p;
+
+ if (xflag) {
+ forbid_setid("-x");
+ xflag = Qfalse;
+ while (!NIL_P(line = rb_io_gets(f))) {
+ line_start++;
+ if (RSTRING(line)->len > 2
+ && RSTRING(line)->ptr[0] == '#'
+ && RSTRING(line)->ptr[1] == '!') {
+ if ((p = strstr(RSTRING(line)->ptr, "ruby")) != 0) {
+ goto start_read;
+ }
+ }
+ }
+ rb_raise(rb_eLoadError, "no Ruby script found in input");
+ }
+
+ c = rb_io_getc(f);
+ if (c == INT2FIX('#')) {
+ line = rb_io_gets(f);
+ if (NIL_P(line)) return;
+ line_start++;
+
+ if (RSTRING(line)->len > 2 && RSTRING(line)->ptr[0] == '!') {
+ if ((p = strstr(RSTRING(line)->ptr, "ruby")) == 0) {
+ /* not ruby script, kick the program */
+ char **argv;
+ char *path;
+ char *pend = RSTRING(line)->ptr + RSTRING(line)->len;
+
+ p = RSTRING(line)->ptr + 1; /* skip `#!' */
+ if (pend[-1] == '\n') pend--; /* chomp line */
+ if (pend[-1] == '\r') pend--;
+ *pend = '\0';
+ while (p < pend && ISSPACE(*p))
+ p++;
+ path = p; /* interpreter path */
+ while (p < pend && !ISSPACE(*p))
+ p++;
+ *p++ = '\0';
+ if (p < pend) {
+ argv = ALLOCA_N(char*, origargc+3);
+ argv[1] = p;
+ MEMCPY(argv+2, origargv+1, char*, origargc);
+ }
+ else {
+ argv = origargv;
+ }
+ argv[0] = path;
+ execv(path, argv);
+
+ ruby_sourcefile = rb_source_filename(fname);
+ ruby_sourceline = 1;
+ rb_fatal("Can't exec %s", path);
+ }
+
+ start_read:
+ p += 4;
+ RSTRING(line)->ptr[RSTRING(line)->len-1] = '\0';
+ if (RSTRING(line)->ptr[RSTRING(line)->len-2] == '\r')
+ RSTRING(line)->ptr[RSTRING(line)->len-2] = '\0';
+ if ((p = strstr(p, " -")) != 0) {
+ p++; /* skip space before `-' */
+ while (*p == '-') {
+ p = moreswitches(p+1);
+ }
+ }
+ }
+ }
+ else if (!NIL_P(c)) {
+ rb_io_ungetc(f, c);
+ }
+ require_libraries(); /* Why here? unnatural */
+ if (NIL_P(c)) return;
+ }
+ rb_compile_file(fname, f, line_start);
+ if (script && ruby__end__seen) {
+ rb_define_global_const("DATA", f);
+ }
+ else if (f != rb_stdin) {
+ rb_io_close(f);
+ }
+
+ if (ruby_parser_stack_on_heap()) {
+ rb_gc();
+ }
+}
+
+void
+rb_load_file(fname)
+ const char *fname;
+{
+ load_file(fname, 0);
+}
+
+static void
+load_stdin()
+{
+ forbid_setid("program input from stdin");
+ load_file("-", 1);
+}
+
+VALUE rb_progname;
+VALUE rb_argv;
+VALUE rb_argv0;
+
+#if defined(PSTAT_SETCMD) || defined(HAVE_SETPROCTITLE)
+#elif defined(_WIN32)
+#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+#else
+#define USE_ENVSPACE_FOR_ARG0
+#endif
+
+#ifdef USE_ENVSPACE_FOR_ARG0
+static struct {
+ char *begin, *end;
+} envspace;
+extern char **environ;
+
+static void
+set_arg0space()
+{
+ char *s;
+ int i;
+
+ if (!environ || (s = environ[0]) == NULL) return;
+ envspace.begin = s;
+ s += strlen(s);
+ for (i = 1; environ[i]; i++) {
+ if (environ[i] == s + 1) {
+ s++;
+ s += strlen(s); /* this one is ok too */
+ }
+ }
+ envspace.end = s;
+}
+#else
+#define set_arg0space() ((void)0)
+#endif
+
+static int
+get_arglen(int argc, char **argv)
+{
+ char *s = argv[0];
+ int i;
+
+ if (!argc) return 0;
+ s += strlen(s);
+ /* See if all the arguments are contiguous in memory */
+ for (i = 1; i < argc; i++) {
+ if (argv[i] == s + 1) {
+ s++;
+ s += strlen(s); /* this one is ok too */
+ }
+ else {
+ break;
+ }
+ }
+#if defined(USE_ENVSPACE_FOR_ARG0)
+ if (environ && (s == environ[0])) {
+ s += strlen(s);
+ for (i = 1; environ[i]; i++) {
+ if (environ[i] == s + 1) {
+ s++;
+ s += strlen(s); /* this one is ok too */
+ }
+ }
+ ruby_setenv("", NULL); /* duplicate environ vars */
+ }
+#endif
+ return s - argv[0];
+}
+
+static void
+set_arg0(val, id)
+ VALUE val;
+ ID id;
+{
+ VALUE progname;
+ char *s;
+ long i;
+ int j;
+#if !defined(PSTAT_SETCMD) && !defined(HAVE_SETPROCTITLE)
+ static int len = 0;
+#endif
+
+ if (origargv == 0) rb_raise(rb_eRuntimeError, "$0 not initialized");
+ StringValue(val);
+ s = RSTRING(val)->ptr;
+ i = RSTRING(val)->len;
+#if defined(PSTAT_SETCMD)
+ if (i >= PST_CLEN) {
+ union pstun j;
+ j.pst_command = s;
+ i = PST_CLEN;
+ RSTRING(val)->len = i;
+ *(s + i) = '\0';
+ pstat(PSTAT_SETCMD, j, PST_CLEN, 0, 0);
+ }
+ else {
+ union pstun j;
+ j.pst_command = s;
+ pstat(PSTAT_SETCMD, j, i, 0, 0);
+ }
+ progname = rb_tainted_str_new(s, i);
+#elif defined(HAVE_SETPROCTITLE)
+ setproctitle("%.*s", (int)i, s);
+ progname = rb_tainted_str_new(s, i);
+#else
+ if (len == 0) {
+ len = get_arglen(origargc, origargv);
+ }
+
+ if (i >= len) {
+ i = len;
+ }
+ memcpy(origargv[0], s, i);
+ s = origargv[0] + i;
+ *s = '\0';
+ if (++i < len) memset(s + 1, ' ', len - i);
+ for (i = len-1, j = origargc-1; j > 0 && i >= 0; --i, --j) {
+ origargv[j] = origargv[0] + i;
+ *origargv[j] = '\0';
+ }
+ progname = rb_tainted_str_new2(origargv[0]);
+#endif
+ rb_progname = rb_obj_freeze(progname);
+}
+
+void
+ruby_script(name)
+ const char *name;
+{
+ if (name) {
+ rb_progname = rb_obj_freeze(rb_tainted_str_new2(name));
+ ruby_sourcefile = rb_source_filename(name);
+ }
+}
+
+static int uid, euid, gid, egid;
+
+static void
+init_ids()
+{
+ uid = (int)getuid();
+ euid = (int)geteuid();
+ gid = (int)getgid();
+ egid = (int)getegid();
+#ifdef VMS
+ uid |= gid << 16;
+ euid |= egid << 16;
+#endif
+ if (uid && (euid != uid || egid != gid)) {
+ rb_set_safe_level(1);
+ }
+}
+
+static void
+forbid_setid(s)
+ const char *s;
+{
+ if (euid != uid)
+ rb_raise(rb_eSecurityError, "no %s allowed while running setuid", s);
+ if (egid != gid)
+ rb_raise(rb_eSecurityError, "no %s allowed while running setgid", s);
+ if (rb_safe_level() > 0)
+ rb_raise(rb_eSecurityError, "no %s allowed in tainted mode", s);
+}
+
+static void
+verbose_setter(val, id, variable)
+ VALUE val;
+ ID id;
+ VALUE *variable;
+{
+ ruby_verbose = RTEST(val) ? Qtrue : val;
+}
+
+void
+ruby_prog_init()
+{
+ init_ids();
+
+ ruby_sourcefile = rb_source_filename("ruby");
+ rb_define_hooked_variable("$VERBOSE", &ruby_verbose, 0, verbose_setter);
+ rb_define_hooked_variable("$-v", &ruby_verbose, 0, verbose_setter);
+ rb_define_hooked_variable("$-w", &ruby_verbose, 0, verbose_setter);
+ rb_define_variable("$DEBUG", &ruby_debug);
+ rb_define_variable("$-d", &ruby_debug);
+ rb_define_readonly_variable("$-p", &do_print);
+ rb_define_readonly_variable("$-l", &do_line);
+
+ rb_define_hooked_variable("$0", &rb_progname, 0, set_arg0);
+ rb_define_hooked_variable("$PROGRAM_NAME", &rb_progname, 0, set_arg0);
+
+ rb_define_readonly_variable("$*", &rb_argv);
+ rb_argv = rb_ary_new();
+ rb_define_global_const("ARGV", rb_argv);
+ rb_define_readonly_variable("$-a", &do_split);
+ rb_global_variable(&rb_argv0);
+
+#ifdef MSDOS
+ /*
+ * There is no way we can refer to them from ruby, so close them to save
+ * space.
+ */
+ (void)fclose(stdaux);
+ (void)fclose(stdprn);
+#endif
+}
+
+void
+ruby_set_argv(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+
+#if defined(USE_DLN_A_OUT)
+ if (origargv) dln_argv0 = origargv[0];
+ else dln_argv0 = argv[0];
+#endif
+ rb_ary_clear(rb_argv);
+ for (i=0; i < argc; i++) {
+ VALUE arg = rb_tainted_str_new2(argv[i]);
+
+ OBJ_FREEZE(arg);
+ rb_ary_push(rb_argv, arg);
+ }
+}
+
+void
+ruby_process_options(argc, argv)
+ int argc;
+ char **argv;
+{
+ origargc = argc; origargv = argv;
+
+ ruby_script(argv[0]); /* for the time being */
+ rb_argv0 = rb_progname;
+#if defined(USE_DLN_A_OUT)
+ dln_argv0 = argv[0];
+#endif
+ set_arg0space();
+ proc_options(argc, argv);
+
+ if (do_check && ruby_nerrs == 0) {
+ printf("Syntax OK\n");
+ exit(0);
+ }
+ if (do_print) {
+ rb_parser_append_print();
+ }
+ if (do_loop) {
+ rb_parser_while_loop(do_line, do_split);
+ }
+}
+/**********************************************************************
+
+ signal.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-29 09:52:47 +0200 (Sun, 29 Jun 2008) $
+ created at: Tue Dec 20 10:13:44 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+#include <signal.h>
+#include <stdio.h>
+
+#ifdef __BEOS__
+#undef SIGBUS
+#endif
+
+#if defined HAVE_SIGPROCMASK || defined HAVE_SIGSETMASK
+#define USE_TRAP_MASK 1
+#else
+#define USE_TRAP_MASK 0
+#endif
+
+#ifndef NSIG
+# ifdef DJGPP
+# define NSIG SIGMAX
+# else
+# define NSIG (_SIGMAX + 1) /* For QNX */
+# endif
+#endif
+
+static struct signals {
+ char *signm;
+ int signo;
+} siglist [] = {
+ {"EXIT", 0},
+#ifdef SIGHUP
+ {"HUP", SIGHUP},
+#endif
+ {"INT", SIGINT},
+#ifdef SIGQUIT
+ {"QUIT", SIGQUIT},
+#endif
+#ifdef SIGILL
+ {"ILL", SIGILL},
+#endif
+#ifdef SIGTRAP
+ {"TRAP", SIGTRAP},
+#endif
+#ifdef SIGIOT
+ {"IOT", SIGIOT},
+#endif
+#ifdef SIGABRT
+ {"ABRT", SIGABRT},
+#endif
+#ifdef SIGEMT
+ {"EMT", SIGEMT},
+#endif
+#ifdef SIGFPE
+ {"FPE", SIGFPE},
+#endif
+#ifdef SIGKILL
+ {"KILL", SIGKILL},
+#endif
+#ifdef SIGBUS
+ {"BUS", SIGBUS},
+#endif
+#ifdef SIGSEGV
+ {"SEGV", SIGSEGV},
+#endif
+#ifdef SIGSYS
+ {"SYS", SIGSYS},
+#endif
+#ifdef SIGPIPE
+ {"PIPE", SIGPIPE},
+#endif
+#ifdef SIGALRM
+ {"ALRM", SIGALRM},
+#endif
+#ifdef SIGTERM
+ {"TERM", SIGTERM},
+#endif
+#ifdef SIGURG
+ {"URG", SIGURG},
+#endif
+#ifdef SIGSTOP
+ {"STOP", SIGSTOP},
+#endif
+#ifdef SIGTSTP
+ {"TSTP", SIGTSTP},
+#endif
+#ifdef SIGCONT
+ {"CONT", SIGCONT},
+#endif
+#ifdef SIGCHLD
+ {"CHLD", SIGCHLD},
+#endif
+#ifdef SIGCLD
+ {"CLD", SIGCLD},
+#else
+# ifdef SIGCHLD
+ {"CLD", SIGCHLD},
+# endif
+#endif
+#ifdef SIGTTIN
+ {"TTIN", SIGTTIN},
+#endif
+#ifdef SIGTTOU
+ {"TTOU", SIGTTOU},
+#endif
+#ifdef SIGIO
+ {"IO", SIGIO},
+#endif
+#ifdef SIGXCPU
+ {"XCPU", SIGXCPU},
+#endif
+#ifdef SIGXFSZ
+ {"XFSZ", SIGXFSZ},
+#endif
+#ifdef SIGVTALRM
+ {"VTALRM", SIGVTALRM},
+#endif
+#ifdef SIGPROF
+ {"PROF", SIGPROF},
+#endif
+#ifdef SIGWINCH
+ {"WINCH", SIGWINCH},
+#endif
+#ifdef SIGUSR1
+ {"USR1", SIGUSR1},
+#endif
+#ifdef SIGUSR2
+ {"USR2", SIGUSR2},
+#endif
+#ifdef SIGLOST
+ {"LOST", SIGLOST},
+#endif
+#ifdef SIGMSG
+ {"MSG", SIGMSG},
+#endif
+#ifdef SIGPWR
+ {"PWR", SIGPWR},
+#endif
+#ifdef SIGPOLL
+ {"POLL", SIGPOLL},
+#endif
+#ifdef SIGDANGER
+ {"DANGER", SIGDANGER},
+#endif
+#ifdef SIGMIGRATE
+ {"MIGRATE", SIGMIGRATE},
+#endif
+#ifdef SIGPRE
+ {"PRE", SIGPRE},
+#endif
+#ifdef SIGGRANT
+ {"GRANT", SIGGRANT},
+#endif
+#ifdef SIGRETRACT
+ {"RETRACT", SIGRETRACT},
+#endif
+#ifdef SIGSOUND
+ {"SOUND", SIGSOUND},
+#endif
+#ifdef SIGINFO
+ {"INFO", SIGINFO},
+#endif
+ {NULL, 0}
+};
+
+static int
+signm2signo(nm)
+ const char *nm;
+{
+ struct signals *sigs;
+
+ for (sigs = siglist; sigs->signm; sigs++)
+ if (strcmp(sigs->signm, nm) == 0)
+ return sigs->signo;
+ return 0;
+}
+
+static char*
+signo2signm(no)
+ int no;
+{
+ struct signals *sigs;
+
+ for (sigs = siglist; sigs->signm; sigs++)
+ if (sigs->signo == no)
+ return sigs->signm;
+ return 0;
+}
+
+const char *
+ruby_signal_name(no)
+ int no;
+{
+ return signo2signm(no);
+}
+
+/*
+ * call-seq:
+ * SignalException.new(sig) => signal_exception
+ *
+ * Construct a new SignalException object. +sig+ should be a known
+ * signal name, or a signal number.
+ */
+
+static VALUE
+esignal_init(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ int argnum = 1;
+ VALUE sig = Qnil;
+ int signo;
+ const char *signm;
+ char tmpnm[(sizeof(int)*CHAR_BIT)/3+4];
+
+ if (argc > 0) {
+ sig = argv[0];
+ if (FIXNUM_P(sig)) argnum = 2;
+ }
+ if (argc < 1 || argnum < argc) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, argnum);
+ }
+ if (argnum == 2) {
+ signo = FIX2INT(sig);
+ if (signo < 0 || signo > NSIG) {
+ rb_raise(rb_eArgError, "invalid signal number (%d)", signo);
+ }
+ if (argc > 1) {
+ sig = argv[1];
+ }
+ else {
+ signm = signo2signm(signo);
+ if (signm) {
+ snprintf(tmpnm, sizeof(tmpnm), "SIG%s", signm);
+ }
+ else {
+ snprintf(tmpnm, sizeof(tmpnm), "SIG%u", signo);
+ }
+ sig = rb_str_new2(signm = tmpnm);
+ }
+ }
+ else {
+ signm = SYMBOL_P(sig) ? rb_id2name(SYM2ID(sig)) : StringValuePtr(sig);
+ if (strncmp(signm, "SIG", 3) == 0) signm += 3;
+ signo = signm2signo(signm);
+ if (!signo) {
+ rb_raise(rb_eArgError, "unsupported name `SIG%s'", signm);
+ }
+ if (SYMBOL_P(sig)) {
+ sig = rb_str_new2(signm);
+ }
+ }
+ rb_call_super(1, &sig);
+ rb_iv_set(self, "signo", INT2NUM(signo));
+
+ return self;
+}
+
+static VALUE
+interrupt_init(self, mesg)
+ VALUE self, mesg;
+{
+ VALUE argv[2];
+
+ argv[0] = INT2FIX(SIGINT);
+ argv[1] = mesg;
+ return rb_call_super(2, argv);
+}
+
+void
+ruby_default_signal(sig)
+ int sig;
+{
+#ifndef MACOS_UNUSE_SIGNAL
+ extern rb_pid_t getpid _((void));
+
+ signal(sig, SIG_DFL);
+ kill(getpid(), sig);
+#endif
+}
+
+/*
+ * call-seq:
+ * Process.kill(signal, pid, ...) => fixnum
+ *
+ * Sends the given signal to the specified process id(s), or to the
+ * current process if _pid_ is zero. _signal_ may be an
+ * integer signal number or a POSIX signal name (either with or without
+ * a +SIG+ prefix). If _signal_ is negative (or starts
+ * with a minus sign), kills process groups instead of
+ * processes. Not all signals are available on all platforms.
+ *
+ * pid = fork do
+ * Signal.trap("HUP") { puts "Ouch!"; exit }
+ * # ... do some work ...
+ * end
+ * # ...
+ * Process.kill("HUP", pid)
+ * Process.wait
+ *
+ * <em>produces:</em>
+ *
+ * Ouch!
+ */
+
+VALUE
+rb_f_kill(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int negative = 0;
+ int sig;
+ int i;
+ char *s;
+
+ rb_secure(2);
+ if (argc < 2)
+ rb_raise(rb_eArgError, "wrong number of arguments -- kill(sig, pid...)");
+ switch (TYPE(argv[0])) {
+ case T_FIXNUM:
+ sig = FIX2INT(argv[0]);
+ break;
+
+ case T_SYMBOL:
+ s = rb_id2name(SYM2ID(argv[0]));
+ if (!s) rb_raise(rb_eArgError, "bad signal");
+ goto str_signal;
+
+ case T_STRING:
+ s = RSTRING(argv[0])->ptr;
+ if (s[0] == '-') {
+ negative++;
+ s++;
+ }
+ str_signal:
+ if (strncmp("SIG", s, 3) == 0)
+ s += 3;
+ if((sig = signm2signo(s)) == 0)
+ rb_raise(rb_eArgError, "unsupported name `SIG%s'", s);
+
+ if (negative)
+ sig = -sig;
+ break;
+
+ default:
+ {
+ VALUE str;
+
+ str = rb_check_string_type(argv[0]);
+ if (!NIL_P(str)) {
+ s = RSTRING(str)->ptr;
+ goto str_signal;
+ }
+ rb_raise(rb_eArgError, "bad signal type %s",
+ rb_obj_classname(argv[0]));
+ }
+ break;
+ }
+
+ if (sig < 0) {
+ sig = -sig;
+ for (i=1; i<argc; i++) {
+ int pid = NUM2INT(argv[i]);
+#ifdef HAS_KILLPG
+ if (killpg(pid, sig) < 0)
+#else
+ if (kill(-pid, sig) < 0)
+#endif
+ rb_sys_fail(0);
+ }
+ }
+ else {
+ for (i=1; i<argc; i++) {
+ Check_Type(argv[i], T_FIXNUM);
+ if (kill(FIX2INT(argv[i]), sig) < 0)
+ rb_sys_fail(0);
+ }
+ }
+ return INT2FIX(i-1);
+}
+
+static struct {
+ VALUE cmd;
+ int safe;
+} trap_list[NSIG];
+static rb_atomic_t trap_pending_list[NSIG];
+static char rb_trap_accept_nativethreads[NSIG];
+rb_atomic_t rb_trap_pending;
+rb_atomic_t rb_trap_immediate;
+int rb_prohibit_interrupt = 1;
+
+void
+rb_gc_mark_trap_list()
+{
+#ifndef MACOS_UNUSE_SIGNAL
+ int i;
+
+ for (i=0; i<NSIG; i++) {
+ if (trap_list[i].cmd)
+ rb_gc_mark(trap_list[i].cmd);
+ }
+#endif /* MACOS_UNUSE_SIGNAL */
+}
+
+#ifdef __dietlibc__
+#define sighandler_t sh_t
+#endif
+
+typedef RETSIGTYPE (*sighandler_t)_((int));
+
+#ifdef POSIX_SIGNAL
+static sighandler_t
+ruby_signal(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ struct sigaction sigact, old;
+
+ rb_trap_accept_nativethreads[signum] = 0;
+
+ sigact.sa_handler = handler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+# ifdef SA_NOCLDWAIT
+ if (signum == SIGCHLD && handler == SIG_IGN)
+ sigact.sa_flags |= SA_NOCLDWAIT;
+# endif
+ sigaction(signum, &sigact, &old);
+ return old.sa_handler;
+}
+
+void
+posix_signal(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ ruby_signal(signum, handler);
+}
+
+# ifdef HAVE_NATIVETHREAD
+static sighandler_t
+ruby_nativethread_signal(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ sighandler_t old;
+
+ old = ruby_signal(signum, handler);
+ rb_trap_accept_nativethreads[signum] = 1;
+ return old;
+}
+
+void
+posix_nativethread_signal(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ ruby_nativethread_signal(signum, handler);
+}
+# endif
+
+#else /* !POSIX_SIGNAL */
+#define ruby_signal(sig,handler) (rb_trap_accept_nativethreads[sig] = 0, signal((sig),(handler)))
+
+# ifdef HAVE_NATIVETHREAD
+static sighandler_t
+ruby_nativethread_signal(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ sighandler_t old;
+
+ old = signal(signum, handler);
+ rb_trap_accept_nativethreads[signum] = 1;
+ return old;
+}
+# endif
+#endif /* POSIX_SIGNAL */
+
+static void signal_exec _((int sig));
+static void
+signal_exec(sig)
+ int sig;
+{
+ if (trap_list[sig].cmd == 0) {
+ switch (sig) {
+ case SIGINT:
+ rb_thread_interrupt();
+ break;
+#ifdef SIGHUP
+ case SIGHUP:
+#endif
+#ifdef SIGQUIT
+ case SIGQUIT:
+#endif
+#ifdef SIGTERM
+ case SIGTERM:
+#endif
+#ifdef SIGALRM
+ case SIGALRM:
+#endif
+#ifdef SIGUSR1
+ case SIGUSR1:
+#endif
+#ifdef SIGUSR2
+ case SIGUSR2:
+#endif
+ rb_thread_signal_raise(sig);
+ break;
+ }
+ }
+ else if (trap_list[sig].cmd == Qundef) {
+ rb_thread_signal_exit();
+ }
+ else {
+ rb_thread_trap_eval(trap_list[sig].cmd, sig, trap_list[sig].safe);
+ }
+}
+
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+static void
+sigsend_to_ruby_thread(int sig)
+{
+# ifdef HAVE_SIGPROCMASK
+ sigset_t mask, old_mask;
+# else
+ int mask, old_mask;
+# endif
+
+# ifdef HAVE_SIGPROCMASK
+ sigfillset(&mask);
+ sigprocmask(SIG_BLOCK, &mask, &old_mask);
+# else
+ mask = sigblock(~0);
+ sigsetmask(mask);
+# endif
+
+ ruby_native_thread_kill(sig);
+}
+#endif
+
+static RETSIGTYPE sighandler _((int));
+static RETSIGTYPE
+sighandler(sig)
+ int sig;
+{
+#ifdef _WIN32
+#define IN_MAIN_CONTEXT(f, a) (rb_w32_main_context(a, f) ? (void)0 : f(a))
+#else
+#define IN_MAIN_CONTEXT(f, a) f(a)
+#endif
+
+ if (sig >= NSIG) {
+ rb_bug("trap_handler: Bad signal %d", sig);
+ }
+
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+ if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) {
+ sigsend_to_ruby_thread(sig);
+ return;
+ }
+#endif
+
+#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
+ if (rb_trap_accept_nativethreads[sig]) {
+ ruby_nativethread_signal(sig, sighandler);
+ }
+ else {
+ ruby_signal(sig, sighandler);
+ }
+#endif
+
+ if (trap_list[sig].cmd == 0 && ATOMIC_TEST(rb_trap_immediate)) {
+ IN_MAIN_CONTEXT(signal_exec, sig);
+ ATOMIC_SET(rb_trap_immediate, 1);
+ }
+ else {
+ ATOMIC_INC(rb_trap_pending);
+ ATOMIC_INC(trap_pending_list[sig]);
+#ifdef _WIN32
+ rb_w32_interrupted();
+#endif
+ }
+}
+
+#ifdef SIGBUS
+static RETSIGTYPE sigbus _((int));
+static RETSIGTYPE
+sigbus(sig)
+ int sig;
+{
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+ if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) {
+ sigsend_to_ruby_thread(sig);
+ return;
+ }
+#endif
+
+ rb_bug("Bus Error");
+}
+#endif
+
+#ifdef SIGSEGV
+static RETSIGTYPE sigsegv _((int));
+static RETSIGTYPE
+sigsegv(sig)
+ int sig;
+{
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+ if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) {
+ sigsend_to_ruby_thread(sig);
+ return;
+ }
+#endif
+
+ rb_bug("Segmentation fault");
+}
+#endif
+
+#ifdef SIGPIPE
+static RETSIGTYPE sigpipe _((int));
+static RETSIGTYPE
+sigpipe(sig)
+ int sig;
+{
+ /* do nothing */
+}
+#endif
+
+void
+rb_trap_exit()
+{
+#ifndef MACOS_UNUSE_SIGNAL
+ if (trap_list[0].cmd) {
+ VALUE trap_exit = trap_list[0].cmd;
+
+ trap_list[0].cmd = 0;
+ rb_eval_cmd(trap_exit, rb_ary_new3(1, INT2FIX(0)), trap_list[0].safe);
+ }
+#endif
+}
+
+void
+rb_trap_exec()
+{
+#ifndef MACOS_UNUSE_SIGNAL
+ int i;
+
+ for (i=0; i<NSIG; i++) {
+ if (trap_pending_list[i]) {
+ trap_pending_list[i] = 0;
+ signal_exec(i);
+ }
+ }
+#endif /* MACOS_UNUSE_SIGNAL */
+ rb_trap_pending = 0;
+}
+
+struct trap_arg {
+#if USE_TRAP_MASK
+# ifdef HAVE_SIGPROCMASK
+ sigset_t mask;
+# else
+ int mask;
+# endif
+#endif
+ VALUE sig, cmd;
+};
+
+# ifdef HAVE_SIGPROCMASK
+static sigset_t trap_last_mask;
+# else
+static int trap_last_mask;
+# endif
+
+static RETSIGTYPE sigexit _((int));
+static RETSIGTYPE
+sigexit(sig)
+ int sig;
+{
+ rb_thread_signal_exit();
+}
+
+static VALUE
+trap(arg)
+ struct trap_arg *arg;
+{
+ sighandler_t func, oldfunc;
+ VALUE command, oldcmd;
+ int sig = -1;
+ char *s;
+
+ func = sighandler;
+ command = arg->cmd;
+ if (NIL_P(command)) {
+ func = SIG_IGN;
+ }
+ else if (TYPE(command) == T_STRING) {
+ SafeStringValue(command); /* taint check */
+ if (RSTRING(command)->len == 0) {
+ func = SIG_IGN;
+ }
+ else if (RSTRING(command)->len == 7) {
+ if (strncmp(RSTRING(command)->ptr, "SIG_IGN", 7) == 0) {
+ func = SIG_IGN;
+ }
+ else if (strncmp(RSTRING(command)->ptr, "SIG_DFL", 7) == 0) {
+ func = SIG_DFL;
+ }
+ else if (strncmp(RSTRING(command)->ptr, "DEFAULT", 7) == 0) {
+ func = SIG_DFL;
+ }
+ }
+ else if (RSTRING(command)->len == 6) {
+ if (strncmp(RSTRING(command)->ptr, "IGNORE", 6) == 0) {
+ func = SIG_IGN;
+ }
+ }
+ else if (RSTRING(command)->len == 4) {
+ if (strncmp(RSTRING(command)->ptr, "EXIT", 4) == 0) {
+ func = sigexit;
+ }
+ }
+ }
+ if (func == SIG_IGN || func == SIG_DFL) {
+ command = 0;
+ }
+
+ switch (TYPE(arg->sig)) {
+ case T_FIXNUM:
+ sig = FIX2INT(arg->sig);
+ break;
+
+ case T_SYMBOL:
+ s = rb_id2name(SYM2ID(arg->sig));
+ if (!s) rb_raise(rb_eArgError, "bad signal");
+ goto str_signal;
+
+ case T_STRING:
+ s = RSTRING(arg->sig)->ptr;
+
+ str_signal:
+ if (strncmp("SIG", s, 3) == 0)
+ s += 3;
+ sig = signm2signo(s);
+ if (sig == 0 && strcmp(s, "EXIT") != 0)
+ rb_raise(rb_eArgError, "unsupported signal SIG%s", s);
+ }
+
+ if (sig < 0 || sig >= NSIG) {
+ rb_raise(rb_eArgError, "invalid signal number (%d)", sig);
+ }
+#if defined(HAVE_SETITIMER)
+ if (sig == SIGVTALRM) {
+ rb_raise(rb_eArgError, "SIGVTALRM reserved for Thread; can't set handler");
+ }
+#endif
+ if (func == SIG_DFL) {
+ switch (sig) {
+ case SIGINT:
+#ifdef SIGHUP
+ case SIGHUP:
+#endif
+#ifdef SIGQUIT
+ case SIGQUIT:
+#endif
+#ifdef SIGTERM
+ case SIGTERM:
+#endif
+#ifdef SIGALRM
+ case SIGALRM:
+#endif
+#ifdef SIGUSR1
+ case SIGUSR1:
+#endif
+#ifdef SIGUSR2
+ case SIGUSR2:
+#endif
+ func = sighandler;
+ break;
+#ifdef SIGBUS
+ case SIGBUS:
+ func = sigbus;
+ break;
+#endif
+#ifdef SIGSEGV
+ case SIGSEGV:
+ func = sigsegv;
+ break;
+#endif
+#ifdef SIGPIPE
+ case SIGPIPE:
+ func = sigpipe;
+ break;
+#endif
+ }
+ }
+ oldfunc = ruby_signal(sig, func);
+ oldcmd = trap_list[sig].cmd;
+ if (!oldcmd) {
+ if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE");
+ else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT");
+ else oldcmd = Qnil;
+ }
+
+ trap_list[sig].cmd = command;
+ trap_list[sig].safe = ruby_safe_level;
+ /* enable at least specified signal. */
+#if USE_TRAP_MASK
+#ifdef HAVE_SIGPROCMASK
+ sigdelset(&arg->mask, sig);
+#else
+ arg->mask &= ~sigmask(sig);
+#endif
+#endif
+ return oldcmd;
+}
+
+#if USE_TRAP_MASK
+static VALUE
+trap_ensure(arg)
+ struct trap_arg *arg;
+{
+ /* enable interrupt */
+#ifdef HAVE_SIGPROCMASK
+ sigprocmask(SIG_SETMASK, &arg->mask, NULL);
+#else
+ sigsetmask(arg->mask);
+#endif
+ trap_last_mask = arg->mask;
+ return 0;
+}
+#endif
+
+void
+rb_trap_restore_mask()
+{
+#if USE_TRAP_MASK
+# ifdef HAVE_SIGPROCMASK
+ sigprocmask(SIG_SETMASK, &trap_last_mask, NULL);
+# else
+ sigsetmask(trap_last_mask);
+# endif
+#endif
+}
+
+/*
+ * call-seq:
+ * Signal.trap( signal, proc ) => obj
+ * Signal.trap( signal ) {| | block } => obj
+ *
+ * Specifies the handling of signals. The first parameter is a signal
+ * name (a string such as ``SIGALRM'', ``SIGUSR1'', and so on) or a
+ * signal number. The characters ``SIG'' may be omitted from the
+ * signal name. The command or block specifies code to be run when the
+ * signal is raised. If the command is the string ``IGNORE'' or
+ * ``SIG_IGN'', the signal will be ignored. If the command is
+ * ``DEFAULT'' or ``SIG_DFL'', the operating system's default handler
+ * will be invoked. If the command is ``EXIT'', the script will be
+ * terminated by the signal. Otherwise, the given command or block
+ * will be run.
+ * The special signal name ``EXIT'' or signal number zero will be
+ * invoked just prior to program termination.
+ * trap returns the previous handler for the given signal.
+ *
+ * Signal.trap(0, proc { puts "Terminating: #{$$}" })
+ * Signal.trap("CLD") { puts "Child died" }
+ * fork && Process.wait
+ *
+ * produces:
+ * Terminating: 27461
+ * Child died
+ * Terminating: 27460
+ */
+static VALUE
+sig_trap(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ struct trap_arg arg;
+
+ rb_secure(2);
+ if (argc == 0 || argc > 2) {
+ rb_raise(rb_eArgError, "wrong number of arguments -- trap(sig, cmd)/trap(sig){...}");
+ }
+
+ arg.sig = argv[0];
+ if (argc == 1) {
+ arg.cmd = rb_block_proc();
+ }
+ else if (argc == 2) {
+ arg.cmd = argv[1];
+ }
+
+ if (OBJ_TAINTED(arg.cmd)) {
+ rb_raise(rb_eSecurityError, "Insecure: tainted signal trap");
+ }
+#if USE_TRAP_MASK
+ /* disable interrupt */
+# ifdef HAVE_SIGPROCMASK
+ sigfillset(&arg.mask);
+ sigprocmask(SIG_BLOCK, &arg.mask, &arg.mask);
+# else
+ arg.mask = sigblock(~0);
+# endif
+
+ return rb_ensure(trap, (VALUE)&arg, trap_ensure, (VALUE)&arg);
+#else
+ return trap(&arg);
+#endif
+}
+
+/*
+ * call-seq:
+ * Signal.list => a_hash
+ *
+ * Returns a list of signal names mapped to the corresponding
+ * underlying signal numbers.
+ *
+ * Signal.list #=> {"ABRT"=>6, "ALRM"=>14, "BUS"=>7, "CHLD"=>17, "CLD"=>17, "CONT"=>18, "FPE"=>8, "HUP"=>1, "ILL"=>4, "INT"=>2, "IO"=>29, "IOT"=>6, "KILL"=>9, "PIPE"=>13, "POLL"=>29, "PROF"=>27, "PWR"=>30, "QUIT"=>3, "SEGV"=>11, "STOP"=>19, "SYS"=>31, "TERM"=>15, "TRAP"=>5, "TSTP"=>20, "TTIN"=>21, "TTOU"=>22, "URG"=>23, "USR1"=>10, "USR2"=>12, "VTALRM"=>26, "WINCH"=>28, "XCPU"=>24, "XFSZ"=>25}
+ */
+static VALUE
+sig_list()
+{
+ VALUE h = rb_hash_new();
+ struct signals *sigs;
+
+ for (sigs = siglist; sigs->signm; sigs++) {
+ rb_hash_aset(h, rb_str_new2(sigs->signm), INT2FIX(sigs->signo));
+ }
+ return h;
+}
+
+static void
+install_sighandler(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ sighandler_t old;
+
+ old = ruby_signal(signum, handler);
+ if (old != SIG_DFL) {
+ ruby_signal(signum, old);
+ }
+}
+
+#if 0
+/*
+ * If you write a handler which works on any native thread
+ * (even if the thread is NOT a ruby's one), please enable
+ * this function and use it to install the handler, instead
+ * of `install_sighandler()'.
+ */
+#ifdef HAVE_NATIVETHREAD
+static void
+install_nativethread_sighandler(signum, handler)
+ int signum;
+ sighandler_t handler;
+{
+ sighandler_t old;
+ int old_st;
+
+ old_st = rb_trap_accept_nativethreads[signum];
+ old = ruby_nativethread_signal(signum, handler);
+ if (old != SIG_DFL) {
+ if (old_st) {
+ ruby_nativethread_signal(signum, old);
+ } else {
+ ruby_signal(signum, old);
+ }
+ }
+}
+#endif
+#endif
+
+static void
+init_sigchld(sig)
+ int sig;
+{
+ sighandler_t oldfunc;
+#if USE_TRAP_MASK
+# ifdef HAVE_SIGPROCMASK
+ sigset_t mask;
+# else
+ int mask;
+# endif
+#endif
+
+#if USE_TRAP_MASK
+ /* disable interrupt */
+# ifdef HAVE_SIGPROCMASK
+ sigfillset(&mask);
+ sigprocmask(SIG_BLOCK, &mask, &mask);
+# else
+ mask = sigblock(~0);
+# endif
+#endif
+
+ oldfunc = ruby_signal(sig, SIG_DFL);
+ if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) {
+ ruby_signal(sig, oldfunc);
+ } else {
+ trap_list[sig].cmd = 0;
+ }
+
+#if USE_TRAP_MASK
+#ifdef HAVE_SIGPROCMASK
+ sigdelset(&mask, sig);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+#else
+ mask &= ~sigmask(sig);
+ sigsetmask(mask);
+#endif
+ trap_last_mask = mask;
+#endif
+}
+
+/*
+ * Many operating systems allow signals to be sent to running
+ * processes. Some signals have a defined effect on the process, while
+ * others may be trapped at the code level and acted upon. For
+ * example, your process may trap the USR1 signal and use it to toggle
+ * debugging, and may use TERM to initiate a controlled shutdown.
+ *
+ * pid = fork do
+ * Signal.trap("USR1") do
+ * $debug = !$debug
+ * puts "Debug now: #$debug"
+ * end
+ * Signal.trap("TERM") do
+ * puts "Terminating..."
+ * shutdown()
+ * end
+ * # . . . do some work . . .
+ * end
+ *
+ * Process.detach(pid)
+ *
+ * # Controlling program:
+ * Process.kill("USR1", pid)
+ * # ...
+ * Process.kill("USR1", pid)
+ * # ...
+ * Process.kill("TERM", pid)
+ *
+ * produces:
+ * Debug now: true
+ * Debug now: false
+ * Terminating...
+ *
+ * The list of available signal names and their interpretation is
+ * system dependent. Signal delivery semantics may also vary between
+ * systems; in particular signal delivery may not always be reliable.
+ */
+void
+Init_signal()
+{
+#ifndef MACOS_UNUSE_SIGNAL
+ VALUE mSignal = rb_define_module("Signal");
+
+ rb_define_global_function("trap", sig_trap, -1);
+ rb_define_module_function(mSignal, "trap", sig_trap, -1);
+ rb_define_module_function(mSignal, "list", sig_list, 0);
+
+ rb_define_method(rb_eSignal, "initialize", esignal_init, -1);
+ rb_attr(rb_eSignal, rb_intern("signo"), 1, 0, 0);
+ rb_alias(rb_eSignal, rb_intern("signm"), rb_intern("message"));
+ rb_define_method(rb_eInterrupt, "initialize", interrupt_init, 1);
+
+ install_sighandler(SIGINT, sighandler);
+#ifdef SIGHUP
+ install_sighandler(SIGHUP, sighandler);
+#endif
+#ifdef SIGQUIT
+ install_sighandler(SIGQUIT, sighandler);
+#endif
+#ifdef SIGTERM
+ install_sighandler(SIGTERM, sighandler);
+#endif
+#ifdef SIGALRM
+ install_sighandler(SIGALRM, sighandler);
+#endif
+#ifdef SIGUSR1
+ install_sighandler(SIGUSR1, sighandler);
+#endif
+#ifdef SIGUSR2
+ install_sighandler(SIGUSR2, sighandler);
+#endif
+
+#ifdef SIGBUS
+ install_sighandler(SIGBUS, sigbus);
+#endif
+#ifdef SIGSEGV
+ install_sighandler(SIGSEGV, sigsegv);
+#endif
+#ifdef SIGPIPE
+ install_sighandler(SIGPIPE, sigpipe);
+#endif
+
+#if defined(SIGCLD)
+ init_sigchld(SIGCLD);
+#elif defined(SIGCHLD)
+ init_sigchld(SIGCHLD);
+#endif
+
+#endif /* MACOS_UNUSE_SIGNAL */
+}
+/**********************************************************************
+
+ sprintf.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-20 01:12:46 +0200 (Fri, 20 Jun 2008) $
+ created at: Fri Oct 15 10:39:26 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "re.h"
+#include <ctype.h>
+#include <math.h>
+
+#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
+#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
+#define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n)))
+
+static void fmt_setup _((char*,int,int,int,int));
+
+static char*
+remove_sign_bits(str, base)
+ char *str;
+ int base;
+{
+ char *s, *t;
+
+ s = t = str;
+
+ if (base == 16) {
+ while (*t == 'f') {
+ t++;
+ }
+ }
+ else if (base == 8) {
+ *t |= EXTENDSIGN(3, strlen(t));
+ while (*t == '7') {
+ t++;
+ }
+ }
+ else if (base == 2) {
+ while (*t == '1') {
+ t++;
+ }
+ }
+ if (t > s) {
+ while (*t) *s++ = *t++;
+ *s = '\0';
+ }
+
+ return str;
+}
+
+static char
+sign_bits(base, p)
+ int base;
+ const char *p;
+{
+ char c = '.';
+
+ switch (base) {
+ case 16:
+ if (*p == 'X') c = 'F';
+ else c = 'f';
+ break;
+ case 8:
+ c = '7'; break;
+ case 2:
+ c = '1'; break;
+ }
+ return c;
+}
+
+#define FNONE 0
+#define FSHARP 1
+#define FMINUS 2
+#define FPLUS 4
+#define FZERO 8
+#define FSPACE 16
+#define FWIDTH 32
+#define FPREC 64
+#define FPREC0 128
+
+#define CHECK(l) do {\
+ while (blen + (l) >= bsiz) {\
+ bsiz*=2;\
+ }\
+ rb_str_resize(result, bsiz);\
+ buf = RSTRING(result)->ptr;\
+} while (0)
+
+#define PUSH(s, l) do { \
+ CHECK(l);\
+ memcpy(&buf[blen], s, l);\
+ blen += (l);\
+} while (0)
+
+#define GETARG() (nextvalue != Qundef ? nextvalue : \
+ posarg < 0 ? \
+ (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
+ (posarg = nextarg++, GETNTHARG(posarg)))
+
+#define GETPOSARG(n) (posarg > 0 ? \
+ (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \
+ ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \
+ (posarg = -1, GETNTHARG(n))))
+
+#define GETNTHARG(nth) \
+ ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
+
+#define GETNUM(n, val) \
+ for (; p < end && ISDIGIT(*p); p++) { \
+ int next_n = 10 * n + (*p - '0'); \
+ if (next_n / 10 != n) {\
+ rb_raise(rb_eArgError, #val " too big"); \
+ } \
+ n = next_n; \
+ } \
+ if (p >= end) { \
+ rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
+ }
+
+#define GETASTER(val) do { \
+ t = p++; \
+ n = 0; \
+ GETNUM(n, val); \
+ if (*p == '$') { \
+ tmp = GETPOSARG(n); \
+ } \
+ else { \
+ tmp = GETARG(); \
+ p = t; \
+ } \
+ val = NUM2INT(tmp); \
+} while (0)
+
+
+/*
+ * call-seq:
+ * format(format_string [, arguments...] ) => string
+ * sprintf(format_string [, arguments...] ) => string
+ *
+ * Returns the string resulting from applying <i>format_string</i> to
+ * any additional arguments. Within the format string, any characters
+ * other than format sequences are copied to the result. A format
+ * sequence consists of a percent sign, followed by optional flags,
+ * width, and precision indicators, then terminated with a field type
+ * character. The field type controls how the corresponding
+ * <code>sprintf</code> argument is to be interpreted, while the flags
+ * modify that interpretation. The field type characters are listed
+ * in the table at the end of this section. The flag characters are:
+ *
+ * Flag | Applies to | Meaning
+ * ---------+--------------+-----------------------------------------
+ * space | bdeEfgGiouxX | Leave a space at the start of
+ * | | positive numbers.
+ * ---------+--------------+-----------------------------------------
+ * (digit)$ | all | Specifies the absolute argument number
+ * | | for this field. Absolute and relative
+ * | | argument numbers cannot be mixed in a
+ * | | sprintf string.
+ * ---------+--------------+-----------------------------------------
+ * # | beEfgGoxX | Use an alternative format. For the
+ * | | conversions `o', `x', `X', and `b',
+ * | | prefix the result with ``0'', ``0x'', ``0X'',
+ * | | and ``0b'', respectively. For `e',
+ * | | `E', `f', `g', and 'G', force a decimal
+ * | | point to be added, even if no digits follow.
+ * | | For `g' and 'G', do not remove trailing zeros.
+ * ---------+--------------+-----------------------------------------
+ * + | bdeEfgGiouxX | Add a leading plus sign to positive numbers.
+ * ---------+--------------+-----------------------------------------
+ * - | all | Left-justify the result of this conversion.
+ * ---------+--------------+-----------------------------------------
+ * 0 (zero) | bdeEfgGiouxX | Pad with zeros, not spaces.
+ * ---------+--------------+-----------------------------------------
+ * * | all | Use the next argument as the field width.
+ * | | If negative, left-justify the result. If the
+ * | | asterisk is followed by a number and a dollar
+ * | | sign, use the indicated argument as the width.
+ *
+ *
+ * The field width is an optional integer, followed optionally by a
+ * period and a precision. The width specifies the minimum number of
+ * characters that will be written to the result for this field. For
+ * numeric fields, the precision controls the number of decimal places
+ * displayed. For string fields, the precision determines the maximum
+ * number of characters to be copied from the string. (Thus, the format
+ * sequence <code>%10.10s</code> will always contribute exactly ten
+ * characters to the result.)
+ *
+ * The field types are:
+ *
+ * Field | Conversion
+ * ------+--------------------------------------------------------------
+ * b | Convert argument as a binary number.
+ * c | Argument is the numeric code for a single character.
+ * d | Convert argument as a decimal number.
+ * E | Equivalent to `e', but uses an uppercase E to indicate
+ * | the exponent.
+ * e | Convert floating point argument into exponential notation
+ * | with one digit before the decimal point. The precision
+ * | determines the number of fractional digits (defaulting to six).
+ * f | Convert floating point argument as [-]ddd.ddd,
+ * | where the precision determines the number of digits after
+ * | the decimal point.
+ * G | Equivalent to `g', but use an uppercase `E' in exponent form.
+ * g | Convert a floating point number using exponential form
+ * | if the exponent is less than -4 or greater than or
+ * | equal to the precision, or in d.dddd form otherwise.
+ * i | Identical to `d'.
+ * o | Convert argument as an octal number.
+ * p | The valuing of argument.inspect.
+ * s | Argument is a string to be substituted. If the format
+ * | sequence contains a precision, at most that many characters
+ * | will be copied.
+ * u | Treat argument as an unsigned decimal number. Negative integers
+ * | are displayed as a 32 bit two's complement plus one for the
+ * | underlying architecture; that is, 2 ** 32 + n. However, since
+ * | Ruby has no inherent limit on bits used to represent the
+ * | integer, this value is preceded by two dots (..) in order to
+ * | indicate a infinite number of leading sign bits.
+ * X | Convert argument as a hexadecimal number using uppercase
+ * | letters. Negative numbers will be displayed with two
+ * | leading periods (representing an infinite string of
+ * | leading 'FF's.
+ * x | Convert argument as a hexadecimal number.
+ * | Negative numbers will be displayed with two
+ * | leading periods (representing an infinite string of
+ * | leading 'ff's.
+ *
+ * Examples:
+ *
+ * sprintf("%d %04x", 123, 123) #=> "123 007b"
+ * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
+ * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
+ * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
+ * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
+ * sprintf("%u", -123) #=> "..4294967173"
+ */
+
+VALUE
+rb_f_sprintf(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
+}
+
+VALUE
+rb_str_format(argc, argv, fmt)
+ int argc;
+ VALUE *argv;
+ VALUE fmt;
+{
+ const char *p, *end;
+ char *buf;
+ int blen, bsiz;
+ VALUE result;
+
+ int width, prec, flags = FNONE;
+ int nextarg = 1;
+ int posarg = 0;
+ int tainted = 0;
+ VALUE nextvalue;
+ VALUE tmp;
+ VALUE str;
+
+#define CHECK_FOR_WIDTH(f) \
+ if ((f) & FWIDTH) { \
+ rb_raise(rb_eArgError, "width given twice"); \
+ } \
+ if ((f) & FPREC0) { \
+ rb_raise(rb_eArgError, "width after precision"); \
+ }
+#define CHECK_FOR_FLAGS(f) \
+ if ((f) & FWIDTH) { \
+ rb_raise(rb_eArgError, "flag after width"); \
+ } \
+ if ((f) & FPREC0) { \
+ rb_raise(rb_eArgError, "flag after precision"); \
+ }
+
+ ++argc;
+ --argv;
+ if (OBJ_TAINTED(fmt)) tainted = 1;
+ StringValue(fmt);
+ fmt = rb_str_new4(fmt);
+ p = RSTRING(fmt)->ptr;
+ end = p + RSTRING(fmt)->len;
+ blen = 0;
+ bsiz = 120;
+ result = rb_str_buf_new(bsiz);
+ buf = RSTRING(result)->ptr;
+
+ for (; p < end; p++) {
+ const char *t;
+ int n;
+
+ for (t = p; t < end && *t != '%'; t++) ;
+ PUSH(p, t - p);
+ if (t >= end) {
+ /* end of fmt string */
+ goto sprint_exit;
+ }
+ p = t + 1; /* skip `%' */
+
+ width = prec = -1;
+ nextvalue = Qundef;
+ retry:
+ switch (*p) {
+ default:
+ if (ISPRINT(*p))
+ rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
+ else
+ rb_raise(rb_eArgError, "malformed format string");
+ break;
+
+ case ' ':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FSPACE;
+ p++;
+ goto retry;
+
+ case '#':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FSHARP;
+ p++;
+ goto retry;
+
+ case '+':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FPLUS;
+ p++;
+ goto retry;
+
+ case '-':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FMINUS;
+ p++;
+ goto retry;
+
+ case '0':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FZERO;
+ p++;
+ goto retry;
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = 0;
+ GETNUM(n, width);
+ if (*p == '$') {
+ if (nextvalue != Qundef) {
+ rb_raise(rb_eArgError, "value given twice - %d$", n);
+ }
+ nextvalue = GETPOSARG(n);
+ p++;
+ goto retry;
+ }
+ CHECK_FOR_WIDTH(flags);
+ width = n;
+ flags |= FWIDTH;
+ goto retry;
+
+ case '*':
+ CHECK_FOR_WIDTH(flags);
+ flags |= FWIDTH;
+ GETASTER(width);
+ if (width < 0) {
+ flags |= FMINUS;
+ width = -width;
+ }
+ p++;
+ goto retry;
+
+ case '.':
+ if (flags & FPREC0) {
+ rb_raise(rb_eArgError, "precision given twice");
+ }
+ flags |= FPREC|FPREC0;
+
+ prec = 0;
+ p++;
+ if (*p == '*') {
+ GETASTER(prec);
+ if (prec < 0) { /* ignore negative precision */
+ flags &= ~FPREC;
+ }
+ p++;
+ goto retry;
+ }
+
+ GETNUM(prec, precision);
+ goto retry;
+
+ case '\n':
+ case '\0':
+ p--;
+ case '%':
+ if (flags != FNONE) {
+ rb_raise(rb_eArgError, "illegal format character - %%");
+ }
+ PUSH("%", 1);
+ break;
+
+ case 'c':
+ {
+ VALUE val = GETARG();
+ char c;
+
+ if (!(flags & FMINUS))
+ while (--width > 0)
+ PUSH(" ", 1);
+ c = NUM2INT(val) & 0xff;
+ PUSH(&c, 1);
+ while (--width > 0)
+ PUSH(" ", 1);
+ }
+ break;
+
+ case 's':
+ case 'p':
+ {
+ VALUE arg = GETARG();
+ long len;
+
+ if (*p == 'p') arg = rb_inspect(arg);
+ str = rb_obj_as_string(arg);
+ if (OBJ_TAINTED(str)) tainted = 1;
+ len = RSTRING(str)->len;
+ if (flags&FPREC) {
+ if (prec < len) {
+ len = prec;
+ }
+ }
+ /* need to adjust multi-byte string pos */
+ if (flags&FWIDTH) {
+ if (width > len) {
+ CHECK(width);
+ width -= len;
+ if (!(flags&FMINUS)) {
+ while (width--) {
+ buf[blen++] = ' ';
+ }
+ }
+ memcpy(&buf[blen], RSTRING_PTR(str), len);
+ blen += len;
+ if (flags&FMINUS) {
+ while (width--) {
+ buf[blen++] = ' ';
+ }
+ }
+ break;
+ }
+ }
+ PUSH(RSTRING(str)->ptr, len);
+ }
+ break;
+
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'b':
+ case 'B':
+ case 'u':
+ {
+ volatile VALUE val = GETARG();
+ char fbuf[32], nbuf[64], *s, *t;
+ const char *prefix = 0;
+ int sign = 0;
+ char sc = 0;
+ long v = 0;
+ int base, bignum = 0;
+ int len, pos;
+ volatile VALUE tmp;
+ volatile VALUE tmp1;
+
+ switch (*p) {
+ case 'd':
+ case 'i':
+ sign = 1; break;
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'b':
+ case 'B':
+ case 'u':
+ default:
+ if (flags&(FPLUS|FSPACE)) sign = 1;
+ break;
+ }
+ if (flags & FSHARP) {
+ switch (*p) {
+ case 'o':
+ prefix = "0"; break;
+ case 'x':
+ prefix = "0x"; break;
+ case 'X':
+ prefix = "0X"; break;
+ case 'b':
+ prefix = "0b"; break;
+ case 'B':
+ prefix = "0B"; break;
+ }
+ if (prefix) {
+ width -= strlen(prefix);
+ }
+ }
+
+ bin_retry:
+ switch (TYPE(val)) {
+ case T_FLOAT:
+ val = rb_dbl2big(RFLOAT(val)->value);
+ if (FIXNUM_P(val)) goto bin_retry;
+ bignum = 1;
+ break;
+ case T_STRING:
+ val = rb_str_to_inum(val, 0, Qtrue);
+ goto bin_retry;
+ case T_BIGNUM:
+ bignum = 1;
+ break;
+ case T_FIXNUM:
+ v = FIX2LONG(val);
+ break;
+ default:
+ val = rb_Integer(val);
+ goto bin_retry;
+ }
+
+ switch (*p) {
+ case 'o':
+ base = 8; break;
+ case 'x':
+ case 'X':
+ base = 16; break;
+ case 'b':
+ case 'B':
+ base = 2; break;
+ case 'u':
+ case 'd':
+ case 'i':
+ default:
+ base = 10; break;
+ }
+
+ if (!bignum) {
+ if (base == 2) {
+ val = rb_int2big(v);
+ goto bin_retry;
+ }
+ if (sign) {
+ char c = *p;
+ if (c == 'i') c = 'd'; /* %d and %i are identical */
+ if (v < 0) {
+ v = -v;
+ sc = '-';
+ width--;
+ }
+ else if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
+ sprintf(fbuf, "%%l%c", c);
+ sprintf(nbuf, fbuf, v);
+ s = nbuf;
+ goto format_integer;
+ }
+ s = nbuf;
+ if (v < 0) {
+ if (base == 10) {
+ rb_warning("negative number for %%u specifier");
+ }
+ if (!(flags&(FPREC|FZERO))) {
+ strcpy(s, "..");
+ s += 2;
+ }
+ }
+ sprintf(fbuf, "%%l%c", *p == 'X' ? 'x' : *p);
+ sprintf(s, fbuf, v);
+ if (v < 0) {
+ char d = 0;
+
+ remove_sign_bits(s, base);
+ switch (base) {
+ case 16:
+ d = 'f'; break;
+ case 8:
+ d = '7'; break;
+ }
+ if (d && *s != d) {
+ memmove(s+1, s, strlen(s)+1);
+ *s = d;
+ }
+ }
+ s = nbuf;
+ goto format_integer;
+ }
+
+ if (sign) {
+ tmp = rb_big2str(val, base);
+ s = RSTRING(tmp)->ptr;
+ if (s[0] == '-') {
+ s++;
+ sc = '-';
+ width--;
+ }
+ else if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
+ goto format_integer;
+ }
+ if (!RBIGNUM(val)->sign) {
+ val = rb_big_clone(val);
+ rb_big_2comp(val);
+ }
+ tmp1 = tmp = rb_big2str0(val, base, RBIGNUM(val)->sign);
+ s = RSTRING(tmp)->ptr;
+ if (*s == '-') {
+ if (base == 10) {
+ rb_warning("negative number for %%u specifier");
+ }
+ remove_sign_bits(++s, base);
+ tmp = rb_str_new(0, 3+strlen(s));
+ t = RSTRING(tmp)->ptr;
+ if (!(flags&(FPREC|FZERO))) {
+ strcpy(t, "..");
+ t += 2;
+ }
+ switch (base) {
+ case 16:
+ if (s[0] != 'f') strcpy(t++, "f"); break;
+ case 8:
+ if (s[0] != '7') strcpy(t++, "7"); break;
+ case 2:
+ if (s[0] != '1') strcpy(t++, "1"); break;
+ }
+ strcpy(t, s);
+ bignum = 2;
+ }
+ s = RSTRING(tmp)->ptr;
+
+ format_integer:
+ pos = -1;
+ len = strlen(s);
+
+ if (*p == 'X') {
+ char *pp = s;
+ while (*pp) {
+ *pp = toupper(*pp);
+ pp++;
+ }
+ }
+ if ((flags&(FZERO|FPREC)) == FZERO) {
+ prec = width;
+ width = 0;
+ }
+ else {
+ if (prec < len) prec = len;
+ width -= prec;
+ }
+ if (!(flags&FMINUS)) {
+ CHECK(width);
+ while (width-- > 0) {
+ buf[blen++] = ' ';
+ }
+ }
+ if (sc) PUSH(&sc, 1);
+ if (prefix) {
+ int plen = strlen(prefix);
+ PUSH(prefix, plen);
+ }
+ CHECK(prec - len);
+ if (!bignum && v < 0) {
+ char c = sign_bits(base, p);
+ while (len < prec--) {
+ buf[blen++] = c;
+ }
+ }
+ else {
+ char c;
+
+ if (!sign && bignum && !RBIGNUM(val)->sign)
+ c = sign_bits(base, p);
+ else
+ c = '0';
+ while (len < prec--) {
+ buf[blen++] = c;
+ }
+ }
+ PUSH(s, len);
+ CHECK(width);
+ while (width-- > 0) {
+ buf[blen++] = ' ';
+ }
+ }
+ break;
+
+ case 'f':
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'E':
+ {
+ VALUE val = GETARG();
+ double fval;
+ int i, need = 6;
+ char fbuf[32];
+
+ fval = RFLOAT(rb_Float(val))->value;
+#if defined(_WIN32) && !defined(__BORLANDC__)
+ if (isnan(fval) || isinf(fval)) {
+ char *expr;
+
+ if (isnan(fval)) {
+ expr = "NaN";
+ }
+ else {
+ expr = "Inf";
+ }
+ need = strlen(expr);
+ if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
+ need++;
+ else if (flags & FSPACE)
+ need++;
+ if ((flags & FWIDTH) && need < width)
+ need = width;
+
+ CHECK(need);
+ sprintf(&buf[blen], "%*s", need, "");
+ if (flags & FMINUS) {
+ if (!isnan(fval) && fval < 0.0)
+ buf[blen++] = '-';
+ else if (flags & FPLUS)
+ buf[blen++] = '+';
+ else if (flags & FSPACE)
+ blen++;
+ strncpy(&buf[blen], expr, strlen(expr));
+ }
+ else if (flags & FZERO) {
+ if (!isnan(fval) && fval < 0.0) {
+ buf[blen++] = '-';
+ need--;
+ }
+ else if (flags & FPLUS) {
+ buf[blen++] = '+';
+ need--;
+ }
+ else if (flags & FSPACE) {
+ blen++;
+ need--;
+ }
+ while (need-- - strlen(expr) > 0) {
+ buf[blen++] = '0';
+ }
+ strncpy(&buf[blen], expr, strlen(expr));
+ }
+ else {
+ if (!isnan(fval) && fval < 0.0)
+ buf[blen + need - strlen(expr) - 1] = '-';
+ else if (flags & FPLUS)
+ buf[blen + need - strlen(expr) - 1] = '+';
+ strncpy(&buf[blen + need - strlen(expr)], expr,
+ strlen(expr));
+ }
+ blen += strlen(&buf[blen]);
+ break;
+ }
+#endif /* defined(_WIN32) && !defined(__BORLANDC__) */
+ fmt_setup(fbuf, *p, flags, width, prec);
+ need = 0;
+ if (*p != 'e' && *p != 'E') {
+ i = INT_MIN;
+ frexp(fval, &i);
+ if (i > 0)
+ need = BIT_DIGITS(i);
+ }
+ need += (flags&FPREC) ? prec : 6;
+ if ((flags&FWIDTH) && need < width)
+ need = width;
+ need += 20;
+
+ CHECK(need);
+ sprintf(&buf[blen], fbuf, fval);
+ blen += strlen(&buf[blen]);
+ }
+ break;
+ }
+ flags = FNONE;
+ }
+
+ sprint_exit:
+ /* XXX - We cannot validiate the number of arguments if (digit)$ style used.
+ */
+ if (posarg >= 0 && nextarg < argc) {
+ const char *mesg = "too many arguments for format string";
+ if (RTEST(ruby_debug)) rb_raise(rb_eArgError, mesg);
+ if (RTEST(ruby_verbose)) rb_warn(mesg);
+ }
+ rb_str_resize(result, blen);
+
+ if (tainted) OBJ_TAINT(result);
+ return result;
+}
+
+static void
+fmt_setup(buf, c, flags, width, prec)
+ char *buf;
+ int c;
+ int flags, width, prec;
+{
+ *buf++ = '%';
+ if (flags & FSHARP) *buf++ = '#';
+ if (flags & FPLUS) *buf++ = '+';
+ if (flags & FMINUS) *buf++ = '-';
+ if (flags & FZERO) *buf++ = '0';
+ if (flags & FSPACE) *buf++ = ' ';
+
+ if (flags & FWIDTH) {
+ sprintf(buf, "%d", width);
+ buf += strlen(buf);
+ }
+
+ if (flags & FPREC) {
+ sprintf(buf, ".%d", prec);
+ buf += strlen(buf);
+ }
+
+ *buf++ = c;
+ *buf = '\0';
+}
+/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
+
+/* static char sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; */
+
+#include "config.h"
+#include "defines.h"
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include "st.h"
+
+typedef struct st_table_entry st_table_entry;
+
+struct st_table_entry {
+ unsigned int hash;
+ st_data_t key;
+ st_data_t record;
+ st_table_entry *next;
+};
+
+#define ST_DEFAULT_MAX_DENSITY 5
+#define ST_DEFAULT_INIT_TABLE_SIZE 11
+
+ /*
+ * DEFAULT_MAX_DENSITY is the default for the largest we allow the
+ * average number of items per bin before increasing the number of
+ * bins
+ *
+ * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins
+ * allocated initially
+ *
+ */
+static int numcmp(long, long);
+static int numhash(long);
+static struct st_hash_type type_numhash = {
+ numcmp,
+ numhash,
+};
+
+/* extern int strcmp(const char *, const char *); */
+static int strhash(const char *);
+static struct st_hash_type type_strhash = {
+ strcmp,
+ strhash,
+};
+
+static void rehash(st_table *);
+
+#ifdef RUBY
+#define malloc xmalloc
+#define calloc xcalloc
+#endif
+
+#define alloc(type) (type*)malloc((unsigned)sizeof(type))
+#define Calloc(n,s) (char*)calloc((n),(s))
+
+#define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0)
+
+#define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key))
+#define do_hash_bin(key,table) (do_hash(key, table)%(table)->num_bins)
+
+/*
+ * MINSIZE is the minimum size of a dictionary.
+ */
+
+#define MINSIZE 8
+
+/*
+Table of prime numbers 2^n+a, 2<=n<=30.
+*/
+static long primes[] = {
+ 8 + 3,
+ 16 + 3,
+ 32 + 5,
+ 64 + 3,
+ 128 + 3,
+ 256 + 27,
+ 512 + 9,
+ 1024 + 9,
+ 2048 + 5,
+ 4096 + 3,
+ 8192 + 27,
+ 16384 + 43,
+ 32768 + 3,
+ 65536 + 45,
+ 131072 + 29,
+ 262144 + 3,
+ 524288 + 21,
+ 1048576 + 7,
+ 2097152 + 17,
+ 4194304 + 15,
+ 8388608 + 9,
+ 16777216 + 43,
+ 33554432 + 35,
+ 67108864 + 15,
+ 134217728 + 29,
+ 268435456 + 3,
+ 536870912 + 11,
+ 1073741824 + 85,
+ 0
+};
+
+static int
+new_size(size)
+ int size;
+{
+ int i;
+
+#if 0
+ for (i=3; i<31; i++) {
+ if ((1<<i) > size) return 1<<i;
+ }
+ return -1;
+#else
+ int newsize;
+
+ for (i = 0, newsize = MINSIZE;
+ i < sizeof(primes)/sizeof(primes[0]);
+ i++, newsize <<= 1)
+ {
+ if (newsize > size) return primes[i];
+ }
+ /* Ran out of polynomials */
+ return -1; /* should raise exception */
+#endif
+}
+
+#ifdef HASH_LOG
+static int collision = 0;
+static int init_st = 0;
+
+static void
+stat_col()
+{
+ FILE *f = fopen("/tmp/col", "w");
+ fprintf(f, "collision: %d\n", collision);
+ fclose(f);
+}
+#endif
+
+st_table*
+st_init_table_with_size(type, size)
+ struct st_hash_type *type;
+ int size;
+{
+ st_table *tbl;
+
+#ifdef HASH_LOG
+ if (init_st == 0) {
+ init_st = 1;
+ atexit(stat_col);
+ }
+#endif
+
+ size = new_size(size); /* round up to prime number */
+
+ tbl = alloc(st_table);
+ tbl->type = type;
+ tbl->num_entries = 0;
+ tbl->num_bins = size;
+ tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*));
+
+ return tbl;
+}
+
+st_table*
+st_init_table(type)
+ struct st_hash_type *type;
+{
+ return st_init_table_with_size(type, 0);
+}
+
+st_table*
+st_init_numtable(void)
+{
+ return st_init_table(&type_numhash);
+}
+
+st_table*
+st_init_numtable_with_size(size)
+ int size;
+{
+ return st_init_table_with_size(&type_numhash, size);
+}
+
+st_table*
+st_init_strtable(void)
+{
+ return st_init_table(&type_strhash);
+}
+
+st_table*
+st_init_strtable_with_size(size)
+ int size;
+{
+ return st_init_table_with_size(&type_strhash, size);
+}
+
+void
+st_free_table(table)
+ st_table *table;
+{
+ register st_table_entry *ptr, *next;
+ int i;
+
+ for(i = 0; i < table->num_bins; i++) {
+ ptr = table->bins[i];
+ while (ptr != 0) {
+ next = ptr->next;
+ free(ptr);
+ ptr = next;
+ }
+ }
+ free(table->bins);
+ free(table);
+}
+
+#define PTR_NOT_EQUAL(table, ptr, hash_val, key) \
+((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key)))
+
+#ifdef HASH_LOG
+#define COLLISION collision++
+#else
+#define COLLISION
+#endif
+
+#define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\
+ bin_pos = hash_val%(table)->num_bins;\
+ ptr = (table)->bins[bin_pos];\
+ if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\
+ COLLISION;\
+ while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\
+ ptr = ptr->next;\
+ }\
+ ptr = ptr->next;\
+ }\
+} while (0)
+
+int
+st_lookup(table, key, value)
+ st_table *table;
+ register st_data_t key;
+ st_data_t *value;
+{
+ unsigned int hash_val, bin_pos;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash(key, table);
+ FIND_ENTRY(table, ptr, hash_val, bin_pos);
+
+ if (ptr == 0) {
+ return 0;
+ }
+ else {
+ if (value != 0) *value = ptr->record;
+ return 1;
+ }
+}
+
+#define ADD_DIRECT(table, key, value, hash_val, bin_pos)\
+do {\
+ st_table_entry *entry;\
+ if (table->num_entries/(table->num_bins) > ST_DEFAULT_MAX_DENSITY) {\
+ rehash(table);\
+ bin_pos = hash_val % table->num_bins;\
+ }\
+ \
+ entry = alloc(st_table_entry);\
+ \
+ entry->hash = hash_val;\
+ entry->key = key;\
+ entry->record = value;\
+ entry->next = table->bins[bin_pos];\
+ table->bins[bin_pos] = entry;\
+ table->num_entries++;\
+} while (0)
+
+int
+st_insert(table, key, value)
+ register st_table *table;
+ register st_data_t key;
+ st_data_t value;
+{
+ unsigned int hash_val, bin_pos;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash(key, table);
+ FIND_ENTRY(table, ptr, hash_val, bin_pos);
+
+ if (ptr == 0) {
+ ADD_DIRECT(table, key, value, hash_val, bin_pos);
+ return 0;
+ }
+ else {
+ ptr->record = value;
+ return 1;
+ }
+}
+
+void
+st_add_direct(table, key, value)
+ st_table *table;
+ st_data_t key;
+ st_data_t value;
+{
+ unsigned int hash_val, bin_pos;
+
+ hash_val = do_hash(key, table);
+ bin_pos = hash_val % table->num_bins;
+ ADD_DIRECT(table, key, value, hash_val, bin_pos);
+}
+
+static void
+rehash(table)
+ register st_table *table;
+{
+ register st_table_entry *ptr, *next, **new_bins;
+ int i, old_num_bins = table->num_bins, new_num_bins;
+ unsigned int hash_val;
+
+ new_num_bins = new_size(old_num_bins+1);
+ new_bins = (st_table_entry**)Calloc(new_num_bins, sizeof(st_table_entry*));
+
+ for(i = 0; i < old_num_bins; i++) {
+ ptr = table->bins[i];
+ while (ptr != 0) {
+ next = ptr->next;
+ hash_val = ptr->hash % new_num_bins;
+ ptr->next = new_bins[hash_val];
+ new_bins[hash_val] = ptr;
+ ptr = next;
+ }
+ }
+ free(table->bins);
+ table->num_bins = new_num_bins;
+ table->bins = new_bins;
+}
+
+st_table*
+st_copy(old_table)
+ st_table *old_table;
+{
+ st_table *new_table;
+ st_table_entry *ptr, *entry;
+ int i, num_bins = old_table->num_bins;
+
+ new_table = alloc(st_table);
+ if (new_table == 0) {
+ return 0;
+ }
+
+ *new_table = *old_table;
+ new_table->bins = (st_table_entry**)
+ Calloc((unsigned)num_bins, sizeof(st_table_entry*));
+
+ if (new_table->bins == 0) {
+ free(new_table);
+ return 0;
+ }
+
+ for(i = 0; i < num_bins; i++) {
+ new_table->bins[i] = 0;
+ ptr = old_table->bins[i];
+ while (ptr != 0) {
+ entry = alloc(st_table_entry);
+ if (entry == 0) {
+ free(new_table->bins);
+ free(new_table);
+ return 0;
+ }
+ *entry = *ptr;
+ entry->next = new_table->bins[i];
+ new_table->bins[i] = entry;
+ ptr = ptr->next;
+ }
+ }
+ return new_table;
+}
+
+int
+st_delete(table, key, value)
+ register st_table *table;
+ register st_data_t *key;
+ st_data_t *value;
+{
+ unsigned int hash_val;
+ st_table_entry *tmp;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash_bin(*key, table);
+ ptr = table->bins[hash_val];
+
+ if (ptr == 0) {
+ if (value != 0) *value = 0;
+ return 0;
+ }
+
+ if (EQUAL(table, *key, ptr->key)) {
+ table->bins[hash_val] = ptr->next;
+ table->num_entries--;
+ if (value != 0) *value = ptr->record;
+ *key = ptr->key;
+ free(ptr);
+ return 1;
+ }
+
+ for(; ptr->next != 0; ptr = ptr->next) {
+ if (EQUAL(table, ptr->next->key, *key)) {
+ tmp = ptr->next;
+ ptr->next = ptr->next->next;
+ table->num_entries--;
+ if (value != 0) *value = tmp->record;
+ *key = tmp->key;
+ free(tmp);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+st_delete_safe(table, key, value, never)
+ register st_table *table;
+ register st_data_t *key;
+ st_data_t *value;
+ st_data_t never;
+{
+ unsigned int hash_val;
+ register st_table_entry *ptr;
+
+ hash_val = do_hash_bin(*key, table);
+ ptr = table->bins[hash_val];
+
+ if (ptr == 0) {
+ if (value != 0) *value = 0;
+ return 0;
+ }
+
+ for(; ptr != 0; ptr = ptr->next) {
+ if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) {
+ table->num_entries--;
+ *key = ptr->key;
+ if (value != 0) *value = ptr->record;
+ ptr->key = ptr->record = never;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+delete_never(key, value, never)
+ st_data_t key, value, never;
+{
+ if (value == never) return ST_DELETE;
+ return ST_CONTINUE;
+}
+
+void
+st_cleanup_safe(table, never)
+ st_table *table;
+ st_data_t never;
+{
+ int num_entries = table->num_entries;
+
+ st_foreach(table, delete_never, never);
+ table->num_entries = num_entries;
+}
+
+int
+st_foreach(table, func, arg)
+ st_table *table;
+ int (*func)();
+ st_data_t arg;
+{
+ st_table_entry *ptr, *last, *tmp;
+ enum st_retval retval;
+ int i;
+
+ for(i = 0; i < table->num_bins; i++) {
+ last = 0;
+ for(ptr = table->bins[i]; ptr != 0;) {
+ retval = (*func)(ptr->key, ptr->record, arg);
+ switch (retval) {
+ case ST_CHECK: /* check if hash is modified during iteration */
+ tmp = 0;
+ if (i < table->num_bins) {
+ for (tmp = table->bins[i]; tmp; tmp=tmp->next) {
+ if (tmp == ptr) break;
+ }
+ }
+ if (!tmp) {
+ /* call func with error notice */
+ return 1;
+ }
+ /* fall through */
+ case ST_CONTINUE:
+ last = ptr;
+ ptr = ptr->next;
+ break;
+ case ST_STOP:
+ return 0;
+ case ST_DELETE:
+ tmp = ptr;
+ if (last == 0) {
+ table->bins[i] = ptr->next;
+ }
+ else {
+ last->next = ptr->next;
+ }
+ ptr = ptr->next;
+ free(tmp);
+ table->num_entries--;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+strhash(string)
+ register const char *string;
+{
+ register int c;
+
+#ifdef HASH_ELFHASH
+ register unsigned int h = 0, g;
+
+ while ((c = *string++) != '\0') {
+ h = ( h << 4 ) + c;
+ if ( g = h & 0xF0000000 )
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+#elif defined(HASH_PERL)
+ register int val = 0;
+
+ while ((c = *string++) != '\0') {
+ val += c;
+ val += (val << 10);
+ val ^= (val >> 6);
+ }
+ val += (val << 3);
+ val ^= (val >> 11);
+
+ return val + (val << 15);
+#else
+ register int val = 0;
+
+ while ((c = *string++) != '\0') {
+ val = val*997 + c;
+ }
+
+ return val + (val>>5);
+#endif
+}
+
+static int
+numcmp(x, y)
+ long x, y;
+{
+ return x != y;
+}
+
+static int
+numhash(n)
+ long n;
+{
+ return n;
+}
+/**********************************************************************
+
+ string.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-17 03:58:28 +0100 (Tue, 17 Feb 2009) $
+ created at: Mon Aug 9 17:12:58 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "re.h"
+
+#define BEG(no) regs->beg[no]
+#define END(no) regs->end[no]
+
+#include <math.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+VALUE rb_cString;
+
+#define STR_TMPLOCK FL_USER1
+#define STR_ASSOC FL_USER3
+#define STR_NOCAPA (ELTS_SHARED|STR_ASSOC)
+
+#define RESIZE_CAPA(str,capacity) do {\
+ REALLOC_N(RSTRING(str)->ptr, char, (capacity)+1);\
+ if (!FL_TEST(str, STR_NOCAPA))\
+ RSTRING(str)->aux.capa = (capacity);\
+} while (0)
+
+VALUE rb_fs;
+
+static inline void
+str_mod_check(s, p, len)
+ VALUE s;
+ char *p;
+ long len;
+{
+ if (RSTRING(s)->ptr != p || RSTRING(s)->len != len) {
+ rb_raise(rb_eRuntimeError, "string modified");
+ }
+}
+
+static inline void
+str_frozen_check(s)
+ VALUE s;
+{
+ if (OBJ_FROZEN(s)) {
+ rb_raise(rb_eRuntimeError, "string frozen");
+ }
+}
+
+static VALUE str_alloc _((VALUE));
+static VALUE
+str_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(str, struct RString);
+ OBJSETUP(str, klass, T_STRING);
+
+ str->ptr = 0;
+ str->len = 0;
+ str->aux.capa = 0;
+
+ return (VALUE)str;
+}
+
+static VALUE
+str_new(klass, ptr, len)
+ VALUE klass;
+ const char *ptr;
+ long len;
+{
+ VALUE str;
+
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative string size (or size too big)");
+ }
+
+ str = str_alloc(klass);
+ RSTRING(str)->len = len;
+ RSTRING(str)->aux.capa = len;
+ RSTRING(str)->ptr = ALLOC_N(char,len+1);
+ if (ptr) {
+ memcpy(RSTRING(str)->ptr, ptr, len);
+ }
+ RSTRING(str)->ptr[len] = '\0';
+ return str;
+}
+
+VALUE
+rb_str_new(ptr, len)
+ const char *ptr;
+ long len;
+{
+ return str_new(rb_cString, ptr, len);
+}
+
+VALUE
+rb_str_new2(ptr)
+ const char *ptr;
+{
+ if (!ptr) {
+ rb_raise(rb_eArgError, "NULL pointer given");
+ }
+ return rb_str_new(ptr, strlen(ptr));
+}
+
+VALUE
+rb_tainted_str_new(ptr, len)
+ const char *ptr;
+ long len;
+{
+ VALUE str = rb_str_new(ptr, len);
+
+ OBJ_TAINT(str);
+ return str;
+}
+
+VALUE
+rb_tainted_str_new2(ptr)
+ const char *ptr;
+{
+ VALUE str = rb_str_new2(ptr);
+
+ OBJ_TAINT(str);
+ return str;
+}
+
+static VALUE
+str_new3(klass, str)
+ VALUE klass, str;
+{
+ VALUE str2 = str_alloc(klass);
+
+ RSTRING(str2)->len = RSTRING(str)->len;
+ RSTRING(str2)->ptr = RSTRING(str)->ptr;
+ RSTRING(str2)->aux.shared = str;
+ FL_SET(str2, ELTS_SHARED);
+
+ return str2;
+}
+
+VALUE
+rb_str_new3(str)
+ VALUE str;
+{
+ VALUE str2 = str_new3(rb_obj_class(str), str);
+
+ OBJ_INFECT(str2, str);
+ return str2;
+}
+
+static VALUE
+str_new4(klass, str)
+ VALUE klass, str;
+{
+ VALUE str2 = str_alloc(klass);
+
+ RSTRING(str2)->len = RSTRING(str)->len;
+ RSTRING(str2)->ptr = RSTRING(str)->ptr;
+ if (FL_TEST(str, ELTS_SHARED)) {
+ FL_SET(str2, ELTS_SHARED);
+ RSTRING(str2)->aux.shared = RSTRING(str)->aux.shared;
+ }
+ else {
+ FL_SET(str, ELTS_SHARED);
+ RSTRING(str)->aux.shared = str2;
+ }
+
+ return str2;
+}
+
+VALUE
+rb_str_new4(orig)
+ VALUE orig;
+{
+ VALUE klass, str;
+
+ if (OBJ_FROZEN(orig)) return orig;
+ klass = rb_obj_class(orig);
+ if (FL_TEST(orig, ELTS_SHARED) && (str = RSTRING(orig)->aux.shared) && klass == RBASIC(str)->klass) {
+ long ofs;
+ ofs = RSTRING(str)->len - RSTRING(orig)->len;
+ if ((ofs > 0) || (!OBJ_TAINTED(str) && OBJ_TAINTED(orig))) {
+ str = str_new3(klass, str);
+ RSTRING(str)->ptr += ofs;
+ RSTRING(str)->len -= ofs;
+ }
+ }
+ else if (FL_TEST(orig, STR_ASSOC)) {
+ str = str_new(klass, RSTRING(orig)->ptr, RSTRING(orig)->len);
+ }
+ else {
+ str = str_new4(klass, orig);
+ }
+ OBJ_INFECT(str, orig);
+ OBJ_FREEZE(str);
+ return str;
+}
+
+VALUE
+rb_str_new5(obj, ptr, len)
+ VALUE obj;
+ const char *ptr;
+ long len;
+{
+ return str_new(rb_obj_class(obj), ptr, len);
+}
+
+#define STR_BUF_MIN_SIZE 128
+
+VALUE
+rb_str_buf_new(capa)
+ long capa;
+{
+ VALUE str = str_alloc(rb_cString);
+
+ if (capa < STR_BUF_MIN_SIZE) {
+ capa = STR_BUF_MIN_SIZE;
+ }
+ RSTRING(str)->ptr = 0;
+ RSTRING(str)->len = 0;
+ RSTRING(str)->aux.capa = capa;
+ RSTRING(str)->ptr = ALLOC_N(char, capa+1);
+ RSTRING(str)->ptr[0] = '\0';
+
+ return str;
+}
+
+VALUE
+rb_str_buf_new2(ptr)
+ const char *ptr;
+{
+ VALUE str;
+ long len = strlen(ptr);
+
+ str = rb_str_buf_new(len);
+ rb_str_buf_cat(str, ptr, len);
+
+ return str;
+}
+
+VALUE
+rb_str_to_str(str)
+ VALUE str;
+{
+ return rb_convert_type(str, T_STRING, "String", "to_str");
+}
+
+static void
+rb_str_shared_replace(str, str2)
+ VALUE str, str2;
+{
+ if (str == str2) return;
+ rb_str_modify(str);
+ if (!FL_TEST(str, ELTS_SHARED)) free(RSTRING(str)->ptr);
+ if (NIL_P(str2)) {
+ RSTRING(str)->ptr = 0;
+ RSTRING(str)->len = 0;
+ RSTRING(str)->aux.capa = 0;
+ FL_UNSET(str, STR_NOCAPA);
+ return;
+ }
+ RSTRING(str)->ptr = RSTRING(str2)->ptr;
+ RSTRING(str)->len = RSTRING(str2)->len;
+ FL_UNSET(str, STR_NOCAPA);
+ if (FL_TEST(str2, STR_NOCAPA)) {
+ FL_SET(str, RBASIC(str2)->flags & STR_NOCAPA);
+ RSTRING(str)->aux.shared = RSTRING(str2)->aux.shared;
+ }
+ else {
+ RSTRING(str)->aux.capa = RSTRING(str2)->aux.capa;
+ }
+ RSTRING(str2)->ptr = 0; /* abandon str2 */
+ RSTRING(str2)->len = 0;
+ RSTRING(str2)->aux.capa = 0;
+ FL_UNSET(str2, STR_NOCAPA);
+ if (OBJ_TAINTED(str2)) OBJ_TAINT(str);
+}
+
+static ID id_to_s;
+
+VALUE
+rb_obj_as_string(obj)
+ VALUE obj;
+{
+ VALUE str;
+
+ if (TYPE(obj) == T_STRING) {
+ return obj;
+ }
+ str = rb_funcall(obj, id_to_s, 0);
+ if (TYPE(str) != T_STRING)
+ return rb_any_to_s(obj);
+ if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
+ return str;
+}
+
+static VALUE rb_str_s_alloc _((VALUE));
+static VALUE rb_str_replace _((VALUE, VALUE));
+
+VALUE
+rb_str_dup(str)
+ VALUE str;
+{
+ VALUE dup = str_alloc(rb_obj_class(str));
+ rb_str_replace(dup, str);
+ return dup;
+}
+
+
+/*
+ * call-seq:
+ * String.new(str="") => new_str
+ *
+ * Returns a new string object containing a copy of <i>str</i>.
+ */
+
+static VALUE
+rb_str_init(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE orig;
+
+ if (rb_scan_args(argc, argv, "01", &orig) == 1)
+ rb_str_replace(str, orig);
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.length => integer
+ *
+ * Returns the length of <i>str</i>.
+ */
+
+static VALUE
+rb_str_length(str)
+ VALUE str;
+{
+ return LONG2NUM(RSTRING(str)->len);
+}
+
+/*
+ * call-seq:
+ * str.empty? => true or false
+ *
+ * Returns <code>true</code> if <i>str</i> has a length of zero.
+ *
+ * "hello".empty? #=> false
+ * "".empty? #=> true
+ */
+
+static VALUE
+rb_str_empty(str)
+ VALUE str;
+{
+ if (RSTRING(str)->len == 0)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * str + other_str => new_str
+ *
+ * Concatenation---Returns a new <code>String</code> containing
+ * <i>other_str</i> concatenated to <i>str</i>.
+ *
+ * "Hello from " + self.to_s #=> "Hello from main"
+ */
+
+VALUE
+rb_str_plus(str1, str2)
+ VALUE str1, str2;
+{
+ VALUE str3;
+
+ StringValue(str2);
+ str3 = rb_str_new(0, RSTRING(str1)->len+RSTRING(str2)->len);
+ memcpy(RSTRING(str3)->ptr, RSTRING(str1)->ptr, RSTRING(str1)->len);
+ memcpy(RSTRING(str3)->ptr + RSTRING(str1)->len,
+ RSTRING(str2)->ptr, RSTRING(str2)->len);
+ RSTRING(str3)->ptr[RSTRING(str3)->len] = '\0';
+
+ if (OBJ_TAINTED(str1) || OBJ_TAINTED(str2))
+ OBJ_TAINT(str3);
+ return str3;
+}
+
+/*
+ * call-seq:
+ * str * integer => new_str
+ *
+ * Copy---Returns a new <code>String</code> containing <i>integer</i> copies of
+ * the receiver.
+ *
+ * "Ho! " * 3 #=> "Ho! Ho! Ho! "
+ */
+
+VALUE
+rb_str_times(str, times)
+ VALUE str;
+ VALUE times;
+{
+ VALUE str2;
+ long i, len;
+
+ len = NUM2LONG(times);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative argument");
+ }
+ if (len && LONG_MAX/len < RSTRING(str)->len) {
+ rb_raise(rb_eArgError, "argument too big");
+ }
+
+ str2 = rb_str_new5(str,0, len *= RSTRING(str)->len);
+ for (i = 0; i < len; i += RSTRING(str)->len) {
+ memcpy(RSTRING(str2)->ptr + i,
+ RSTRING(str)->ptr, RSTRING(str)->len);
+ }
+ RSTRING(str2)->ptr[RSTRING(str2)->len] = '\0';
+
+ OBJ_INFECT(str2, str);
+
+ return str2;
+}
+
+/*
+ * call-seq:
+ * str % arg => new_str
+ *
+ * Format---Uses <i>str</i> as a format specification, and returns the result
+ * of applying it to <i>arg</i>. If the format specification contains more than
+ * one substitution, then <i>arg</i> must be an <code>Array</code> containing
+ * the values to be substituted. See <code>Kernel::sprintf</code> for details
+ * of the format string.
+ *
+ * "%05d" % 123 #=> "00123"
+ * "%-5s: %08x" % [ "ID", self.id ] #=> "ID : 200e14d6"
+ */
+
+static VALUE
+rb_str_format_m(str, arg)
+ VALUE str, arg;
+{
+ volatile VALUE tmp = rb_check_array_type(arg);
+
+ if (!NIL_P(tmp)) {
+ return rb_str_format(RARRAY_LEN(tmp), RARRAY_PTR(tmp), str);
+ }
+ return rb_str_format(1, &arg, str);
+}
+
+static const char null_str[] = "";
+
+static int
+str_independent(str)
+ VALUE str;
+{
+ if (FL_TEST(str, STR_TMPLOCK)) {
+ rb_raise(rb_eRuntimeError, "can't modify string; temporarily locked");
+ }
+ if (OBJ_FROZEN(str)) rb_error_frozen("string");
+ if (!OBJ_TAINTED(str) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify string");
+ if (RSTRING(str)->ptr == null_str) return 0;
+ if (!FL_TEST(str, ELTS_SHARED)) return 1;
+ return 0;
+}
+
+static void
+str_make_independent(str)
+ VALUE str;
+{
+ char *ptr;
+
+ ptr = ALLOC_N(char, RSTRING(str)->len+1);
+ if (RSTRING(str)->ptr) {
+ memcpy(ptr, RSTRING(str)->ptr, RSTRING(str)->len);
+ }
+ ptr[RSTRING(str)->len] = 0;
+ RSTRING(str)->ptr = ptr;
+ RSTRING(str)->aux.capa = RSTRING(str)->len;
+ FL_UNSET(str, STR_NOCAPA);
+}
+
+void
+rb_str_modify(str)
+ VALUE str;
+{
+ if (!str_independent(str))
+ str_make_independent(str);
+}
+
+void
+rb_str_associate(str, add)
+ VALUE str, add;
+{
+ if (FL_TEST(str, STR_ASSOC)) {
+ /* already associated */
+ rb_ary_concat(RSTRING(str)->aux.shared, add);
+ }
+ else {
+ if (FL_TEST(str, ELTS_SHARED)) {
+ str_make_independent(str);
+ }
+ else if (RSTRING(str)->aux.capa != RSTRING(str)->len) {
+ RESIZE_CAPA(str, RSTRING(str)->len);
+ }
+ RSTRING(str)->aux.shared = add;
+ FL_SET(str, STR_ASSOC);
+ }
+}
+
+VALUE
+rb_str_associated(str)
+ VALUE str;
+{
+ if (FL_TEST(str, STR_ASSOC)) {
+ return RSTRING(str)->aux.shared;
+ }
+ return Qfalse;
+}
+
+#define make_null_str(s) do { \
+ FL_SET(s, ELTS_SHARED); \
+ RSTRING(s)->ptr = (char *)null_str; \
+ RSTRING(s)->aux.shared = 0; \
+ } while (0)
+
+static VALUE
+rb_str_s_alloc(klass)
+ VALUE klass;
+{
+ VALUE str = str_alloc(klass);
+ make_null_str(str);
+ return str;
+}
+
+VALUE
+rb_string_value(ptr)
+ volatile VALUE *ptr;
+{
+ VALUE s = *ptr;
+ if (TYPE(s) != T_STRING) {
+ s = rb_str_to_str(s);
+ *ptr = s;
+ }
+ if (!RSTRING(s)->ptr) {
+ make_null_str(s);
+ }
+ return s;
+}
+
+char *
+rb_string_value_ptr(ptr)
+ volatile VALUE *ptr;
+{
+ return RSTRING(rb_string_value(ptr))->ptr;
+}
+
+char *
+rb_string_value_cstr(ptr)
+ volatile VALUE *ptr;
+{
+ VALUE str = rb_string_value(ptr);
+ char *s = RSTRING(str)->ptr;
+
+ if (!s || RSTRING(str)->len != strlen(s)) {
+ rb_raise(rb_eArgError, "string contains null byte");
+ }
+ return s;
+}
+
+VALUE
+rb_check_string_type(str)
+ VALUE str;
+{
+ str = rb_check_convert_type(str, T_STRING, "String", "to_str");
+ if (!NIL_P(str) && !RSTRING(str)->ptr) {
+ make_null_str(str);
+ }
+ return str;
+}
+
+VALUE
+rb_str_substr(str, beg, len)
+ VALUE str;
+ long beg, len;
+{
+ VALUE str2;
+
+ if (len < 0) return Qnil;
+ if (beg > RSTRING(str)->len) return Qnil;
+ if (beg < 0) {
+ beg += RSTRING(str)->len;
+ if (beg < 0) return Qnil;
+ }
+ if (beg + len > RSTRING(str)->len) {
+ len = RSTRING(str)->len - beg;
+ }
+ if (len < 0) {
+ len = 0;
+ }
+ if (len == 0) {
+ str2 = rb_str_new5(str,0,0);
+ }
+ else if (len > sizeof(struct RString)/2 &&
+ beg + len == RSTRING(str)->len && !FL_TEST(str, STR_ASSOC)) {
+ str2 = rb_str_new4(str);
+ str2 = str_new3(rb_obj_class(str2), str2);
+ RSTRING(str2)->ptr += RSTRING(str2)->len - len;
+ RSTRING(str2)->len = len;
+ }
+ else {
+ str2 = rb_str_new5(str, RSTRING(str)->ptr+beg, len);
+ }
+ OBJ_INFECT(str2, str);
+
+ return str2;
+}
+
+VALUE
+rb_str_freeze(str)
+ VALUE str;
+{
+ return rb_obj_freeze(str);
+}
+
+VALUE
+rb_str_dup_frozen(str)
+ VALUE str;
+{
+ if (FL_TEST(str, ELTS_SHARED) && RSTRING(str)->aux.shared) {
+ VALUE shared = RSTRING(str)->aux.shared;
+ if (RSTRING(shared)->len == RSTRING(str)->len) {
+ OBJ_FREEZE(shared);
+ return shared;
+ }
+ }
+ if (OBJ_FROZEN(str)) return str;
+ str = rb_str_dup(str);
+ OBJ_FREEZE(str);
+ return str;
+}
+
+VALUE
+rb_str_locktmp(str)
+ VALUE str;
+{
+ if (FL_TEST(str, STR_TMPLOCK)) {
+ rb_raise(rb_eRuntimeError, "temporal locking already locked string");
+ }
+ FL_SET(str, STR_TMPLOCK);
+ return str;
+}
+
+VALUE
+rb_str_unlocktmp(str)
+ VALUE str;
+{
+ if (!FL_TEST(str, STR_TMPLOCK)) {
+ rb_raise(rb_eRuntimeError, "temporal unlocking already unlocked string");
+ }
+ FL_UNSET(str, STR_TMPLOCK);
+ return str;
+}
+
+VALUE
+rb_str_resize(str, len)
+ VALUE str;
+ long len;
+{
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative string size (or size too big)");
+ }
+
+ rb_str_modify(str);
+ if (len != RSTRING(str)->len) {
+ if (RSTRING(str)->len < len || RSTRING(str)->len - len > 1024) {
+ REALLOC_N(RSTRING(str)->ptr, char, len+1);
+ if (!FL_TEST(str, STR_NOCAPA)) {
+ RSTRING(str)->aux.capa = len;
+ }
+ }
+ RSTRING(str)->len = len;
+ RSTRING(str)->ptr[len] = '\0'; /* sentinel */
+ }
+ return str;
+}
+
+static VALUE
+str_buf_cat(str, ptr, len)
+ VALUE str;
+ const char *ptr;
+ long len;
+{
+ long capa, total, off = -1;;
+
+ rb_str_modify(str);
+ if (ptr >= RSTRING(str)->ptr && ptr <= RSTRING(str)->ptr + RSTRING(str)->len) {
+ off = ptr - RSTRING(str)->ptr;
+ }
+ if (len == 0) return 0;
+ if (FL_TEST(str, STR_ASSOC)) {
+ FL_UNSET(str, STR_ASSOC);
+ capa = RSTRING(str)->aux.capa = RSTRING(str)->len;
+ }
+ else {
+ capa = RSTRING(str)->aux.capa;
+ }
+ if (RSTRING(str)->len >= LONG_MAX - len) {
+ rb_raise(rb_eArgError, "string sizes too big");
+ }
+ total = RSTRING(str)->len+len;
+ if (capa <= total) {
+ while (total > capa) {
+ if (capa + 1 >= LONG_MAX / 2) {
+ capa = total;
+ break;
+ }
+ capa = (capa + 1) * 2;
+ }
+ RESIZE_CAPA(str, capa);
+ }
+ if (off != -1) {
+ ptr = RSTRING(str)->ptr + off;
+ }
+ memcpy(RSTRING(str)->ptr + RSTRING(str)->len, ptr, len);
+ RSTRING(str)->len = total;
+ RSTRING(str)->ptr[total] = '\0'; /* sentinel */
+
+ return str;
+}
+
+VALUE
+rb_str_buf_cat(str, ptr, len)
+ VALUE str;
+ const char *ptr;
+ long len;
+{
+ if (len == 0) return str;
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative string size (or size too big)");
+ }
+ return str_buf_cat(str, ptr, len);
+}
+
+VALUE
+rb_str_buf_cat2(str, ptr)
+ VALUE str;
+ const char *ptr;
+{
+ return rb_str_buf_cat(str, ptr, strlen(ptr));
+}
+
+VALUE
+rb_str_cat(str, ptr, len)
+ VALUE str;
+ const char *ptr;
+ long len;
+{
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative string size (or size too big)");
+ }
+ if (FL_TEST(str, STR_ASSOC)) {
+ rb_str_modify(str);
+ 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 */
+ return str;
+ }
+
+ return rb_str_buf_cat(str, ptr, len);
+}
+
+VALUE
+rb_str_cat2(str, ptr)
+ VALUE str;
+ const char *ptr;
+{
+ return rb_str_cat(str, ptr, strlen(ptr));
+}
+
+VALUE
+rb_str_buf_append(str, str2)
+ VALUE str, str2;
+{
+ str_buf_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ OBJ_INFECT(str, str2);
+ return str;
+}
+
+VALUE
+rb_str_append(str, str2)
+ VALUE str, str2;
+{
+ StringValue(str2);
+ rb_str_modify(str);
+ if (RSTRING(str2)->len > 0) {
+ if (FL_TEST(str, STR_ASSOC)) {
+ long len = RSTRING(str)->len+RSTRING(str2)->len;
+ REALLOC_N(RSTRING(str)->ptr, char, len+1);
+ memcpy(RSTRING(str)->ptr + RSTRING(str)->len,
+ RSTRING(str2)->ptr, RSTRING(str2)->len);
+ RSTRING(str)->ptr[len] = '\0'; /* sentinel */
+ RSTRING(str)->len = len;
+ }
+ else {
+ return rb_str_buf_append(str, str2);
+ }
+ }
+ OBJ_INFECT(str, str2);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str << fixnum => str
+ * str.concat(fixnum) => str
+ * str << obj => str
+ * str.concat(obj) => str
+ *
+ * Append---Concatenates the given object to <i>str</i>. If the object is a
+ * <code>Fixnum</code> between 0 and 255, it is converted to a character before
+ * concatenation.
+ *
+ * a = "hello "
+ * a << "world" #=> "hello world"
+ * a.concat(33) #=> "hello world!"
+ */
+
+VALUE
+rb_str_concat(str1, str2)
+ VALUE str1, str2;
+{
+ if (FIXNUM_P(str2)) {
+ int i = FIX2INT(str2);
+ if (0 <= i && i <= 0xff) { /* byte */
+ char c = i;
+ return rb_str_cat(str1, &c, 1);
+ }
+ }
+ str1 = rb_str_append(str1, str2);
+
+ return str1;
+}
+
+int
+rb_str_hash(str)
+ VALUE str;
+{
+ register long len = RSTRING(str)->len;
+ register char *p = RSTRING(str)->ptr;
+ register int key = 0;
+
+#if defined(HASH_ELFHASH)
+ register unsigned int g;
+
+ while (len--) {
+ key = (key << 4) + *p++;
+ if (g = key & 0xF0000000)
+ key ^= g >> 24;
+ key &= ~g;
+ }
+#elif defined(HASH_PERL)
+ while (len--) {
+ key += *p++;
+ key += (key << 10);
+ key ^= (key >> 6);
+ }
+ key += (key << 3);
+ key ^= (key >> 11);
+ key += (key << 15);
+#else
+ while (len--) {
+ key = key*65599 + *p;
+ p++;
+ }
+ key = key + (key>>5);
+#endif
+ return key;
+}
+
+/*
+ * call-seq:
+ * str.hash => fixnum
+ *
+ * Return a hash based on the string's length and content.
+ */
+
+static VALUE
+rb_str_hash_m(str)
+ VALUE str;
+{
+ int key = rb_str_hash(str);
+ return INT2FIX(key);
+}
+
+#define lesser(a,b) (((a)>(b))?(b):(a))
+
+int
+rb_str_cmp(str1, str2)
+ VALUE str1, str2;
+{
+ long len;
+ int retval;
+
+ len = lesser(RSTRING(str1)->len, RSTRING(str2)->len);
+ retval = rb_memcmp(RSTRING(str1)->ptr, RSTRING(str2)->ptr, len);
+ if (retval == 0) {
+ if (RSTRING(str1)->len == RSTRING(str2)->len) return 0;
+ if (RSTRING(str1)->len > RSTRING(str2)->len) return 1;
+ return -1;
+ }
+ if (retval > 0) return 1;
+ return -1;
+}
+
+
+/*
+ * call-seq:
+ * str == obj => true or false
+ *
+ * Equality---If <i>obj</i> is not a <code>String</code>, returns
+ * <code>false</code>. Otherwise, returns <code>true</code> if <i>str</i>
+ * <code><=></code> <i>obj</i> returns zero.
+ */
+
+static VALUE
+rb_str_equal(str1, str2)
+ VALUE str1, str2;
+{
+ if (str1 == str2) return Qtrue;
+ if (TYPE(str2) != T_STRING) {
+ if (!rb_respond_to(str2, rb_intern("to_str"))) {
+ return Qfalse;
+ }
+ return rb_equal(str2, str1);
+ }
+ if (RSTRING(str1)->len == RSTRING(str2)->len &&
+ rb_str_cmp(str1, str2) == 0) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
+
+/*
+ * call-seq:
+ * str.eql?(other) => true or false
+ *
+ * Two strings are equal if the have the same length and content.
+ */
+
+static VALUE
+rb_str_eql(str1, str2)
+ VALUE str1, str2;
+{
+ if (TYPE(str2) != T_STRING || RSTRING(str1)->len != RSTRING(str2)->len)
+ return Qfalse;
+
+ if (memcmp(RSTRING(str1)->ptr, RSTRING(str2)->ptr,
+ lesser(RSTRING(str1)->len, RSTRING(str2)->len)) == 0)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * str <=> other_str => -1, 0, +1
+ *
+ * Comparison---Returns -1 if <i>other_str</i> is less than, 0 if
+ * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than
+ * <i>str</i>. If the strings are of different lengths, and the strings are
+ * equal when compared up to the shortest length, then the longer string is
+ * considered greater than the shorter one. If the variable <code>$=</code> is
+ * <code>false</code>, the comparison is based on comparing the binary values
+ * of each character in the string. In older versions of Ruby, setting
+ * <code>$=</code> allowed case-insensitive comparisons; this is now deprecated
+ * in favor of using <code>String#casecmp</code>.
+ *
+ * <code><=></code> is the basis for the methods <code><</code>,
+ * <code><=</code>, <code>></code>, <code>>=</code>, and <code>between?</code>,
+ * included from module <code>Comparable</code>. The method
+ * <code>String#==</code> does not use <code>Comparable#==</code>.
+ *
+ * "abcdef" <=> "abcde" #=> 1
+ * "abcdef" <=> "abcdef" #=> 0
+ * "abcdef" <=> "abcdefg" #=> -1
+ * "abcdef" <=> "ABCDEF" #=> 1
+ */
+
+static VALUE
+rb_str_cmp_m(str1, str2)
+ VALUE str1, str2;
+{
+ long result;
+
+ if (TYPE(str2) != T_STRING) {
+ if (!rb_respond_to(str2, rb_intern("to_str"))) {
+ return Qnil;
+ }
+ else if (!rb_respond_to(str2, rb_intern("<=>"))) {
+ return Qnil;
+ }
+ else {
+ VALUE tmp = rb_funcall(str2, rb_intern("<=>"), 1, str1);
+
+ if (NIL_P(tmp)) return Qnil;
+ if (!FIXNUM_P(tmp)) {
+ return rb_funcall(LONG2FIX(0), '-', 1, tmp);
+ }
+ result = -FIX2LONG(tmp);
+ }
+ }
+ else {
+ result = rb_str_cmp(str1, str2);
+ }
+ return LONG2NUM(result);
+}
+
+/*
+ * call-seq:
+ * str.casecmp(other_str) => -1, 0, +1
+ *
+ * Case-insensitive version of <code>String#<=></code>.
+ *
+ * "abcdef".casecmp("abcde") #=> 1
+ * "aBcDeF".casecmp("abcdef") #=> 0
+ * "abcdef".casecmp("abcdefg") #=> -1
+ * "abcdef".casecmp("ABCDEF") #=> 0
+ */
+
+static VALUE
+rb_str_casecmp(str1, str2)
+ VALUE str1, str2;
+{
+ long len;
+ int retval;
+
+ StringValue(str2);
+ len = lesser(RSTRING(str1)->len, RSTRING(str2)->len);
+ retval = rb_memcicmp(RSTRING(str1)->ptr, RSTRING(str2)->ptr, len);
+ if (retval == 0) {
+ if (RSTRING(str1)->len == RSTRING(str2)->len) return INT2FIX(0);
+ if (RSTRING(str1)->len > RSTRING(str2)->len) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+ if (retval == 0) return INT2FIX(0);
+ if (retval > 0) return INT2FIX(1);
+ return INT2FIX(-1);
+}
+
+static long
+rb_str_index(str, sub, offset)
+ VALUE str, sub;
+ long offset;
+{
+ long pos;
+
+ if (offset < 0) {
+ offset += RSTRING(str)->len;
+ if (offset < 0) return -1;
+ }
+ if (RSTRING(str)->len - offset < RSTRING(sub)->len) return -1;
+ if (RSTRING(sub)->len == 0) return offset;
+ pos = rb_memsearch(RSTRING(sub)->ptr, RSTRING(sub)->len,
+ RSTRING(str)->ptr+offset, RSTRING(str)->len-offset);
+ if (pos < 0) return pos;
+ return pos + offset;
+}
+
+
+/*
+ * call-seq:
+ * str.index(substring [, offset]) => fixnum or nil
+ * str.index(fixnum [, offset]) => fixnum or nil
+ * str.index(regexp [, offset]) => fixnum or nil
+ *
+ * Returns the index of the first occurrence of the given <i>substring</i>,
+ * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
+ * <code>nil</code> if not found. If the second parameter is present, it
+ * specifies the position in the string to begin the search.
+ *
+ * "hello".index('e') #=> 1
+ * "hello".index('lo') #=> 3
+ * "hello".index('a') #=> nil
+ * "hello".index(101) #=> 1
+ * "hello".index(/[aeiou]/, -3) #=> 4
+ */
+
+static VALUE
+rb_str_index_m(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE sub;
+ VALUE initpos;
+ long pos;
+
+ if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) {
+ pos = NUM2LONG(initpos);
+ }
+ else {
+ pos = 0;
+ }
+ if (pos < 0) {
+ pos += RSTRING(str)->len;
+ if (pos < 0) {
+ if (TYPE(sub) == T_REGEXP) {
+ rb_backref_set(Qnil);
+ }
+ return Qnil;
+ }
+ }
+
+ switch (TYPE(sub)) {
+ case T_REGEXP:
+ pos = rb_reg_adjust_startpos(sub, str, pos, 0);
+ pos = rb_reg_search(sub, str, pos, 0);
+ break;
+
+ case T_FIXNUM:
+ {
+ int c = FIX2INT(sub);
+ long len = RSTRING(str)->len;
+ unsigned char *p = (unsigned char*)RSTRING(str)->ptr;
+
+ for (;pos<len;pos++) {
+ if (p[pos] == c) return LONG2NUM(pos);
+ }
+ return Qnil;
+ }
+
+ default: {
+ VALUE tmp;
+
+ tmp = rb_check_string_type(sub);
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eTypeError, "type mismatch: %s given",
+ rb_obj_classname(sub));
+ }
+ sub = tmp;
+ }
+ /* fall through */
+ case T_STRING:
+ pos = rb_str_index(str, sub, pos);
+ break;
+ }
+
+ if (pos == -1) return Qnil;
+ return LONG2NUM(pos);
+}
+
+static long
+rb_str_rindex(str, sub, pos)
+ VALUE str, sub;
+ long pos;
+{
+ long len = RSTRING(sub)->len;
+ char *s, *sbeg, *t;
+
+ /* substring longer than string */
+ if (RSTRING(str)->len < len) return -1;
+ if (RSTRING(str)->len - pos < len) {
+ pos = RSTRING(str)->len - len;
+ }
+ sbeg = RSTRING(str)->ptr;
+ s = RSTRING(str)->ptr + pos;
+ t = RSTRING(sub)->ptr;
+ if (len) {
+ while (sbeg <= s) {
+ if (rb_memcmp(s, t, len) == 0) {
+ return s - RSTRING(str)->ptr;
+ }
+ s--;
+ }
+ return -1;
+ }
+ else {
+ return pos;
+ }
+}
+
+
+/*
+ * call-seq:
+ * str.rindex(substring [, fixnum]) => fixnum or nil
+ * str.rindex(fixnum [, fixnum]) => fixnum or nil
+ * str.rindex(regexp [, fixnum]) => fixnum or nil
+ *
+ * Returns the index of the last occurrence of the given <i>substring</i>,
+ * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
+ * <code>nil</code> if not found. If the second parameter is present, it
+ * specifies the position in the string to end the search---characters beyond
+ * this point will not be considered.
+ *
+ * "hello".rindex('e') #=> 1
+ * "hello".rindex('l') #=> 3
+ * "hello".rindex('a') #=> nil
+ * "hello".rindex(101) #=> 1
+ * "hello".rindex(/[aeiou]/, -2) #=> 1
+ */
+
+static VALUE
+rb_str_rindex_m(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE sub;
+ VALUE position;
+ long pos;
+
+ if (rb_scan_args(argc, argv, "11", &sub, &position) == 2) {
+ pos = NUM2LONG(position);
+ if (pos < 0) {
+ pos += RSTRING(str)->len;
+ if (pos < 0) {
+ if (TYPE(sub) == T_REGEXP) {
+ rb_backref_set(Qnil);
+ }
+ return Qnil;
+ }
+ }
+ if (pos > RSTRING(str)->len) pos = RSTRING(str)->len;
+ }
+ else {
+ pos = RSTRING(str)->len;
+ }
+
+ switch (TYPE(sub)) {
+ case T_REGEXP:
+ if (RREGEXP(sub)->len) {
+ pos = rb_reg_adjust_startpos(sub, str, pos, 1);
+ pos = rb_reg_search(sub, str, pos, 1);
+ }
+ if (pos >= 0) return LONG2NUM(pos);
+ break;
+
+ case T_STRING:
+ pos = rb_str_rindex(str, sub, pos);
+ if (pos >= 0) return LONG2NUM(pos);
+ break;
+
+ case T_FIXNUM:
+ {
+ int c = FIX2INT(sub);
+ unsigned char *p = (unsigned char*)RSTRING(str)->ptr + pos;
+ unsigned char *pbeg = (unsigned char*)RSTRING(str)->ptr;
+
+ if (pos == RSTRING(str)->len) {
+ if (pos == 0) return Qnil;
+ --p;
+ }
+ while (pbeg <= p) {
+ if (*p == c) return LONG2NUM((char*)p - RSTRING(str)->ptr);
+ p--;
+ }
+ return Qnil;
+ }
+
+ default:
+ rb_raise(rb_eTypeError, "type mismatch: %s given",
+ rb_obj_classname(sub));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * str =~ obj => fixnum or nil
+ *
+ * Match---If <i>obj</i> is a <code>Regexp</code>, use it as a pattern to match
+ * against <i>str</i>,and returns the position the match starts, or
+ * <code>nil</code> if there is no match. Otherwise, invokes
+ * <i>obj.=~</i>, passing <i>str</i> as an argument. The default
+ * <code>=~</code> in <code>Object</code> returns <code>false</code>.
+ *
+ * "cat o' 9 tails" =~ /\d/ #=> 7
+ * "cat o' 9 tails" =~ 9 #=> false
+ */
+
+static VALUE
+rb_str_match(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_STRING:
+ rb_raise(rb_eTypeError, "type mismatch: String given");
+
+ case T_REGEXP:
+ return rb_reg_match(y, x);
+
+ default:
+ return rb_funcall(y, rb_intern("=~"), 1, x);
+ }
+}
+
+
+static VALUE get_pat _((VALUE, int));
+
+
+/*
+ * call-seq:
+ * str.match(pattern) => matchdata or nil
+ *
+ * Converts <i>pattern</i> to a <code>Regexp</code> (if it isn't already one),
+ * then invokes its <code>match</code> method on <i>str</i>.
+ *
+ * 'hello'.match('(.)\1') #=> #<MatchData:0x401b3d30>
+ * 'hello'.match('(.)\1')[0] #=> "ll"
+ * 'hello'.match(/(.)\1/)[0] #=> "ll"
+ * 'hello'.match('xx') #=> nil
+ */
+
+static VALUE
+rb_str_match_m(str, re)
+ VALUE str, re;
+{
+ return rb_funcall(get_pat(re, 0), rb_intern("match"), 1, str);
+}
+
+static char
+succ_char(s)
+ char *s;
+{
+ char c = *s;
+
+ /* numerics */
+ if ('0' <= c && c < '9') (*s)++;
+ else if (c == '9') {
+ *s = '0';
+ return '1';
+ }
+ /* small alphabets */
+ else if ('a' <= c && c < 'z') (*s)++;
+ else if (c == 'z') {
+ return *s = 'a';
+ }
+ /* capital alphabets */
+ else if ('A' <= c && c < 'Z') (*s)++;
+ else if (c == 'Z') {
+ return *s = 'A';
+ }
+ return 0;
+}
+
+
+/*
+ * call-seq:
+ * str.succ => new_str
+ * str.next => new_str
+ *
+ * Returns the successor to <i>str</i>. The successor is calculated by
+ * incrementing characters starting from the rightmost alphanumeric (or
+ * the rightmost character if there are no alphanumerics) in the
+ * string. Incrementing a digit always results in another digit, and
+ * incrementing a letter results in another letter of the same case.
+ * Incrementing nonalphanumerics uses the underlying character set's
+ * collating sequence.
+ *
+ * If the increment generates a ``carry,'' the character to the left of
+ * it is incremented. This process repeats until there is no carry,
+ * adding an additional character if necessary.
+ *
+ * "abcd".succ #=> "abce"
+ * "THX1138".succ #=> "THX1139"
+ * "<<koala>>".succ #=> "<<koalb>>"
+ * "1999zzz".succ #=> "2000aaa"
+ * "ZZZ9999".succ #=> "AAAA0000"
+ * "***".succ #=> "**+"
+ */
+
+static VALUE
+rb_str_succ(orig)
+ VALUE orig;
+{
+ VALUE str;
+ char *sbeg, *s;
+ int c = -1;
+ long n = 0;
+
+ str = rb_str_new5(orig, RSTRING(orig)->ptr, RSTRING(orig)->len);
+ OBJ_INFECT(str, orig);
+ if (RSTRING(str)->len == 0) return str;
+
+ sbeg = RSTRING(str)->ptr; s = sbeg + RSTRING(str)->len - 1;
+
+ while (sbeg <= s) {
+ if (ISALNUM(*s)) {
+ if ((c = succ_char(s)) == 0) break;
+ n = s - sbeg;
+ }
+ s--;
+ }
+ if (c == -1) { /* str contains no alnum */
+ sbeg = RSTRING(str)->ptr; s = sbeg + RSTRING(str)->len - 1;
+ c = '\001';
+ while (sbeg <= s) {
+ if ((*s += 1) != 0) break;
+ s--;
+ }
+ }
+ if (s < sbeg) {
+ RESIZE_CAPA(str, RSTRING(str)->len + 1);
+ s = RSTRING(str)->ptr + n;
+ memmove(s+1, s, RSTRING(str)->len - n);
+ *s = c;
+ RSTRING(str)->len += 1;
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ }
+
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.succ! => str
+ * str.next! => str
+ *
+ * Equivalent to <code>String#succ</code>, but modifies the receiver in
+ * place.
+ */
+
+static VALUE
+rb_str_succ_bang(str)
+ VALUE str;
+{
+ rb_str_shared_replace(str, rb_str_succ(str));
+
+ return str;
+}
+
+VALUE
+rb_str_upto(beg, end, excl)
+ VALUE beg, end;
+ int excl;
+{
+ VALUE current, after_end;
+ ID succ = rb_intern("succ");
+ int n;
+
+ StringValue(end);
+ n = rb_str_cmp(beg, end);
+ if (n > 0 || (excl && n == 0)) return beg;
+ after_end = rb_funcall(end, succ, 0, 0);
+ current = beg;
+ while (!rb_str_equal(current, after_end)) {
+ rb_yield(current);
+ if (!excl && rb_str_equal(current, end)) break;
+ current = rb_funcall(current, succ, 0, 0);
+ StringValue(current);
+ if (excl && rb_str_equal(current, end)) break;
+ StringValue(current);
+ if (RSTRING(current)->len > RSTRING(end)->len || RSTRING(current)->len == 0)
+ break;
+ }
+
+ return beg;
+}
+
+
+/*
+ * call-seq:
+ * str.upto(other_str) {|s| block } => str
+ *
+ * Iterates through successive values, starting at <i>str</i> and
+ * ending at <i>other_str</i> inclusive, passing each value in turn to
+ * the block. The <code>String#succ</code> method is used to generate
+ * each value.
+ *
+ * "a8".upto("b6") {|s| print s, ' ' }
+ * for s in "a8".."b6"
+ * print s, ' '
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * a8 a9 b0 b1 b2 b3 b4 b5 b6
+ * a8 a9 b0 b1 b2 b3 b4 b5 b6
+ */
+
+static VALUE
+rb_str_upto_m(beg, end)
+ VALUE beg, end;
+{
+ return rb_str_upto(beg, end, Qfalse);
+}
+
+static VALUE
+rb_str_subpat(str, re, nth)
+ VALUE str, re;
+ int nth;
+{
+ if (rb_reg_search(re, str, 0, 0) >= 0) {
+ return rb_reg_nth_match(nth, rb_backref_get());
+ }
+ return Qnil;
+}
+
+static VALUE
+rb_str_aref(str, indx)
+ VALUE str;
+ VALUE indx;
+{
+ long idx;
+
+ switch (TYPE(indx)) {
+ case T_FIXNUM:
+ idx = FIX2LONG(indx);
+
+ num_index:
+ if (idx < 0) {
+ idx = RSTRING(str)->len + idx;
+ }
+ if (idx < 0 || RSTRING(str)->len <= idx) {
+ return Qnil;
+ }
+ return INT2FIX(RSTRING(str)->ptr[idx] & 0xff);
+
+ case T_REGEXP:
+ return rb_str_subpat(str, indx, 0);
+
+ case T_STRING:
+ if (rb_str_index(str, indx, 0) != -1)
+ return rb_str_dup(indx);
+ return Qnil;
+
+ default:
+ /* check if indx is Range */
+ {
+ long beg, len;
+ VALUE tmp;
+
+ switch (rb_range_beg_len(indx, &beg, &len, RSTRING(str)->len, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ return Qnil;
+ default:
+ tmp = rb_str_substr(str, beg, len);
+ OBJ_INFECT(tmp, indx);
+ return tmp;
+ }
+ }
+ idx = NUM2LONG(indx);
+ goto num_index;
+ }
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * call-seq:
+ * str[fixnum] => fixnum or nil
+ * str[fixnum, fixnum] => new_str or nil
+ * str[range] => new_str or nil
+ * str[regexp] => new_str or nil
+ * str[regexp, fixnum] => new_str or nil
+ * str[other_str] => new_str or nil
+ * str.slice(fixnum) => fixnum or nil
+ * str.slice(fixnum, fixnum) => new_str or nil
+ * str.slice(range) => new_str or nil
+ * str.slice(regexp) => new_str or nil
+ * str.slice(regexp, fixnum) => new_str or nil
+ * str.slice(other_str) => new_str or nil
+ *
+ * Element Reference---If passed a single <code>Fixnum</code>, returns the code
+ * of the character at that position. If passed two <code>Fixnum</code>
+ * objects, returns a substring starting at the offset given by the first, and
+ * a length given by the second. If given a range, a substring containing
+ * characters at offsets given by the range is returned. In all three cases, if
+ * an offset is negative, it is counted from the end of <i>str</i>. Returns
+ * <code>nil</code> if the initial offset falls outside the string, the length
+ * is negative, or the beginning of the range is greater than the end.
+ *
+ * If a <code>Regexp</code> is supplied, the matching portion of <i>str</i> is
+ * returned. If a numeric parameter follows the regular expression, that
+ * component of the <code>MatchData</code> is returned instead. If a
+ * <code>String</code> is given, that string is returned if it occurs in
+ * <i>str</i>. In both cases, <code>nil</code> is returned if there is no
+ * match.
+ *
+ * a = "hello there"
+ * a[1] #=> 101
+ * a[1,3] #=> "ell"
+ * a[1..3] #=> "ell"
+ * a[-3,2] #=> "er"
+ * a[-4..-2] #=> "her"
+ * a[12..-1] #=> nil
+ * a[-2..-4] #=> ""
+ * a[/[aeiou](.)\1/] #=> "ell"
+ * a[/[aeiou](.)\1/, 0] #=> "ell"
+ * a[/[aeiou](.)\1/, 1] #=> "l"
+ * a[/[aeiou](.)\1/, 2] #=> nil
+ * a["lo"] #=> "lo"
+ * a["bye"] #=> nil
+ */
+
+static VALUE
+rb_str_aref_m(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ if (argc == 2) {
+ if (TYPE(argv[0]) == T_REGEXP) {
+ return rb_str_subpat(str, argv[0], NUM2INT(argv[1]));
+ }
+ return rb_str_substr(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]));
+ }
+ if (argc != 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ }
+ return rb_str_aref(str, argv[0]);
+}
+
+static void
+rb_str_splice(str, beg, len, val)
+ VALUE str;
+ long beg, len;
+ VALUE val;
+{
+ if (len < 0) rb_raise(rb_eIndexError, "negative length %ld", len);
+
+ StringValue(val);
+ rb_str_modify(str);
+
+ if (RSTRING(str)->len < beg) {
+ out_of_range:
+ rb_raise(rb_eIndexError, "index %ld out of string", beg);
+ }
+ if (beg < 0) {
+ if (-beg > RSTRING(str)->len) {
+ goto out_of_range;
+ }
+ beg += RSTRING(str)->len;
+ }
+ if (RSTRING(str)->len < len || RSTRING(str)->len < beg + len) {
+ len = RSTRING(str)->len - beg;
+ }
+
+ if (len < RSTRING(val)->len) {
+ /* expand string */
+ RESIZE_CAPA(str, RSTRING(str)->len + RSTRING(val)->len - len + 1);
+ }
+
+ if (RSTRING(val)->len != len) {
+ memmove(RSTRING(str)->ptr + beg + RSTRING(val)->len,
+ RSTRING(str)->ptr + beg + len,
+ RSTRING(str)->len - (beg + len));
+ }
+ if (RSTRING(str)->len < beg && len < 0) {
+ MEMZERO(RSTRING(str)->ptr + RSTRING(str)->len, char, -len);
+ }
+ if (RSTRING(val)->len > 0) {
+ memmove(RSTRING(str)->ptr+beg, RSTRING(val)->ptr, RSTRING(val)->len);
+ }
+ RSTRING(str)->len += RSTRING(val)->len - len;
+ if (RSTRING(str)->ptr) {
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ }
+ OBJ_INFECT(str, val);
+}
+
+void
+rb_str_update(str, beg, len, val)
+ VALUE str;
+ long beg, len;
+ VALUE val;
+{
+ rb_str_splice(str, beg, len, val);
+}
+
+static void
+rb_str_subpat_set(str, re, nth, val)
+ VALUE str, re;
+ int nth;
+ VALUE val;
+{
+ VALUE match;
+ long start, end, len;
+
+ if (rb_reg_search(re, str, 0, 0) < 0) {
+ rb_raise(rb_eIndexError, "regexp not matched");
+ }
+ match = rb_backref_get();
+ if (nth >= RMATCH(match)->regs->num_regs) {
+ out_of_range:
+ rb_raise(rb_eIndexError, "index %d out of regexp", nth);
+ }
+ if (nth < 0) {
+ if (-nth >= RMATCH(match)->regs->num_regs) {
+ goto out_of_range;
+ }
+ nth += RMATCH(match)->regs->num_regs;
+ }
+
+ start = RMATCH(match)->BEG(nth);
+ if (start == -1) {
+ rb_raise(rb_eIndexError, "regexp group %d not matched", nth);
+ }
+ end = RMATCH(match)->END(nth);
+ len = end - start;
+ rb_str_splice(str, start, len, val);
+}
+
+static VALUE
+rb_str_aset(str, indx, val)
+ VALUE str;
+ VALUE indx, val;
+{
+ long idx, beg;
+
+ switch (TYPE(indx)) {
+ case T_FIXNUM:
+ idx = FIX2LONG(indx);
+ num_index:
+ if (RSTRING(str)->len <= idx) {
+ out_of_range:
+ rb_raise(rb_eIndexError, "index %ld out of string", idx);
+ }
+ if (idx < 0) {
+ if (-idx > RSTRING(str)->len)
+ goto out_of_range;
+ idx += RSTRING(str)->len;
+ }
+ if (FIXNUM_P(val)) {
+ rb_str_modify(str);
+ if (RSTRING(str)->len == idx) {
+ RSTRING(str)->len += 1;
+ RESIZE_CAPA(str, RSTRING(str)->len);
+ }
+ RSTRING(str)->ptr[idx] = FIX2INT(val) & 0xff;
+ }
+ else {
+ rb_str_splice(str, idx, 1, val);
+ }
+ return val;
+
+ case T_REGEXP:
+ rb_str_subpat_set(str, indx, 0, val);
+ return val;
+
+ case T_STRING:
+ beg = rb_str_index(str, indx, 0);
+ if (beg < 0) {
+ rb_raise(rb_eIndexError, "string not matched");
+ }
+ rb_str_splice(str, beg, RSTRING(indx)->len, val);
+ return val;
+
+ default:
+ /* check if indx is Range */
+ {
+ long beg, len;
+ if (rb_range_beg_len(indx, &beg, &len, RSTRING(str)->len, 2)) {
+ rb_str_splice(str, beg, len, val);
+ return val;
+ }
+ }
+ idx = NUM2LONG(indx);
+ goto num_index;
+ }
+}
+
+/*
+ * call-seq:
+ * str[fixnum] = fixnum
+ * str[fixnum] = new_str
+ * str[fixnum, fixnum] = new_str
+ * str[range] = aString
+ * str[regexp] = new_str
+ * str[regexp, fixnum] = new_str
+ * str[other_str] = new_str
+ *
+ * Element Assignment---Replaces some or all of the content of <i>str</i>. The
+ * portion of the string affected is determined using the same criteria as
+ * <code>String#[]</code>. If the replacement string is not the same length as
+ * the text it is replacing, the string will be adjusted accordingly. If the
+ * regular expression or string is used as the index doesn't match a position
+ * in the string, <code>IndexError</code> is raised. If the regular expression
+ * form is used, the optional second <code>Fixnum</code> allows you to specify
+ * which portion of the match to replace (effectively using the
+ * <code>MatchData</code> indexing rules. The forms that take a
+ * <code>Fixnum</code> will raise an <code>IndexError</code> if the value is
+ * out of range; the <code>Range</code> form will raise a
+ * <code>RangeError</code>, and the <code>Regexp</code> and <code>String</code>
+ * forms will silently ignore the assignment.
+ */
+
+static VALUE
+rb_str_aset_m(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ if (argc == 3) {
+ if (TYPE(argv[0]) == T_REGEXP) {
+ rb_str_subpat_set(str, argv[0], NUM2INT(argv[1]), argv[2]);
+ }
+ else {
+ rb_str_splice(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
+ }
+ return argv[2];
+ }
+ if (argc != 2) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+ }
+ return rb_str_aset(str, argv[0], argv[1]);
+}
+
+/*
+ * call-seq:
+ * str.insert(index, other_str) => str
+ *
+ * Inserts <i>other_str</i> before the character at the given
+ * <i>index</i>, modifying <i>str</i>. Negative indices count from the
+ * end of the string, and insert <em>after</em> the given character.
+ * The intent is insert <i>aString</i> so that it starts at the given
+ * <i>index</i>.
+ *
+ * "abcd".insert(0, 'X') #=> "Xabcd"
+ * "abcd".insert(3, 'X') #=> "abcXd"
+ * "abcd".insert(4, 'X') #=> "abcdX"
+ * "abcd".insert(-3, 'X') #=> "abXcd"
+ * "abcd".insert(-1, 'X') #=> "abcdX"
+ */
+
+static VALUE
+rb_str_insert(str, idx, str2)
+ VALUE str, idx, str2;
+{
+ long pos = NUM2LONG(idx);
+
+ if (pos == -1) {
+ pos = RSTRING(str)->len;
+ }
+ else if (pos < 0) {
+ pos++;
+ }
+ rb_str_splice(str, pos, 0, str2);
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.slice!(fixnum) => fixnum or nil
+ * str.slice!(fixnum, fixnum) => new_str or nil
+ * str.slice!(range) => new_str or nil
+ * str.slice!(regexp) => new_str or nil
+ * str.slice!(other_str) => new_str or nil
+ *
+ * Deletes the specified portion from <i>str</i>, and returns the portion
+ * deleted. The forms that take a <code>Fixnum</code> will raise an
+ * <code>IndexError</code> if the value is out of range; the <code>Range</code>
+ * form will raise a <code>RangeError</code>, and the <code>Regexp</code> and
+ * <code>String</code> forms will silently ignore the assignment.
+ *
+ * string = "this is a string"
+ * string.slice!(2) #=> 105
+ * string.slice!(3..6) #=> " is "
+ * string.slice!(/s.*t/) #=> "sa st"
+ * string.slice!("r") #=> "r"
+ * string #=> "thing"
+ */
+
+static VALUE
+rb_str_slice_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE result;
+ VALUE buf[3];
+ int i;
+
+ if (argc < 1 || 2 < argc) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ }
+ for (i=0; i<argc; i++) {
+ buf[i] = argv[i];
+ }
+ buf[i] = rb_str_new(0,0);
+ result = rb_str_aref_m(argc, buf, str);
+ if (!NIL_P(result)) {
+ rb_str_aset_m(argc+1, buf, str);
+ }
+ return result;
+}
+
+static VALUE
+get_pat(pat, quote)
+ VALUE pat;
+ int quote;
+{
+ VALUE val;
+
+ switch (TYPE(pat)) {
+ case T_REGEXP:
+ return pat;
+
+ case T_STRING:
+ break;
+
+ default:
+ val = rb_check_string_type(pat);
+ if (NIL_P(val)) {
+ Check_Type(pat, T_REGEXP);
+ }
+ pat = val;
+ }
+
+ if (quote) {
+ pat = rb_reg_quote(pat);
+ }
+
+ return rb_reg_regcomp(pat);
+}
+
+
+/*
+ * call-seq:
+ * str.sub!(pattern, replacement) => str or nil
+ * str.sub!(pattern) {|match| block } => str or nil
+ *
+ * Performs the substitutions of <code>String#sub</code> in place,
+ * returning <i>str</i>, or <code>nil</code> if no substitutions were
+ * performed.
+ */
+
+static VALUE
+rb_str_sub_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE pat, repl, match;
+ struct re_registers *regs;
+ int iter = 0;
+ int tainted = 0;
+ long plen;
+
+ if (argc == 1 && rb_block_given_p()) {
+ iter = 1;
+ }
+ else if (argc == 2) {
+ repl = argv[1];
+ StringValue(repl);
+ if (OBJ_TAINTED(repl)) tainted = 1;
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+ }
+
+ pat = get_pat(argv[0], 1);
+ if (rb_reg_search(pat, str, 0, 0) >= 0) {
+ match = rb_backref_get();
+ regs = RMATCH(match)->regs;
+
+ if (iter) {
+ char *p = RSTRING(str)->ptr; long len = RSTRING(str)->len;
+
+ rb_match_busy(match);
+ repl = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match)));
+ str_mod_check(str, p, len);
+ str_frozen_check(str);
+ rb_backref_set(match);
+ }
+ else {
+ repl = rb_reg_regsub(repl, str, regs);
+ }
+ rb_str_modify(str);
+ if (OBJ_TAINTED(repl)) tainted = 1;
+ plen = END(0) - BEG(0);
+ if (RSTRING(repl)->len > plen) {
+ RESIZE_CAPA(str, RSTRING(str)->len + RSTRING(repl)->len - plen);
+ }
+ if (RSTRING(repl)->len != plen) {
+ memmove(RSTRING(str)->ptr + BEG(0) + RSTRING(repl)->len,
+ RSTRING(str)->ptr + BEG(0) + plen,
+ RSTRING(str)->len - BEG(0) - plen);
+ }
+ memcpy(RSTRING(str)->ptr + BEG(0),
+ RSTRING(repl)->ptr, RSTRING(repl)->len);
+ RSTRING(str)->len += RSTRING(repl)->len - plen;
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ if (tainted) OBJ_TAINT(str);
+
+ return str;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.sub(pattern, replacement) => new_str
+ * str.sub(pattern) {|match| block } => new_str
+ *
+ * Returns a copy of <i>str</i> with the <em>first</em> occurrence of
+ * <i>pattern</i> replaced with either <i>replacement</i> or the value of the
+ * block. The <i>pattern</i> will typically be a <code>Regexp</code>; if it is
+ * a <code>String</code> then no regular expression metacharacters will be
+ * interpreted (that is <code>/\d/</code> will match a digit, but
+ * <code>'\d'</code> will match a backslash followed by a 'd').
+ *
+ * If the method call specifies <i>replacement</i>, special variables such as
+ * <code>$&</code> will not be useful, as substitution into the string occurs
+ * before the pattern match starts. However, the sequences <code>\1</code>,
+ * <code>\2</code>, etc., may be used.
+ *
+ * In the block form, the current match string is passed in as a parameter, and
+ * variables such as <code>$1</code>, <code>$2</code>, <code>$`</code>,
+ * <code>$&</code>, and <code>$'</code> will be set appropriately. The value
+ * returned by the block will be substituted for the match on each call.
+ *
+ * The result inherits any tainting in the original string or any supplied
+ * replacement string.
+ *
+ * "hello".sub(/[aeiou]/, '*') #=> "h*llo"
+ * "hello".sub(/([aeiou])/, '<\1>') #=> "h<e>llo"
+ * "hello".sub(/./) {|s| s[0].to_s + ' ' } #=> "104 ello"
+ */
+
+static VALUE
+rb_str_sub(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_sub_bang(argc, argv, str);
+ return str;
+}
+
+static VALUE
+str_gsub(argc, argv, str, bang)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+ int bang;
+{
+ VALUE pat, val, repl, match, dest;
+ struct re_registers *regs;
+ long beg, n;
+ long offset, blen, slen, len;
+ int iter = 0;
+ char *buf, *bp, *sp, *cp;
+ int tainted = 0;
+
+ if (argc == 1 && rb_block_given_p()) {
+ iter = 1;
+ }
+ else if (argc == 2) {
+ repl = argv[1];
+ StringValue(repl);
+ if (OBJ_TAINTED(repl)) tainted = 1;
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+ }
+
+ pat = get_pat(argv[0], 1);
+ offset=0; n=0;
+ beg = rb_reg_search(pat, str, 0, 0);
+ if (beg < 0) {
+ if (bang) return Qnil; /* no match, no substitution */
+ return rb_str_dup(str);
+ }
+
+ blen = RSTRING(str)->len + 30; /* len + margin */
+ dest = str_new(0, 0, blen);
+ buf = RSTRING(dest)->ptr;
+ bp = buf;
+ sp = cp = RSTRING(str)->ptr;
+ slen = RSTRING(str)->len;
+
+ rb_str_locktmp(dest);
+ while (beg >= 0) {
+ n++;
+ match = rb_backref_get();
+ regs = RMATCH(match)->regs;
+ if (iter) {
+ rb_match_busy(match);
+ val = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match)));
+ str_mod_check(str, sp, slen);
+ if (bang) str_frozen_check(str);
+ if (val == dest) { /* paranoid chack [ruby-dev:24827] */
+ rb_raise(rb_eRuntimeError, "block should not cheat");
+ }
+ rb_backref_set(match);
+ }
+ else {
+ val = rb_reg_regsub(repl, str, regs);
+ }
+ if (OBJ_TAINTED(val)) tainted = 1;
+ len = (bp - buf) + (beg - offset) + RSTRING(val)->len + 3;
+ if (blen < len) {
+ while (blen < len) blen *= 2;
+ len = bp - buf;
+ RESIZE_CAPA(dest, blen);
+ RSTRING(dest)->len = blen;
+ buf = RSTRING(dest)->ptr;
+ bp = buf + len;
+ }
+ len = beg - offset; /* copy pre-match substr */
+ memcpy(bp, cp, len);
+ bp += len;
+ memcpy(bp, RSTRING(val)->ptr, RSTRING(val)->len);
+ bp += RSTRING(val)->len;
+ offset = END(0);
+ if (BEG(0) == END(0)) {
+ /*
+ * Always consume at least one character of the input string
+ * in order to prevent infinite loops.
+ */
+ if (RSTRING(str)->len <= END(0)) break;
+ len = mbclen2(RSTRING(str)->ptr[END(0)], pat);
+ memcpy(bp, RSTRING(str)->ptr+END(0), len);
+ bp += len;
+ offset = END(0) + len;
+ }
+ cp = RSTRING(str)->ptr + offset;
+ if (offset > RSTRING(str)->len) break;
+ beg = rb_reg_search(pat, str, offset, 0);
+ }
+ if (RSTRING(str)->len > offset) {
+ len = bp - buf;
+ if (blen - len < RSTRING(str)->len - offset) {
+ blen = len + RSTRING(str)->len - offset;
+ RESIZE_CAPA(dest, blen);
+ buf = RSTRING(dest)->ptr;
+ bp = buf + len;
+ }
+ memcpy(bp, cp, RSTRING(str)->len - offset);
+ bp += RSTRING(str)->len - offset;
+ }
+ rb_backref_set(match);
+ *bp = '\0';
+ rb_str_unlocktmp(dest);
+ if (bang) {
+ if (str_independent(str)) {
+ free(RSTRING(str)->ptr);
+ }
+ FL_UNSET(str, STR_NOCAPA);
+ RSTRING(str)->ptr = buf;
+ RSTRING(str)->aux.capa = blen;
+ RSTRING(dest)->ptr = 0;
+ RSTRING(dest)->len = 0;
+ }
+ else {
+ RBASIC(dest)->klass = rb_obj_class(str);
+ OBJ_INFECT(dest, str);
+ str = dest;
+ }
+ RSTRING(str)->len = bp - buf;
+
+ if (tainted) OBJ_TAINT(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.gsub!(pattern, replacement) => str or nil
+ * str.gsub!(pattern) {|match| block } => str or nil
+ *
+ * Performs the substitutions of <code>String#gsub</code> in place, returning
+ * <i>str</i>, or <code>nil</code> if no substitutions were performed.
+ */
+
+static VALUE
+rb_str_gsub_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ return str_gsub(argc, argv, str, 1);
+}
+
+
+/*
+ * call-seq:
+ * str.gsub(pattern, replacement) => new_str
+ * str.gsub(pattern) {|match| block } => new_str
+ *
+ * Returns a copy of <i>str</i> with <em>all</em> occurrences of <i>pattern</i>
+ * replaced with either <i>replacement</i> or the value of the block. The
+ * <i>pattern</i> will typically be a <code>Regexp</code>; if it is a
+ * <code>String</code> then no regular expression metacharacters will be
+ * interpreted (that is <code>/\d/</code> will match a digit, but
+ * <code>'\d'</code> will match a backslash followed by a 'd').
+ *
+ * If a string is used as the replacement, special variables from the match
+ * (such as <code>$&</code> and <code>$1</code>) cannot be substituted into it,
+ * as substitution into the string occurs before the pattern match
+ * starts. However, the sequences <code>\1</code>, <code>\2</code>, and so on
+ * may be used to interpolate successive groups in the match.
+ *
+ * In the block form, the current match string is passed in as a parameter, and
+ * variables such as <code>$1</code>, <code>$2</code>, <code>$`</code>,
+ * <code>$&</code>, and <code>$'</code> will be set appropriately. The value
+ * returned by the block will be substituted for the match on each call.
+ *
+ * The result inherits any tainting in the original string or any supplied
+ * replacement string.
+ *
+ * "hello".gsub(/[aeiou]/, '*') #=> "h*ll*"
+ * "hello".gsub(/([aeiou])/, '<\1>') #=> "h<e>ll<o>"
+ * "hello".gsub(/./) {|s| s[0].to_s + ' '} #=> "104 101 108 108 111 "
+ */
+
+static VALUE
+rb_str_gsub(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ return str_gsub(argc, argv, str, 0);
+}
+
+
+/*
+ * call-seq:
+ * str.replace(other_str) => str
+ *
+ * Replaces the contents and taintedness of <i>str</i> with the corresponding
+ * values in <i>other_str</i>.
+ *
+ * s = "hello" #=> "hello"
+ * s.replace "world" #=> "world"
+ */
+
+static VALUE
+rb_str_replace(str, str2)
+ VALUE str, str2;
+{
+ if (str == str2) return str;
+
+ StringValue(str2);
+ if (FL_TEST(str2, ELTS_SHARED)) {
+ if (str_independent(str)) {
+ free(RSTRING(str)->ptr);
+ }
+ RSTRING(str)->len = RSTRING(str2)->len;
+ RSTRING(str)->ptr = RSTRING(str2)->ptr;
+ FL_SET(str, ELTS_SHARED);
+ FL_UNSET(str, STR_ASSOC);
+ RSTRING(str)->aux.shared = RSTRING(str2)->aux.shared;
+ }
+ else {
+ if (str_independent(str)) {
+ rb_str_resize(str, RSTRING(str2)->len);
+ memcpy(RSTRING(str)->ptr, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ if (!RSTRING(str)->ptr) {
+ make_null_str(str);
+ }
+ }
+ else {
+ RSTRING(str)->ptr = RSTRING(str2)->ptr;
+ RSTRING(str)->len = RSTRING(str2)->len;
+ str_make_independent(str);
+ }
+ if (FL_TEST(str2, STR_ASSOC)) {
+ FL_SET(str, STR_ASSOC);
+ RSTRING(str)->aux.shared = RSTRING(str2)->aux.shared;
+ }
+ }
+
+ OBJ_INFECT(str, str2);
+ return str;
+}
+
+static VALUE
+uscore_get()
+{
+ VALUE line;
+
+ line = rb_lastline_get();
+ if (TYPE(line) != T_STRING) {
+ rb_raise(rb_eTypeError, "$_ value need to be String (%s given)",
+ NIL_P(line) ? "nil" : rb_obj_classname(line));
+ }
+ return line;
+}
+
+/*
+ * call-seq:
+ * sub!(pattern, replacement) => $_ or nil
+ * sub!(pattern) {|...| block } => $_ or nil
+ *
+ * Equivalent to <code>$_.sub!(<i>args</i>)</code>.
+ */
+
+static VALUE
+rb_f_sub_bang(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_str_sub_bang(argc, argv, uscore_get());
+}
+
+/*
+ * call-seq:
+ * sub(pattern, replacement) => $_
+ * sub(pattern) { block } => $_
+ *
+ * Equivalent to <code>$_.sub(<i>args</i>)</code>, except that
+ * <code>$_</code> will be updated if substitution occurs.
+ */
+
+static VALUE
+rb_f_sub(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE str = rb_str_dup(uscore_get());
+
+ if (NIL_P(rb_str_sub_bang(argc, argv, str)))
+ return str;
+ rb_lastline_set(str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * gsub!(pattern, replacement) => string or nil
+ * gsub!(pattern) {|...| block } => string or nil
+ *
+ * Equivalent to <code>Kernel::gsub</code>, except <code>nil</code> is
+ * returned if <code>$_</code> is not modified.
+ *
+ * $_ = "quick brown fox"
+ * gsub! /cat/, '*' #=> nil
+ * $_ #=> "quick brown fox"
+ */
+
+static VALUE
+rb_f_gsub_bang(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_str_gsub_bang(argc, argv, uscore_get());
+}
+
+/*
+ * call-seq:
+ * gsub(pattern, replacement) => string
+ * gsub(pattern) {|...| block } => string
+ *
+ * Equivalent to <code>$_.gsub...</code>, except that <code>$_</code>
+ * receives the modified result.
+ *
+ * $_ = "quick brown fox"
+ * gsub /[aeiou]/, '*' #=> "q**ck br*wn f*x"
+ * $_ #=> "q**ck br*wn f*x"
+ */
+
+static VALUE
+rb_f_gsub(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE str = rb_str_dup(uscore_get());
+
+ if (NIL_P(rb_str_gsub_bang(argc, argv, str)))
+ return str;
+ rb_lastline_set(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.reverse! => str
+ *
+ * Reverses <i>str</i> in place.
+ */
+
+static VALUE
+rb_str_reverse_bang(str)
+ VALUE str;
+{
+ char *s, *e;
+ char c;
+
+ if (RSTRING(str)->len > 1) {
+ rb_str_modify(str);
+ s = RSTRING(str)->ptr;
+ e = s + RSTRING(str)->len - 1;
+ while (s < e) {
+ c = *s;
+ *s++ = *e;
+ *e-- = c;
+ }
+ }
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.reverse => new_str
+ *
+ * Returns a new string with the characters from <i>str</i> in reverse order.
+ *
+ * "stressed".reverse #=> "desserts"
+ */
+
+static VALUE
+rb_str_reverse(str)
+ VALUE str;
+{
+ VALUE obj;
+ char *s, *e, *p;
+
+ if (RSTRING(str)->len <= 1) return rb_str_dup(str);
+
+ obj = rb_str_new5(str, 0, RSTRING(str)->len);
+ s = RSTRING(str)->ptr; e = s + RSTRING(str)->len - 1;
+ p = RSTRING(obj)->ptr;
+
+ while (e >= s) {
+ *p++ = *e--;
+ }
+ OBJ_INFECT(obj, str);
+
+ return obj;
+}
+
+
+/*
+ * call-seq:
+ * str.include? other_str => true or false
+ * str.include? fixnum => true or false
+ *
+ * Returns <code>true</code> if <i>str</i> contains the given string or
+ * character.
+ *
+ * "hello".include? "lo" #=> true
+ * "hello".include? "ol" #=> false
+ * "hello".include? ?h #=> true
+ */
+
+static VALUE
+rb_str_include(str, arg)
+ VALUE str, arg;
+{
+ long i;
+
+ if (FIXNUM_P(arg)) {
+ if (memchr(RSTRING(str)->ptr, FIX2INT(arg), RSTRING(str)->len))
+ return Qtrue;
+ return Qfalse;
+ }
+
+ StringValue(arg);
+ i = rb_str_index(str, arg, 0);
+
+ if (i == -1) return Qfalse;
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * str.to_i(base=10) => integer
+ *
+ * Returns the result of interpreting leading characters in <i>str</i> as an
+ * integer base <i>base</i> (2, 8, 10, or 16). Extraneous characters past the
+ * end of a valid number are ignored. If there is not a valid number at the
+ * start of <i>str</i>, <code>0</code> is returned. This method never raises an
+ * exception.
+ *
+ * "12345".to_i #=> 12345
+ * "99 red balloons".to_i #=> 99
+ * "0a".to_i #=> 0
+ * "0a".to_i(16) #=> 10
+ * "hello".to_i #=> 0
+ * "1100101".to_i(2) #=> 101
+ * "1100101".to_i(8) #=> 294977
+ * "1100101".to_i(10) #=> 1100101
+ * "1100101".to_i(16) #=> 17826049
+ */
+
+static VALUE
+rb_str_to_i(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE b;
+ int base;
+
+ rb_scan_args(argc, argv, "01", &b);
+ if (argc == 0) base = 10;
+ else base = NUM2INT(b);
+
+ if (base < 0) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ return rb_str_to_inum(str, base, Qfalse);
+}
+
+
+/*
+ * call-seq:
+ * str.to_f => float
+ *
+ * Returns the result of interpreting leading characters in <i>str</i> as a
+ * floating point number. Extraneous characters past the end of a valid number
+ * are ignored. If there is not a valid number at the start of <i>str</i>,
+ * <code>0.0</code> is returned. This method never raises an exception.
+ *
+ * "123.45e1".to_f #=> 1234.5
+ * "45.67 degrees".to_f #=> 45.67
+ * "thx1138".to_f #=> 0.0
+ */
+
+static VALUE
+rb_str_to_f(str)
+ VALUE str;
+{
+ return rb_float_new(rb_str_to_dbl(str, Qfalse));
+}
+
+
+/*
+ * call-seq:
+ * str.to_s => str
+ * str.to_str => str
+ *
+ * Returns the receiver.
+ */
+
+static VALUE
+rb_str_to_s(str)
+ VALUE str;
+{
+ if (rb_obj_class(str) != rb_cString) {
+ VALUE dup = str_alloc(rb_cString);
+ rb_str_replace(dup, str);
+ return dup;
+ }
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.inspect => string
+ *
+ * Returns a printable version of _str_, with special characters
+ * escaped.
+ *
+ * str = "hello"
+ * str[3] = 8
+ * str.inspect #=> "hel\010o"
+ */
+
+VALUE
+rb_str_inspect(str)
+ VALUE str;
+{
+ char *p, *pend;
+ VALUE result = rb_str_buf_new2("\"");
+ char s[5];
+
+ p = RSTRING(str)->ptr; pend = p + RSTRING(str)->len;
+ while (p < pend) {
+ char c = *p++;
+ if (ismbchar(c) && p < pend) {
+ int len = mbclen(c);
+ rb_str_buf_cat(result, p - 1, len);
+ p += len - 1;
+ }
+ else if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
+ s[0] = '\\'; s[1] = c;
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (ISPRINT(c)) {
+ s[0] = c;
+ rb_str_buf_cat(result, s, 1);
+ }
+ else if (c == '\n') {
+ s[0] = '\\'; s[1] = 'n';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\r') {
+ s[0] = '\\'; s[1] = 'r';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\t') {
+ s[0] = '\\'; s[1] = 't';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\f') {
+ s[0] = '\\'; s[1] = 'f';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\013') {
+ s[0] = '\\'; s[1] = 'v';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\010') {
+ s[0] = '\\'; s[1] = 'b';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == '\007') {
+ s[0] = '\\'; s[1] = 'a';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else if (c == 033) {
+ s[0] = '\\'; s[1] = 'e';
+ rb_str_buf_cat(result, s, 2);
+ }
+ else {
+ sprintf(s, "\\%03o", c & 0377);
+ rb_str_buf_cat2(result, s);
+ }
+ }
+ rb_str_buf_cat2(result, "\"");
+
+ OBJ_INFECT(result, str);
+ return result;
+}
+
+
+/*
+ * call-seq:
+ * str.dump => new_str
+ *
+ * Produces a version of <i>str</i> with all nonprinting characters replaced by
+ * <code>\nnn</code> notation and all special characters escaped.
+ */
+
+VALUE
+rb_str_dump(str)
+ VALUE str;
+{
+ long len;
+ char *p, *pend;
+ char *q, *qend;
+ VALUE result;
+
+ len = 2; /* "" */
+ p = RSTRING(str)->ptr; pend = p + RSTRING(str)->len;
+ while (p < pend) {
+ char c = *p++;
+ switch (c) {
+ case '"': case '\\':
+ case '\n': case '\r':
+ case '\t': case '\f':
+ case '\013': case '\010': case '\007': case '\033':
+ len += 2;
+ break;
+
+ case '#':
+ len += IS_EVSTR(p, pend) ? 2 : 1;
+ break;
+
+ default:
+ if (ISPRINT(c)) {
+ len++;
+ }
+ else {
+ len += 4; /* \nnn */
+ }
+ break;
+ }
+ }
+
+ result = rb_str_new5(str, 0, len);
+ p = RSTRING(str)->ptr; pend = p + RSTRING(str)->len;
+ q = RSTRING(result)->ptr; qend = q + len;
+
+ *q++ = '"';
+ while (p < pend) {
+ char c = *p++;
+
+ if (c == '"' || c == '\\') {
+ *q++ = '\\';
+ *q++ = c;
+ }
+ else if (c == '#') {
+ if (IS_EVSTR(p, pend)) *q++ = '\\';
+ *q++ = '#';
+ }
+ else if (ISPRINT(c)) {
+ *q++ = c;
+ }
+ else if (c == '\n') {
+ *q++ = '\\';
+ *q++ = 'n';
+ }
+ else if (c == '\r') {
+ *q++ = '\\';
+ *q++ = 'r';
+ }
+ else if (c == '\t') {
+ *q++ = '\\';
+ *q++ = 't';
+ }
+ else if (c == '\f') {
+ *q++ = '\\';
+ *q++ = 'f';
+ }
+ else if (c == '\013') {
+ *q++ = '\\';
+ *q++ = 'v';
+ }
+ else if (c == '\010') {
+ *q++ = '\\';
+ *q++ = 'b';
+ }
+ else if (c == '\007') {
+ *q++ = '\\';
+ *q++ = 'a';
+ }
+ else if (c == '\033') {
+ *q++ = '\\';
+ *q++ = 'e';
+ }
+ else {
+ *q++ = '\\';
+ sprintf(q, "%03o", c&0xff);
+ q += 3;
+ }
+ }
+ *q++ = '"';
+
+ OBJ_INFECT(result, str);
+ return result;
+}
+
+
+/*
+ * call-seq:
+ * str.upcase! => str or nil
+ *
+ * Upcases the contents of <i>str</i>, returning <code>nil</code> if no changes
+ * were made.
+ */
+
+static VALUE
+rb_str_upcase_bang(str)
+ VALUE str;
+{
+ char *s, *send;
+ int modify = 0;
+
+ rb_str_modify(str);
+ s = RSTRING(str)->ptr; send = s + RSTRING(str)->len;
+ while (s < send) {
+ if (ismbchar(*s)) {
+ s+=mbclen(*s) - 1;
+ }
+ else if (ISLOWER(*s)) {
+ *s = toupper(*s);
+ modify = 1;
+ }
+ s++;
+ }
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.upcase => new_str
+ *
+ * Returns a copy of <i>str</i> with all lowercase letters replaced with their
+ * uppercase counterparts. The operation is locale insensitive---only
+ * characters ``a'' to ``z'' are affected.
+ *
+ * "hEllO".upcase #=> "HELLO"
+ */
+
+static VALUE
+rb_str_upcase(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_upcase_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.downcase! => str or nil
+ *
+ * Downcases the contents of <i>str</i>, returning <code>nil</code> if no
+ * changes were made.
+ */
+
+static VALUE
+rb_str_downcase_bang(str)
+ VALUE str;
+{
+ char *s, *send;
+ int modify = 0;
+
+ rb_str_modify(str);
+ s = RSTRING(str)->ptr; send = s + RSTRING(str)->len;
+ while (s < send) {
+ if (ismbchar(*s)) {
+ s+=mbclen(*s) - 1;
+ }
+ else if (ISUPPER(*s)) {
+ *s = tolower(*s);
+ modify = 1;
+ }
+ s++;
+ }
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.downcase => new_str
+ *
+ * Returns a copy of <i>str</i> with all uppercase letters replaced with their
+ * lowercase counterparts. The operation is locale insensitive---only
+ * characters ``A'' to ``Z'' are affected.
+ *
+ * "hEllO".downcase #=> "hello"
+ */
+
+static VALUE
+rb_str_downcase(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_downcase_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.capitalize! => str or nil
+ *
+ * Modifies <i>str</i> by converting the first character to uppercase and the
+ * remainder to lowercase. Returns <code>nil</code> if no changes are made.
+ *
+ * a = "hello"
+ * a.capitalize! #=> "Hello"
+ * a #=> "Hello"
+ * a.capitalize! #=> nil
+ */
+
+static VALUE
+rb_str_capitalize_bang(str)
+ VALUE str;
+{
+ char *s, *send;
+ int modify = 0;
+
+ rb_str_modify(str);
+ if (RSTRING(str)->len == 0 || !RSTRING(str)->ptr) return Qnil;
+ s = RSTRING(str)->ptr; send = s + RSTRING(str)->len;
+ if (ISLOWER(*s)) {
+ *s = toupper(*s);
+ modify = 1;
+ }
+ while (++s < send) {
+ if (ismbchar(*s)) {
+ s+=mbclen(*s) - 1;
+ }
+ else if (ISUPPER(*s)) {
+ *s = tolower(*s);
+ modify = 1;
+ }
+ }
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.capitalize => new_str
+ *
+ * Returns a copy of <i>str</i> with the first character converted to uppercase
+ * and the remainder to lowercase.
+ *
+ * "hello".capitalize #=> "Hello"
+ * "HELLO".capitalize #=> "Hello"
+ * "123ABC".capitalize #=> "123abc"
+ */
+
+static VALUE
+rb_str_capitalize(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_capitalize_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.swapcase! => str or nil
+ *
+ * Equivalent to <code>String#swapcase</code>, but modifies the receiver in
+ * place, returning <i>str</i>, or <code>nil</code> if no changes were made.
+ */
+
+static VALUE
+rb_str_swapcase_bang(str)
+ VALUE str;
+{
+ char *s, *send;
+ int modify = 0;
+
+ rb_str_modify(str);
+ s = RSTRING(str)->ptr; send = s + RSTRING(str)->len;
+ while (s < send) {
+ if (ismbchar(*s)) {
+ s+=mbclen(*s) - 1;
+ }
+ else if (ISUPPER(*s)) {
+ *s = tolower(*s);
+ modify = 1;
+ }
+ else if (ISLOWER(*s)) {
+ *s = toupper(*s);
+ modify = 1;
+ }
+ s++;
+ }
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.swapcase => new_str
+ *
+ * Returns a copy of <i>str</i> with uppercase alphabetic characters converted
+ * to lowercase and lowercase characters converted to uppercase.
+ *
+ * "Hello".swapcase #=> "hELLO"
+ * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11"
+ */
+
+static VALUE
+rb_str_swapcase(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_swapcase_bang(str);
+ return str;
+}
+
+typedef unsigned char *USTR;
+
+struct tr {
+ int gen, now, max;
+ char *p, *pend;
+};
+
+static int
+trnext(t)
+ struct tr *t;
+{
+ for (;;) {
+ if (!t->gen) {
+ if (t->p == t->pend) return -1;
+ if (t->p < t->pend - 1 && *t->p == '\\') {
+ t->p++;
+ }
+ t->now = *(USTR)t->p++;
+ if (t->p < t->pend - 1 && *t->p == '-') {
+ t->p++;
+ if (t->p < t->pend) {
+ if (t->now > *(USTR)t->p) {
+ t->p++;
+ continue;
+ }
+ t->gen = 1;
+ t->max = *(USTR)t->p++;
+ }
+ }
+ return t->now;
+ }
+ else if (++t->now < t->max) {
+ return t->now;
+ }
+ else {
+ t->gen = 0;
+ return t->max;
+ }
+ }
+}
+
+static VALUE rb_str_delete_bang _((int,VALUE*,VALUE));
+
+static VALUE
+tr_trans(str, src, repl, sflag)
+ VALUE str, src, repl;
+ int sflag;
+{
+ struct tr trsrc, trrepl;
+ int cflag = 0;
+ int trans[256];
+ int i, c, modify = 0;
+ char *s, *send;
+
+ StringValue(src);
+ StringValue(repl);
+ if (RSTRING(str)->len == 0 || !RSTRING(str)->ptr) return Qnil;
+ trsrc.p = RSTRING(src)->ptr; trsrc.pend = trsrc.p + RSTRING(src)->len;
+ if (RSTRING(src)->len >= 2 && RSTRING(src)->ptr[0] == '^') {
+ cflag++;
+ trsrc.p++;
+ }
+ if (RSTRING(repl)->len == 0) {
+ return rb_str_delete_bang(1, &src, str);
+ }
+ trrepl.p = RSTRING(repl)->ptr;
+ trrepl.pend = trrepl.p + RSTRING(repl)->len;
+ trsrc.gen = trrepl.gen = 0;
+ trsrc.now = trrepl.now = 0;
+ trsrc.max = trrepl.max = 0;
+
+ if (cflag) {
+ for (i=0; i<256; i++) {
+ trans[i] = 1;
+ }
+ while ((c = trnext(&trsrc)) >= 0) {
+ trans[c & 0xff] = -1;
+ }
+ while ((c = trnext(&trrepl)) >= 0)
+ /* retrieve last replacer */;
+ for (i=0; i<256; i++) {
+ if (trans[i] >= 0) {
+ trans[i] = trrepl.now;
+ }
+ }
+ }
+ else {
+ int r;
+
+ for (i=0; i<256; i++) {
+ trans[i] = -1;
+ }
+ while ((c = trnext(&trsrc)) >= 0) {
+ r = trnext(&trrepl);
+ if (r == -1) r = trrepl.now;
+ trans[c & 0xff] = r;
+ }
+ }
+
+ rb_str_modify(str);
+ s = RSTRING(str)->ptr; send = s + RSTRING(str)->len;
+ if (sflag) {
+ char *t = s;
+ int c0, last = -1;
+
+ while (s < send) {
+ c0 = *s++;
+ if ((c = trans[c0 & 0xff]) >= 0) {
+ if (last == c) continue;
+ last = c;
+ *t++ = c & 0xff;
+ modify = 1;
+ }
+ else {
+ last = -1;
+ *t++ = c0;
+ }
+ }
+ if (RSTRING(str)->len > (t - RSTRING(str)->ptr)) {
+ RSTRING(str)->len = (t - RSTRING(str)->ptr);
+ modify = 1;
+ *t = '\0';
+ }
+ }
+ else {
+ while (s < send) {
+ if ((c = trans[*s & 0xff]) >= 0) {
+ *s = c & 0xff;
+ modify = 1;
+ }
+ s++;
+ }
+ }
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.tr!(from_str, to_str) => str or nil
+ *
+ * Translates <i>str</i> in place, using the same rules as
+ * <code>String#tr</code>. Returns <i>str</i>, or <code>nil</code> if no
+ * changes were made.
+ */
+
+static VALUE
+rb_str_tr_bang(str, src, repl)
+ VALUE str, src, repl;
+{
+ return tr_trans(str, src, repl, 0);
+}
+
+
+/*
+ * call-seq:
+ * str.tr(from_str, to_str) => new_str
+ *
+ * Returns a copy of <i>str</i> with the characters in <i>from_str</i> replaced
+ * by the corresponding characters in <i>to_str</i>. If <i>to_str</i> is
+ * shorter than <i>from_str</i>, it is padded with its last character. Both
+ * strings may use the c1--c2 notation to denote ranges of characters, and
+ * <i>from_str</i> may start with a <code>^</code>, which denotes all
+ * characters except those listed.
+ *
+ * "hello".tr('aeiou', '*') #=> "h*ll*"
+ * "hello".tr('^aeiou', '*') #=> "*e**o"
+ * "hello".tr('el', 'ip') #=> "hippo"
+ * "hello".tr('a-y', 'b-z') #=> "ifmmp"
+ */
+
+static VALUE
+rb_str_tr(str, src, repl)
+ VALUE str, src, repl;
+{
+ str = rb_str_dup(str);
+ tr_trans(str, src, repl, 0);
+ return str;
+}
+
+static void
+tr_setup_table(str, table, init)
+ VALUE str;
+ char table[256];
+ int init;
+{
+ char buf[256];
+ struct tr tr;
+ int i, c;
+ int cflag = 0;
+
+ tr.p = RSTRING(str)->ptr; tr.pend = tr.p + RSTRING(str)->len;
+ tr.gen = tr.now = tr.max = 0;
+ if (RSTRING(str)->len > 1 && RSTRING(str)->ptr[0] == '^') {
+ cflag = 1;
+ tr.p++;
+ }
+
+ if (init) {
+ for (i=0; i<256; i++) {
+ table[i] = 1;
+ }
+ }
+ for (i=0; i<256; i++) {
+ buf[i] = cflag;
+ }
+ while ((c = trnext(&tr)) >= 0) {
+ buf[c & 0xff] = !cflag;
+ }
+ for (i=0; i<256; i++) {
+ table[i] = table[i] && buf[i];
+ }
+}
+
+
+/*
+ * call-seq:
+ * str.delete!([other_str]+>) => str or nil
+ *
+ * Performs a <code>delete</code> operation in place, returning <i>str</i>, or
+ * <code>nil</code> if <i>str</i> was not modified.
+ */
+
+static VALUE
+rb_str_delete_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ char *s, *send, *t;
+ char squeez[256];
+ int modify = 0;
+ int init = 1;
+ int i;
+
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ for (i=0; i<argc; i++) {
+ VALUE s = argv[i];
+
+ StringValue(s);
+ tr_setup_table(s, squeez, init);
+ init = 0;
+ }
+
+ rb_str_modify(str);
+ s = t = RSTRING(str)->ptr;
+ if (!s || RSTRING(str)->len == 0) return Qnil;
+ send = s + RSTRING(str)->len;
+ while (s < send) {
+ if (squeez[*s & 0xff])
+ modify = 1;
+ else
+ *t++ = *s;
+ s++;
+ }
+ *t = '\0';
+ RSTRING(str)->len = t - RSTRING(str)->ptr;
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.delete([other_str]+) => new_str
+ *
+ * Returns a copy of <i>str</i> with all characters in the intersection of its
+ * arguments deleted. Uses the same rules for building the set of characters as
+ * <code>String#count</code>.
+ *
+ * "hello".delete "l","lo" #=> "heo"
+ * "hello".delete "lo" #=> "he"
+ * "hello".delete "aeiou", "^e" #=> "hell"
+ * "hello".delete "ej-m" #=> "ho"
+ */
+
+static VALUE
+rb_str_delete(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_delete_bang(argc, argv, str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.squeeze!([other_str]*) => str or nil
+ *
+ * Squeezes <i>str</i> in place, returning either <i>str</i>, or
+ * <code>nil</code> if no changes were made.
+ */
+
+static VALUE
+rb_str_squeeze_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ char squeez[256];
+ char *s, *send, *t;
+ int c, save, modify = 0;
+ int init = 1;
+ int i;
+
+ if (argc == 0) {
+ for (i=0; i<256; i++) {
+ squeez[i] = 1;
+ }
+ }
+ else {
+ for (i=0; i<argc; i++) {
+ VALUE s = argv[i];
+
+ StringValue(s);
+ tr_setup_table(s, squeez, init);
+ init = 0;
+ }
+ }
+
+ rb_str_modify(str);
+ s = t = RSTRING(str)->ptr;
+ if (!s || RSTRING(str)->len == 0) return Qnil;
+ send = s + RSTRING(str)->len;
+ save = -1;
+ while (s < send) {
+ c = *s++ & 0xff;
+ if (c != save || !squeez[c]) {
+ *t++ = save = c;
+ }
+ }
+ *t = '\0';
+ if (t - RSTRING(str)->ptr != RSTRING(str)->len) {
+ RSTRING(str)->len = t - RSTRING(str)->ptr;
+ modify = 1;
+ }
+
+ if (modify) return str;
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.squeeze([other_str]*) => new_str
+ *
+ * Builds a set of characters from the <i>other_str</i> parameter(s) using the
+ * procedure described for <code>String#count</code>. Returns a new string
+ * where runs of the same character that occur in this set are replaced by a
+ * single character. If no arguments are given, all runs of identical
+ * characters are replaced by a single character.
+ *
+ * "yellow moon".squeeze #=> "yelow mon"
+ * " now is the".squeeze(" ") #=> " now is the"
+ * "putters shoot balls".squeeze("m-z") #=> "puters shot balls"
+ */
+
+static VALUE
+rb_str_squeeze(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_squeeze_bang(argc, argv, str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.tr_s!(from_str, to_str) => str or nil
+ *
+ * Performs <code>String#tr_s</code> processing on <i>str</i> in place,
+ * returning <i>str</i>, or <code>nil</code> if no changes were made.
+ */
+
+static VALUE
+rb_str_tr_s_bang(str, src, repl)
+ VALUE str, src, repl;
+{
+ return tr_trans(str, src, repl, 1);
+}
+
+
+/*
+ * call-seq:
+ * str.tr_s(from_str, to_str) => new_str
+ *
+ * Processes a copy of <i>str</i> as described under <code>String#tr</code>,
+ * then removes duplicate characters in regions that were affected by the
+ * translation.
+ *
+ * "hello".tr_s('l', 'r') #=> "hero"
+ * "hello".tr_s('el', '*') #=> "h*o"
+ * "hello".tr_s('el', 'hx') #=> "hhxo"
+ */
+
+static VALUE
+rb_str_tr_s(str, src, repl)
+ VALUE str, src, repl;
+{
+ str = rb_str_dup(str);
+ tr_trans(str, src, repl, 1);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.count([other_str]+) => fixnum
+ *
+ * Each <i>other_str</i> parameter defines a set of characters to count. The
+ * intersection of these sets defines the characters to count in
+ * <i>str</i>. Any <i>other_str</i> that starts with a caret (^) is
+ * negated. The sequence c1--c2 means all characters between c1 and c2.
+ *
+ * a = "hello world"
+ * a.count "lo" #=> 5
+ * a.count "lo", "o" #=> 2
+ * a.count "hello", "^l" #=> 4
+ * a.count "ej-m" #=> 4
+ */
+
+static VALUE
+rb_str_count(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ char table[256];
+ char *s, *send;
+ int init = 1;
+ int i;
+
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ for (i=0; i<argc; i++) {
+ VALUE s = argv[i];
+
+ StringValue(s);
+ tr_setup_table(s, table, init);
+ init = 0;
+ }
+
+ s = RSTRING(str)->ptr;
+ if (!s || RSTRING(str)->len == 0) return INT2FIX(0);
+ send = s + RSTRING(str)->len;
+ i = 0;
+ while (s < send) {
+ if (table[*s++ & 0xff]) {
+ i++;
+ }
+ }
+ return INT2NUM(i);
+}
+
+
+/*
+ * call-seq:
+ * str.split(pattern=$;, [limit]) => anArray
+ *
+ * Divides <i>str</i> into substrings based on a delimiter, returning an array
+ * of these substrings.
+ *
+ * If <i>pattern</i> is a <code>String</code>, then its contents are used as
+ * the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
+ * space, <i>str</i> is split on whitespace, with leading whitespace and runs
+ * of contiguous whitespace characters ignored.
+ *
+ * If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
+ * pattern matches. Whenever the pattern matches a zero-length string,
+ * <i>str</i> is split into individual characters.
+ *
+ * If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If
+ * <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
+ * split on whitespace as if ` ' were specified.
+ *
+ * If the <i>limit</i> parameter is omitted, trailing null fields are
+ * suppressed. If <i>limit</i> is a positive number, at most that number of
+ * fields will be returned (if <i>limit</i> is <code>1</code>, the entire
+ * string is returned as the only entry in an array). If negative, there is no
+ * limit to the number of fields returned, and trailing null fields are not
+ * suppressed.
+ *
+ * " now's the time".split #=> ["now's", "the", "time"]
+ * " now's the time".split(' ') #=> ["now's", "the", "time"]
+ * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
+ * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
+ * "hello".split(//) #=> ["h", "e", "l", "l", "o"]
+ * "hello".split(//, 3) #=> ["h", "e", "llo"]
+ * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"]
+ *
+ * "mellow yellow".split("ello") #=> ["m", "w y", "w"]
+ * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"]
+ * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"]
+ * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""]
+ */
+
+static VALUE
+rb_str_split_m(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE spat;
+ VALUE limit;
+ int awk_split = Qfalse;
+ long beg, end, i = 0;
+ int lim = 0;
+ VALUE result, tmp;
+
+ if (rb_scan_args(argc, argv, "02", &spat, &limit) == 2) {
+ lim = NUM2INT(limit);
+ if (lim <= 0) limit = Qnil;
+ else if (lim == 1) {
+ if (RSTRING(str)->len == 0)
+ return rb_ary_new2(0);
+ return rb_ary_new3(1, str);
+ }
+ i = 1;
+ }
+
+ if (NIL_P(spat)) {
+ if (!NIL_P(rb_fs)) {
+ spat = rb_fs;
+ goto fs_set;
+ }
+ awk_split = Qtrue;
+ }
+ else {
+ fs_set:
+ if (TYPE(spat) == T_STRING && RSTRING(spat)->len == 1) {
+ if (RSTRING(spat)->ptr[0] == ' ') {
+ awk_split = Qtrue;
+ }
+ else {
+ spat = rb_reg_regcomp(rb_reg_quote(spat));
+ }
+ }
+ else {
+ spat = get_pat(spat, 1);
+ }
+ }
+
+ result = rb_ary_new();
+ beg = 0;
+ if (awk_split) {
+ char *ptr = RSTRING(str)->ptr;
+ long len = RSTRING(str)->len;
+ char *eptr = ptr + len;
+ int skip = 1;
+
+ for (end = beg = 0; ptr<eptr; ptr++) {
+ if (skip) {
+ if (ISSPACE(*ptr)) {
+ beg++;
+ }
+ else {
+ end = beg+1;
+ skip = 0;
+ if (!NIL_P(limit) && lim <= i) break;
+ }
+ }
+ else {
+ if (ISSPACE(*ptr)) {
+ rb_ary_push(result, rb_str_substr(str, beg, end-beg));
+ skip = 1;
+ beg = end + 1;
+ if (!NIL_P(limit)) ++i;
+ }
+ else {
+ end++;
+ }
+ }
+ }
+ }
+ else {
+ long start = beg;
+ long idx;
+ int last_null = 0;
+ struct re_registers *regs;
+
+ while ((end = rb_reg_search(spat, str, start, 0)) >= 0) {
+ regs = RMATCH(rb_backref_get())->regs;
+ if (start == end && BEG(0) == END(0)) {
+ if (!RSTRING(str)->ptr) {
+ rb_ary_push(result, rb_str_new("", 0));
+ break;
+ }
+ else if (last_null == 1) {
+ rb_ary_push(result, rb_str_substr(str, beg, mbclen2(RSTRING(str)->ptr[beg],spat)));
+ beg = start;
+ }
+ else {
+ start += mbclen2(RSTRING(str)->ptr[start],spat);
+ last_null = 1;
+ continue;
+ }
+ }
+ else {
+ rb_ary_push(result, rb_str_substr(str, beg, end-beg));
+ beg = start = END(0);
+ }
+ last_null = 0;
+
+ for (idx=1; idx < regs->num_regs; idx++) {
+ if (BEG(idx) == -1) continue;
+ if (BEG(idx) == END(idx))
+ tmp = rb_str_new5(str, 0, 0);
+ else
+ tmp = rb_str_substr(str, BEG(idx), END(idx)-BEG(idx));
+ rb_ary_push(result, tmp);
+ }
+ if (!NIL_P(limit) && lim <= ++i) break;
+ }
+ }
+ if (RSTRING(str)->len > 0 && (!NIL_P(limit) || RSTRING(str)->len > beg || lim < 0)) {
+ if (RSTRING(str)->len == beg)
+ tmp = rb_str_new5(str, 0, 0);
+ else
+ tmp = rb_str_substr(str, beg, RSTRING(str)->len-beg);
+ rb_ary_push(result, tmp);
+ }
+ if (NIL_P(limit) && lim == 0) {
+ while (RARRAY(result)->len > 0 &&
+ RSTRING(RARRAY(result)->ptr[RARRAY(result)->len-1])->len == 0)
+ rb_ary_pop(result);
+ }
+
+ return result;
+}
+
+VALUE
+rb_str_split(str, sep0)
+ VALUE str;
+ const char *sep0;
+{
+ VALUE sep;
+
+ StringValue(str);
+ sep = rb_str_new2(sep0);
+ return rb_str_split_m(1, &sep, str);
+}
+
+/*
+ * call-seq:
+ * split([pattern [, limit]]) => array
+ *
+ * Equivalent to <code>$_.split(<i>pattern</i>, <i>limit</i>)</code>.
+ * See <code>String#split</code>.
+ */
+
+static VALUE
+rb_f_split(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_str_split_m(argc, argv, uscore_get());
+}
+
+/*
+ * call-seq:
+ * str.each(separator=$/) {|substr| block } => str
+ * str.each_line(separator=$/) {|substr| block } => str
+ *
+ * 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.
+ *
+ * print "Example one\n"
+ * "hello\nworld".each {|s| p s}
+ * print "Example two\n"
+ * "hello\nworld".each('l') {|s| p s}
+ * print "Example three\n"
+ * "hello\n\n\nworld".each('') {|s| p s}
+ *
+ * <em>produces:</em>
+ *
+ * Example one
+ * "hello\n"
+ * "world"
+ * Example two
+ * "hel"
+ * "l"
+ * "o\nworl"
+ * "d"
+ * Example three
+ * "hello\n\n\n"
+ * "world"
+ */
+
+static VALUE
+rb_str_each_line(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE rs;
+ int newline;
+ char *p = RSTRING(str)->ptr, *pend = p + RSTRING(str)->len, *s;
+ char *ptr = p;
+ long len = RSTRING(str)->len, rslen;
+ VALUE line;
+
+ if (rb_scan_args(argc, argv, "01", &rs) == 0) {
+ rs = rb_rs;
+ }
+
+ if (NIL_P(rs)) {
+ rb_yield(str);
+ return str;
+ }
+ StringValue(rs);
+ rslen = RSTRING(rs)->len;
+ if (rslen == 0) {
+ newline = '\n';
+ }
+ else {
+ newline = RSTRING(rs)->ptr[rslen-1];
+ }
+
+ for (s = p, p += rslen; p < pend; p++) {
+ if (rslen == 0 && *p == '\n') {
+ if (*++p != '\n') continue;
+ while (*p == '\n') p++;
+ }
+ if (RSTRING(str)->ptr < p && p[-1] == newline &&
+ (rslen <= 1 ||
+ rb_memcmp(RSTRING(rs)->ptr, p-rslen, rslen) == 0)) {
+ line = rb_str_new5(str, s, p - s);
+ OBJ_INFECT(line, str);
+ rb_yield(line);
+ str_mod_check(str, ptr, len);
+ s = p;
+ }
+ }
+
+ if (s != pend) {
+ if (p > pend) p = pend;
+ line = rb_str_new5(str, s, p - s);
+ OBJ_INFECT(line, str);
+ rb_yield(line);
+ }
+
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.each_byte {|fixnum| block } => str
+ *
+ * Passes each byte in <i>str</i> to the given block.
+ *
+ * "hello".each_byte {|c| print c, ' ' }
+ *
+ * <em>produces:</em>
+ *
+ * 104 101 108 108 111
+ */
+
+static VALUE
+rb_str_each_byte(str)
+ VALUE str;
+{
+ long i;
+
+ for (i=0; i<RSTRING(str)->len; i++) {
+ rb_yield(INT2FIX(RSTRING(str)->ptr[i] & 0xff));
+ }
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.chop! => str or nil
+ *
+ * Processes <i>str</i> as for <code>String#chop</code>, returning <i>str</i>,
+ * or <code>nil</code> if <i>str</i> is the empty string. See also
+ * <code>String#chomp!</code>.
+ */
+
+static VALUE
+rb_str_chop_bang(str)
+ VALUE str;
+{
+ if (RSTRING(str)->len > 0) {
+ rb_str_modify(str);
+ RSTRING(str)->len--;
+ if (RSTRING(str)->ptr[RSTRING(str)->len] == '\n') {
+ if (RSTRING(str)->len > 0 &&
+ RSTRING(str)->ptr[RSTRING(str)->len-1] == '\r') {
+ RSTRING(str)->len--;
+ }
+ }
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ return str;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.chop => new_str
+ *
+ * Returns a new <code>String</code> with the last character removed. If the
+ * string ends with <code>\r\n</code>, both characters are removed. Applying
+ * <code>chop</code> to an empty string returns an empty
+ * string. <code>String#chomp</code> is often a safer alternative, as it leaves
+ * the string unchanged if it doesn't end in a record separator.
+ *
+ * "string\r\n".chop #=> "string"
+ * "string\n\r".chop #=> "string\n"
+ * "string\n".chop #=> "string"
+ * "string".chop #=> "strin"
+ * "x".chop.chop #=> ""
+ */
+
+static VALUE
+rb_str_chop(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_chop_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * chop! => $_ or nil
+ *
+ * Equivalent to <code>$_.chop!</code>.
+ *
+ * a = "now\r\n"
+ * $_ = a
+ * chop! #=> "now"
+ * chop! #=> "no"
+ * chop! #=> "n"
+ * chop! #=> ""
+ * chop! #=> nil
+ * $_ #=> ""
+ * a #=> ""
+ */
+
+static VALUE
+rb_f_chop_bang(str)
+ VALUE str;
+{
+ return rb_str_chop_bang(uscore_get());
+}
+
+/*
+ * call-seq:
+ * chop => string
+ *
+ * Equivalent to <code>($_.dup).chop!</code>, except <code>nil</code>
+ * is never returned. See <code>String#chop!</code>.
+ *
+ * a = "now\r\n"
+ * $_ = a
+ * chop #=> "now"
+ * $_ #=> "now"
+ * chop #=> "no"
+ * chop #=> "n"
+ * chop #=> ""
+ * chop #=> ""
+ * a #=> "now\r\n"
+ */
+
+static VALUE
+rb_f_chop()
+{
+ VALUE str = uscore_get();
+
+ if (RSTRING(str)->len > 0) {
+ str = rb_str_dup(str);
+ rb_str_chop_bang(str);
+ rb_lastline_set(str);
+ }
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.chomp!(separator=$/) => str or nil
+ *
+ * Modifies <i>str</i> in place as described for <code>String#chomp</code>,
+ * returning <i>str</i>, or <code>nil</code> if no modifications were made.
+ */
+
+static VALUE
+rb_str_chomp_bang(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE rs;
+ int newline;
+ char *p;
+ long len, rslen;
+
+ if (rb_scan_args(argc, argv, "01", &rs) == 0) {
+ len = RSTRING(str)->len;
+ if (len == 0) return Qnil;
+ p = RSTRING(str)->ptr;
+ rs = rb_rs;
+ if (rs == rb_default_rs) {
+ smart_chomp:
+ rb_str_modify(str);
+ if (RSTRING(str)->ptr[len-1] == '\n') {
+ RSTRING(str)->len--;
+ if (RSTRING(str)->len > 0 &&
+ RSTRING(str)->ptr[RSTRING(str)->len-1] == '\r') {
+ RSTRING(str)->len--;
+ }
+ }
+ else if (RSTRING(str)->ptr[len-1] == '\r') {
+ RSTRING(str)->len--;
+ }
+ else {
+ return Qnil;
+ }
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ return str;
+ }
+ }
+ if (NIL_P(rs)) return Qnil;
+ StringValue(rs);
+ len = RSTRING(str)->len;
+ if (len == 0) return Qnil;
+ p = RSTRING(str)->ptr;
+ rslen = RSTRING(rs)->len;
+ if (rslen == 0) {
+ while (len>0 && p[len-1] == '\n') {
+ len--;
+ if (len>0 && p[len-1] == '\r')
+ len--;
+ }
+ if (len < RSTRING(str)->len) {
+ rb_str_modify(str);
+ RSTRING(str)->len = len;
+ RSTRING(str)->ptr[len] = '\0';
+ return str;
+ }
+ return Qnil;
+ }
+ if (rslen > len) return Qnil;
+ newline = RSTRING(rs)->ptr[rslen-1];
+ if (rslen == 1 && newline == '\n')
+ goto smart_chomp;
+
+ if (p[len-1] == newline &&
+ (rslen <= 1 ||
+ rb_memcmp(RSTRING(rs)->ptr, p+len-rslen, rslen) == 0)) {
+ rb_str_modify(str);
+ RSTRING(str)->len -= rslen;
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ return str;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.chomp(separator=$/) => new_str
+ *
+ * Returns a new <code>String</code> with the given record separator removed
+ * from the end of <i>str</i> (if present). If <code>$/</code> has not been
+ * changed from the default Ruby record separator, then <code>chomp</code> also
+ * removes carriage return characters (that is it will remove <code>\n</code>,
+ * <code>\r</code>, and <code>\r\n</code>).
+ *
+ * "hello".chomp #=> "hello"
+ * "hello\n".chomp #=> "hello"
+ * "hello\r\n".chomp #=> "hello"
+ * "hello\n\r".chomp #=> "hello\n"
+ * "hello\r".chomp #=> "hello"
+ * "hello \n there".chomp #=> "hello \n there"
+ * "hello".chomp("llo") #=> "he"
+ */
+
+static VALUE
+rb_str_chomp(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_chomp_bang(argc, argv, str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * chomp! => $_ or nil
+ * chomp!(string) => $_ or nil
+ *
+ * Equivalent to <code>$_.chomp!(<em>string</em>)</code>. See
+ * <code>String#chomp!</code>
+ *
+ * $_ = "now\n"
+ * chomp! #=> "now"
+ * $_ #=> "now"
+ * chomp! "x" #=> nil
+ * $_ #=> "now"
+ */
+
+static VALUE
+rb_f_chomp_bang(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_str_chomp_bang(argc, argv, uscore_get());
+}
+
+/*
+ * call-seq:
+ * chomp => $_
+ * chomp(string) => $_
+ *
+ * Equivalent to <code>$_ = $_.chomp(<em>string</em>)</code>. See
+ * <code>String#chomp</code>.
+ *
+ * $_ = "now\n"
+ * chomp #=> "now"
+ * $_ #=> "now"
+ * chomp "ow" #=> "n"
+ * $_ #=> "n"
+ * chomp "xxx" #=> "n"
+ * $_ #=> "n"
+ */
+
+static VALUE
+rb_f_chomp(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE str = uscore_get();
+ VALUE dup = rb_str_dup(str);
+
+ if (NIL_P(rb_str_chomp_bang(argc, argv, dup)))
+ return str;
+ rb_lastline_set(dup);
+ return dup;
+}
+
+
+/*
+ * call-seq:
+ * str.lstrip! => self or nil
+ *
+ * Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no
+ * change was made. See also <code>String#rstrip!</code> and
+ * <code>String#strip!</code>.
+ *
+ * " hello ".lstrip #=> "hello "
+ * "hello".lstrip! #=> nil
+ */
+
+static VALUE
+rb_str_lstrip_bang(str)
+ VALUE str;
+{
+ char *s, *t, *e;
+
+ s = RSTRING(str)->ptr;
+ if (!s || RSTRING(str)->len == 0) return Qnil;
+ e = t = s + RSTRING(str)->len;
+ /* remove spaces at head */
+ while (s < t && ISSPACE(*s)) s++;
+
+ if (s > RSTRING(str)->ptr) {
+ rb_str_modify(str);
+ RSTRING(str)->len = t-s;
+ memmove(RSTRING(str)->ptr, s, RSTRING(str)->len);
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ return str;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.lstrip => new_str
+ *
+ * Returns a copy of <i>str</i> with leading whitespace removed. See also
+ * <code>String#rstrip</code> and <code>String#strip</code>.
+ *
+ * " hello ".lstrip #=> "hello "
+ * "hello".lstrip #=> "hello"
+ */
+
+static VALUE
+rb_str_lstrip(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_lstrip_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.rstrip! => self or nil
+ *
+ * Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if
+ * no change was made. See also <code>String#lstrip!</code> and
+ * <code>String#strip!</code>.
+ *
+ * " hello ".rstrip #=> " hello"
+ * "hello".rstrip! #=> nil
+ */
+
+static VALUE
+rb_str_rstrip_bang(str)
+ VALUE str;
+{
+ char *s, *t, *e;
+
+ s = RSTRING(str)->ptr;
+ if (!s || RSTRING(str)->len == 0) return Qnil;
+ e = t = s + RSTRING(str)->len;
+
+ /* remove trailing '\0's */
+ while (s < t && t[-1] == '\0') t--;
+
+ /* remove trailing spaces */
+ while (s < t && ISSPACE(*(t-1))) t--;
+
+ if (t < e) {
+ rb_str_modify(str);
+ RSTRING(str)->len = t-s;
+ RSTRING(str)->ptr[RSTRING(str)->len] = '\0';
+ return str;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.rstrip => new_str
+ *
+ * Returns a copy of <i>str</i> with trailing whitespace removed. See also
+ * <code>String#lstrip</code> and <code>String#strip</code>.
+ *
+ * " hello ".rstrip #=> " hello"
+ * "hello".rstrip #=> "hello"
+ */
+
+static VALUE
+rb_str_rstrip(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_rstrip_bang(str);
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.strip! => str or nil
+ *
+ * Removes leading and trailing whitespace from <i>str</i>. Returns
+ * <code>nil</code> if <i>str</i> was not altered.
+ */
+
+static VALUE
+rb_str_strip_bang(str)
+ VALUE str;
+{
+ VALUE l = rb_str_lstrip_bang(str);
+ VALUE r = rb_str_rstrip_bang(str);
+
+ if (NIL_P(l) && NIL_P(r)) return Qnil;
+ return str;
+}
+
+
+/*
+ * call-seq:
+ * str.strip => new_str
+ *
+ * Returns a copy of <i>str</i> with leading and trailing whitespace removed.
+ *
+ * " hello ".strip #=> "hello"
+ * "\tgoodbye\r\n".strip #=> "goodbye"
+ */
+
+static VALUE
+rb_str_strip(str)
+ VALUE str;
+{
+ str = rb_str_dup(str);
+ rb_str_strip_bang(str);
+ return str;
+}
+
+static VALUE
+scan_once(str, pat, start)
+ VALUE str, pat;
+ long *start;
+{
+ VALUE result, match;
+ struct re_registers *regs;
+ long i;
+
+ if (rb_reg_search(pat, str, *start, 0) >= 0) {
+ match = rb_backref_get();
+ regs = RMATCH(match)->regs;
+ if (BEG(0) == END(0)) {
+ /*
+ * Always consume at least one character of the input string
+ */
+ if (RSTRING(str)->len > END(0))
+ *start = END(0)+mbclen2(RSTRING(str)->ptr[END(0)],pat);
+ else
+ *start = END(0)+1;
+ }
+ else {
+ *start = END(0);
+ }
+ if (regs->num_regs == 1) {
+ return rb_reg_nth_match(0, match);
+ }
+ result = rb_ary_new2(regs->num_regs);
+ for (i=1; i < regs->num_regs; i++) {
+ rb_ary_push(result, rb_reg_nth_match(i, match));
+ }
+
+ return result;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * str.scan(pattern) => array
+ * str.scan(pattern) {|match, ...| block } => str
+ *
+ * Both forms iterate through <i>str</i>, matching the pattern (which may be a
+ * <code>Regexp</code> or a <code>String</code>). For each match, a result is
+ * generated and either added to the result array or passed to the block. If
+ * the pattern contains no groups, each individual result consists of the
+ * matched string, <code>$&</code>. If the pattern contains groups, each
+ * individual result is itself an array containing one entry per group.
+ *
+ * a = "cruel world"
+ * a.scan(/\w+/) #=> ["cruel", "world"]
+ * a.scan(/.../) #=> ["cru", "el ", "wor"]
+ * a.scan(/(...)/) #=> [["cru"], ["el "], ["wor"]]
+ * a.scan(/(..)(..)/) #=> [["cr", "ue"], ["l ", "wo"]]
+ *
+ * And the block form:
+ *
+ * a.scan(/\w+/) {|w| print "<<#{w}>> " }
+ * print "\n"
+ * a.scan(/(.)(.)/) {|x,y| print y, x }
+ * print "\n"
+ *
+ * <em>produces:</em>
+ *
+ * <<cruel>> <<world>>
+ * rceu lowlr
+ */
+
+static VALUE
+rb_str_scan(str, pat)
+ VALUE str, pat;
+{
+ VALUE result;
+ long start = 0;
+ VALUE match = Qnil;
+ char *p = RSTRING(str)->ptr; long len = RSTRING(str)->len;
+
+ pat = get_pat(pat, 1);
+ if (!rb_block_given_p()) {
+ VALUE ary = rb_ary_new();
+
+ while (!NIL_P(result = scan_once(str, pat, &start))) {
+ match = rb_backref_get();
+ rb_ary_push(ary, result);
+ }
+ rb_backref_set(match);
+ return ary;
+ }
+
+ while (!NIL_P(result = scan_once(str, pat, &start))) {
+ match = rb_backref_get();
+ rb_match_busy(match);
+ rb_yield(result);
+ str_mod_check(str, p, len);
+ rb_backref_set(match); /* restore $~ value */
+ }
+ rb_backref_set(match);
+ return str;
+}
+
+/*
+ * call-seq:
+ * scan(pattern) => array
+ * scan(pattern) {|///| block } => $_
+ *
+ * Equivalent to calling <code>$_.scan</code>. See
+ * <code>String#scan</code>.
+ */
+
+static VALUE
+rb_f_scan(self, pat)
+ VALUE self, pat;
+{
+ return rb_str_scan(uscore_get(), pat);
+}
+
+
+/*
+ * call-seq:
+ * str.hex => integer
+ *
+ * Treats leading characters from <i>str</i> as a string of hexadecimal digits
+ * (with an optional sign and an optional <code>0x</code>) and returns the
+ * corresponding number. Zero is returned on error.
+ *
+ * "0x0a".hex #=> 10
+ * "-1234".hex #=> -4660
+ * "0".hex #=> 0
+ * "wombat".hex #=> 0
+ */
+
+static VALUE
+rb_str_hex(str)
+ VALUE str;
+{
+ return rb_str_to_inum(str, 16, Qfalse);
+}
+
+
+/*
+ * call-seq:
+ * str.oct => integer
+ *
+ * Treats leading characters of <i>str</i> as a string of octal digits (with an
+ * optional sign) and returns the corresponding number. Returns 0 if the
+ * conversion fails.
+ *
+ * "123".oct #=> 83
+ * "-377".oct #=> -255
+ * "bad".oct #=> 0
+ * "0377bad".oct #=> 255
+ */
+
+static VALUE
+rb_str_oct(str)
+ VALUE str;
+{
+ return rb_str_to_inum(str, -8, Qfalse);
+}
+
+
+/*
+ * call-seq:
+ * str.crypt(other_str) => new_str
+ *
+ * Applies a one-way cryptographic hash to <i>str</i> by invoking the standard
+ * library function <code>crypt</code>. The argument is the salt string, which
+ * should be two characters long, each character drawn from
+ * <code>[a-zA-Z0-9./]</code>.
+ */
+
+static VALUE
+rb_str_crypt(str, salt)
+ VALUE str, salt;
+{
+ extern char *crypt();
+ VALUE result;
+ char *s;
+
+ StringValue(salt);
+ if (RSTRING(salt)->len < 2)
+ rb_raise(rb_eArgError, "salt too short(need >=2 bytes)");
+
+ if (RSTRING(str)->ptr) s = RSTRING(str)->ptr;
+ else s = "";
+ result = rb_str_new2(crypt(s, RSTRING(salt)->ptr));
+ OBJ_INFECT(result, str);
+ OBJ_INFECT(result, salt);
+ return result;
+}
+
+
+/*
+ * call-seq:
+ * str.intern => symbol
+ * str.to_sym => symbol
+ *
+ * Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
+ * symbol if it did not previously exist. See <code>Symbol#id2name</code>.
+ *
+ * "Koala".intern #=> :Koala
+ * s = 'cat'.to_sym #=> :cat
+ * s == :cat #=> true
+ * s = '@cat'.to_sym #=> :@cat
+ * s == :@cat #=> true
+ *
+ * This can also be used to create symbols that cannot be represented using the
+ * <code>:xxx</code> notation.
+ *
+ * 'cat and dog'.to_sym #=> :"cat and dog"
+ */
+
+VALUE
+rb_str_intern(s)
+ VALUE s;
+{
+ volatile VALUE str = s;
+ ID id;
+
+ if (!RSTRING(str)->ptr || RSTRING(str)->len == 0) {
+ rb_raise(rb_eArgError, "interning empty string");
+ }
+ if (strlen(RSTRING(str)->ptr) != RSTRING(str)->len)
+ rb_raise(rb_eArgError, "symbol string may not contain `\\0'");
+ if (OBJ_TAINTED(str) && rb_safe_level() >= 1 && !rb_sym_interned_p(str)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't intern tainted string");
+ }
+ id = rb_intern(RSTRING(str)->ptr);
+ return ID2SYM(id);
+}
+
+
+/*
+ * call-seq:
+ * str.sum(n=16) => integer
+ *
+ * Returns a basic <em>n</em>-bit checksum of the characters in <i>str</i>,
+ * where <em>n</em> is the optional <code>Fixnum</code> parameter, defaulting
+ * to 16. The result is simply the sum of the binary value of each character in
+ * <i>str</i> modulo <code>2n - 1</code>. This is not a particularly good
+ * checksum.
+ */
+
+static VALUE
+rb_str_sum(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ VALUE vbits;
+ int bits;
+ char *ptr, *p, *pend;
+ long len;
+
+ if (rb_scan_args(argc, argv, "01", &vbits) == 0) {
+ bits = 16;
+ }
+ else bits = NUM2INT(vbits);
+
+ ptr = p = RSTRING(str)->ptr;
+ len = RSTRING(str)->len;
+ pend = p + len;
+ if (bits >= sizeof(long)*CHAR_BIT) {
+ VALUE sum = INT2FIX(0);
+
+ while (p < pend) {
+ str_mod_check(str, ptr, len);
+ sum = rb_funcall(sum, '+', 1, INT2FIX((unsigned char)*p));
+ p++;
+ }
+ if (bits != 0) {
+ VALUE mod;
+
+ mod = rb_funcall(INT2FIX(1), rb_intern("<<"), 1, INT2FIX(bits));
+ mod = rb_funcall(mod, '-', 1, INT2FIX(1));
+ sum = rb_funcall(sum, '&', 1, mod);
+ }
+ return sum;
+ }
+ else {
+ unsigned long sum = 0;
+
+ while (p < pend) {
+ str_mod_check(str, ptr, len);
+ sum += (unsigned char)*p;
+ p++;
+ }
+ if (bits != 0) {
+ sum &= (((unsigned long)1)<<bits)-1;
+ }
+ return rb_int2inum(sum);
+ }
+}
+
+static VALUE
+rb_str_justify(argc, argv, str, jflag)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+ char jflag;
+{
+ VALUE w;
+ long width, flen = 0;
+ VALUE res;
+ char *p, *pend, *f = " ";
+ long n;
+ VALUE pad;
+
+ rb_scan_args(argc, argv, "11", &w, &pad);
+ width = NUM2LONG(w);
+ if (argc == 2) {
+ StringValue(pad);
+ f = RSTRING(pad)->ptr;
+ flen = RSTRING(pad)->len;
+ if (flen == 0) {
+ rb_raise(rb_eArgError, "zero width padding");
+ }
+ }
+ if (width < 0 || RSTRING(str)->len >= width) return rb_str_dup(str);
+ res = rb_str_new5(str, 0, width);
+ p = RSTRING(res)->ptr;
+ if (jflag != 'l') {
+ n = width - RSTRING(str)->len;
+ pend = p + ((jflag == 'r') ? n : n/2);
+ if (flen <= 1) {
+ while (p < pend) {
+ *p++ = *f;
+ }
+ }
+ else {
+ char *q = f;
+ while (p + flen <= pend) {
+ memcpy(p,f,flen);
+ p += flen;
+ }
+ while (p < pend) {
+ *p++ = *q++;
+ }
+ }
+ }
+ memcpy(p, RSTRING(str)->ptr, RSTRING(str)->len);
+ if (jflag != 'r') {
+ p += RSTRING(str)->len; pend = RSTRING(res)->ptr + width;
+ if (flen <= 1) {
+ while (p < pend) {
+ *p++ = *f;
+ }
+ }
+ else {
+ while (p + flen <= pend) {
+ memcpy(p,f,flen);
+ p += flen;
+ }
+ while (p < pend) {
+ *p++ = *f++;
+ }
+ }
+ }
+ OBJ_INFECT(res, str);
+ if (flen > 0) OBJ_INFECT(res, pad);
+ return res;
+}
+
+
+/*
+ * call-seq:
+ * str.ljust(integer, padstr=' ') => new_str
+ *
+ * If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ * <code>String</code> of length <i>integer</i> with <i>str</i> left justified
+ * and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ *
+ * "hello".ljust(4) #=> "hello"
+ * "hello".ljust(20) #=> "hello "
+ * "hello".ljust(20, '1234') #=> "hello123412341234123"
+ */
+
+static VALUE
+rb_str_ljust(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ return rb_str_justify(argc, argv, str, 'l');
+}
+
+
+/*
+ * call-seq:
+ * str.rjust(integer, padstr=' ') => new_str
+ *
+ * If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ * <code>String</code> of length <i>integer</i> with <i>str</i> right justified
+ * and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ *
+ * "hello".rjust(4) #=> "hello"
+ * "hello".rjust(20) #=> " hello"
+ * "hello".rjust(20, '1234') #=> "123412341234123hello"
+ */
+
+static VALUE
+rb_str_rjust(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ return rb_str_justify(argc, argv, str, 'r');
+}
+
+
+/*
+ * call-seq:
+ * str.center(integer, padstr) => new_str
+ *
+ * If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ * <code>String</code> of length <i>integer</i> with <i>str</i> centered and
+ * padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ *
+ * "hello".center(4) #=> "hello"
+ * "hello".center(20) #=> " hello "
+ * "hello".center(20, '123') #=> "1231231hello12312312"
+ */
+
+static VALUE
+rb_str_center(argc, argv, str)
+ int argc;
+ VALUE *argv;
+ VALUE str;
+{
+ return rb_str_justify(argc, argv, str, 'c');
+}
+
+void
+rb_str_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (!NIL_P(val) && TYPE(val) != T_STRING) {
+ rb_raise(rb_eTypeError, "value of %s must be String", rb_id2name(id));
+ }
+ *var = val;
+}
+
+
+/*
+ * A <code>String</code> object holds and manipulates an arbitrary sequence of
+ * bytes, typically representing characters. String objects may be created
+ * using <code>String::new</code> or as literals.
+ *
+ * Because of aliasing issues, users of strings should be aware of the methods
+ * that modify the contents of a <code>String</code> object. Typically,
+ * methods with names ending in ``!'' modify their receiver, while those
+ * without a ``!'' return a new <code>String</code>. However, there are
+ * exceptions, such as <code>String#[]=</code>.
+ *
+ */
+
+void
+Init_String()
+{
+ rb_cString = rb_define_class("String", rb_cObject);
+ rb_include_module(rb_cString, rb_mComparable);
+ rb_include_module(rb_cString, rb_mEnumerable);
+ rb_define_alloc_func(rb_cString, rb_str_s_alloc);
+ rb_define_method(rb_cString, "initialize", rb_str_init, -1);
+ rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1);
+ rb_define_method(rb_cString, "<=>", rb_str_cmp_m, 1);
+ rb_define_method(rb_cString, "==", rb_str_equal, 1);
+ rb_define_method(rb_cString, "eql?", rb_str_eql, 1);
+ rb_define_method(rb_cString, "hash", rb_str_hash_m, 0);
+ rb_define_method(rb_cString, "casecmp", rb_str_casecmp, 1);
+ rb_define_method(rb_cString, "+", rb_str_plus, 1);
+ rb_define_method(rb_cString, "*", rb_str_times, 1);
+ rb_define_method(rb_cString, "%", rb_str_format_m, 1);
+ rb_define_method(rb_cString, "[]", rb_str_aref_m, -1);
+ rb_define_method(rb_cString, "[]=", rb_str_aset_m, -1);
+ 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, "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);
+ rb_define_method(rb_cString, "succ", rb_str_succ, 0);
+ rb_define_method(rb_cString, "succ!", rb_str_succ_bang, 0);
+ rb_define_method(rb_cString, "next", rb_str_succ, 0);
+ rb_define_method(rb_cString, "next!", rb_str_succ_bang, 0);
+ rb_define_method(rb_cString, "upto", rb_str_upto_m, 1);
+ rb_define_method(rb_cString, "index", rb_str_index_m, -1);
+ rb_define_method(rb_cString, "rindex", rb_str_rindex_m, -1);
+ rb_define_method(rb_cString, "replace", rb_str_replace, 1);
+
+ rb_define_method(rb_cString, "to_i", rb_str_to_i, -1);
+ rb_define_method(rb_cString, "to_f", rb_str_to_f, 0);
+ rb_define_method(rb_cString, "to_s", rb_str_to_s, 0);
+ rb_define_method(rb_cString, "to_str", rb_str_to_s, 0);
+ rb_define_method(rb_cString, "inspect", rb_str_inspect, 0);
+ rb_define_method(rb_cString, "dump", rb_str_dump, 0);
+
+ rb_define_method(rb_cString, "upcase", rb_str_upcase, 0);
+ rb_define_method(rb_cString, "downcase", rb_str_downcase, 0);
+ rb_define_method(rb_cString, "capitalize", rb_str_capitalize, 0);
+ rb_define_method(rb_cString, "swapcase", rb_str_swapcase, 0);
+
+ rb_define_method(rb_cString, "upcase!", rb_str_upcase_bang, 0);
+ rb_define_method(rb_cString, "downcase!", rb_str_downcase_bang, 0);
+ rb_define_method(rb_cString, "capitalize!", rb_str_capitalize_bang, 0);
+ rb_define_method(rb_cString, "swapcase!", rb_str_swapcase_bang, 0);
+
+ rb_define_method(rb_cString, "hex", rb_str_hex, 0);
+ rb_define_method(rb_cString, "oct", rb_str_oct, 0);
+ rb_define_method(rb_cString, "split", rb_str_split_m, -1);
+ rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
+ rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
+ rb_define_method(rb_cString, "concat", rb_str_concat, 1);
+ rb_define_method(rb_cString, "<<", rb_str_concat, 1);
+ rb_define_method(rb_cString, "crypt", rb_str_crypt, 1);
+ rb_define_method(rb_cString, "intern", rb_str_intern, 0);
+ rb_define_method(rb_cString, "to_sym", rb_str_intern, 0);
+
+ rb_define_method(rb_cString, "include?", rb_str_include, 1);
+
+ rb_define_method(rb_cString, "scan", rb_str_scan, 1);
+
+ rb_define_method(rb_cString, "ljust", rb_str_ljust, -1);
+ rb_define_method(rb_cString, "rjust", rb_str_rjust, -1);
+ rb_define_method(rb_cString, "center", rb_str_center, -1);
+
+ rb_define_method(rb_cString, "sub", rb_str_sub, -1);
+ rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);
+ rb_define_method(rb_cString, "chop", rb_str_chop, 0);
+ rb_define_method(rb_cString, "chomp", rb_str_chomp, -1);
+ rb_define_method(rb_cString, "strip", rb_str_strip, 0);
+ rb_define_method(rb_cString, "lstrip", rb_str_lstrip, 0);
+ rb_define_method(rb_cString, "rstrip", rb_str_rstrip, 0);
+
+ rb_define_method(rb_cString, "sub!", rb_str_sub_bang, -1);
+ rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1);
+ rb_define_method(rb_cString, "chop!", rb_str_chop_bang, 0);
+ rb_define_method(rb_cString, "chomp!", rb_str_chomp_bang, -1);
+ rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0);
+ rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, 0);
+ rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, 0);
+
+ rb_define_method(rb_cString, "tr", rb_str_tr, 2);
+ rb_define_method(rb_cString, "tr_s", rb_str_tr_s, 2);
+ rb_define_method(rb_cString, "delete", rb_str_delete, -1);
+ rb_define_method(rb_cString, "squeeze", rb_str_squeeze, -1);
+ rb_define_method(rb_cString, "count", rb_str_count, -1);
+
+ rb_define_method(rb_cString, "tr!", rb_str_tr_bang, 2);
+ rb_define_method(rb_cString, "tr_s!", rb_str_tr_s_bang, 2);
+ rb_define_method(rb_cString, "delete!", rb_str_delete_bang, -1);
+ rb_define_method(rb_cString, "squeeze!", rb_str_squeeze_bang, -1);
+
+ rb_define_method(rb_cString, "each_line", rb_str_each_line, -1);
+ rb_define_method(rb_cString, "each", rb_str_each_line, -1);
+ rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);
+
+ rb_define_method(rb_cString, "sum", rb_str_sum, -1);
+
+ rb_define_global_function("sub", rb_f_sub, -1);
+ rb_define_global_function("gsub", rb_f_gsub, -1);
+
+ rb_define_global_function("sub!", rb_f_sub_bang, -1);
+ rb_define_global_function("gsub!", rb_f_gsub_bang, -1);
+
+ rb_define_global_function("chop", rb_f_chop, 0);
+ rb_define_global_function("chop!", rb_f_chop_bang, 0);
+
+ rb_define_global_function("chomp", rb_f_chomp, -1);
+ rb_define_global_function("chomp!", rb_f_chomp_bang, -1);
+
+ rb_define_global_function("split", rb_f_split, -1);
+ rb_define_global_function("scan", rb_f_scan, 1);
+
+ rb_define_method(rb_cString, "slice", rb_str_aref_m, -1);
+ rb_define_method(rb_cString, "slice!", rb_str_slice_bang, -1);
+
+ id_to_s = rb_intern("to_s");
+
+ rb_fs = Qnil;
+ rb_define_variable("$;", &rb_fs);
+ rb_define_variable("$-F", &rb_fs);
+}
+/**********************************************************************
+
+ struct.c -
+
+ $Author: shyouhei $
+ $Date: 2008-06-15 15:44:44 +0200 (Sun, 15 Jun 2008) $
+ created at: Tue Mar 22 18:44:30 JST 1995
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "env.h"
+
+VALUE rb_cStruct;
+
+static VALUE struct_alloc _((VALUE));
+
+VALUE
+rb_struct_iv_get(c, name)
+ VALUE c;
+ char *name;
+{
+ ID id;
+
+ id = rb_intern(name);
+ for (;;) {
+ if (rb_ivar_defined(c, id))
+ return rb_ivar_get(c, id);
+ c = RCLASS(c)->super;
+ if (c == 0 || c == rb_cStruct)
+ return Qnil;
+ }
+}
+
+VALUE
+rb_struct_s_members(klass)
+ VALUE klass;
+{
+ VALUE members = rb_struct_iv_get(klass, "__members__");
+
+ if (NIL_P(members)) {
+ rb_bug("non-initialized struct");
+ }
+ return members;
+}
+
+VALUE
+rb_struct_members(s)
+ VALUE s;
+{
+ VALUE members = rb_struct_s_members(rb_obj_class(s));
+
+ if (RSTRUCT(s)->len != RARRAY(members)->len) {
+ rb_raise(rb_eTypeError, "struct size differs (%d required %d given)",
+ RARRAY(members)->len, RSTRUCT(s)->len);
+ }
+ return members;
+}
+
+static VALUE
+rb_struct_s_members_m(klass)
+ VALUE klass;
+{
+ VALUE members, ary;
+ VALUE *p, *pend;
+
+ members = rb_struct_s_members(klass);
+ ary = rb_ary_new2(RARRAY(members)->len);
+ p = RARRAY(members)->ptr; pend = p + RARRAY(members)->len;
+ while (p < pend) {
+ rb_ary_push(ary, rb_str_new2(rb_id2name(SYM2ID(*p))));
+ p++;
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * struct.members => array
+ *
+ * Returns an array of strings representing the names of the instance
+ * variables.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.members #=> ["name", "address", "zip"]
+ */
+
+static VALUE
+rb_struct_members_m(obj)
+ VALUE obj;
+{
+ return rb_struct_s_members_m(rb_obj_class(obj));
+}
+
+VALUE
+rb_struct_getmember(obj, id)
+ VALUE obj;
+ ID id;
+{
+ VALUE members, slot;
+ long i;
+
+ members = rb_struct_members(obj);
+ slot = ID2SYM(id);
+ for (i=0; i<RARRAY(members)->len; i++) {
+ if (RARRAY(members)->ptr[i] == slot) {
+ return RSTRUCT(obj)->ptr[i];
+ }
+ }
+ rb_name_error(id, "%s is not struct member", rb_id2name(id));
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_struct_ref(obj)
+ VALUE obj;
+{
+ return rb_struct_getmember(obj, ruby_frame->orig_func);
+}
+
+static VALUE rb_struct_ref0(obj) VALUE obj; {return RSTRUCT(obj)->ptr[0];}
+static VALUE rb_struct_ref1(obj) VALUE obj; {return RSTRUCT(obj)->ptr[1];}
+static VALUE rb_struct_ref2(obj) VALUE obj; {return RSTRUCT(obj)->ptr[2];}
+static VALUE rb_struct_ref3(obj) VALUE obj; {return RSTRUCT(obj)->ptr[3];}
+static VALUE rb_struct_ref4(obj) VALUE obj; {return RSTRUCT(obj)->ptr[4];}
+static VALUE rb_struct_ref5(obj) VALUE obj; {return RSTRUCT(obj)->ptr[5];}
+static VALUE rb_struct_ref6(obj) VALUE obj; {return RSTRUCT(obj)->ptr[6];}
+static VALUE rb_struct_ref7(obj) VALUE obj; {return RSTRUCT(obj)->ptr[7];}
+static VALUE rb_struct_ref8(obj) VALUE obj; {return RSTRUCT(obj)->ptr[8];}
+static VALUE rb_struct_ref9(obj) VALUE obj; {return RSTRUCT(obj)->ptr[9];}
+
+static VALUE (*ref_func[10])() = {
+ rb_struct_ref0,
+ rb_struct_ref1,
+ rb_struct_ref2,
+ rb_struct_ref3,
+ rb_struct_ref4,
+ rb_struct_ref5,
+ rb_struct_ref6,
+ rb_struct_ref7,
+ rb_struct_ref8,
+ rb_struct_ref9,
+};
+
+static void
+rb_struct_modify(s)
+ VALUE s;
+{
+ if (OBJ_FROZEN(s)) rb_error_frozen("Struct");
+ if (!OBJ_TAINTED(s) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify Struct");
+}
+
+static VALUE
+rb_struct_set(obj, val)
+ VALUE obj, val;
+{
+ VALUE members, slot;
+ ID id;
+ long i;
+
+ members = rb_struct_members(obj);
+ rb_struct_modify(obj);
+ id = ruby_frame->orig_func;
+ for (i=0; i<RARRAY(members)->len; i++) {
+ slot = RARRAY(members)->ptr[i];
+ if (rb_id_attrset(SYM2ID(slot)) == id) {
+ return RSTRUCT(obj)->ptr[i] = val;
+ }
+ }
+ rb_name_error(ruby_frame->last_func, "`%s' is not a struct member",
+ rb_id2name(id));
+ return Qnil; /* not reached */
+}
+
+static VALUE
+make_struct(name, members, klass)
+ VALUE name, members, klass;
+{
+ VALUE nstr;
+ ID id;
+ long i;
+
+ OBJ_FREEZE(members);
+ if (NIL_P(name)) {
+ nstr = rb_class_new(klass);
+ rb_make_metaclass(nstr, RBASIC(klass)->klass);
+ rb_class_inherited(klass, nstr);
+ }
+ else {
+ char *cname = StringValuePtr(name);
+ id = rb_intern(cname);
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "identifier %s needs to be constant", cname);
+ }
+ if (rb_const_defined_at(klass, id)) {
+ rb_warn("redefining constant Struct::%s", cname);
+ rb_mod_remove_const(klass, ID2SYM(id));
+ }
+ nstr = rb_define_class_under(klass, rb_id2name(id), klass);
+ }
+ rb_iv_set(nstr, "__size__", LONG2NUM(RARRAY(members)->len));
+ rb_iv_set(nstr, "__members__", members);
+
+ rb_define_alloc_func(nstr, struct_alloc);
+ rb_define_singleton_method(nstr, "new", rb_class_new_instance, -1);
+ rb_define_singleton_method(nstr, "[]", rb_class_new_instance, -1);
+ rb_define_singleton_method(nstr, "members", rb_struct_s_members_m, 0);
+ for (i=0; i< RARRAY(members)->len; i++) {
+ ID id = SYM2ID(RARRAY(members)->ptr[i]);
+ if (rb_is_local_id(id) || rb_is_const_id(id)) {
+ if (i<10) {
+ rb_define_method_id(nstr, id, ref_func[i], 0);
+ }
+ else {
+ rb_define_method_id(nstr, id, rb_struct_ref, 0);
+ }
+ rb_define_method_id(nstr, rb_id_attrset(id), rb_struct_set, 1);
+ }
+ }
+
+ return nstr;
+}
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_struct_define(const char *name, ...)
+#else
+rb_struct_define(name, va_alist)
+ const char *name;
+ va_dcl
+#endif
+{
+ va_list ar;
+ VALUE nm, ary;
+ char *mem;
+
+ if (!name) nm = Qnil;
+ else nm = rb_str_new2(name);
+ ary = rb_ary_new();
+
+ va_init_list(ar, name);
+ while ((mem = va_arg(ar, char*)) != 0) {
+ ID slot = rb_intern(mem);
+ rb_ary_push(ary, ID2SYM(slot));
+ }
+ va_end(ar);
+
+ return make_struct(nm, ary, rb_cStruct);
+}
+
+/*
+ * call-seq:
+ * Struct.new( [aString] [, aSym]+> ) => StructClass
+ * StructClass.new(arg, ...) => obj
+ * StructClass[arg, ...] => obj
+ *
+ * Creates a new class, named by <i>aString</i>, containing accessor
+ * methods for the given symbols. If the name <i>aString</i> is
+ * omitted, an anonymous structure class will be created. Otherwise,
+ * the name of this struct will appear as a constant in class
+ * <code>Struct</code>, so it must be unique for all
+ * <code>Struct</code>s in the system and should start with a capital
+ * letter. Assigning a structure class to a constant effectively gives
+ * the class the name of the constant.
+ *
+ * <code>Struct::new</code> returns a new <code>Class</code> object,
+ * which can then be used to create specific instances of the new
+ * structure. The number of actual parameters must be
+ * less than or equal to the number of attributes defined for this
+ * class; unset parameters default to \nil{}. Passing too many
+ * parameters will raise an \E{ArgumentError}.
+ *
+ * The remaining methods listed in this section (class and instance)
+ * are defined for this generated class.
+ *
+ * # Create a structure with a name in Struct
+ * Struct.new("Customer", :name, :address) #=> Struct::Customer
+ * Struct::Customer.new("Dave", "123 Main") #=> #<Struct::Customer name="Dave", address="123 Main">
+ *
+ * # Create a structure named by its constant
+ * Customer = Struct.new(:name, :address) #=> Customer
+ * Customer.new("Dave", "123 Main") #=> #<Customer name="Dave", address="123 Main">
+ */
+
+static VALUE
+rb_struct_s_def(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE name, rest;
+ long i;
+ VALUE st;
+ 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);
+ }
+ st = make_struct(name, rest, klass);
+ if (rb_block_given_p()) {
+ rb_mod_module_eval(0, 0, st);
+ }
+
+ return st;
+}
+
+/*
+ */
+
+static VALUE
+rb_struct_initialize(self, values)
+ VALUE self, values;
+{
+ VALUE klass = rb_obj_class(self);
+ VALUE size;
+ long n;
+
+ rb_struct_modify(self);
+ size = rb_struct_iv_get(klass, "__size__");
+ n = FIX2LONG(size);
+ if (n < RARRAY(values)->len) {
+ rb_raise(rb_eArgError, "struct size differs");
+ }
+ MEMCPY(RSTRUCT(self)->ptr, RARRAY(values)->ptr, VALUE, RARRAY(values)->len);
+ if (n > RARRAY(values)->len) {
+ rb_mem_clear(RSTRUCT(self)->ptr+RARRAY(values)->len,
+ n-RARRAY(values)->len);
+ }
+ return Qnil;
+}
+
+static VALUE
+struct_alloc(klass)
+ VALUE klass;
+{
+ VALUE size;
+ long n;
+ NEWOBJ(st, struct RStruct);
+ OBJSETUP(st, klass, T_STRUCT);
+
+ size = rb_struct_iv_get(klass, "__size__");
+ n = FIX2LONG(size);
+
+ st->ptr = ALLOC_N(VALUE, n);
+ rb_mem_clear(st->ptr, n);
+ st->len = n;
+
+ return (VALUE)st;
+}
+
+VALUE
+rb_struct_alloc(klass, values)
+ VALUE klass, values;
+{
+ return rb_class_new_instance(RARRAY(values)->len, RARRAY(values)->ptr, klass);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_struct_new(VALUE klass, ...)
+#else
+rb_struct_new(klass, va_alist)
+ VALUE klass;
+ va_dcl
+#endif
+{
+ VALUE sz, *mem;
+ long size, i;
+ va_list args;
+
+ sz = rb_struct_iv_get(klass, "__size__");
+ size = FIX2LONG(sz);
+ mem = ALLOCA_N(VALUE, size);
+ va_init_list(args, klass);
+ for (i=0; i<size; i++) {
+ mem[i] = va_arg(args, VALUE);
+ }
+ va_end(args);
+
+ return rb_class_new_instance(size, mem, klass);
+}
+
+/*
+ * call-seq:
+ * struct.each {|obj| block } => struct
+ *
+ * Calls <i>block</i> once for each instance variable, passing the
+ * value as a parameter.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.each {|x| puts(x) }
+ *
+ * <em>produces:</em>
+ *
+ * Joe Smith
+ * 123 Maple, Anytown NC
+ * 12345
+ */
+
+static VALUE
+rb_struct_each(s)
+ VALUE s;
+{
+ long i;
+
+ for (i=0; i<RSTRUCT(s)->len; i++) {
+ rb_yield(RSTRUCT(s)->ptr[i]);
+ }
+ return s;
+}
+
+/*
+ * call-seq:
+ * struct.each_pair {|sym, obj| block } => struct
+ *
+ * Calls <i>block</i> once for each instance variable, passing the name
+ * (as a symbol) and the value as parameters.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.each_pair {|name, value| puts("#{name} => #{value}") }
+ *
+ * <em>produces:</em>
+ *
+ * name => Joe Smith
+ * address => 123 Maple, Anytown NC
+ * zip => 12345
+ */
+
+static VALUE
+rb_struct_each_pair(s)
+ VALUE s;
+{
+ VALUE members;
+ long i;
+
+ members = rb_struct_members(s);
+ for (i=0; i<RSTRUCT(s)->len; i++) {
+ rb_yield_values(2, rb_ary_entry(members, i), RSTRUCT(s)->ptr[i]);
+ }
+ return s;
+}
+
+static VALUE
+inspect_struct(s)
+ VALUE s;
+{
+ char *cname = rb_class2name(rb_obj_class(s));
+ VALUE str, members;
+ long i;
+
+ members = rb_struct_members(s);
+ str = rb_str_buf_new2("#<struct ");
+ rb_str_cat2(str, cname);
+ rb_str_cat2(str, " ");
+ for (i=0; i<RSTRUCT(s)->len; i++) {
+ VALUE slot;
+ ID id;
+ char *p;
+
+ if (i > 0) {
+ rb_str_cat2(str, ", ");
+ }
+ slot = RARRAY(members)->ptr[i];
+ id = SYM2ID(slot);
+ if (rb_is_local_id(id) || rb_is_const_id(id)) {
+ p = rb_id2name(id);
+ rb_str_cat2(str, p);
+ }
+ else {
+ rb_str_append(str, rb_inspect(slot));
+ }
+ rb_str_cat2(str, "=");
+ rb_str_append(str, rb_inspect(RSTRUCT(s)->ptr[i]));
+ }
+ rb_str_cat2(str, ">");
+ OBJ_INFECT(str, s);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * struct.to_s => string
+ * struct.inspect => string
+ *
+ * Describe the contents of this struct in a string.
+ */
+
+static VALUE
+rb_struct_inspect(s)
+ VALUE s;
+{
+ if (rb_inspecting_p(s)) {
+ char *cname = rb_class2name(rb_obj_class(s));
+ size_t len = strlen(cname) + 14;
+ VALUE str = rb_str_new(0, len);
+
+ snprintf(RSTRING(str)->ptr, len+1, "#<struct %s:...>", cname);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ return str;
+ }
+ return rb_protect_inspect(inspect_struct, s, 0);
+}
+
+/*
+ * call-seq:
+ * struct.to_a => array
+ * struct.values => array
+ *
+ * Returns the values for this instance as an array.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.to_a[1] #=> "123 Maple, Anytown NC"
+ */
+
+static VALUE
+rb_struct_to_a(s)
+ VALUE s;
+{
+ return rb_ary_new4(RSTRUCT(s)->len, RSTRUCT(s)->ptr);
+}
+
+/* :nodoc: */
+static VALUE
+rb_struct_init_copy(copy, s)
+ VALUE copy, s;
+{
+ if (copy == s) return copy;
+ rb_check_frozen(copy);
+ if (!rb_obj_is_instance_of(s, rb_obj_class(copy))) {
+ rb_raise(rb_eTypeError, "wrong argument class");
+ }
+ if (RSTRUCT(copy)->len != RSTRUCT(s)->len) {
+ rb_raise(rb_eTypeError, "struct size mismatch");
+ }
+ MEMCPY(RSTRUCT(copy)->ptr, RSTRUCT(s)->ptr, VALUE, RSTRUCT(copy)->len);
+
+ return copy;
+}
+
+static VALUE
+rb_struct_aref_id(s, id)
+ VALUE s;
+ ID id;
+{
+ VALUE members;
+ long i, len;
+
+ members = rb_struct_members(s);
+ len = RARRAY(members)->len;
+ for (i=0; i<len; i++) {
+ if (SYM2ID(RARRAY(members)->ptr[i]) == id) {
+ return RSTRUCT(s)->ptr[i];
+ }
+ }
+ rb_name_error(id, "no member '%s' in struct", rb_id2name(id));
+ return Qnil; /* not reached */
+}
+
+/*
+ * call-seq:
+ * struct[symbol] => anObject
+ * struct[fixnum] => anObject
+ *
+ * Attribute Reference---Returns the value of the instance variable
+ * named by <i>symbol</i>, or indexed (0..length-1) by
+ * <i>fixnum</i>. Will raise <code>NameError</code> if the named
+ * variable does not exist, or <code>IndexError</code> if the index is
+ * out of range.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ *
+ * joe["name"] #=> "Joe Smith"
+ * joe[:name] #=> "Joe Smith"
+ * joe[0] #=> "Joe Smith"
+ */
+
+VALUE
+rb_struct_aref(s, idx)
+ VALUE s, idx;
+{
+ long i;
+
+ if (TYPE(idx) == T_STRING || TYPE(idx) == T_SYMBOL) {
+ return rb_struct_aref_id(s, rb_to_id(idx));
+ }
+
+ i = NUM2LONG(idx);
+ if (i < 0) i = RSTRUCT(s)->len + i;
+ if (i < 0)
+ rb_raise(rb_eIndexError, "offset %ld too small for struct(size:%ld)",
+ i, RSTRUCT(s)->len);
+ if (RSTRUCT(s)->len <= i)
+ rb_raise(rb_eIndexError, "offset %ld too large for struct(size:%ld)",
+ i, RSTRUCT(s)->len);
+ return RSTRUCT(s)->ptr[i];
+}
+
+static VALUE
+rb_struct_aset_id(s, id, val)
+ VALUE s, val;
+ ID id;
+{
+ VALUE members;
+ long i, len;
+
+ members = rb_struct_members(s);
+ rb_struct_modify(s);
+ len = RARRAY(members)->len;
+ if (RSTRUCT(s)->len != RARRAY(members)->len) {
+ rb_raise(rb_eTypeError, "struct size differs (%d required %d given)",
+ RARRAY(members)->len, RSTRUCT(s)->len);
+ }
+ for (i=0; i<len; i++) {
+ if (SYM2ID(RARRAY(members)->ptr[i]) == id) {
+ RSTRUCT(s)->ptr[i] = val;
+ return val;
+ }
+ }
+ rb_name_error(id, "no member '%s' in struct", rb_id2name(id));
+}
+
+/*
+ * call-seq:
+ * struct[symbol] = obj => obj
+ * struct[fixnum] = obj => obj
+ *
+ * Attribute Assignment---Assigns to the instance variable named by
+ * <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
+ * returns it. Will raise a <code>NameError</code> if the named
+ * variable does not exist, or an <code>IndexError</code> if the index
+ * is out of range.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ *
+ * joe["name"] = "Luke"
+ * joe[:zip] = "90210"
+ *
+ * joe.name #=> "Luke"
+ * joe.zip #=> "90210"
+ */
+
+VALUE
+rb_struct_aset(s, idx, val)
+ VALUE s, idx, val;
+{
+ long i;
+
+ if (TYPE(idx) == T_STRING || TYPE(idx) == T_SYMBOL) {
+ return rb_struct_aset_id(s, rb_to_id(idx), val);
+ }
+
+ i = NUM2LONG(idx);
+ if (i < 0) i = RSTRUCT(s)->len + i;
+ if (i < 0) {
+ rb_raise(rb_eIndexError, "offset %ld too small for struct(size:%ld)",
+ i, RSTRUCT(s)->len);
+ }
+ if (RSTRUCT(s)->len <= i) {
+ rb_raise(rb_eIndexError, "offset %ld too large for struct(size:%ld)",
+ i, RSTRUCT(s)->len);
+ }
+ rb_struct_modify(s);
+ return RSTRUCT(s)->ptr[i] = val;
+}
+
+static VALUE struct_entry _((VALUE, long));
+static VALUE
+struct_entry(s, n)
+ VALUE s;
+ long n;
+{
+ return rb_struct_aref(s, LONG2NUM(n));
+}
+
+/*
+ * call-seq:
+ * struct.values_at(selector,... ) => an_array
+ *
+ * Returns an array containing the elements in
+ * _self_ corresponding to the given selector(s). The selectors
+ * may be either integer indices or ranges.
+ * See also </code>.select<code>.
+ *
+ * a = %w{ a b c d e f }
+ * a.values_at(1, 3, 5)
+ * a.values_at(1, 3, 5, 7)
+ * a.values_at(-1, -3, -5, -7)
+ * a.values_at(1..3, 2...5)
+ */
+
+static VALUE
+rb_struct_values_at(argc, argv, s)
+ int argc;
+ VALUE *argv;
+ VALUE s;
+{
+ return rb_values_at(s, RSTRUCT(s)->len, argc, argv, struct_entry);
+}
+
+/*
+ * call-seq:
+ * struct.select {|i| block } => array
+ *
+ * Invokes the block passing in successive elements from
+ * <i>struct</i>, returning an array containing those elements
+ * for which the block returns a true value (equivalent to
+ * <code>Enumerable#select</code>).
+ *
+ * Lots = Struct.new(:a, :b, :c, :d, :e, :f)
+ * l = Lots.new(11, 22, 33, 44, 55, 66)
+ * l.select {|v| (v % 2).zero? } #=> [22, 44, 66]
+ */
+
+static VALUE
+rb_struct_select(argc, argv, s)
+ int argc;
+ VALUE *argv;
+ VALUE s;
+{
+ VALUE result;
+ long i;
+
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ result = rb_ary_new();
+ for (i = 0; i < RSTRUCT(s)->len; i++) {
+ if (RTEST(rb_yield(RSTRUCT(s)->ptr[i]))) {
+ rb_ary_push(result, RSTRUCT(s)->ptr[i]);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * call-seq:
+ * struct == other_struct => true or false
+ *
+ * Equality---Returns <code>true</code> if <i>other_struct</i> is
+ * equal to this one: they must be of the same class as generated by
+ * <code>Struct::new</code>, and the values of all instance variables
+ * must be equal (according to <code>Object#==</code>).
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
+ * joe == joejr #=> true
+ * joe == jane #=> false
+ */
+
+static VALUE
+rb_struct_equal(s, s2)
+ VALUE s, s2;
+{
+ long i;
+
+ if (s == s2) return Qtrue;
+ if (TYPE(s2) != T_STRUCT) return Qfalse;
+ if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
+ if (RSTRUCT(s)->len != RSTRUCT(s2)->len) {
+ rb_bug("inconsistent struct"); /* should never happen */
+ }
+
+ for (i=0; i<RSTRUCT(s)->len; i++) {
+ if (!rb_equal(RSTRUCT(s)->ptr[i], RSTRUCT(s2)->ptr[i])) return Qfalse;
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * struct.hash => fixnum
+ *
+ * Return a hash value based on this struct's contents.
+ */
+
+static VALUE
+rb_struct_hash(s)
+ VALUE s;
+{
+ long i, h;
+ VALUE n;
+
+ h = rb_hash(rb_obj_class(s));
+ for (i = 0; i < RSTRUCT(s)->len; i++) {
+ h = (h << 1) | (h<0 ? 1 : 0);
+ n = rb_hash(RSTRUCT(s)->ptr[i]);
+ h ^= NUM2LONG(n);
+ }
+ return LONG2FIX(h);
+}
+
+/*
+ * code-seq:
+ * struct.eql?(other) => true or false
+ *
+ * Two structures are equal if they are the same object, or if all their
+ * fields are equal (using <code>eql?</code>).
+ */
+
+static VALUE
+rb_struct_eql(s, s2)
+ VALUE s, s2;
+{
+ long i;
+
+ if (s == s2) return Qtrue;
+ if (TYPE(s2) != T_STRUCT) return Qfalse;
+ if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
+ if (RSTRUCT(s)->len != RSTRUCT(s2)->len) {
+ rb_bug("inconsistent struct"); /* should never happen */
+ }
+
+ for (i=0; i<RSTRUCT(s)->len; i++) {
+ if (!rb_eql(RSTRUCT(s)->ptr[i], RSTRUCT(s2)->ptr[i])) return Qfalse;
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * struct.length => fixnum
+ * struct.size => fixnum
+ *
+ * Returns the number of instance variables.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.length #=> 3
+ */
+
+static VALUE
+rb_struct_size(s)
+ VALUE s;
+{
+ return LONG2FIX(RSTRUCT(s)->len);
+}
+
+/*
+ * A <code>Struct</code> is a convenient way to bundle a number of
+ * attributes together, using accessor methods, without having to write
+ * an explicit class.
+ *
+ * The <code>Struct</code> class is a generator of specific classes,
+ * each one of which is defined to hold a set of variables and their
+ * accessors. In these examples, we'll call the generated class
+ * ``<i>Customer</i>Class,'' and we'll show an example instance of that
+ * class as ``<i>Customer</i>Inst.''
+ *
+ * In the descriptions that follow, the parameter <i>symbol</i> refers
+ * to a symbol, which is either a quoted string or a
+ * <code>Symbol</code> (such as <code>:name</code>).
+ */
+void
+Init_Struct()
+{
+ rb_cStruct = rb_define_class("Struct", rb_cObject);
+ rb_include_module(rb_cStruct, rb_mEnumerable);
+
+ rb_undef_alloc_func(rb_cStruct);
+ rb_define_singleton_method(rb_cStruct, "new", rb_struct_s_def, -1);
+
+ rb_define_method(rb_cStruct, "initialize", rb_struct_initialize, -2);
+ rb_define_method(rb_cStruct, "initialize_copy", rb_struct_init_copy, 1);
+
+ rb_define_method(rb_cStruct, "==", rb_struct_equal, 1);
+ rb_define_method(rb_cStruct, "eql?", rb_struct_eql, 1);
+ rb_define_method(rb_cStruct, "hash", rb_struct_hash, 0);
+
+ rb_define_method(rb_cStruct, "to_s", rb_struct_inspect, 0);
+ rb_define_method(rb_cStruct, "inspect", rb_struct_inspect, 0);
+ rb_define_method(rb_cStruct, "to_a", rb_struct_to_a, 0);
+ rb_define_method(rb_cStruct, "values", rb_struct_to_a, 0);
+ rb_define_method(rb_cStruct, "size", rb_struct_size, 0);
+ rb_define_method(rb_cStruct, "length", rb_struct_size, 0);
+
+ rb_define_method(rb_cStruct, "each", rb_struct_each, 0);
+ rb_define_method(rb_cStruct, "each_pair", rb_struct_each_pair, 0);
+ rb_define_method(rb_cStruct, "[]", rb_struct_aref, 1);
+ rb_define_method(rb_cStruct, "[]=", rb_struct_aset, 2);
+ rb_define_method(rb_cStruct, "select", rb_struct_select, -1);
+ rb_define_method(rb_cStruct, "values_at", rb_struct_values_at, -1);
+
+ rb_define_method(rb_cStruct, "members", rb_struct_members_m, 0);
+}
+/**********************************************************************
+
+ time.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-02 12:03:25 +0100 (Mon, 02 Feb 2009) $
+ created at: Tue Dec 28 14:31:59 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <math.h>
+
+VALUE rb_cTime;
+
+struct time_object {
+ struct timeval tv;
+ struct tm tm;
+ int gmt;
+ int tm_got;
+};
+
+#define GetTimeval(obj, tobj) \
+ Data_Get_Struct(obj, struct time_object, tobj)
+
+static void time_free _((void *));
+static VALUE time_utc_offset _((VALUE));
+
+static void
+time_free(tobj)
+ void *tobj;
+{
+ if (tobj) free(tobj);
+}
+
+static VALUE time_s_alloc _((VALUE));
+static VALUE
+time_s_alloc(klass)
+ VALUE klass;
+{
+ VALUE obj;
+ struct time_object *tobj;
+
+ obj = Data_Make_Struct(klass, struct time_object, 0, time_free, tobj);
+ tobj->tm_got=0;
+ tobj->tv.tv_sec = 0;
+ tobj->tv.tv_usec = 0;
+
+ return obj;
+}
+
+static void
+time_modify(time)
+ VALUE time;
+{
+ rb_check_frozen(time);
+ if (!OBJ_TAINTED(time) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify Time");
+}
+
+/*
+ * Document-method: now
+ *
+ * Synonym for <code>Time.new</code>. Returns a +Time+ object
+ * initialized tot he current system time.
+ *
+ * call-seq:
+ * Time.new -> time
+ *
+ * Returns a <code>Time</code> object initialized to the current system
+ * time. <b>Note:</b> The object created will be created using the
+ * resolution available on your system clock, and so may include
+ * fractional seconds.
+ *
+ * a = Time.new #=> Wed Apr 09 08:56:03 CDT 2003
+ * b = Time.new #=> Wed Apr 09 08:56:03 CDT 2003
+ * a == b #=> false
+ * "%.6f" % a.to_f #=> "1049896563.230740"
+ * "%.6f" % b.to_f #=> "1049896563.231466"
+ *
+ */
+
+static VALUE
+time_init(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ time_modify(time);
+ GetTimeval(time, tobj);
+ tobj->tm_got=0;
+ tobj->tv.tv_sec = 0;
+ tobj->tv.tv_usec = 0;
+ if (gettimeofday(&tobj->tv, 0) < 0) {
+ rb_sys_fail("gettimeofday");
+ }
+
+ return time;
+}
+
+#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
+
+static void
+time_overflow_p(secp, usecp)
+ time_t *secp, *usecp;
+{
+ time_t tmp, sec = *secp, usec = *usecp;
+
+ if (usec >= 1000000) { /* usec positive overflow */
+ tmp = sec + usec / 1000000;
+ usec %= 1000000;
+ if (sec > 0 && tmp < 0) {
+ rb_raise(rb_eRangeError, "out of Time range");
+ }
+ sec = tmp;
+ }
+ if (usec < 0) { /* usec negative overflow */
+ tmp = sec + NDIV(usec,1000000); /* negative div */
+ usec = NMOD(usec,1000000); /* negative mod */
+ if (sec < 0 && tmp > 0) {
+ rb_raise(rb_eRangeError, "out of Time range");
+ }
+ sec = tmp;
+ }
+#ifndef NEGATIVE_TIME_T
+ if (sec < 0 || (sec == 0 && usec < 0))
+ rb_raise(rb_eArgError, "time must be positive");
+#endif
+ *secp = sec;
+ *usecp = usec;
+}
+
+static VALUE time_new_internal _((VALUE, time_t, time_t));
+static VALUE
+time_new_internal(klass, sec, usec)
+ VALUE klass;
+ time_t sec, usec;
+{
+ VALUE time = time_s_alloc(klass);
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ time_overflow_p(&sec, &usec);
+ tobj->tv.tv_sec = sec;
+ tobj->tv.tv_usec = usec;
+
+ return time;
+}
+
+VALUE
+rb_time_new(sec, usec)
+ time_t sec, usec;
+{
+ return time_new_internal(rb_cTime, sec, usec);
+}
+
+static struct timeval
+time_timeval(time, interval)
+ VALUE time;
+ int interval;
+{
+ struct timeval t;
+ char *tstr = interval ? "time interval" : "time";
+
+#ifndef NEGATIVE_TIME_T
+ interval = 1;
+#endif
+
+ switch (TYPE(time)) {
+ case T_FIXNUM:
+ t.tv_sec = FIX2LONG(time);
+ if (interval && t.tv_sec < 0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ t.tv_usec = 0;
+ break;
+
+ case T_FLOAT:
+ if (interval && RFLOAT(time)->value < 0.0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ else {
+ double f, d;
+
+ d = modf(RFLOAT(time)->value, &f);
+ if (d < 0) {
+ d += 1;
+ f -= 1;
+ }
+ t.tv_sec = (time_t)f;
+ if (f != t.tv_sec) {
+ rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT(time)->value);
+ }
+ t.tv_usec = (time_t)(d*1e6+0.5);
+ }
+ break;
+
+ case T_BIGNUM:
+ t.tv_sec = NUM2LONG(time);
+ if (interval && t.tv_sec < 0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ t.tv_usec = 0;
+ break;
+
+ default:
+ rb_raise(rb_eTypeError, "can't convert %s into %s",
+ rb_obj_classname(time), tstr);
+ break;
+ }
+ return t;
+}
+
+struct timeval
+rb_time_interval(time)
+ VALUE time;
+{
+ return time_timeval(time, Qtrue);
+}
+
+struct timeval
+rb_time_timeval(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct timeval t;
+
+ if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
+ GetTimeval(time, tobj);
+ t = tobj->tv;
+ return t;
+ }
+ return time_timeval(time, Qfalse);
+}
+
+/*
+ * call-seq:
+ * Time.at( aTime ) => time
+ * Time.at( seconds [, microseconds] ) => time
+ *
+ * Creates a new time object with the value given by <i>aTime</i>, or
+ * the given number of <i>seconds</i> (and optional
+ * <i>microseconds</i>) from epoch. A non-portable feature allows the
+ * offset to be negative on some systems.
+ *
+ * Time.at(0) #=> Wed Dec 31 18:00:00 CST 1969
+ * Time.at(946702800) #=> Fri Dec 31 23:00:00 CST 1999
+ * Time.at(-284061600) #=> Sat Dec 31 00:00:00 CST 1960
+ */
+
+static VALUE
+time_s_at(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ struct timeval tv;
+ VALUE time, t;
+
+ if (rb_scan_args(argc, argv, "11", &time, &t) == 2) {
+ tv.tv_sec = NUM2LONG(time);
+ tv.tv_usec = NUM2LONG(t);
+ }
+ else {
+ tv = rb_time_timeval(time);
+ }
+ t = time_new_internal(klass, tv.tv_sec, tv.tv_usec);
+ if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
+ struct time_object *tobj, *tobj2;
+
+ GetTimeval(time, tobj);
+ GetTimeval(t, tobj2);
+ tobj2->gmt = tobj->gmt;
+ }
+ return t;
+}
+
+static char *months [12] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec",
+};
+
+static long
+obj2long(obj)
+ VALUE obj;
+{
+ if (TYPE(obj) == T_STRING) {
+ obj = rb_str_to_inum(obj, 10, Qfalse);
+ }
+
+ return NUM2LONG(obj);
+}
+
+static void
+time_arg(argc, argv, tm, usec)
+ int argc;
+ VALUE *argv;
+ struct tm *tm;
+ time_t *usec;
+{
+ VALUE v[8];
+ int i;
+ long year;
+
+ MEMZERO(tm, struct tm, 1);
+ *usec = 0;
+ if (argc == 10) {
+ v[0] = argv[5];
+ v[1] = argv[4];
+ v[2] = argv[3];
+ v[3] = argv[2];
+ v[4] = argv[1];
+ v[5] = argv[0];
+ v[6] = Qnil;
+ tm->tm_isdst = RTEST(argv[8]) ? 1 : 0;
+ }
+ else {
+ rb_scan_args(argc, argv, "17", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6],&v[7]);
+ /* v[6] may be usec or zone (parsedate) */
+ /* v[7] is wday (parsedate; ignored) */
+ tm->tm_wday = -1;
+ tm->tm_isdst = -1;
+ }
+
+ year = obj2long(v[0]);
+
+ if (0 <= year && year < 39) {
+ year += 100;
+ rb_warning("2 digits year is used");
+ }
+ else if (69 <= year && year < 139) {
+ rb_warning("2 or 3 digits year is used");
+ }
+ else {
+ year -= 1900;
+ }
+
+ tm->tm_year = year;
+
+ if (NIL_P(v[1])) {
+ tm->tm_mon = 0;
+ }
+ else {
+ VALUE s = rb_check_string_type(v[1]);
+ if (!NIL_P(s)) {
+ tm->tm_mon = -1;
+ for (i=0; i<12; i++) {
+ if (RSTRING(s)->len == 3 &&
+ strcasecmp(months[i], RSTRING(s)->ptr) == 0) {
+ tm->tm_mon = i;
+ break;
+ }
+ }
+ if (tm->tm_mon == -1) {
+ char c = RSTRING(s)->ptr[0];
+
+ if ('0' <= c && c <= '9') {
+ tm->tm_mon = obj2long(s)-1;
+ }
+ }
+ }
+ else {
+ tm->tm_mon = obj2long(v[1])-1;
+ }
+ }
+ if (NIL_P(v[2])) {
+ tm->tm_mday = 1;
+ }
+ else {
+ tm->tm_mday = obj2long(v[2]);
+ }
+ tm->tm_hour = NIL_P(v[3])?0:obj2long(v[3]);
+ tm->tm_min = NIL_P(v[4])?0:obj2long(v[4]);
+ tm->tm_sec = NIL_P(v[5])?0:obj2long(v[5]);
+ if (!NIL_P(v[6])) {
+ if (argc == 8) {
+ /* v[6] is timezone, but ignored */
+ }
+ else if (argc == 7) {
+ *usec = obj2long(v[6]);
+ }
+ }
+
+ /* value validation */
+ if (
+ tm->tm_year != year ||
+#ifndef NEGATIVE_TIME_T
+ tm->tm_year < 69 ||
+#endif
+ tm->tm_mon < 0 || tm->tm_mon > 11
+ || tm->tm_mday < 1 || tm->tm_mday > 31
+ || tm->tm_hour < 0 || tm->tm_hour > 23
+ || tm->tm_min < 0 || tm->tm_min > 59
+ || tm->tm_sec < 0 || tm->tm_sec > 60)
+ rb_raise(rb_eArgError, "argument out of range");
+}
+
+static VALUE time_gmtime _((VALUE));
+static VALUE time_localtime _((VALUE));
+static VALUE time_get_tm _((VALUE, int));
+
+static int
+leap_year_p(y)
+ long y;
+{
+ return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
+}
+
+#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
+
+static time_t
+timegm_noleapsecond(tm)
+ struct tm *tm;
+{
+ static int common_year_yday_offset[] = {
+ -1,
+ -1 + 31,
+ -1 + 31 + 28,
+ -1 + 31 + 28 + 31,
+ -1 + 31 + 28 + 31 + 30,
+ -1 + 31 + 28 + 31 + 30 + 31,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
+ /* 1 2 3 4 5 6 7 8 9 10 11 */
+ };
+ static int leap_year_yday_offset[] = {
+ -1,
+ -1 + 31,
+ -1 + 31 + 29,
+ -1 + 31 + 29 + 31,
+ -1 + 31 + 29 + 31 + 30,
+ -1 + 31 + 29 + 31 + 30 + 31,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
+ /* 1 2 3 4 5 6 7 8 9 10 11 */
+ };
+
+ long tm_year = tm->tm_year;
+ int tm_yday = tm->tm_mday;
+ if (leap_year_p(tm_year + 1900))
+ tm_yday += leap_year_yday_offset[tm->tm_mon];
+ else
+ tm_yday += common_year_yday_offset[tm->tm_mon];
+
+ /*
+ * `Seconds Since the Epoch' in SUSv3:
+ * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
+ * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
+ * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
+ */
+ return tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 +
+ (time_t)(tm_yday +
+ (tm_year-70)*365 +
+ DIV(tm_year-69,4) -
+ DIV(tm_year-1,100) +
+ DIV(tm_year+299,400))*86400;
+}
+
+static int
+tmcmp(a, b)
+ struct tm *a;
+ struct tm *b;
+{
+ if (a->tm_year != b->tm_year)
+ return a->tm_year < b->tm_year ? -1 : 1;
+ else if (a->tm_mon != b->tm_mon)
+ return a->tm_mon < b->tm_mon ? -1 : 1;
+ else if (a->tm_mday != b->tm_mday)
+ return a->tm_mday < b->tm_mday ? -1 : 1;
+ else if (a->tm_hour != b->tm_hour)
+ return a->tm_hour < b->tm_hour ? -1 : 1;
+ else if (a->tm_min != b->tm_min)
+ return a->tm_min < b->tm_min ? -1 : 1;
+ else if (a->tm_sec != b->tm_sec)
+ return a->tm_sec < b->tm_sec ? -1 : 1;
+ else
+ return 0;
+}
+
+#if SIZEOF_TIME_T == SIZEOF_LONG
+typedef unsigned long unsigned_time_t;
+#elif SIZEOF_TIME_T == SIZEOF_INT
+typedef unsigned int unsigned_time_t;
+#elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
+typedef unsigned LONG_LONG unsigned_time_t;
+#else
+# error cannot find integer type which size is same as time_t.
+#endif
+
+static time_t
+search_time_t(tptr, utc_p)
+ struct tm *tptr;
+ int utc_p;
+{
+ time_t guess, guess_lo, guess_hi;
+ struct tm *tm, tm_lo, tm_hi;
+ int d, have_guess;
+ int find_dst;
+
+ find_dst = 0 < tptr->tm_isdst;
+
+#ifdef NEGATIVE_TIME_T
+ guess_lo = (time_t)~((unsigned_time_t)~(time_t)0 >> 1);
+#else
+ guess_lo = 0;
+#endif
+ guess_hi = ((time_t)-1) < ((time_t)0) ?
+ (time_t)((unsigned_time_t)~(time_t)0 >> 1) :
+ ~(time_t)0;
+
+ guess = timegm_noleapsecond(tptr);
+ tm = (utc_p ? gmtime : localtime)(&guess);
+ if (tm) {
+ d = tmcmp(tptr, tm);
+ if (d == 0) return guess;
+ if (d < 0) {
+ guess_hi = guess;
+ guess -= 24 * 60 * 60;
+ }
+ else {
+ guess_lo = guess;
+ guess += 24 * 60 * 60;
+ }
+ if (guess_lo < guess && guess < guess_hi &&
+ (tm = (utc_p ? gmtime : localtime)(&guess)) != NULL) {
+ d = tmcmp(tptr, tm);
+ if (d == 0) return guess;
+ if (d < 0)
+ guess_hi = guess;
+ else
+ guess_lo = guess;
+ }
+ }
+
+ tm = (utc_p ? gmtime : localtime)(&guess_lo);
+ if (!tm) goto error;
+ d = tmcmp(tptr, tm);
+ if (d < 0) goto out_of_range;
+ if (d == 0) return guess_lo;
+ tm_lo = *tm;
+
+ tm = (utc_p ? gmtime : localtime)(&guess_hi);
+ if (!tm) goto error;
+ d = tmcmp(tptr, tm);
+ if (d > 0) goto out_of_range;
+ if (d == 0) return guess_hi;
+ tm_hi = *tm;
+
+ have_guess = 0;
+
+ while (guess_lo + 1 < guess_hi) {
+ /* there is a gap between guess_lo and guess_hi. */
+ unsigned long range = 0;
+ if (!have_guess) {
+ int a, b;
+ /*
+ Try precious guess by a linear interpolation at first.
+ `a' and `b' is a coefficient of guess_lo and guess_hi as:
+
+ guess = (guess_lo * a + guess_hi * b) / (a + b)
+
+ However this causes overflow in most cases, following assignment
+ is used instead:
+
+ guess = guess_lo / d * a + (guess_lo % d) * a / d
+ + guess_hi / d * b + (guess_hi % d) * b / d
+ where d = a + b
+
+ To avoid overflow in this assignment, `d' is restricted to less than
+ sqrt(2**31). By this restriction and other reasons, the guess is
+ not accurate and some error is expected. `range' approximates
+ the maximum error.
+
+ When these parameters are not suitable, i.e. guess is not within
+ guess_lo and guess_hi, simple guess by binary search is used.
+ */
+ range = 366 * 24 * 60 * 60;
+ a = (tm_hi.tm_year - tptr->tm_year);
+ b = (tptr->tm_year - tm_lo.tm_year);
+ /* 46000 is selected as `some big number less than sqrt(2**31)'. */
+ if (a + b <= 46000 / 12) {
+ range = 31 * 24 * 60 * 60;
+ a *= 12;
+ b *= 12;
+ a += tm_hi.tm_mon - tptr->tm_mon;
+ b += tptr->tm_mon - tm_lo.tm_mon;
+ if (a + b <= 46000 / 31) {
+ range = 24 * 60 * 60;
+ a *= 31;
+ b *= 31;
+ a += tm_hi.tm_mday - tptr->tm_mday;
+ b += tptr->tm_mday - tm_lo.tm_mday;
+ if (a + b <= 46000 / 24) {
+ range = 60 * 60;
+ a *= 24;
+ b *= 24;
+ a += tm_hi.tm_hour - tptr->tm_hour;
+ b += tptr->tm_hour - tm_lo.tm_hour;
+ if (a + b <= 46000 / 60) {
+ range = 60;
+ a *= 60;
+ b *= 60;
+ a += tm_hi.tm_min - tptr->tm_min;
+ b += tptr->tm_min - tm_lo.tm_min;
+ if (a + b <= 46000 / 60) {
+ range = 1;
+ a *= 60;
+ b *= 60;
+ a += tm_hi.tm_sec - tptr->tm_sec;
+ b += tptr->tm_sec - tm_lo.tm_sec;
+ }
+ }
+ }
+ }
+ }
+ if (a <= 0) a = 1;
+ if (b <= 0) b = 1;
+ d = a + b;
+ /*
+ Although `/' and `%' may produce unexpected result with negative
+ argument, it doesn't cause serious problem because there is a
+ fail safe.
+ */
+ guess = guess_lo / d * a + (guess_lo % d) * a / d
+ + guess_hi / d * b + (guess_hi % d) * b / d;
+ have_guess = 1;
+ }
+
+ if (guess <= guess_lo || guess_hi <= guess) {
+ /* Precious guess is invalid. try binary search. */
+ guess = guess_lo / 2 + guess_hi / 2;
+ if (guess <= guess_lo)
+ guess = guess_lo + 1;
+ else if (guess >= guess_hi)
+ guess = guess_hi - 1;
+ range = 0;
+ }
+
+ tm = (utc_p ? gmtime : localtime)(&guess);
+ if (!tm) goto error;
+ have_guess = 0;
+
+ d = tmcmp(tptr, tm);
+ if (d < 0) {
+ guess_hi = guess;
+ tm_hi = *tm;
+ if (range) {
+ guess = guess - range;
+ range = 0;
+ if (guess_lo < guess && guess < guess_hi)
+ have_guess = 1;
+ }
+ }
+ else if (d > 0) {
+ guess_lo = guess;
+ tm_lo = *tm;
+ if (range) {
+ guess = guess + range;
+ range = 0;
+ if (guess_lo < guess && guess < guess_hi)
+ have_guess = 1;
+ }
+ }
+ else {
+ if (!utc_p) {
+ /* If localtime is nonmonotonic, another result may exist. */
+ time_t guess2;
+ if (find_dst) {
+ guess2 = guess - 2 * 60 * 60;
+ tm = localtime(&guess2);
+ if (tm) {
+ if (tptr->tm_hour != (tm->tm_hour + 2) % 24 ||
+ tptr->tm_min != tm->tm_min ||
+ tptr->tm_sec != tm->tm_sec) {
+ guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
+ (tm->tm_min - tptr->tm_min) * 60 +
+ (tm->tm_sec - tptr->tm_sec);
+ if (tptr->tm_mday != tm->tm_mday)
+ guess2 += 24 * 60 * 60;
+ if (guess != guess2) {
+ tm = localtime(&guess2);
+ if (tmcmp(tptr, tm) == 0) {
+ if (guess < guess2)
+ return guess;
+ else
+ return guess2;
+ }
+ }
+ }
+ }
+ }
+ else {
+ guess2 = guess + 2 * 60 * 60;
+ tm = localtime(&guess2);
+ if (tm) {
+ if ((tptr->tm_hour + 2) % 24 != tm->tm_hour ||
+ tptr->tm_min != tm->tm_min ||
+ tptr->tm_sec != tm->tm_sec) {
+ guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
+ (tm->tm_min - tptr->tm_min) * 60 +
+ (tm->tm_sec - tptr->tm_sec);
+ if (tptr->tm_mday != tm->tm_mday)
+ guess2 -= 24 * 60 * 60;
+ if (guess != guess2) {
+ tm = localtime(&guess2);
+ if (tmcmp(tptr, tm) == 0) {
+ if (guess < guess2)
+ return guess2;
+ else
+ return guess;
+ }
+ }
+ }
+ }
+ }
+ }
+ return guess;
+ }
+ }
+ /* Given argument has no corresponding time_t. Let's outerpolation. */
+ if (tm_lo.tm_year == tptr->tm_year && tm_lo.tm_mon == tptr->tm_mon) {
+ return guess_lo +
+ (tptr->tm_mday - tm_lo.tm_mday) * 24 * 60 * 60 +
+ (tptr->tm_hour - tm_lo.tm_hour) * 60 * 60 +
+ (tptr->tm_min - tm_lo.tm_min) * 60 +
+ (tptr->tm_sec - tm_lo.tm_sec);
+ }
+ else if (tm_hi.tm_year == tptr->tm_year && tm_hi.tm_mon == tptr->tm_mon) {
+ return guess_hi +
+ (tptr->tm_mday - tm_hi.tm_mday) * 24 * 60 * 60 +
+ (tptr->tm_hour - tm_hi.tm_hour) * 60 * 60 +
+ (tptr->tm_min - tm_hi.tm_min) * 60 +
+ (tptr->tm_sec - tm_hi.tm_sec);
+ }
+
+ out_of_range:
+ rb_raise(rb_eArgError, "time out of range");
+
+ error:
+ rb_raise(rb_eArgError, "gmtime/localtime error");
+ return 0; /* not reached */
+}
+
+static time_t
+make_time_t(tptr, utc_p)
+ struct tm *tptr;
+ int utc_p;
+{
+ time_t t;
+ struct tm *tmp, buf;
+ buf = *tptr;
+ if (utc_p) {
+#if defined(HAVE_TIMEGM)
+ if ((t = timegm(&buf)) != -1)
+ return t;
+#ifdef NEGATIVE_TIME_T
+ if ((tmp = gmtime(&t)) &&
+ tptr->tm_year == tmp->tm_year &&
+ tptr->tm_mon == tmp->tm_mon &&
+ tptr->tm_mday == tmp->tm_mday &&
+ tptr->tm_hour == tmp->tm_hour &&
+ tptr->tm_min == tmp->tm_min &&
+ tptr->tm_sec == tmp->tm_sec)
+ return t;
+#endif
+#endif
+ return search_time_t(&buf, utc_p);
+ }
+ else {
+#if defined(HAVE_MKTIME)
+ if ((t = mktime(&buf)) != -1)
+ return t;
+#ifdef NEGATIVE_TIME_T
+ if ((tmp = localtime(&t)) &&
+ tptr->tm_year == tmp->tm_year &&
+ tptr->tm_mon == tmp->tm_mon &&
+ tptr->tm_mday == tmp->tm_mday &&
+ tptr->tm_hour == tmp->tm_hour &&
+ tptr->tm_min == tmp->tm_min &&
+ tptr->tm_sec == tmp->tm_sec)
+ return t;
+#endif
+#endif
+ return search_time_t(&buf, utc_p);
+ }
+}
+
+static VALUE
+time_utc_or_local(argc, argv, utc_p, klass)
+ int argc;
+ VALUE *argv;
+ int utc_p;
+ VALUE klass;
+{
+ struct tm tm;
+ VALUE time;
+ time_t usec;
+
+ time_arg(argc, argv, &tm, &usec);
+ time = time_new_internal(klass, make_time_t(&tm, utc_p), usec);
+ if (utc_p) return time_gmtime(time);
+ return time_localtime(time);
+}
+
+/*
+ * call-seq:
+ * Time.utc( year [, month, day, hour, min, sec, usec] ) => time
+ * Time.utc( sec, min, hour, day, month, year, wday, yday, isdst, tz
+ * ) => time
+ * Time.gm( year [, month, day, hour, min, sec, usec] ) => time
+ * Time.gm( sec, min, hour, day, month, year, wday, yday, isdst, tz
+ * ) => time
+ *
+ * Creates a time based on given values, interpreted as UTC (GMT). The
+ * year must be specified. Other values default to the minimum value
+ * for that field (and may be <code>nil</code> or omitted). Months may
+ * be specified by numbers from 1 to 12, or by the three-letter English
+ * month names. Hours are specified on a 24-hour clock (0..23). Raises
+ * an <code>ArgumentError</code> if any values are out of range. Will
+ * also accept ten arguments in the order output by
+ * <code>Time#to_a</code>.
+ *
+ * Time.utc(2000,"jan",1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ * Time.gm(2000,"jan",1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ */
+static VALUE
+time_s_mkutc(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return time_utc_or_local(argc, argv, Qtrue, klass);
+}
+
+/*
+ * call-seq:
+ * Time.local( year [, month, day, hour, min, sec, usec] ) => time
+ * Time.local( sec, min, hour, day, month, year, wday, yday, isdst,
+ * tz ) => time
+ * Time.mktime( year, month, day, hour, min, sec, usec ) => time
+ *
+ * Same as <code>Time::gm</code>, but interprets the values in the
+ * local time zone.
+ *
+ * Time.local(2000,"jan",1,20,15,1) #=> Sat Jan 01 20:15:01 CST 2000
+ */
+
+static VALUE
+time_s_mktime(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return time_utc_or_local(argc, argv, Qfalse, klass);
+}
+
+/*
+ * call-seq:
+ * time.to_i => int
+ * time.tv_sec => int
+ *
+ * Returns the value of <i>time</i> as an integer number of seconds
+ * since epoch.
+ *
+ * t = Time.now
+ * "%10.5f" % t.to_f #=> "1049896564.17839"
+ * t.to_i #=> 1049896564
+ */
+
+static VALUE
+time_to_i(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ return LONG2NUM(tobj->tv.tv_sec);
+}
+
+/*
+ * call-seq:
+ * time.to_f => float
+ *
+ * Returns the value of <i>time</i> as a floating point number of
+ * seconds since epoch.
+ *
+ * t = Time.now
+ * "%10.5f" % t.to_f #=> "1049896564.13654"
+ * t.to_i #=> 1049896564
+ */
+
+static VALUE
+time_to_f(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ return rb_float_new((double)tobj->tv.tv_sec+(double)tobj->tv.tv_usec/1e6);
+}
+
+/*
+ * call-seq:
+ * time.usec => int
+ * time.tv_usec => int
+ *
+ * Returns just the number of microseconds for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * "%10.6f" % t.to_f #=> "1049896564.259970"
+ * t.usec #=> 259970
+ */
+
+static VALUE
+time_usec(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ return LONG2NUM(tobj->tv.tv_usec);
+}
+
+/*
+ * call-seq:
+ * time <=> other_time => -1, 0, +1
+ * time <=> numeric => -1, 0, +1
+ *
+ * Comparison---Compares <i>time</i> with <i>other_time</i> or with
+ * <i>numeric</i>, which is the number of seconds (possibly
+ * fractional) since epoch.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t2 = t + 2592000 #=> Fri May 09 08:56:03 CDT 2003
+ * t <=> t2 #=> -1
+ * t2 <=> t #=> 1
+ * t <=> t #=> 0
+ */
+
+static VALUE
+time_cmp(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj1, *tobj2;
+
+ GetTimeval(time1, tobj1);
+ if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
+ GetTimeval(time2, tobj2);
+ if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
+ if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0);
+ if (tobj1->tv.tv_usec > tobj2->tv.tv_usec) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+ if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * time.eql?(other_time)
+ *
+ * Return <code>true</code> if <i>time</i> and <i>other_time</i> are
+ * both <code>Time</code> objects with the same seconds and fractional
+ * seconds.
+ */
+
+static VALUE
+time_eql(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj1, *tobj2;
+
+ GetTimeval(time1, tobj1);
+ if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
+ GetTimeval(time2, tobj2);
+ if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
+ if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * time.utc? => true or false
+ * time.gmt? => true or false
+ *
+ * Returns <code>true</code> if <i>time</i> represents a time in UTC
+ * (GMT).
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.utc? #=> false
+ * t = Time.gm(2000,"jan",1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ * t.utc? #=> true
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.gmt? #=> false
+ * t = Time.gm(2000,1,1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ * t.gmt? #=> true
+ */
+
+static VALUE
+time_utc_p(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->gmt) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * time.hash => fixnum
+ *
+ * Return a hash code for this time object.
+ */
+
+static VALUE
+time_hash(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ long hash;
+
+ GetTimeval(time, tobj);
+ hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec;
+ return LONG2FIX(hash);
+}
+
+/* :nodoc: */
+static VALUE
+time_init_copy(copy, time)
+ VALUE copy, time;
+{
+ struct time_object *tobj, *tcopy;
+
+ if (copy == time) return copy;
+ time_modify(copy);
+ if (TYPE(time) != T_DATA || RDATA(time)->dfree != time_free) {
+ rb_raise(rb_eTypeError, "wrong argument type");
+ }
+ GetTimeval(time, tobj);
+ GetTimeval(copy, tcopy);
+ MEMCPY(tcopy, tobj, struct time_object, 1);
+
+ return copy;
+}
+
+static VALUE
+time_dup(time)
+ VALUE time;
+{
+ VALUE dup = time_s_alloc(CLASS_OF(time));
+ time_init_copy(dup, time);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * time.localtime => time
+ *
+ * Converts <i>time</i> to local time (using the local time zone in
+ * effect for this process) modifying the receiver.
+ *
+ * t = Time.gm(2000, "jan", 1, 20, 15, 1)
+ * t.gmt? #=> true
+ * t.localtime #=> Sat Jan 01 14:15:01 CST 2000
+ * t.gmt? #=> false
+ */
+
+static VALUE
+time_localtime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm_tmp;
+ time_t t;
+
+ GetTimeval(time, tobj);
+ if (!tobj->gmt) {
+ if (tobj->tm_got)
+ return time;
+ }
+ else {
+ time_modify(time);
+ }
+ t = tobj->tv.tv_sec;
+ tm_tmp = localtime(&t);
+ if (!tm_tmp)
+ rb_raise(rb_eArgError, "localtime error");
+ tobj->tm = *tm_tmp;
+ tobj->tm_got = 1;
+ tobj->gmt = 0;
+ return time;
+}
+
+/*
+ * call-seq:
+ * time.gmtime => time
+ * time.utc => time
+ *
+ * Converts <i>time</i> to UTC (GMT), modifying the receiver.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.gmt? #=> false
+ * t.gmtime #=> Wed Apr 09 13:56:03 UTC 2003
+ * t.gmt? #=> true
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.utc? #=> false
+ * t.utc #=> Wed Apr 09 13:56:04 UTC 2003
+ * t.utc? #=> true
+ */
+
+static VALUE
+time_gmtime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm_tmp;
+ time_t t;
+
+ GetTimeval(time, tobj);
+ if (tobj->gmt) {
+ if (tobj->tm_got)
+ return time;
+ }
+ else {
+ time_modify(time);
+ }
+ t = tobj->tv.tv_sec;
+ tm_tmp = gmtime(&t);
+ if (!tm_tmp)
+ rb_raise(rb_eArgError, "gmtime error");
+ tobj->tm = *tm_tmp;
+ tobj->tm_got = 1;
+ tobj->gmt = 1;
+ return time;
+}
+
+/*
+ * call-seq:
+ * time.getlocal => new_time
+ *
+ * Returns a new <code>new_time</code> object representing <i>time</i> in
+ * local time (using the local time zone in effect for this process).
+ *
+ * t = Time.gm(2000,1,1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ * t.gmt? #=> true
+ * l = t.getlocal #=> Sat Jan 01 14:15:01 CST 2000
+ * l.gmt? #=> false
+ * t == l #=> true
+ */
+
+static VALUE
+time_getlocaltime(time)
+ VALUE time;
+{
+ return time_localtime(time_dup(time));
+}
+
+/*
+ * call-seq:
+ * time.getgm => new_time
+ * time.getutc => new_time
+ *
+ * Returns a new <code>new_time</code> object representing <i>time</i> in
+ * UTC.
+ *
+ * t = Time.local(2000,1,1,20,15,1) #=> Sat Jan 01 20:15:01 CST 2000
+ * t.gmt? #=> false
+ * y = t.getgm #=> Sun Jan 02 02:15:01 UTC 2000
+ * y.gmt? #=> true
+ * t == y #=> true
+ */
+
+static VALUE
+time_getgmtime(time)
+ VALUE time;
+{
+ return time_gmtime(time_dup(time));
+}
+
+static VALUE
+time_get_tm(time, gmt)
+ VALUE time;
+ int gmt;
+{
+ if (gmt) return time_gmtime(time);
+ return time_localtime(time);
+}
+
+/*
+ * call-seq:
+ * time.asctime => string
+ * time.ctime => string
+ *
+ * Returns a canonical string representation of <i>time</i>.
+ *
+ * Time.now.asctime #=> "Wed Apr 9 08:56:03 2003"
+ */
+
+static VALUE
+time_asctime(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ char *s;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ s = asctime(&tobj->tm);
+ if (s[24] == '\n') s[24] = '\0';
+
+ return rb_str_new2(s);
+}
+
+/*
+ * call-seq:
+ * time.inspect => string
+ * time.to_s => string
+ *
+ * Returns a string representing <i>time</i>. Equivalent to calling
+ * <code>Time#strftime</code> with a format string of ``<code>%a</code>
+ * <code>%b</code> <code>%d</code> <code>%H:%M:%S</code>
+ * <code>%Z</code> <code>%Y</code>''.
+ *
+ * Time.now.to_s #=> "Wed Apr 09 08:56:04 CDT 2003"
+ */
+
+static VALUE
+time_to_s(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ char buf[128];
+ int len;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ if (tobj->gmt == 1) {
+ len = strftime(buf, 128, "%a %b %d %H:%M:%S UTC %Y", &tobj->tm);
+ }
+ else {
+ time_t off;
+ char buf2[32];
+ char sign = '+';
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+ off = tobj->tm.tm_gmtoff;
+#else
+ VALUE tmp = time_utc_offset(time);
+ off = NUM2INT(tmp);
+#endif
+ if (off < 0) {
+ sign = '-';
+ off = -off;
+ }
+ sprintf(buf2, "%%a %%b %%d %%H:%%M:%%S %c%02d%02d %%Y",
+ sign, (int)(off/3600), (int)(off%3600/60));
+ len = strftime(buf, 128, buf2, &tobj->tm);
+ }
+ return rb_str_new(buf, len);
+}
+
+static VALUE
+time_add(tobj, offset, sign)
+ struct time_object *tobj;
+ VALUE offset;
+ int sign;
+{
+ double v = NUM2DBL(offset);
+ double f, d;
+ unsigned_time_t sec_off;
+ time_t usec_off, sec, usec;
+ VALUE result;
+
+ if (v < 0) {
+ v = -v;
+ sign = -sign;
+ }
+ d = modf(v, &f);
+ sec_off = (unsigned_time_t)f;
+ if (f != (double)sec_off)
+ rb_raise(rb_eRangeError, "time %s %f out of Time range",
+ sign < 0 ? "-" : "+", v);
+ usec_off = (time_t)(d*1e6+0.5);
+
+ if (sign < 0) {
+ sec = tobj->tv.tv_sec - sec_off;
+ usec = tobj->tv.tv_usec - usec_off;
+ if (sec > tobj->tv.tv_sec)
+ rb_raise(rb_eRangeError, "time - %f out of Time range", v);
+ }
+ else {
+ sec = tobj->tv.tv_sec + sec_off;
+ usec = tobj->tv.tv_usec + usec_off;
+ if (sec < tobj->tv.tv_sec)
+ rb_raise(rb_eRangeError, "time + %f out of Time range", v);
+ }
+ result = rb_time_new(sec, usec);
+ if (tobj->gmt) {
+ GetTimeval(result, tobj);
+ tobj->gmt = 1;
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * time + numeric => time
+ *
+ * Addition---Adds some number of seconds (possibly fractional) to
+ * <i>time</i> and returns that value as a new time.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t + (60 * 60 * 24) #=> Thu Apr 10 08:56:03 CDT 2003
+ */
+
+static VALUE
+time_plus(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj;
+ GetTimeval(time1, tobj);
+
+ if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
+ rb_raise(rb_eTypeError, "time + time?");
+ }
+ return time_add(tobj, time2, 1);
+}
+
+/*
+ * call-seq:
+ * time - other_time => float
+ * time - numeric => time
+ *
+ * Difference---Returns a new time that represents the difference
+ * between two times, or subtracts the given number of seconds in
+ * <i>numeric</i> from <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t2 = t + 2592000 #=> Fri May 09 08:56:03 CDT 2003
+ * t2 - t #=> 2592000.0
+ * t2 - 2592000 #=> Wed Apr 09 08:56:03 CDT 2003
+ */
+
+static VALUE
+time_minus(time1, time2)
+ VALUE time1, time2;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time1, tobj);
+ if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
+ struct time_object *tobj2;
+ double f;
+
+ GetTimeval(time2, tobj2);
+ f = (double)tobj->tv.tv_sec - (double)tobj2->tv.tv_sec;
+ f += ((double)tobj->tv.tv_usec - (double)tobj2->tv.tv_usec)*1e-6;
+ /* XXX: should check float overflow on 64bit time_t platforms */
+
+ return rb_float_new(f);
+ }
+ return time_add(tobj, time2, -1);
+}
+
+/*
+ * call-seq:
+ * time.succ => new_time
+ *
+ * Return a new time object, one second later than <code>time</code>.
+ */
+
+static VALUE
+time_succ(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ int gmt;
+
+ GetTimeval(time, tobj);
+ gmt = tobj->gmt;
+ time = rb_time_new(tobj->tv.tv_sec + 1, tobj->tv.tv_usec);
+ GetTimeval(time, tobj);
+ tobj->gmt = gmt;
+ return time;
+}
+
+/*
+ * call-seq:
+ * time.sec => fixnum
+ *
+ * Returns the second of the minute (0..60)<em>[Yes, seconds really can
+ * range from zero to 60. This allows the system to inject leap seconds
+ * every now and then to correct for the fact that years are not really
+ * a convenient number of hours long.]</em> for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.sec #=> 4
+ */
+
+static VALUE
+time_sec(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_sec);
+}
+
+/*
+ * call-seq:
+ * time.min => fixnum
+ *
+ * Returns the minute of the hour (0..59) for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.min #=> 56
+ */
+
+static VALUE
+time_min(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_min);
+}
+
+/*
+ * call-seq:
+ * time.hour => fixnum
+ *
+ * Returns the hour of the day (0..23) for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.hour #=> 8
+ */
+
+static VALUE
+time_hour(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_hour);
+}
+
+/*
+ * call-seq:
+ * time.day => fixnum
+ * time.mday => fixnum
+ *
+ * Returns the day of the month (1..n) for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.day #=> 9
+ * t.mday #=> 9
+ */
+
+static VALUE
+time_mday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_mday);
+}
+
+/*
+ * call-seq:
+ * time.mon => fixnum
+ * time.month => fixnum
+ *
+ * Returns the month of the year (1..12) for <i>time</i>.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:03 CDT 2003
+ * t.mon #=> 4
+ * t.month #=> 4
+ */
+
+static VALUE
+time_mon(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_mon+1);
+}
+
+/*
+ * call-seq:
+ * time.year => fixnum
+ *
+ * Returns the year for <i>time</i> (including the century).
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.year #=> 2003
+ */
+
+static VALUE
+time_year(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return LONG2NUM((long)tobj->tm.tm_year+1900);
+}
+
+/*
+ * call-seq:
+ * time.wday => fixnum
+ *
+ * Returns an integer representing the day of the week, 0..6, with
+ * Sunday == 0.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.wday #=> 3
+ */
+
+static VALUE
+time_wday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_wday);
+}
+
+/*
+ * call-seq:
+ * time.yday => fixnum
+ *
+ * Returns an integer representing the day of the year, 1..366.
+ *
+ * t = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t.yday #=> 99
+ */
+
+static VALUE
+time_yday(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return INT2FIX(tobj->tm.tm_yday+1);
+}
+
+/*
+ * call-seq:
+ * time.isdst => true or false
+ * time.dst? => true or false
+ *
+ * Returns <code>true</code> if <i>time</i> occurs during Daylight
+ * Saving Time in its time zone.
+ *
+ * Time.local(2000, 7, 1).isdst #=> true
+ * Time.local(2000, 1, 1).isdst #=> false
+ * Time.local(2000, 7, 1).dst? #=> true
+ * Time.local(2000, 1, 1).dst? #=> false
+ */
+
+static VALUE
+time_isdst(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return tobj->tm.tm_isdst?Qtrue:Qfalse;
+}
+
+/*
+ * call-seq:
+ * time.zone => string
+ *
+ * Returns the name of the time zone used for <i>time</i>. As of Ruby
+ * 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
+ *
+ * t = Time.gm(2000, "jan", 1, 20, 15, 1)
+ * t.zone #=> "UTC"
+ * t = Time.local(2000, "jan", 1, 20, 15, 1)
+ * t.zone #=> "CST"
+ */
+
+static VALUE
+time_zone(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+#if !defined(HAVE_TM_ZONE) && (!defined(HAVE_TZNAME) || !defined(HAVE_DAYLIGHT))
+ char buf[64];
+ int len;
+#endif
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+
+ if (tobj->gmt == 1) {
+ return rb_str_new2("UTC");
+ }
+#if defined(HAVE_TM_ZONE)
+ return rb_str_new2(tobj->tm.tm_zone);
+#elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
+ return rb_str_new2(tzname[daylight && tobj->tm.tm_isdst]);
+#else
+ len = strftime(buf, 64, "%Z", &tobj->tm);
+ return rb_str_new(buf, len);
+#endif
+}
+
+/*
+ * call-seq:
+ * time.gmt_offset => fixnum
+ * time.gmtoff => fixnum
+ * time.utc_offset => fixnum
+ *
+ * Returns the offset in seconds between the timezone of <i>time</i>
+ * and UTC.
+ *
+ * t = Time.gm(2000,1,1,20,15,1) #=> Sat Jan 01 20:15:01 UTC 2000
+ * t.gmt_offset #=> 0
+ * l = t.getlocal #=> Sat Jan 01 14:15:01 CST 2000
+ * l.gmt_offset #=> -21600
+ */
+
+static VALUE
+time_utc_offset(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+
+ if (tobj->gmt == 1) {
+ return INT2FIX(0);
+ }
+ else {
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+ return INT2NUM(tobj->tm.tm_gmtoff);
+#else
+ struct tm *u, *l;
+ time_t t;
+ long off;
+ l = &tobj->tm;
+ t = tobj->tv.tv_sec;
+ u = gmtime(&t);
+ if (!u)
+ rb_raise(rb_eArgError, "gmtime error");
+ if (l->tm_year != u->tm_year)
+ off = l->tm_year < u->tm_year ? -1 : 1;
+ else if (l->tm_mon != u->tm_mon)
+ off = l->tm_mon < u->tm_mon ? -1 : 1;
+ else if (l->tm_mday != u->tm_mday)
+ off = l->tm_mday < u->tm_mday ? -1 : 1;
+ else
+ off = 0;
+ off = off * 24 + l->tm_hour - u->tm_hour;
+ off = off * 60 + l->tm_min - u->tm_min;
+ off = off * 60 + l->tm_sec - u->tm_sec;
+ return LONG2FIX(off);
+#endif
+ }
+}
+
+/*
+ * call-seq:
+ * time.to_a => array
+ *
+ * Returns a ten-element <i>array</i> of values for <i>time</i>:
+ * {<code>[ sec, min, hour, day, month, year, wday, yday, isdst, zone
+ * ]</code>}. See the individual methods for an explanation of the
+ * valid ranges of each value. The ten elements can be passed directly
+ * to <code>Time::utc</code> or <code>Time::local</code> to create a
+ * new <code>Time</code>.
+ *
+ * now = Time.now #=> Wed Apr 09 08:56:04 CDT 2003
+ * t = now.to_a #=> [4, 56, 8, 9, 4, 2003, 3, 99, true, "CDT"]
+ */
+
+static VALUE
+time_to_a(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ return rb_ary_new3(10,
+ INT2FIX(tobj->tm.tm_sec),
+ INT2FIX(tobj->tm.tm_min),
+ INT2FIX(tobj->tm.tm_hour),
+ INT2FIX(tobj->tm.tm_mday),
+ INT2FIX(tobj->tm.tm_mon+1),
+ LONG2NUM((long)tobj->tm.tm_year+1900),
+ INT2FIX(tobj->tm.tm_wday),
+ INT2FIX(tobj->tm.tm_yday+1),
+ tobj->tm.tm_isdst?Qtrue:Qfalse,
+ time_zone(time));
+}
+
+#define SMALLBUF 100
+static int
+rb_strftime(buf, format, time)
+ char **buf;
+ const char *format;
+ struct tm *time;
+{
+ int size, len, flen;
+
+ (*buf)[0] = '\0';
+ flen = strlen(format);
+ if (flen == 0) {
+ return 0;
+ }
+ errno = 0;
+ len = strftime(*buf, SMALLBUF, format, time);
+ if (len != 0 || (**buf == '\0' && errno != ERANGE)) return len;
+ for (size=1024; ; size*=2) {
+ *buf = xmalloc(size);
+ (*buf)[0] = '\0';
+ len = strftime(*buf, size, format, time);
+ /*
+ * buflen can be zero EITHER because there's not enough
+ * room in the string, or because the control command
+ * goes to the empty string. Make a reasonable guess that
+ * if the buffer is 1024 times bigger than the length of the
+ * format string, it's not failing for lack of room.
+ */
+ if (len > 0 || size >= 1024 * flen) return len;
+ free(*buf);
+ }
+ /* not reached */
+}
+
+/*
+ * call-seq:
+ * time.strftime( string ) => string
+ *
+ * Formats <i>time</i> according to the directives in the given format
+ * string. Any text not listed as a directive will be passed through
+ * to the output string.
+ *
+ * Format meaning:
+ * %a - The abbreviated weekday name (``Sun'')
+ * %A - The full weekday name (``Sunday'')
+ * %b - The abbreviated month name (``Jan'')
+ * %B - The full month name (``January'')
+ * %c - The preferred local date and time representation
+ * %d - Day of the month (01..31)
+ * %H - Hour of the day, 24-hour clock (00..23)
+ * %I - Hour of the day, 12-hour clock (01..12)
+ * %j - Day of the year (001..366)
+ * %m - Month of the year (01..12)
+ * %M - Minute of the hour (00..59)
+ * %p - Meridian indicator (``AM'' or ``PM'')
+ * %S - Second of the minute (00..60)
+ * %U - Week number of the current year,
+ * starting with the first Sunday as the first
+ * day of the first week (00..53)
+ * %W - Week number of the current year,
+ * starting with the first Monday as the first
+ * day of the first week (00..53)
+ * %w - Day of the week (Sunday is 0, 0..6)
+ * %x - Preferred representation for the date alone, no time
+ * %X - Preferred representation for the time alone, no date
+ * %y - Year without a century (00..99)
+ * %Y - Year with century
+ * %Z - Time zone name
+ * %% - Literal ``%'' character
+ *
+ * t = Time.now
+ * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 04/09/2003"
+ * t.strftime("at %I:%M%p") #=> "at 08:56AM"
+ */
+
+static VALUE
+time_strftime(time, format)
+ VALUE time, format;
+{
+ struct time_object *tobj;
+ char buffer[SMALLBUF], *buf = buffer;
+ const char *fmt;
+ long len;
+ VALUE str;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got == 0) {
+ time_get_tm(time, tobj->gmt);
+ }
+ StringValue(format);
+ format = rb_str_new4(format);
+ fmt = RSTRING(format)->ptr;
+ len = RSTRING(format)->len;
+ if (len == 0) {
+ rb_warning("strftime called with empty format string");
+ }
+ else if (strlen(fmt) < len) {
+ /* Ruby string may contain \0's. */
+ const char *p = fmt, *pe = fmt + len;
+
+ str = rb_str_new(0, 0);
+ while (p < pe) {
+ len = rb_strftime(&buf, p, &tobj->tm);
+ rb_str_cat(str, buf, len);
+ p += strlen(p);
+ if (buf != buffer) {
+ free(buf);
+ buf = buffer;
+ }
+ for (fmt = p; p < pe && !*p; ++p);
+ if (p > fmt) rb_str_cat(str, fmt, p - fmt);
+ }
+ return str;
+ }
+ else {
+ len = rb_strftime(&buf, RSTRING(format)->ptr, &tobj->tm);
+ }
+ str = rb_str_new(buf, len);
+ if (buf != buffer) free(buf);
+ return str;
+}
+
+/*
+ * call-seq:
+ * Time.times => struct_tms
+ *
+ * Deprecated in favor of <code>Process::times</code>
+ */
+
+static VALUE
+time_s_times(obj)
+ VALUE obj;
+{
+ rb_warn("obsolete method Time::times; use Process::times");
+ return rb_proc_times(obj);
+}
+
+/*
+ * undocumented
+ */
+
+static VALUE
+time_mdump(time)
+ VALUE time;
+{
+ struct time_object *tobj;
+ struct tm *tm;
+ unsigned long p, s;
+ char buf[8];
+ time_t t;
+ int i;
+
+ GetTimeval(time, tobj);
+
+ t = tobj->tv.tv_sec;
+ tm = gmtime(&t);
+
+ if ((tm->tm_year & 0xffff) != tm->tm_year)
+ rb_raise(rb_eArgError, "year too big to marshal");
+
+ p = 0x1UL << 31 | /* 1 */
+ tobj->gmt << 30 | /* 1 */
+ tm->tm_year << 14 | /* 16 */
+ tm->tm_mon << 10 | /* 4 */
+ tm->tm_mday << 5 | /* 5 */
+ tm->tm_hour; /* 5 */
+ s = tm->tm_min << 26 | /* 6 */
+ tm->tm_sec << 20 | /* 6 */
+ tobj->tv.tv_usec; /* 20 */
+
+ for (i=0; i<4; i++) {
+ buf[i] = p & 0xff;
+ p = RSHIFT(p, 8);
+ }
+ for (i=4; i<8; i++) {
+ buf[i] = s & 0xff;
+ s = RSHIFT(s, 8);
+ }
+
+ return rb_str_new(buf, 8);
+}
+
+/*
+ * call-seq:
+ * time._dump => string
+ *
+ * Dump _time_ for marshaling.
+ */
+
+static VALUE
+time_dump(argc, argv, time)
+ int argc;
+ VALUE *argv;
+ VALUE time;
+{
+ VALUE str;
+
+ rb_scan_args(argc, argv, "01", 0);
+ str = time_mdump(time);
+ if (FL_TEST(time, FL_EXIVAR)) {
+ rb_copy_generic_ivar(str, time);
+ FL_SET(str, FL_EXIVAR);
+ }
+
+ return str;
+}
+
+/*
+ * undocumented
+ */
+
+static VALUE
+time_mload(time, str)
+ VALUE time, str;
+{
+ struct time_object *tobj;
+ unsigned long p, s;
+ time_t sec, usec;
+ unsigned char *buf;
+ struct tm tm;
+ int i, gmt;
+
+ time_modify(time);
+ StringValue(str);
+ buf = (unsigned char *)RSTRING(str)->ptr;
+ if (RSTRING(str)->len != 8) {
+ rb_raise(rb_eTypeError, "marshaled time format differ");
+ }
+
+ p = s = 0;
+ for (i=0; i<4; i++) {
+ p |= buf[i]<<(8*i);
+ }
+ for (i=4; i<8; i++) {
+ s |= buf[i]<<(8*(i-4));
+ }
+
+ if ((p & (1UL<<31)) == 0) {
+ sec = p;
+ usec = s;
+ }
+ else {
+ p &= ~(1UL<<31);
+ gmt = (p >> 30) & 0x1;
+ tm.tm_year = (p >> 14) & 0xffff;
+ tm.tm_mon = (p >> 10) & 0xf;
+ tm.tm_mday = (p >> 5) & 0x1f;
+ tm.tm_hour = p & 0x1f;
+ tm.tm_min = (s >> 26) & 0x3f;
+ tm.tm_sec = (s >> 20) & 0x3f;
+ tm.tm_isdst = 0;
+
+ sec = make_time_t(&tm, Qtrue);
+ usec = (time_t)(s & 0xfffff);
+ }
+ time_overflow_p(&sec, &usec);
+
+ GetTimeval(time, tobj);
+ tobj->tm_got = 0;
+ tobj->gmt = gmt;
+ tobj->tv.tv_sec = sec;
+ tobj->tv.tv_usec = usec;
+ return time;
+}
+
+/*
+ * call-seq:
+ * Time._load(string) => time
+ *
+ * Unmarshal a dumped +Time+ object.
+ */
+
+static VALUE
+time_load(klass, str)
+ VALUE klass, str;
+{
+ VALUE time = time_s_alloc(klass);
+
+ if (FL_TEST(str, FL_EXIVAR)) {
+ rb_copy_generic_ivar(time, str);
+ FL_SET(time, FL_EXIVAR);
+ }
+ time_mload(time, str);
+ return time;
+}
+
+/*
+ * <code>Time</code> is an abstraction of dates and times. Time is
+ * stored internally as the number of seconds and microseconds since
+ * the <em>epoch</em>, January 1, 1970 00:00 UTC. On some operating
+ * systems, this offset is allowed to be negative. Also see the
+ * library modules <code>Date</code> and <code>ParseDate</code>. The
+ * <code>Time</code> class treats GMT (Greenwich Mean Time) and UTC
+ * (Coordinated Universal Time)<em>[Yes, UTC really does stand for
+ * Coordinated Universal Time. There was a committee involved.]</em>
+ * as equivalent. GMT is the older way of referring to these
+ * baseline times but persists in the names of calls on Posix
+ * systems.
+ *
+ * All times are stored with some number of microseconds. Be aware of
+ * this fact when comparing times with each other---times that are
+ * apparently equal when displayed may be different when compared.
+ */
+
+void
+Init_Time()
+{
+ rb_cTime = rb_define_class("Time", rb_cObject);
+ rb_include_module(rb_cTime, rb_mComparable);
+
+ rb_define_alloc_func(rb_cTime, time_s_alloc);
+ rb_define_singleton_method(rb_cTime, "now", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_cTime, "at", time_s_at, -1);
+ rb_define_singleton_method(rb_cTime, "utc", time_s_mkutc, -1);
+ rb_define_singleton_method(rb_cTime, "gm", time_s_mkutc, -1);
+ rb_define_singleton_method(rb_cTime, "local", time_s_mktime, -1);
+ rb_define_singleton_method(rb_cTime, "mktime", time_s_mktime, -1);
+
+ rb_define_singleton_method(rb_cTime, "times", time_s_times, 0);
+
+ rb_define_method(rb_cTime, "to_i", time_to_i, 0);
+ rb_define_method(rb_cTime, "to_f", time_to_f, 0);
+ rb_define_method(rb_cTime, "<=>", time_cmp, 1);
+ rb_define_method(rb_cTime, "eql?", time_eql, 1);
+ rb_define_method(rb_cTime, "hash", time_hash, 0);
+ rb_define_method(rb_cTime, "initialize", time_init, 0);
+ rb_define_method(rb_cTime, "initialize_copy", time_init_copy, 1);
+
+ rb_define_method(rb_cTime, "localtime", time_localtime, 0);
+ rb_define_method(rb_cTime, "gmtime", time_gmtime, 0);
+ rb_define_method(rb_cTime, "utc", time_gmtime, 0);
+ rb_define_method(rb_cTime, "getlocal", time_getlocaltime, 0);
+ rb_define_method(rb_cTime, "getgm", time_getgmtime, 0);
+ rb_define_method(rb_cTime, "getutc", time_getgmtime, 0);
+
+ rb_define_method(rb_cTime, "ctime", time_asctime, 0);
+ rb_define_method(rb_cTime, "asctime", time_asctime, 0);
+ rb_define_method(rb_cTime, "to_s", time_to_s, 0);
+ rb_define_method(rb_cTime, "inspect", time_to_s, 0);
+ rb_define_method(rb_cTime, "to_a", time_to_a, 0);
+
+ rb_define_method(rb_cTime, "+", time_plus, 1);
+ rb_define_method(rb_cTime, "-", time_minus, 1);
+
+ rb_define_method(rb_cTime, "succ", time_succ, 0);
+ rb_define_method(rb_cTime, "sec", time_sec, 0);
+ rb_define_method(rb_cTime, "min", time_min, 0);
+ rb_define_method(rb_cTime, "hour", time_hour, 0);
+ rb_define_method(rb_cTime, "mday", time_mday, 0);
+ rb_define_method(rb_cTime, "day", time_mday, 0);
+ rb_define_method(rb_cTime, "mon", time_mon, 0);
+ rb_define_method(rb_cTime, "month", time_mon, 0);
+ rb_define_method(rb_cTime, "year", time_year, 0);
+ rb_define_method(rb_cTime, "wday", time_wday, 0);
+ rb_define_method(rb_cTime, "yday", time_yday, 0);
+ rb_define_method(rb_cTime, "isdst", time_isdst, 0);
+ rb_define_method(rb_cTime, "dst?", time_isdst, 0);
+ rb_define_method(rb_cTime, "zone", time_zone, 0);
+ rb_define_method(rb_cTime, "gmtoff", time_utc_offset, 0);
+ rb_define_method(rb_cTime, "gmt_offset", time_utc_offset, 0);
+ rb_define_method(rb_cTime, "utc_offset", time_utc_offset, 0);
+
+ rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
+ rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
+
+ rb_define_method(rb_cTime, "tv_sec", time_to_i, 0);
+ rb_define_method(rb_cTime, "tv_usec", time_usec, 0);
+ rb_define_method(rb_cTime, "usec", time_usec, 0);
+
+ rb_define_method(rb_cTime, "strftime", time_strftime, 1);
+
+ /* methods for marshaling */
+ rb_define_method(rb_cTime, "_dump", time_dump, -1);
+ rb_define_singleton_method(rb_cTime, "_load", time_load, 1);
+#if 0
+ /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */
+ rb_define_method(rb_cTime, "marshal_dump", time_mdump, 0);
+ rb_define_method(rb_cTime, "marshal_load", time_mload, 1);
+#endif
+}
+/**********************************************************************
+
+ util.c -
+
+ $Author: wyhaines $
+ $Date: 2009-08-19 15:53:23 +0200 (Wed, 19 Aug 2009) $
+ created at: Fri Mar 10 17:22:34 JST 1995
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <math.h>
+#include <float.h>
+
+#ifdef _WIN32
+#include "missing/file.h"
+#endif
+
+#include "util.h"
+#ifndef HAVE_STRING_H
+char *strchr _((char*,char));
+#endif
+
+unsigned long
+scan_oct(start, len, retlen)
+ const char *start;
+ int len;
+ int *retlen;
+{
+ register const char *s = start;
+ register unsigned long retval = 0;
+
+ while (len-- && *s >= '0' && *s <= '7') {
+ retval <<= 3;
+ retval |= *s++ - '0';
+ }
+ *retlen = s - start;
+ return retval;
+}
+
+unsigned long
+scan_hex(start, len, retlen)
+ const char *start;
+ int len;
+ int *retlen;
+{
+ static char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
+ register const char *s = start;
+ register unsigned long retval = 0;
+ char *tmp;
+
+ while (len-- && *s && (tmp = strchr(hexdigit, *s))) {
+ retval <<= 4;
+ retval |= (tmp - hexdigit) & 15;
+ s++;
+ }
+ *retlen = s - start;
+ return retval;
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+#if defined(MSDOS) || defined(__CYGWIN32__) || defined(_WIN32)
+/*
+ * Copyright (c) 1993, Intergraph Corporation
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the perl README file.
+ *
+ * Various Unix compatibility functions and NT specific functions.
+ *
+ * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
+ *
+ */
+
+
+/*
+ * Suffix appending for in-place editing under MS-DOS and OS/2 (and now NT!).
+ *
+ * Here are the rules:
+ *
+ * Style 0: Append the suffix exactly as standard perl would do it.
+ * If the filesystem groks it, use it. (HPFS will always
+ * grok it. So will NTFS. FAT will rarely accept it.)
+ *
+ * Style 1: The suffix begins with a '.'. The extension is replaced.
+ * If the name matches the original name, use the fallback method.
+ *
+ * Style 2: The suffix is a single character, not a '.'. Try to add the
+ * suffix to the following places, using the first one that works.
+ * [1] Append to extension.
+ * [2] Append to filename,
+ * [3] Replace end of extension,
+ * [4] Replace end of filename.
+ * If the name matches the original name, use the fallback method.
+ *
+ * Style 3: Any other case: Ignore the suffix completely and use the
+ * fallback method.
+ *
+ * Fallback method: Change the extension to ".$$$". If that matches the
+ * original name, then change the extension to ".~~~".
+ *
+ * If filename is more than 1000 characters long, we die a horrible
+ * death. Sorry.
+ *
+ * The filename restriction is a cheat so that we can use buf[] to store
+ * assorted temporary goo.
+ *
+ * Examples, assuming style 0 failed.
+ *
+ * suffix = ".bak" (style 1)
+ * foo.bar => foo.bak
+ * foo.bak => foo.$$$ (fallback)
+ * foo.$$$ => foo.~~~ (fallback)
+ * makefile => makefile.bak
+ *
+ * suffix = "~" (style 2)
+ * foo.c => foo.c~
+ * foo.c~ => foo.c~~
+ * foo.c~~ => foo~.c~~
+ * foo~.c~~ => foo~~.c~~
+ * foo~~~~~.c~~ => foo~~~~~.$$$ (fallback)
+ *
+ * foo.pas => foo~.pas
+ * makefile => makefile.~
+ * longname.fil => longname.fi~
+ * longname.fi~ => longnam~.fi~
+ * longnam~.fi~ => longnam~.$$$
+ *
+ */
+
+
+static int valid_filename(char *s);
+
+static char suffix1[] = ".$$$";
+static char suffix2[] = ".~~~";
+
+#define ext (&buf[1000])
+
+#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
+
+void
+ruby_add_suffix(str, suffix)
+ VALUE str;
+ char *suffix;
+{
+ int baselen;
+ int extlen = strlen(suffix);
+ char *s, *t, *p;
+ long slen;
+ char buf[1024];
+
+ if (RSTRING(str)->len > 1000)
+ rb_fatal("Cannot do inplace edit on long filename (%ld characters)",
+ RSTRING(str)->len);
+
+#if defined(DJGPP) || defined(__CYGWIN32__) || defined(_WIN32)
+ /* Style 0 */
+ slen = RSTRING(str)->len;
+ rb_str_cat(str, suffix, extlen);
+#if defined(DJGPP)
+ if (_USE_LFN) return;
+#else
+ if (valid_filename(RSTRING(str)->ptr)) return;
+#endif
+
+ /* Fooey, style 0 failed. Fix str before continuing. */
+ RSTRING(str)->ptr[RSTRING(str)->len = slen] = '\0';
+#endif
+
+ slen = extlen;
+ t = buf; baselen = 0; s = RSTRING(str)->ptr;
+ while ((*t = *s) && *s != '.') {
+ baselen++;
+ if (*s == '\\' || *s == '/') baselen = 0;
+ s++; t++;
+ }
+ p = t;
+
+ t = ext; extlen = 0;
+ while (*t++ = *s++) extlen++;
+ if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; }
+
+ if (*suffix == '.') { /* Style 1 */
+ if (strEQ(ext, suffix)) goto fallback;
+ strcpy(p, suffix);
+ }
+ else if (suffix[1] == '\0') { /* Style 2 */
+ if (extlen < 4) {
+ ext[extlen] = *suffix;
+ ext[++extlen] = '\0';
+ }
+ else if (baselen < 8) {
+ *p++ = *suffix;
+ }
+ else if (ext[3] != *suffix) {
+ ext[3] = *suffix;
+ }
+ else if (buf[7] != *suffix) {
+ buf[7] = *suffix;
+ }
+ else goto fallback;
+ strcpy(p, ext);
+ }
+ else { /* Style 3: Panic */
+fallback:
+ (void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5);
+ }
+ rb_str_resize(str, strlen(buf));
+ memcpy(RSTRING(str)->ptr, buf, RSTRING(str)->len);
+}
+
+#if defined(__CYGWIN32__) || defined(_WIN32)
+static int
+valid_filename(char *s)
+{
+ int fd;
+
+ /*
+ // if the file exists, then it's a valid filename!
+ */
+
+ if (_access(s, 0) == 0) {
+ return 1;
+ }
+
+ /*
+ // It doesn't exist, so see if we can open it.
+ */
+
+ if ((fd = _open(s, O_CREAT, 0666)) >= 0) {
+ _close(fd);
+ _unlink(s); /* don't leave it laying around */
+ return 1;
+ }
+ return 0;
+}
+#endif
+#endif
+
+#if defined __DJGPP__
+
+#include <dpmi.h>
+
+static char dbcs_table[256];
+
+int
+make_dbcs_table()
+{
+ __dpmi_regs r;
+ struct {
+ unsigned char start;
+ unsigned char end;
+ } vec;
+ int offset;
+
+ memset(&r, 0, sizeof(r));
+ r.x.ax = 0x6300;
+ __dpmi_int(0x21, &r);
+ offset = r.x.ds * 16 + r.x.si;
+
+ for (;;) {
+ int i;
+ dosmemget(offset, sizeof vec, &vec);
+ if (!vec.start && !vec.end)
+ break;
+ for (i = vec.start; i <= vec.end; i++)
+ dbcs_table[i] = 1;
+ offset += 2;
+ }
+}
+
+int
+mblen(const char *s, size_t n)
+{
+ static int need_init = 1;
+ if (need_init) {
+ make_dbcs_table();
+ need_init = 0;
+ }
+ if (s) {
+ if (n == 0 || *s == 0)
+ return 0;
+ else if (!s[1])
+ return 1;
+ return dbcs_table[(unsigned char)*s] + 1;
+ }
+ else
+ return 1;
+}
+
+struct PathList {
+ struct PathList *next;
+ char *path;
+};
+
+struct PathInfo {
+ struct PathList *head;
+ int count;
+};
+
+static int
+push_element(const char *path, VALUE vinfo)
+{
+ struct PathList *p;
+ struct PathInfo *info = (struct PathInfo *)vinfo;
+
+ p = ALLOC(struct PathList);
+ MEMZERO(p, struct PathList, 1);
+ p->path = ruby_strdup(path);
+ p->next = info->head;
+ info->head = p;
+ info->count++;
+
+ return 0;
+}
+
+#include <dirent.h>
+int __opendir_flags = __OPENDIR_PRESERVE_CASE;
+
+char **
+__crt0_glob_function(char *path)
+{
+ int len = strlen(path);
+ int i;
+ char **rv;
+ char path_buffer[PATH_MAX];
+ char *buf = path_buffer;
+ char *p;
+ struct PathInfo info;
+ struct PathList *plist;
+
+ if (PATH_MAX <= len)
+ buf = ruby_xmalloc(len + 1);
+
+ strncpy(buf, path, len);
+ buf[len] = '\0';
+
+ for (p = buf; *p; p += mblen(p, MB_CUR_MAX))
+ if (*p == '\\')
+ *p = '/';
+
+ info.count = 0;
+ info.head = 0;
+
+ ruby_glob(buf, 0, push_element, (VALUE)&info);
+
+ if (buf != path_buffer)
+ ruby_xfree(buf);
+
+ if (info.count == 0)
+ return 0;
+
+ rv = ruby_xmalloc((info.count + 1) * sizeof (char *));
+
+ plist = info.head;
+ i = 0;
+ while (plist) {
+ struct PathList *cur;
+ rv[i] = plist->path;
+ cur = plist;
+ plist = plist->next;
+ ruby_xfree(cur);
+ i++;
+ }
+ rv[i] = 0;
+ return rv;
+}
+
+#endif
+
+/* mm.c */
+
+#define A ((int*)a)
+#define B ((int*)b)
+#define C ((int*)c)
+#define D ((int*)d)
+
+#define mmprepare(base, size) do {\
+ if (((long)base & (0x3)) == 0)\
+ if (size >= 16) mmkind = 1;\
+ else mmkind = 0;\
+ else mmkind = -1;\
+ high = (size & (~0xf));\
+ low = (size & 0x0c);\
+} while (0)\
+
+#define mmarg mmkind, size, high, low
+
+static void mmswap_(a, b, mmarg)
+ register char *a, *b;
+ int mmarg;
+{
+ register int s;
+ if (a == b) return;
+ if (mmkind >= 0) {
+ if (mmkind > 0) {
+ register char *t = a + high;
+ do {
+ s = A[0]; A[0] = B[0]; B[0] = s;
+ s = A[1]; A[1] = B[1]; B[1] = s;
+ s = A[2]; A[2] = B[2]; B[2] = s;
+ s = A[3]; A[3] = B[3]; B[3] = s; a += 16; b += 16;
+ } while (a < t);
+ }
+ if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = s;
+ if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = s;
+ if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = s;}}}
+ }
+ else {
+ register char *t = a + size;
+ do {s = *a; *a++ = *b; *b++ = s;} while (a < t);
+ }
+}
+#define mmswap(a,b) mmswap_((a),(b),mmarg)
+
+static void mmrot3_(a, b, c, mmarg)
+ register char *a, *b, *c;
+ int mmarg;
+{
+ register int s;
+ if (mmkind >= 0) {
+ if (mmkind > 0) {
+ register char *t = a + high;
+ do {
+ s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
+ s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
+ s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;
+ s = A[3]; A[3] = B[3]; B[3] = C[3]; C[3] = s; a += 16; b += 16; c += 16;
+ } while (a < t);
+ }
+ if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
+ if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
+ if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;}}}
+ }
+ else {
+ register char *t = a + size;
+ do {s = *a; *a++ = *b; *b++ = *c; *c++ = s;} while (a < t);
+ }
+}
+#define mmrot3(a,b,c) mmrot3_((a),(b),(c),mmarg)
+
+/* qs6.c */
+/*****************************************************/
+/* */
+/* qs6 (Quick sort function) */
+/* */
+/* by Tomoyuki Kawamura 1995.4.21 */
+/* kawamura@tokuyama.ac.jp */
+/*****************************************************/
+
+typedef struct { char *LL, *RR; } stack_node; /* Stack structure for L,l,R,r */
+#define PUSH(ll,rr) do { top->LL = (ll); top->RR = (rr); ++top; } while (0) /* Push L,l,R,r */
+#define POP(ll,rr) do { --top; ll = top->LL; rr = top->RR; } while (0) /* Pop L,l,R,r */
+
+#define med3(a,b,c) ((*cmp)(a,b,d)<0 ? \
+ ((*cmp)(b,c,d)<0 ? b : ((*cmp)(a,c,d)<0 ? c : a)) : \
+ ((*cmp)(b,c,d)>0 ? b : ((*cmp)(a,c,d)<0 ? a : c)))
+
+void ruby_qsort (base, nel, size, cmp, d)
+ void* base;
+ const int nel;
+ const int size;
+ int (*cmp)();
+ void *d;
+{
+ register char *l, *r, *m; /* l,r:left,right group m:median point */
+ register int t, eq_l, eq_r; /* eq_l: all items in left group are equal to S */
+ char *L = base; /* left end of curren region */
+ char *R = (char*)base + size*(nel-1); /* right end of current region */
+ int chklim = 63; /* threshold of ordering element check */
+ stack_node stack[32], *top = stack; /* 32 is enough for 32bit CPU */
+ int mmkind, high, low;
+
+ if (nel <= 1) return; /* need not to sort */
+ mmprepare(base, size);
+ goto start;
+
+ nxt:
+ if (stack == top) return; /* return if stack is empty */
+ POP(L,R);
+
+ for (;;) {
+ start:
+ if (L + size == R) { /* 2 elements */
+ if ((*cmp)(L,R,d) > 0) mmswap(L,R); goto nxt;
+ }
+
+ l = L; r = R;
+ t = (r - l + size) / size; /* number of elements */
+ m = l + size * (t >> 1); /* calculate median value */
+
+ if (t >= 60) {
+ register char *m1;
+ register char *m3;
+ if (t >= 200) {
+ t = size*(t>>3); /* number of bytes in splitting 8 */
+ {
+ register char *p1 = l + t;
+ register char *p2 = p1 + t;
+ register char *p3 = p2 + t;
+ m1 = med3(p1, p2, p3);
+ p1 = m + t;
+ p2 = p1 + t;
+ p3 = p2 + t;
+ m3 = med3(p1, p2, p3);
+ }
+ }
+ else {
+ t = size*(t>>2); /* number of bytes in splitting 4 */
+ m1 = l + t;
+ m3 = m + t;
+ }
+ m = med3(m1, m, m3);
+ }
+
+ if ((t = (*cmp)(l,m,d)) < 0) { /*3-5-?*/
+ if ((t = (*cmp)(m,r,d)) < 0) { /*3-5-7*/
+ if (chklim && nel >= chklim) { /* check if already ascending order */
+ char *p;
+ chklim = 0;
+ for (p=l; p<r; p+=size) if ((*cmp)(p,p+size,d) > 0) goto fail;
+ goto nxt;
+ }
+ fail: goto loopA; /*3-5-7*/
+ }
+ if (t > 0) {
+ if ((*cmp)(l,r,d) <= 0) {mmswap(m,r); goto loopA;} /*3-5-4*/
+ mmrot3(r,m,l); goto loopA; /*3-5-2*/
+ }
+ goto loopB; /*3-5-5*/
+ }
+
+ if (t > 0) { /*7-5-?*/
+ if ((t = (*cmp)(m,r,d)) > 0) { /*7-5-3*/
+ if (chklim && nel >= chklim) { /* check if already ascending order */
+ char *p;
+ chklim = 0;
+ for (p=l; p<r; p+=size) if ((*cmp)(p,p+size,d) < 0) goto fail2;
+ while (l<r) {mmswap(l,r); l+=size; r-=size;} /* reverse region */
+ goto nxt;
+ }
+ fail2: mmswap(l,r); goto loopA; /*7-5-3*/
+ }
+ if (t < 0) {
+ if ((*cmp)(l,r,d) <= 0) {mmswap(l,m); goto loopB;} /*7-5-8*/
+ mmrot3(l,m,r); goto loopA; /*7-5-6*/
+ }
+ mmswap(l,r); goto loopA; /*7-5-5*/
+ }
+
+ if ((t = (*cmp)(m,r,d)) < 0) {goto loopA;} /*5-5-7*/
+ if (t > 0) {mmswap(l,r); goto loopB;} /*5-5-3*/
+
+ /* determining splitting type in case 5-5-5 */ /*5-5-5*/
+ for (;;) {
+ if ((l += size) == r) goto nxt; /*5-5-5*/
+ if (l == m) continue;
+ if ((t = (*cmp)(l,m,d)) > 0) {mmswap(l,r); l = L; goto loopA;}/*575-5*/
+ if (t < 0) {mmswap(L,l); l = L; goto loopB;} /*535-5*/
+ }
+
+ loopA: eq_l = 1; eq_r = 1; /* splitting type A */ /* left <= median < right */
+ for (;;) {
+ for (;;) {
+ if ((l += size) == r)
+ {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
+ if (l == m) continue;
+ if ((t = (*cmp)(l,m,d)) > 0) {eq_r = 0; break;}
+ if (t < 0) eq_l = 0;
+ }
+ for (;;) {
+ if (l == (r -= size))
+ {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
+ if (r == m) {m = l; break;}
+ if ((t = (*cmp)(r,m,d)) < 0) {eq_l = 0; break;}
+ if (t == 0) break;
+ }
+ mmswap(l,r); /* swap left and right */
+ }
+
+ loopB: eq_l = 1; eq_r = 1; /* splitting type B */ /* left < median <= right */
+ for (;;) {
+ for (;;) {
+ if (l == (r -= size))
+ {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
+ if (r == m) continue;
+ if ((t = (*cmp)(r,m,d)) < 0) {eq_l = 0; break;}
+ if (t > 0) eq_r = 0;
+ }
+ for (;;) {
+ if ((l += size) == r)
+ {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
+ if (l == m) {m = r; break;}
+ if ((t = (*cmp)(l,m,d)) > 0) {eq_r = 0; break;}
+ if (t == 0) break;
+ }
+ mmswap(l,r); /* swap left and right */
+ }
+
+ fin:
+ if (eq_l == 0) /* need to sort left side */
+ if (eq_r == 0) /* need to sort right side */
+ if (l-L < R-r) {PUSH(r,R); R = l;} /* sort left side first */
+ else {PUSH(L,l); L = r;} /* sort right side first */
+ else R = l; /* need to sort left side only */
+ else if (eq_r == 0) L = r; /* need to sort right side only */
+ else goto nxt; /* need not to sort both sides */
+ }
+}
+
+char *
+ruby_strdup(str)
+ const char *str;
+{
+ char *tmp;
+ int len = strlen(str) + 1;
+
+ tmp = xmalloc(len);
+ memcpy(tmp, str, len);
+
+ return tmp;
+}
+
+char *
+ruby_getcwd()
+{
+#ifdef HAVE_GETCWD
+ int size = 200;
+ char *buf = xmalloc(size);
+
+ while (!getcwd(buf, size)) {
+ if (errno != ERANGE) {
+ free(buf);
+ rb_sys_fail("getcwd");
+ }
+ size *= 2;
+ buf = xrealloc(buf, size);
+ }
+#else
+# ifndef PATH_MAX
+# define PATH_MAX 8192
+# endif
+ char *buf = xmalloc(PATH_MAX+1);
+
+ if (!getwd(buf)) {
+ free(buf);
+ rb_sys_fail("getwd");
+ }
+#endif
+ return buf;
+}
+
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * 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.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* 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.
+ *
+ * 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).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 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(const char *s00, char **se)
+{
+#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
+
+ 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 == '.') {
+ if (!ISDIGIT(s[1]))
+ goto dig_done;
+ 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;
+
+ /* 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 {
+ /* 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;
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+ try_quick = Rounding == 1;
+#else
+ try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+ 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+1);
+
+#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;
+ }
+
+ /* 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.
+ */
+#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;
+ }
+ else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ 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;
+ }
+ }
+ 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);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ 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);
+ }
+
+ /* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+ switch (rounding) {
+ case 0: goto trimzeros;
+ case 2: goto roundoff;
+ }
+#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++;
+ }
+ 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
+/**********************************************************************
+
+ variable.c -
+
+ $Author: shyouhei $
+ $Date: 2009-02-02 03:34:12 +0100 (Mon, 02 Feb 2009) $
+ created at: Tue Apr 19 23:55:15 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "st.h"
+#include "util.h"
+
+st_table *rb_global_tbl;
+st_table *rb_class_tbl;
+static ID autoload, classpath, tmp_classpath;
+
+void
+Init_var_tables()
+{
+ rb_global_tbl = st_init_numtable();
+ rb_class_tbl = st_init_numtable();
+ autoload = rb_intern("__autoload__");
+ classpath = rb_intern("__classpath__");
+ tmp_classpath = rb_intern("__tmp_classpath__");
+}
+
+struct fc_result {
+ ID name;
+ VALUE klass;
+ VALUE path;
+ VALUE track;
+ struct fc_result *prev;
+};
+
+static VALUE
+fc_path(fc, name)
+ struct fc_result *fc;
+ ID name;
+{
+ VALUE path, tmp;
+
+ path = rb_str_new2(rb_id2name(name));
+ while (fc) {
+ if (fc->track == rb_cObject) break;
+ if (ROBJECT(fc->track)->iv_tbl &&
+ st_lookup(ROBJECT(fc->track)->iv_tbl, classpath, &tmp)) {
+ tmp = rb_str_dup(tmp);
+ rb_str_cat2(tmp, "::");
+ rb_str_append(tmp, path);
+
+ return tmp;
+ }
+ tmp = rb_str_new2(rb_id2name(fc->name));
+ rb_str_cat2(tmp, "::");
+ rb_str_append(tmp, path);
+ path = tmp;
+ fc = fc->prev;
+ }
+ return path;
+}
+
+static int
+fc_i(key, value, res)
+ ID key;
+ VALUE value;
+ struct fc_result *res;
+{
+ if (!rb_is_const_id(key)) return ST_CONTINUE;
+
+ if (value == res->klass) {
+ res->path = fc_path(res, key);
+ return ST_STOP;
+ }
+ switch (TYPE(value)) {
+ case T_MODULE:
+ case T_CLASS:
+ if (!RCLASS(value)->iv_tbl) return ST_CONTINUE;
+ else {
+ struct fc_result arg;
+ struct fc_result *list;
+
+ list = res;
+ while (list) {
+ if (list->track == value) return ST_CONTINUE;
+ list = list->prev;
+ }
+
+ arg.name = key;
+ arg.path = 0;
+ arg.klass = res->klass;
+ arg.track = value;
+ arg.prev = res;
+ st_foreach_safe(RCLASS(value)->iv_tbl, fc_i, (st_data_t)&arg);
+ if (arg.path) {
+ res->path = arg.path;
+ return ST_STOP;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+find_class_path(klass)
+ VALUE klass;
+{
+ struct fc_result arg;
+
+ arg.name = 0;
+ arg.path = 0;
+ arg.klass = klass;
+ arg.track = rb_cObject;
+ arg.prev = 0;
+ if (RCLASS(rb_cObject)->iv_tbl) {
+ st_foreach_safe(RCLASS(rb_cObject)->iv_tbl, fc_i, (st_data_t)&arg);
+ }
+ if (arg.path == 0) {
+ st_foreach(rb_class_tbl, fc_i, (st_data_t)&arg);
+ }
+ if (arg.path) {
+ if (!ROBJECT(klass)->iv_tbl) {
+ ROBJECT(klass)->iv_tbl = st_init_numtable();
+ }
+ st_insert(ROBJECT(klass)->iv_tbl, classpath, arg.path);
+ st_delete(RCLASS(klass)->iv_tbl, &tmp_classpath, 0);
+ return arg.path;
+ }
+ return Qnil;
+}
+
+static VALUE
+classname(klass)
+ VALUE klass;
+{
+ VALUE path = Qnil;
+
+ if (!klass) klass = rb_cObject;
+ if (ROBJECT(klass)->iv_tbl) {
+ if (!st_lookup(ROBJECT(klass)->iv_tbl, classpath, &path)) {
+ ID classid = rb_intern("__classid__");
+
+ if (!st_lookup(ROBJECT(klass)->iv_tbl, classid, &path)) {
+ return find_class_path(klass);
+ }
+ path = rb_str_new2(rb_id2name(SYM2ID(path)));
+ st_insert(ROBJECT(klass)->iv_tbl, classpath, path);
+ st_delete(RCLASS(klass)->iv_tbl, (st_data_t*)&classid, 0);
+ }
+ if (TYPE(path) != T_STRING) {
+ rb_bug("class path is not set properly");
+ }
+ return path;
+ }
+ return find_class_path(klass);
+}
+
+/*
+ * call-seq:
+ * mod.name => string
+ *
+ * Returns the name of the module <i>mod</i>.
+ */
+
+VALUE
+rb_mod_name(mod)
+ VALUE mod;
+{
+ VALUE path = classname(mod);
+
+ if (!NIL_P(path)) return rb_str_dup(path);
+ return rb_str_new(0,0);
+}
+
+VALUE
+rb_class_path(klass)
+ VALUE klass;
+{
+ VALUE path = classname(klass);
+
+ if (!NIL_P(path)) return path;
+ if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl,
+ tmp_classpath, &path)) {
+ return path;
+ }
+ else {
+ char *s = "Class";
+ size_t len;
+
+ if (TYPE(klass) == T_MODULE) {
+ if (rb_obj_class(klass) == rb_cModule) {
+ s = "Module";
+ }
+ else {
+ s = rb_class2name(RBASIC(klass)->klass);
+ }
+ }
+ len = 2 + strlen(s) + 3 + 2 * SIZEOF_LONG + 1;
+ path = rb_str_new(0, len);
+ snprintf(RSTRING(path)->ptr, len+1, "#<%s:0x%lx>", s, klass);
+ RSTRING(path)->len = strlen(RSTRING(path)->ptr);
+ rb_ivar_set(klass, tmp_classpath, path);
+
+ return path;
+ }
+}
+
+void
+rb_set_class_path(klass, under, name)
+ VALUE klass, under;
+ const char *name;
+{
+ VALUE str;
+
+ if (under == rb_cObject) {
+ str = rb_str_new2(name);
+ }
+ else {
+ str = rb_str_dup(rb_class_path(under));
+ rb_str_cat2(str, "::");
+ rb_str_cat2(str, name);
+ }
+ rb_ivar_set(klass, classpath, str);
+}
+
+VALUE
+rb_path2class(path)
+ const char *path;
+{
+ const char *pbeg, *p;
+ ID id;
+ VALUE c = rb_cObject;
+ VALUE str = 0;
+
+ if (path[0] == '#') {
+ rb_raise(rb_eArgError, "can't retrieve anonymous class %s", path);
+ }
+ pbeg = p = path;
+ while (*p) {
+ while (*p && *p != ':') p++;
+ if (str) {
+ RSTRING(str)->len = 0;
+ rb_str_cat(str, pbeg, p-pbeg);
+ }
+ else {
+ str = rb_str_new(pbeg, p-pbeg);
+ }
+ id = rb_intern(RSTRING(str)->ptr);
+ if (p[0] == ':') {
+ if (p[1] != ':') goto undefined_class;
+ p += 2;
+ pbeg = p;
+ }
+ if (!rb_const_defined(c, id)) {
+ undefined_class:
+ rb_raise(rb_eArgError, "undefined class/module %.*s", p-path, path);
+ }
+ c = rb_const_get_at(c, id);
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s does not refer class/module", path);
+ }
+ }
+
+ return c;
+}
+
+void
+rb_name_class(klass, id)
+ VALUE klass;
+ ID id;
+{
+ rb_iv_set(klass, "__classid__", ID2SYM(id));
+}
+
+VALUE
+rb_class_name(klass)
+ VALUE klass;
+{
+ return rb_class_path(rb_class_real(klass));
+}
+
+char *
+rb_class2name(klass)
+ VALUE klass;
+{
+ return RSTRING(rb_class_name(klass))->ptr;
+}
+
+char *
+rb_obj_classname(obj)
+ VALUE obj;
+{
+ return rb_class2name(CLASS_OF(obj));
+}
+
+struct trace_var {
+ int removed;
+ void (*func)();
+ VALUE data;
+ struct trace_var *next;
+};
+
+struct global_variable {
+ int counter;
+ void *data;
+ VALUE (*getter)();
+ void (*setter)();
+ void (*marker)();
+ int block_trace;
+ struct trace_var *trace;
+};
+
+struct global_entry {
+ struct global_variable *var;
+ ID id;
+};
+
+static VALUE undef_getter();
+static void undef_setter();
+static void undef_marker();
+
+static VALUE val_getter();
+static void val_setter();
+static void val_marker();
+
+static VALUE var_getter();
+static void var_setter();
+static void var_marker();
+
+struct global_entry*
+rb_global_entry(id)
+ ID id;
+{
+ struct global_entry *entry;
+
+ if (!st_lookup(rb_global_tbl, id, (st_data_t *)&entry)) {
+ struct global_variable *var;
+ entry = ALLOC(struct global_entry);
+ var = ALLOC(struct global_variable);
+ entry->id = id;
+ entry->var = var;
+ var->counter = 1;
+ var->data = 0;
+ var->getter = undef_getter;
+ var->setter = undef_setter;
+ var->marker = undef_marker;
+
+ var->block_trace = 0;
+ var->trace = 0;
+ st_add_direct(rb_global_tbl, id, (st_data_t)entry);
+ }
+ return entry;
+}
+
+static VALUE
+undef_getter(id)
+ ID id;
+{
+ rb_warning("global variable `%s' not initialized", rb_id2name(id));
+
+ return Qnil;
+}
+
+static void
+undef_setter(val, id, data, var)
+ VALUE val;
+ ID id;
+ void *data;
+ struct global_variable *var;
+{
+ var->getter = val_getter;
+ var->setter = val_setter;
+ var->marker = val_marker;
+
+ var->data = (void*)val;
+}
+
+static void
+undef_marker()
+{
+}
+
+static VALUE
+val_getter(id, val)
+ ID id;
+ VALUE val;
+{
+ return val;
+}
+
+static void
+val_setter(val, id, data, var)
+ VALUE val;
+ ID id;
+ void *data;
+ struct global_variable *var;
+{
+ var->data = (void*)val;
+}
+
+static void
+val_marker(data)
+ VALUE data;
+{
+ if (data) rb_gc_mark_maybe(data);
+}
+
+static VALUE
+var_getter(id, var)
+ ID id;
+ VALUE *var;
+{
+ if (!var) return Qnil;
+ return *var;
+}
+
+static void
+var_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ *var = val;
+}
+
+static void
+var_marker(var)
+ VALUE *var;
+{
+ if (var) rb_gc_mark_maybe(*var);
+}
+
+static void
+readonly_setter(val, id, var)
+ VALUE val;
+ ID id;
+ void *var;
+{
+ rb_name_error(id, "%s is a read-only variable", rb_id2name(id));
+}
+
+static int
+mark_global_entry(key, entry)
+ ID key;
+ struct global_entry *entry;
+{
+ struct trace_var *trace;
+ struct global_variable *var = entry->var;
+
+ (*var->marker)(var->data);
+ trace = var->trace;
+ while (trace) {
+ if (trace->data) rb_gc_mark_maybe(trace->data);
+ trace = trace->next;
+ }
+ return ST_CONTINUE;
+}
+
+void
+rb_gc_mark_global_tbl()
+{
+ if (rb_global_tbl) {
+ st_foreach(rb_global_tbl, mark_global_entry, 0);
+ }
+}
+
+static ID
+global_id(name)
+ const char *name;
+{
+ ID id;
+
+ if (name[0] == '$') id = rb_intern(name);
+ else {
+ char *buf = ALLOCA_N(char, strlen(name)+2);
+ buf[0] = '$';
+ strcpy(buf+1, name);
+ id = rb_intern(buf);
+ }
+ return id;
+}
+
+void
+rb_define_hooked_variable(name, var, getter, setter)
+ const char *name;
+ VALUE *var;
+ VALUE (*getter)();
+ void (*setter)();
+{
+ struct global_variable *gvar;
+ ID id = global_id(name);
+
+ gvar = rb_global_entry(id)->var;
+ gvar->data = (void*)var;
+ gvar->getter = getter?getter:var_getter;
+ gvar->setter = setter?setter:var_setter;
+ gvar->marker = var_marker;
+}
+
+void
+rb_define_variable(name, var)
+ const char *name;
+ VALUE *var;
+{
+ rb_define_hooked_variable(name, var, 0, 0);
+}
+
+void
+rb_define_readonly_variable(name, var)
+ const char *name;
+ VALUE *var;
+{
+ rb_define_hooked_variable(name, var, 0, readonly_setter);
+}
+
+void
+rb_define_virtual_variable(name, getter, setter)
+ const char *name;
+ VALUE (*getter)();
+ void (*setter)();
+{
+ if (!getter) getter = val_getter;
+ if (!setter) setter = readonly_setter;
+ rb_define_hooked_variable(name, 0, getter, setter);
+}
+
+static void
+rb_trace_eval(cmd, val)
+ VALUE cmd, val;
+{
+ rb_eval_cmd(cmd, rb_ary_new3(1, val), 0);
+}
+
+/*
+ * call-seq:
+ * trace_var(symbol, cmd ) => nil
+ * trace_var(symbol) {|val| block } => nil
+ *
+ * Controls tracing of assignments to global variables. The parameter
+ * +symbol_ identifies the variable (as either a string name or a
+ * symbol identifier). _cmd_ (which may be a string or a
+ * +Proc+ object) or block is executed whenever the variable
+ * is assigned. The block or +Proc+ object receives the
+ * variable's new value as a parameter. Also see
+ * <code>Kernel::untrace_var</code>.
+ *
+ * trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
+ * $_ = "hello"
+ * $_ = ' there'
+ *
+ * <em>produces:</em>
+ *
+ * $_ is now 'hello'
+ * $_ is now ' there'
+ */
+
+VALUE
+rb_f_trace_var(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE var, cmd;
+ struct global_entry *entry;
+ struct trace_var *trace;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "11", &var, &cmd) == 1) {
+ cmd = rb_block_proc();
+ }
+ if (NIL_P(cmd)) {
+ return rb_f_untrace_var(argc, argv);
+ }
+ entry = rb_global_entry(rb_to_id(var));
+ if (OBJ_TAINTED(cmd)) {
+ rb_raise(rb_eSecurityError, "Insecure: tainted variable trace");
+ }
+ trace = ALLOC(struct trace_var);
+ trace->next = entry->var->trace;
+ trace->func = rb_trace_eval;
+ trace->data = cmd;
+ trace->removed = 0;
+ entry->var->trace = trace;
+
+ return Qnil;
+}
+
+static void
+remove_trace(var)
+ struct global_variable *var;
+{
+ struct trace_var *trace = var->trace;
+ struct trace_var t;
+ struct trace_var *next;
+
+ t.next = trace;
+ trace = &t;
+ while (trace->next) {
+ next = trace->next;
+ if (next->removed) {
+ trace->next = next->next;
+ free(next);
+ }
+ else {
+ trace = next;
+ }
+ }
+ var->trace = t.next;
+}
+
+/*
+ * call-seq:
+ * untrace_var(symbol [, cmd] ) => array or nil
+ *
+ * Removes tracing for the specified command on the given global
+ * variable and returns +nil+. If no command is specified,
+ * removes all tracing for that variable and returns an array
+ * containing the commands actually removed.
+ */
+
+VALUE
+rb_f_untrace_var(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE var, cmd;
+ ID id;
+ struct global_entry *entry;
+ struct trace_var *trace;
+
+ rb_secure(4);
+ rb_scan_args(argc, argv, "11", &var, &cmd);
+ id = rb_to_id(var);
+ if (!st_lookup(rb_global_tbl, id, (st_data_t *)&entry)) {
+ rb_name_error(id, "undefined global variable %s", rb_id2name(id));
+ }
+
+ trace = entry->var->trace;
+ if (NIL_P(cmd)) {
+ VALUE ary = rb_ary_new();
+
+ while (trace) {
+ struct trace_var *next = trace->next;
+ rb_ary_push(ary, (VALUE)trace->data);
+ trace->removed = 1;
+ trace = next;
+ }
+
+ if (!entry->var->block_trace) remove_trace(entry->var);
+ return ary;
+ }
+ else {
+ while (trace) {
+ if (trace->data == cmd) {
+ trace->removed = 1;
+ if (!entry->var->block_trace) remove_trace(entry->var);
+ return rb_ary_new3(1, cmd);
+ }
+ trace = trace->next;
+ }
+ }
+ return Qnil;
+}
+
+VALUE
+rb_gvar_get(entry)
+ struct global_entry *entry;
+{
+ struct global_variable *var = entry->var;
+ return (*var->getter)(entry->id, var->data, var);
+}
+
+struct trace_data {
+ struct trace_var *trace;
+ VALUE val;
+};
+
+static VALUE
+trace_ev(data)
+ struct trace_data *data;
+{
+ struct trace_var *trace = data->trace;
+
+ while (trace) {
+ (*trace->func)(trace->data, data->val);
+ trace = trace->next;
+ }
+ return Qnil; /* not reached */
+}
+
+static VALUE
+trace_en(var)
+ struct global_variable *var;
+{
+ var->block_trace = 0;
+ remove_trace(var);
+ return Qnil; /* not reached */
+}
+
+VALUE
+rb_gvar_set(entry, val)
+ struct global_entry *entry;
+ VALUE val;
+{
+ struct trace_data trace;
+ struct global_variable *var = entry->var;
+
+ if (rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't change global variable value");
+ (*var->setter)(val, entry->id, var->data, var);
+
+ if (var->trace && !var->block_trace) {
+ var->block_trace = 1;
+ trace.trace = var->trace;
+ trace.val = val;
+ rb_ensure(trace_ev, (VALUE)&trace, trace_en, (VALUE)var);
+ }
+ return val;
+}
+
+VALUE
+rb_gv_set(name, val)
+ const char *name;
+ VALUE val;
+{
+ struct global_entry *entry;
+
+ entry = rb_global_entry(global_id(name));
+ return rb_gvar_set(entry, val);
+}
+
+VALUE
+rb_gv_get(name)
+ const char *name;
+{
+ struct global_entry *entry;
+
+ entry = rb_global_entry(global_id(name));
+ return rb_gvar_get(entry);
+}
+
+VALUE
+rb_gvar_defined(entry)
+ struct global_entry *entry;
+{
+ if (entry->var->getter == undef_getter) return Qfalse;
+ return Qtrue;
+}
+
+static int
+gvar_i(key, entry, ary)
+ ID key;
+ struct global_entry *entry;
+ VALUE ary;
+{
+ rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * global_variables => array
+ *
+ * Returns an array of the names of global variables.
+ *
+ * global_variables.grep /std/ #=> ["$stderr", "$stdout", "$stdin"]
+ */
+
+VALUE
+rb_f_global_variables()
+{
+ VALUE ary = rb_ary_new();
+ char buf[4];
+ char *s = "&`'+123456789";
+
+ st_foreach(rb_global_tbl, gvar_i, ary);
+ if (!NIL_P(rb_backref_get())) {
+ while (*s) {
+ sprintf(buf, "$%c", *s++);
+ rb_ary_push(ary, rb_str_new2(buf));
+ }
+ }
+ return ary;
+}
+
+void
+rb_alias_variable(name1, name2)
+ ID name1;
+ ID name2;
+{
+ struct global_entry *entry1, *entry2;
+
+ if (rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't alias global variable");
+
+ entry2 = rb_global_entry(name2);
+ if (!st_lookup(rb_global_tbl, name1, (st_data_t *)&entry1)) {
+ entry1 = ALLOC(struct global_entry);
+ entry1->id = name1;
+ st_add_direct(rb_global_tbl, name1, (st_data_t)entry1);
+ }
+ else if (entry1->var != entry2->var) {
+ struct global_variable *var = entry1->var;
+ if (var->block_trace) {
+ rb_raise(rb_eRuntimeError, "can't alias in tracer");
+ }
+ var->counter--;
+ if (var->counter == 0) {
+ struct trace_var *trace = var->trace;
+ while (trace) {
+ struct trace_var *next = trace->next;
+ free(trace);
+ trace = next;
+ }
+ free(var);
+ }
+ }
+ else {
+ return;
+ }
+ entry2->var->counter++;
+ entry1->var = entry2->var;
+}
+
+static int special_generic_ivar = 0;
+static st_table *generic_iv_tbl;
+
+st_table*
+rb_generic_ivar_table(obj)
+ VALUE obj;
+{
+ st_table *tbl;
+
+ if (!FL_TEST(obj, FL_EXIVAR)) return 0;
+ if (!generic_iv_tbl) return 0;
+ if (!st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) return 0;
+ return tbl;
+}
+
+static VALUE
+generic_ivar_get(obj, id, warn)
+ VALUE obj;
+ ID id;
+ int warn;
+{
+ st_table *tbl;
+ VALUE val;
+
+ if (generic_iv_tbl) {
+ if (st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) {
+ if (st_lookup(tbl, id, &val)) {
+ return val;
+ }
+ }
+ }
+ if (warn) {
+ rb_warning("instance variable %s not initialized", rb_id2name(id));
+ }
+ return Qnil;
+}
+
+static void
+generic_ivar_set(obj, id, val)
+ VALUE obj;
+ ID id;
+ VALUE val;
+{
+ st_table *tbl;
+
+ if (rb_special_const_p(obj)) {
+ special_generic_ivar = 1;
+ }
+ if (!generic_iv_tbl) {
+ generic_iv_tbl = st_init_numtable();
+ }
+
+ if (!st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) {
+ FL_SET(obj, FL_EXIVAR);
+ tbl = st_init_numtable();
+ st_add_direct(generic_iv_tbl, obj, (st_data_t)tbl);
+ st_add_direct(tbl, id, val);
+ return;
+ }
+ st_insert(tbl, id, val);
+}
+
+static VALUE
+generic_ivar_defined(obj, id)
+ VALUE obj;
+ ID id;
+{
+ st_table *tbl;
+ VALUE val;
+
+ if (!generic_iv_tbl) return Qfalse;
+ if (!st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) return Qfalse;
+ if (st_lookup(tbl, id, &val)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static int
+generic_ivar_remove(obj, id, valp)
+ VALUE obj;
+ ID id;
+ VALUE *valp;
+{
+ st_table *tbl;
+ int status;
+
+ if (!generic_iv_tbl) return 0;
+ if (!st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) return 0;
+ status = st_delete(tbl, &id, valp);
+ if (tbl->num_entries == 0) {
+ st_delete(generic_iv_tbl, &obj, (st_data_t *)&tbl);
+ st_free_table(tbl);
+ }
+ return status;
+}
+
+void
+rb_mark_generic_ivar(obj)
+ VALUE obj;
+{
+ st_table *tbl;
+
+ if (!generic_iv_tbl) return;
+ if (st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) {
+ rb_mark_tbl(tbl);
+ }
+}
+
+static int
+givar_mark_i(key, value)
+ ID key;
+ VALUE value;
+{
+ rb_gc_mark(value);
+ return ST_CONTINUE;
+}
+
+static int
+givar_i(obj, tbl)
+ VALUE obj;
+ st_table *tbl;
+{
+ if (rb_special_const_p(obj)) {
+ st_foreach(tbl, givar_mark_i, 0);
+ }
+ return ST_CONTINUE;
+}
+
+void
+rb_mark_generic_ivar_tbl()
+{
+ if (!generic_iv_tbl) return;
+ if (special_generic_ivar == 0) return;
+ st_foreach_safe(generic_iv_tbl, givar_i, 0);
+}
+
+void
+rb_free_generic_ivar(obj)
+ VALUE obj;
+{
+ st_table *tbl;
+
+ if (st_delete(generic_iv_tbl, &obj, (st_data_t *)&tbl))
+ st_free_table(tbl);
+}
+
+void
+rb_copy_generic_ivar(clone, obj)
+ VALUE clone, obj;
+{
+ st_table *tbl;
+
+ if (!generic_iv_tbl) return;
+ if (st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) {
+ st_table *old;
+
+ if (st_lookup(generic_iv_tbl, clone, (st_data_t *)&old)) {
+ st_free_table(old);
+ st_insert(generic_iv_tbl, clone, (st_data_t)st_copy(tbl));
+ }
+ else {
+ st_add_direct(generic_iv_tbl, clone, (st_data_t)st_copy(tbl));
+ }
+ }
+}
+
+static VALUE
+ivar_get(obj, id, warn)
+ VALUE obj;
+ ID id;
+ int warn;
+{
+ VALUE val;
+
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id, &val))
+ return val;
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
+ return generic_ivar_get(obj, id, warn);
+ break;
+ }
+ if (warn) {
+ rb_warning("instance variable %s not initialized", rb_id2name(id));
+ }
+ return Qnil;
+}
+
+VALUE
+rb_ivar_get(obj, id)
+ VALUE obj;
+ ID id;
+{
+ return ivar_get(obj, id, Qtrue);
+}
+
+VALUE
+rb_attr_get(obj, id)
+ VALUE obj;
+ ID id;
+{
+ return ivar_get(obj, id, Qfalse);
+}
+
+VALUE
+rb_ivar_set(obj, id, val)
+ VALUE obj;
+ ID id;
+ VALUE val;
+{
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+ if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (!ROBJECT(obj)->iv_tbl) ROBJECT(obj)->iv_tbl = st_init_numtable();
+ st_insert(ROBJECT(obj)->iv_tbl, id, val);
+ break;
+ default:
+ generic_ivar_set(obj, id, val);
+ break;
+ }
+ return val;
+}
+
+VALUE
+rb_ivar_defined(obj, id)
+ VALUE obj;
+ ID id;
+{
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id, 0))
+ return Qtrue;
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
+ return generic_ivar_defined(obj, id);
+ break;
+ }
+ return Qfalse;
+}
+
+static int
+ivar_i(key, entry, ary)
+ ID key;
+ struct global_entry *entry;
+ VALUE ary;
+{
+ if (rb_is_instance_id(key)) {
+ rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * obj.instance_variables => array
+ *
+ * Returns an array of instance variable names for the receiver. Note
+ * that simply defining an accessor does not create the corresponding
+ * instance variable.
+ *
+ * class Fred
+ * attr_accessor :a1
+ * def initialize
+ * @iv = 3
+ * end
+ * end
+ * Fred.new.instance_variables #=> ["@iv"]
+ */
+
+VALUE
+rb_obj_instance_variables(obj)
+ VALUE obj;
+{
+ VALUE ary;
+
+ ary = rb_ary_new();
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl) {
+ st_foreach_safe(ROBJECT(obj)->iv_tbl, ivar_i, ary);
+ }
+ break;
+ default:
+ if (!generic_iv_tbl) break;
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+ st_table *tbl;
+
+ if (st_lookup(generic_iv_tbl, obj, (st_data_t *)&tbl)) {
+ st_foreach_safe(tbl, ivar_i, ary);
+ }
+ }
+ break;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * obj.remove_instance_variable(symbol) => obj
+ *
+ * Removes the named instance variable from <i>obj</i>, returning that
+ * variable's value.
+ *
+ * class Dummy
+ * attr_reader :var
+ * def initialize
+ * @var = 99
+ * end
+ * def remove
+ * remove_instance_variable(:@var)
+ * end
+ * end
+ * d = Dummy.new
+ * d.var #=> 99
+ * d.remove #=> 99
+ * d.var #=> nil
+ */
+
+VALUE
+rb_obj_remove_instance_variable(obj, name)
+ VALUE obj, name;
+{
+ VALUE val = Qnil;
+ ID id = rb_to_id(name);
+
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+ if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl && st_delete(ROBJECT(obj)->iv_tbl, (st_data_t*)&id, &val)) {
+ return val;
+ }
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+ if (generic_ivar_remove(obj, id, &val)) {
+ return val;
+ }
+ }
+ break;
+ }
+ rb_name_error(id, "instance variable %s not defined", rb_id2name(id));
+ return Qnil; /* not reached */
+}
+
+NORETURN(static void uninitialized_constant _((VALUE, ID)));
+static void
+uninitialized_constant(klass, id)
+ VALUE klass;
+ ID id;
+{
+ if (klass && klass != rb_cObject)
+ rb_name_error(id, "uninitialized constant %s::%s",
+ rb_class2name(klass),
+ rb_id2name(id));
+ else {
+ rb_name_error(id, "uninitialized constant %s", rb_id2name(id));
+ }
+}
+
+static VALUE
+const_missing(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_funcall(klass, rb_intern("const_missing"), 1, ID2SYM(id));
+}
+
+
+/*
+ * call-seq:
+ * mod.const_missing(sym) => obj
+ *
+ * Invoked when a reference is made to an undefined constant in
+ * <i>mod</i>. It is passed a symbol for the undefined constant, and
+ * returns a value to be used for that constant. The
+ * following code is a (very bad) example: if reference is made to
+ * an undefined constant, it attempts to load a file whose name is
+ * the lowercase version of the constant (thus class <code>Fred</code> is
+ * assumed to be in file <code>fred.rb</code>). If found, it returns the
+ * value of the loaded class. It therefore implements a perverse
+ * kind of autoload facility.
+ *
+ * def Object.const_missing(name)
+ * @looked_for ||= {}
+ * str_name = name.to_s
+ * raise "Class not found: #{name}" if @looked_for[str_name]
+ * @looked_for[str_name] = 1
+ * file = str_name.downcase
+ * require file
+ * klass = const_get(name)
+ * return klass if klass
+ * raise "Class not found: #{name}"
+ * end
+ *
+ */
+
+VALUE
+rb_mod_const_missing(klass, name)
+ VALUE klass, name;
+{
+ ruby_frame = ruby_frame->prev; /* pop frame for "const_missing" */
+ uninitialized_constant(klass, rb_to_id(name));
+ return Qnil; /* not reached */
+}
+
+static struct st_table *
+check_autoload_table(av)
+ VALUE av;
+{
+ Check_Type(av, T_DATA);
+ if (RDATA(av)->dmark != (RUBY_DATA_FUNC)rb_mark_tbl ||
+ RDATA(av)->dfree != (RUBY_DATA_FUNC)st_free_table) {
+ rb_raise(rb_eTypeError, "wrong autoload table: %s", RSTRING(rb_inspect(av))->ptr);
+ }
+ return (struct st_table *)DATA_PTR(av);
+}
+
+void
+rb_autoload(mod, id, file)
+ VALUE mod;
+ ID id;
+ const char *file;
+{
+ VALUE av, fn;
+ struct st_table *tbl;
+
+ if (!rb_is_const_id(id)) {
+ rb_raise(rb_eNameError, "autoload must be constant name", rb_id2name(id));
+ }
+ if (!file || !*file) {
+ rb_raise(rb_eArgError, "empty file name");
+ }
+
+ if ((tbl = RCLASS(mod)->iv_tbl) && st_lookup(tbl, id, &av) && av != Qundef)
+ return;
+
+ rb_const_set(mod, id, Qundef);
+ tbl = RCLASS(mod)->iv_tbl;
+ if (st_lookup(tbl, autoload, &av)) {
+ tbl = check_autoload_table(av);
+ }
+ else {
+ av = Data_Wrap_Struct(0 , rb_mark_tbl, st_free_table, 0);
+ st_add_direct(tbl, autoload, av);
+ DATA_PTR(av) = tbl = st_init_numtable();
+ }
+ fn = rb_str_new2(file);
+ FL_UNSET(fn, FL_TAINT);
+ OBJ_FREEZE(fn);
+ st_insert(tbl, id, (st_data_t)rb_node_newnode(NODE_MEMO, fn, ruby_safe_level, 0));
+}
+
+static NODE*
+autoload_delete(mod, id)
+ VALUE mod;
+ ID id;
+{
+ VALUE val;
+ st_data_t load = 0;
+
+ st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, 0);
+ if (st_lookup(RCLASS(mod)->iv_tbl, autoload, &val)) {
+ struct st_table *tbl = check_autoload_table(val);
+
+ st_delete(tbl, (st_data_t*)&id, &load);
+
+ if (tbl->num_entries == 0) {
+ id = autoload;
+ st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, &val);
+ }
+ }
+
+ return (NODE *)load;
+}
+
+VALUE
+rb_autoload_load(klass, id)
+ VALUE klass;
+ ID id;
+{
+ VALUE file;
+ NODE *load = autoload_delete(klass, id);
+
+ if (!load || !(file = load->nd_lit) || rb_provided(RSTRING(file)->ptr)) {
+ return Qfalse;
+ }
+ return rb_require_safe(file, load->nd_nth);
+}
+
+static VALUE
+autoload_file(mod, id)
+ VALUE mod;
+ ID id;
+{
+ VALUE val, file;
+ struct st_table *tbl;
+ st_data_t load;
+
+ if (!st_lookup(RCLASS(mod)->iv_tbl, autoload, &val) ||
+ !(tbl = check_autoload_table(val)) || !st_lookup(tbl, id, &load)) {
+ return Qnil;
+ }
+ file = ((NODE *)load)->nd_lit;
+ Check_Type(file, T_STRING);
+ if (!RSTRING(file)->ptr || !*RSTRING(file)->ptr) {
+ rb_raise(rb_eArgError, "empty file name");
+ }
+ if (!rb_provided(RSTRING(file)->ptr)) {
+ return file;
+ }
+
+ /* already loaded but not defined */
+ st_delete(tbl, (st_data_t*)&id, 0);
+ if (!tbl->num_entries) {
+ id = autoload;
+ st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, &val);
+ }
+ return Qnil;
+}
+
+VALUE
+rb_autoload_p(mod, id)
+ VALUE mod;
+ ID id;
+{
+ struct st_table *tbl = RCLASS(mod)->iv_tbl;
+ VALUE val;
+
+ if (!tbl || !st_lookup(tbl, id, &val) || val != Qundef) {
+ return Qnil;
+ }
+ return autoload_file(mod, id);
+}
+
+static VALUE
+rb_const_get_0(klass, id, exclude, recurse)
+ VALUE klass;
+ ID id;
+ int exclude, recurse;
+{
+ VALUE value, tmp;
+ int mod_retry = 0;
+
+ tmp = klass;
+ retry:
+ while (tmp) {
+ while (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) {
+ if (value == Qundef) {
+ if (!RTEST(rb_autoload_load(tmp, id))) break;
+ continue;
+ }
+ if (exclude && tmp == rb_cObject && klass != rb_cObject) {
+ rb_warn("toplevel constant %s referenced by %s::%s",
+ rb_id2name(id), rb_class2name(klass), rb_id2name(id));
+ }
+ return value;
+ }
+ if (!recurse && klass != rb_cObject) break;
+ tmp = RCLASS(tmp)->super;
+ }
+ if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) {
+ mod_retry = 1;
+ tmp = rb_cObject;
+ goto retry;
+ }
+
+ return const_missing(klass, id);
+}
+
+VALUE
+rb_const_get_from(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_get_0(klass, id, Qtrue, Qtrue);
+}
+
+VALUE
+rb_const_get(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_get_0(klass, id, Qfalse, Qtrue);
+}
+
+VALUE
+rb_const_get_at(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_get_0(klass, id, Qtrue, Qfalse);
+}
+
+/*
+ * call-seq:
+ * remove_const(sym) => obj
+ *
+ * Removes the definition of the given constant, returning that
+ * constant's value. Predefined classes and singleton objects (such as
+ * <i>true</i>) cannot be removed.
+ */
+
+VALUE
+rb_mod_remove_const(mod, name)
+ VALUE mod, name;
+{
+ ID id = rb_to_id(name);
+ VALUE val;
+
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as a constant name", rb_id2name(id));
+ }
+ if (!OBJ_TAINTED(mod) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't remove constant");
+ if (OBJ_FROZEN(mod)) rb_error_frozen("class/module");
+
+ if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, (st_data_t*)&id, &val)) {
+ if (val == Qundef) {
+ autoload_delete(mod, id);
+ val = Qnil;
+ }
+ return val;
+ }
+ if (rb_const_defined_at(mod, id)) {
+ rb_name_error(id, "cannot remove %s::%s",
+ rb_class2name(mod), rb_id2name(id));
+ }
+ rb_name_error(id, "constant %s::%s not defined",
+ rb_class2name(mod), rb_id2name(id));
+ return Qnil; /* not reached */
+}
+
+static int
+sv_i(key, value, tbl)
+ ID key;
+ VALUE value;
+ st_table *tbl;
+{
+ if (rb_is_const_id(key)) {
+ if (!st_lookup(tbl, key, 0)) {
+ st_insert(tbl, key, key);
+ }
+ }
+ return ST_CONTINUE;
+}
+
+void*
+rb_mod_const_at(mod, data)
+ VALUE mod;
+ void *data;
+{
+ st_table *tbl = data;
+ if (!tbl) {
+ tbl = st_init_numtable();
+ }
+ if (RCLASS(mod)->iv_tbl) {
+ st_foreach_safe(RCLASS(mod)->iv_tbl, sv_i, (st_data_t)tbl);
+ }
+ return tbl;
+}
+
+void*
+rb_mod_const_of(mod, data)
+ VALUE mod;
+ void *data;
+{
+ VALUE tmp = mod;
+ for (;;) {
+ data = rb_mod_const_at(tmp, data);
+ tmp = RCLASS(tmp)->super;
+ if (!tmp) break;
+ if (tmp == rb_cObject && mod != rb_cObject) break;
+ }
+ return data;
+}
+
+static int
+list_i(key, value, ary)
+ ID key, value;
+ VALUE ary;
+{
+ rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
+ return ST_CONTINUE;
+}
+
+VALUE
+rb_const_list(data)
+ void *data;
+{
+ st_table *tbl = data;
+ VALUE ary;
+
+ if (!tbl) return rb_ary_new2(0);
+ ary = rb_ary_new2(tbl->num_entries);
+ st_foreach(tbl, list_i, ary);
+ st_free_table(tbl);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * mod.constants => array
+ *
+ * Returns an array of the names of the constants accessible in
+ * <i>mod</i>. This includes the names of constants in any included
+ * modules (example at start of section).
+ */
+
+VALUE
+rb_mod_constants(mod)
+ VALUE mod;
+{
+ return rb_const_list(rb_mod_const_of(mod, 0));
+}
+
+static int
+rb_const_defined_0(klass, id, exclude, recurse)
+ VALUE klass;
+ ID id;
+ int exclude, recurse;
+{
+ VALUE value, tmp;
+ int mod_retry = 0;
+
+ tmp = klass;
+ retry:
+ while (tmp) {
+ if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl, id, &value)) {
+ if (value == Qundef && NIL_P(autoload_file(klass, id)))
+ return Qfalse;
+ return Qtrue;
+ }
+ if (!recurse && klass != rb_cObject) break;
+ tmp = RCLASS(tmp)->super;
+ }
+ if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) {
+ mod_retry = 1;
+ tmp = rb_cObject;
+ goto retry;
+ }
+ return Qfalse;
+}
+
+int
+rb_const_defined_from(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_defined_0(klass, id, Qtrue, Qtrue);
+}
+
+int
+rb_const_defined(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_defined_0(klass, id, Qfalse, Qtrue);
+}
+
+int
+rb_const_defined_at(klass, id)
+ VALUE klass;
+ ID id;
+{
+ return rb_const_defined_0(klass, id, Qtrue, Qfalse);
+}
+
+static void
+mod_av_set(klass, id, val, isconst)
+ VALUE klass;
+ ID id;
+ VALUE val;
+ int isconst;
+{
+ char *dest = isconst ? "constant" : "class variable";
+
+ if (!OBJ_TAINTED(klass) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't set %s", dest);
+ if (OBJ_FROZEN(klass)) {
+ if (BUILTIN_TYPE(klass) == T_MODULE) {
+ rb_error_frozen("module");
+ }
+ else {
+ rb_error_frozen("class");
+ }
+ }
+ if (!RCLASS(klass)->iv_tbl) {
+ RCLASS(klass)->iv_tbl = st_init_numtable();
+ }
+ else if (isconst) {
+ VALUE value = Qfalse;
+
+ if (st_lookup(RCLASS(klass)->iv_tbl, id, &value)) {
+ if (value == Qundef)
+ autoload_delete(klass, id);
+ else
+ rb_warn("already initialized %s %s", dest, rb_id2name(id));
+ }
+ }
+
+ st_insert(RCLASS(klass)->iv_tbl, id, val);
+}
+
+void
+rb_const_set(klass, id, val)
+ VALUE klass;
+ ID id;
+ VALUE val;
+{
+ if (NIL_P(klass)) {
+ rb_raise(rb_eTypeError, "no class/module to define constant %s",
+ rb_id2name(id));
+ }
+ mod_av_set(klass, id, val, Qtrue);
+}
+
+void
+rb_define_const(klass, name, val)
+ VALUE klass;
+ const char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+
+ if (!rb_is_const_id(id)) {
+ rb_warn("rb_define_const: invalid name `%s' for constant", name);
+ }
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ rb_const_set(klass, id, val);
+}
+
+void
+rb_define_global_const(name, val)
+ const char *name;
+ VALUE val;
+{
+ rb_define_const(rb_cObject, name, val);
+}
+
+static VALUE
+original_module(c)
+ VALUE c;
+{
+ if (TYPE(c) == T_ICLASS)
+ return RBASIC(c)->klass;
+ return c;
+}
+
+static void
+cvar_override_check(id, a)
+ ID id;
+ VALUE a;
+{
+ VALUE base = original_module(a);
+
+ a = RCLASS(a)->super;
+ while (a) {
+ if (RCLASS(a)->iv_tbl) {
+ if (st_lookup(RCLASS(a)->iv_tbl,id,0)) {
+ rb_warning("class variable %s of %s is overridden by %s",
+ rb_id2name(id), rb_class2name(original_module(a)),
+ rb_class2name(base));
+ }
+ }
+ a = RCLASS(a)->super;
+ }
+}
+
+void
+rb_cvar_set(klass, id, val, warn)
+ VALUE klass;
+ ID id;
+ VALUE val;
+ int warn;
+{
+ VALUE tmp;
+
+ tmp = klass;
+ while (tmp) {
+ if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,0)) {
+ if (OBJ_FROZEN(tmp)) rb_error_frozen("class/module");
+ if (!OBJ_TAINTED(tmp) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify class variable");
+ if (warn && RTEST(ruby_verbose) && klass != tmp) {
+ rb_warning("already initialized class variable %s", rb_id2name(id));
+ }
+ st_insert(RCLASS(tmp)->iv_tbl,id,val);
+ if (RTEST(ruby_verbose)) {
+ cvar_override_check(id, tmp);
+ }
+ return;
+ }
+ tmp = RCLASS(tmp)->super;
+ }
+
+ mod_av_set(klass, id, val, Qfalse);
+}
+
+VALUE
+rb_cvar_get(klass, id)
+ VALUE klass;
+ ID id;
+{
+ VALUE value;
+ VALUE tmp;
+
+ tmp = klass;
+ while (tmp) {
+ if (RCLASS(tmp)->iv_tbl) {
+ if (st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) {
+ if (RTEST(ruby_verbose)) {
+ cvar_override_check(id, tmp);
+ }
+ return value;
+ }
+ }
+ tmp = RCLASS(tmp)->super;
+ }
+
+ rb_name_error(id,"uninitialized class variable %s in %s",
+ rb_id2name(id), rb_class2name(klass));
+ return Qnil; /* not reached */
+}
+
+VALUE
+rb_cvar_defined(klass, id)
+ VALUE klass;
+ ID id;
+{
+ VALUE tmp;
+
+ tmp = klass;
+ while (tmp) {
+ if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,0)) {
+ return Qtrue;
+ }
+ tmp = RCLASS(tmp)->super;
+ }
+
+ return Qfalse;
+}
+
+void
+rb_cv_set(klass, name, val)
+ VALUE klass;
+ const char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "wrong class variable name %s", name);
+ }
+ rb_cvar_set(klass, id, val, Qfalse);
+}
+
+VALUE
+rb_cv_get(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ ID id = rb_intern(name);
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "wrong class variable name %s", name);
+ }
+ return rb_cvar_get(klass, id);
+}
+
+void
+rb_define_class_variable(klass, name, val)
+ VALUE klass;
+ const char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "wrong class variable name %s", name);
+ }
+ rb_cvar_set(klass, id, val, Qtrue);
+}
+
+static int
+cv_i(key, value, ary)
+ ID key;
+ VALUE value;
+ VALUE ary;
+{
+ if (rb_is_class_id(key)) {
+ VALUE kval = rb_str_new2(rb_id2name(key));
+ if (!rb_ary_includes(ary, kval)) {
+ rb_ary_push(ary, kval);
+ }
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * mod.class_variables => array
+ *
+ * Returns an array of the names of class variables in <i>mod</i> and
+ * the ancestors of <i>mod</i>.
+ *
+ * class One
+ * @@var1 = 1
+ * end
+ * class Two < One
+ * @@var2 = 2
+ * end
+ * One.class_variables #=> ["@@var1"]
+ * Two.class_variables #=> ["@@var2", "@@var1"]
+ */
+
+VALUE
+rb_mod_class_variables(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ for (;;) {
+ if (RCLASS(obj)->iv_tbl) {
+ st_foreach_safe(RCLASS(obj)->iv_tbl, cv_i, ary);
+ }
+ obj = RCLASS(obj)->super;
+ if (!obj) break;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * remove_class_variable(sym) => obj
+ *
+ * Removes the definition of the <i>sym</i>, returning that
+ * constant's value.
+ *
+ * class Dummy
+ * @@var = 99
+ * puts @@var
+ * remove_class_variable(:@@var)
+ * puts(defined? @@var)
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 99
+ * nil
+ */
+
+VALUE
+rb_mod_remove_cvar(mod, name)
+ VALUE mod, name;
+{
+ ID id = rb_to_id(name);
+ VALUE val;
+
+ if (!rb_is_class_id(id)) {
+ rb_name_error(id, "wrong class variable name %s", rb_id2name(id));
+ }
+ if (!OBJ_TAINTED(mod) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't remove class variable");
+ if (OBJ_FROZEN(mod)) rb_error_frozen("class/module");
+
+ if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, (st_data_t*)&id, &val)) {
+ return val;
+ }
+ if (rb_cvar_defined(mod, id)) {
+ rb_name_error(id, "cannot remove %s for %s",
+ rb_id2name(id), rb_class2name(mod));
+ }
+ rb_name_error(id, "class variable %s not defined for %s",
+ rb_id2name(id), rb_class2name(mod));
+ return Qnil; /* not reached */
+}
+
+VALUE
+rb_iv_get(obj, name)
+ VALUE obj;
+ const char *name;
+{
+ ID id = rb_intern(name);
+
+ return rb_ivar_get(obj, id);
+}
+
+VALUE
+rb_iv_set(obj, name, val)
+ VALUE obj;
+ const char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+
+ return rb_ivar_set(obj, id, val);
+}
+/**********************************************************************
+
+ version.c -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Thu Sep 30 20:08:01 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "version.h"
+#include <stdio.h>
+
+const char ruby_version[] = RUBY_VERSION;
+const char ruby_release_date[] = RUBY_RELEASE_DATE;
+const char ruby_platform[] = RUBY_PLATFORM;
+const int ruby_patchlevel = RUBY_PATCHLEVEL;
+
+void
+Init_version()
+{
+ VALUE v = rb_obj_freeze(rb_str_new2(ruby_version));
+ VALUE d = rb_obj_freeze(rb_str_new2(ruby_release_date));
+ VALUE p = rb_obj_freeze(rb_str_new2(ruby_platform));
+
+ rb_define_global_const("RUBY_VERSION", v);
+ rb_define_global_const("RUBY_RELEASE_DATE", d);
+ rb_define_global_const("RUBY_PLATFORM", p);
+ rb_define_global_const("RUBY_PATCHLEVEL", INT2FIX(RUBY_PATCHLEVEL));
+
+ /* obsolete constants */
+ rb_define_global_const("VERSION", v);
+ rb_define_global_const("RELEASE_DATE", d);
+ rb_define_global_const("PLATFORM", p);
+}
+
+void
+ruby_show_version()
+{
+ printf("ruby %s (%s patchlevel %d) [%s]\n", RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PATCHLEVEL, RUBY_PLATFORM);
+ fflush(stdout);
+}
+
+void
+ruby_show_copyright()
+{
+ printf("ruby - Copyright (C) 1993-%d Yukihiro Matsumoto\n", RUBY_RELEASE_YEAR);
+ exit(0);
+}
+#define PACKAGE_NAME ""
+#define PACKAGE_TARNAME ""
+#define PACKAGE_VERSION ""
+#define PACKAGE_STRING ""
+#define PACKAGE_BUGREPORT ""
+#define USE_BUILTIN_FRAME_ADDRESS 1
+#define STDC_HEADERS 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_LONG_LONG 1
+#define HAVE_OFF_T 1
+#define SIZEOF_INT 4
+#define SIZEOF_SHORT 2
+#define SIZEOF_LONG 4
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF___INT64 0
+#define SIZEOF_OFF_T 8
+#define SIZEOF_VOIDP 4
+#define SIZEOF_FLOAT 4
+#define SIZEOF_DOUBLE 8
+#define SIZEOF_TIME_T 4
+#define rb_pid_t pid_t
+#define rb_gid_t gid_t
+#define rb_uid_t uid_t
+#define HAVE_PROTOTYPES 1
+#define TOKEN_PASTE(x,y) x##y
+#define HAVE_STDARG_PROTOTYPES 1
+#define NORETURN(x) __attribute__ ((noreturn)) x
+#define NOINLINE(x) __attribute__ ((noinline)) x
+#define HAVE_DECL_SYS_NERR 1
+#define HAVE_LIBDL 1
+#define HAVE_DIRENT_H 1
+#define STDC_HEADERS 1
+#define HAVE_SYS_WAIT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_SYS_FILE_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_SYSCALL_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_SYS_FCNTL_H 1
+#define HAVE_SYS_SELECT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TIMES_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_PWD_H 1
+#define HAVE_GRP_H 1
+#define HAVE_UTIME_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_SYS_RESOURCE_H 1
+#define HAVE_NETINET_IN_SYSTM_H 1
+#define HAVE_FLOAT_H 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_UCONTEXT_H 1
+#define SIZEOF_RLIM_T 8
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+#define HAVE_ST_BLKSIZE 1
+#define HAVE_STRUCT_STAT_ST_BLOCKS 1
+#define HAVE_ST_BLOCKS 1
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+#define HAVE_ST_RDEV 1
+#define GETGROUPS_T gid_t
+#define RETSIGTYPE void
+#define HAVE_ALLOCA_H 1
+#define HAVE_ALLOCA 1
+#define HAVE_FSEEKO 1
+#define HAVE_FTELLO 1
+#define HAVE_DUP2 1
+#define HAVE_MEMMOVE 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRNCASECMP 1
+#define HAVE_STRERROR 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRCHR 1
+#define HAVE_STRSTR 1
+#define HAVE_STRTOUL 1
+#define HAVE_CRYPT 1
+#define HAVE_FLOCK 1
+#define HAVE_VSNPRINTF 1
+#define HAVE_ISNAN 1
+#define HAVE_FINITE 1
+#define HAVE_ISINF 1
+#define HAVE_HYPOT 1
+#define HAVE_ACOSH 1
+#define HAVE_ERF 1
+#define HAVE_FMOD 1
+#define HAVE_KILLPG 1
+#define HAVE_WAIT4 1
+#define HAVE_WAITPID 1
+#define HAVE_SYSCALL 1
+#define HAVE_CHROOT 1
+#define HAVE_FSYNC 1
+#define HAVE_GETCWD 1
+#define HAVE_TRUNCATE 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_TIMES 1
+#define HAVE_UTIMES 1
+#define HAVE_FCNTL 1
+#define HAVE_LOCKF 1
+#define HAVE_LSTAT 1
+#define HAVE_SYMLINK 1
+#define HAVE_LINK 1
+#define HAVE_READLINK 1
+#define HAVE_SETITIMER 1
+#define HAVE_SETRUID 1
+#define HAVE_SETEUID 1
+#define HAVE_SETREUID 1
+#define HAVE_SETRGID 1
+#define HAVE_SETEGID 1
+#define HAVE_SETREGID 1
+#define HAVE_ISSETUGID 1
+#define HAVE_PAUSE 1
+#define HAVE_LCHOWN 1
+#define HAVE_LCHMOD 1
+#define HAVE_GETPGRP 1
+#define HAVE_SETPGRP 1
+#define HAVE_GETPGID 1
+#define HAVE_SETPGID 1
+#define HAVE_INITGROUPS 1
+#define HAVE_GETGROUPS 1
+#define HAVE_SETGROUPS 1
+#define HAVE_GETPRIORITY 1
+#define HAVE_GETRLIMIT 1
+#define HAVE_SETRLIMIT 1
+#define HAVE_SYSCONF 1
+#define HAVE_DLOPEN 1
+#define HAVE_SIGPROCMASK 1
+#define HAVE_SIGACTION 1
+#define HAVE__SETJMP 1
+#define HAVE_SETSID 1
+#define HAVE_TELLDIR 1
+#define HAVE_SEEKDIR 1
+#define HAVE_FCHMOD 1
+#define HAVE_MKTIME 1
+#define HAVE_TIMEGM 1
+#define HAVE_GETTIMEOFDAY 1
+#define HAVE_COSH 1
+#define HAVE_SINH 1
+#define HAVE_TANH 1
+#define HAVE_ROUND 1
+#define HAVE_SETUID 1
+#define HAVE_SETGID 1
+#define HAVE_SETENV 1
+#define HAVE_UNSETENV 1
+#define HAVE_STRUCT_TM_TM_ZONE 1
+#define HAVE_TM_ZONE 1
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+#define HAVE_DAYLIGHT 1
+#define HAVE_VAR_TIMEZONE 1
+#define TYPEOF_VAR_TIMEZONE long
+#define NEGATIVE_TIME_T 1
+#define POSIX_SIGNAL 1
+#define GETPGRP_VOID 1
+#define SETPGRP_VOID 1
+#define RSHIFT(x,y) ((x)>>(int)y)
+#define FILE_COUNT _r
+#define FILE_READPTR _p
+#define NEED_IO_SEEK_BETWEEN_RW 1
+#define HAVE__SC_CLK_TCK 1
+#define STACK_GROW_DIRECTION -1
+#define DEFAULT_KCODE KCODE_NONE
+#define DLEXT_MAXLEN 7
+#define DLEXT ".bundle"
+#define RUBY_LIB "/usr/local/lib/ruby/1.8"
+#define RUBY_SITE_LIB "/usr/local/lib/ruby/site_ruby"
+#define RUBY_SITE_LIB2 "/usr/local/lib/ruby/site_ruby/1.8"
+#define RUBY_PLATFORM "i686-darwin9.8.0"
+#define RUBY_ARCHLIB "/usr/local/lib/ruby/1.8/i686-darwin9.8.0"
+#define RUBY_SITE_ARCHLIB "/usr/local/lib/ruby/site_ruby/1.8/i686-darwin9.8.0"
+/************************************************
+
+ defines.h -
+
+ $Author: shyouhei $
+ $Date: 2008-06-15 16:06:16 +0200 (Sun, 15 Jun 2008) $
+ created at: Wed May 18 00:21:44 JST 1994
+
+************************************************/
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#define RUBY
+
+#ifdef __cplusplus
+# ifndef HAVE_PROTOTYPES
+# define HAVE_PROTOTYPES 1
+# endif
+# ifndef HAVE_STDARG_PROTOTYPES
+# define HAVE_STDARG_PROTOTYPES 1
+# endif
+#endif
+
+#undef _
+#ifdef HAVE_PROTOTYPES
+# define _(args) args
+#else
+# define _(args) ()
+#endif
+
+#undef __
+#ifdef HAVE_STDARG_PROTOTYPES
+# define __(args) args
+#else
+# define __(args) ()
+#endif
+
+#ifdef __cplusplus
+#define ANYARGS ...
+#else
+#define ANYARGS
+#endif
+
+#define xmalloc ruby_xmalloc
+#define xcalloc ruby_xcalloc
+#define xrealloc ruby_xrealloc
+#define xfree ruby_xfree
+
+void *xmalloc _((long));
+void *xcalloc _((long,long));
+void *xrealloc _((void*,long));
+void xfree _((void*));
+
+#if SIZEOF_LONG_LONG > 0
+# define LONG_LONG long long
+#elif SIZEOF___INT64 > 0
+# define HAVE_LONG_LONG 1
+# define LONG_LONG __int64
+# undef SIZEOF_LONG_LONG
+# define SIZEOF_LONG_LONG SIZEOF___INT64
+#endif
+
+#if SIZEOF_INT*2 <= SIZEOF_LONG_LONG
+# define BDIGIT unsigned int
+# define SIZEOF_BDIGITS SIZEOF_INT
+# define BDIGIT_DBL unsigned LONG_LONG
+# define BDIGIT_DBL_SIGNED LONG_LONG
+#elif SIZEOF_INT*2 <= SIZEOF_LONG
+# define BDIGIT unsigned int
+# define SIZEOF_BDIGITS SIZEOF_INT
+# define BDIGIT_DBL unsigned long
+# define BDIGIT_DBL_SIGNED long
+#elif SIZEOF_SHORT*2 <= SIZEOF_LONG
+# define BDIGIT unsigned short
+# define SIZEOF_BDIGITS SIZEOF_SHORT
+# define BDIGIT_DBL unsigned long
+# define BDIGIT_DBL_SIGNED long
+#else
+# define BDIGIT unsigned short
+# define SIZEOF_BDIGITS (SIZEOF_LONG/2)
+# define BDIGIT_DBL unsigned long
+# define BDIGIT_DBL_SIGNED long
+#endif
+
+#ifdef __CYGWIN__
+#undef _WIN32
+#endif
+
+#if defined(MSDOS) || defined(_WIN32) || defined(__human68k__) || defined(__EMX__)
+#define DOSISH 1
+#ifndef _WIN32_WCE
+# define DOSISH_DRIVE_LETTER
+#endif
+#endif
+
+/* define RUBY_USE_EUC/SJIS for default kanji-code */
+#ifndef DEFAULT_KCODE
+#if defined(DOSISH) || defined(__CYGWIN__) || defined(__MACOS__) || defined(OS2)
+#define DEFAULT_KCODE KCODE_SJIS
+#else
+#define DEFAULT_KCODE KCODE_EUC
+#endif
+#endif
+
+#ifdef __NeXT__
+/* NextStep, OpenStep, Rhapsody */
+#ifndef S_IRUSR
+#define S_IRUSR 0000400 /* read permission, owner */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 0000040 /* read permission, group */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 0000004 /* read permission, other */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 0000200 /* write permission, owner */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 0000020 /* write permission, group */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0000002 /* write permission, other */
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 0000100 /* execute/search permission, owner */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0000010 /* execute/search permission, group */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0000001 /* execute/search permission, other */
+#endif
+#ifndef S_IRWXU
+#define S_IRWXU 0000700 /* read, write, execute permissions, owner */
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG 0000070 /* read, write, execute permissions, group */
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO 0000007 /* read, write, execute permissions, other */
+#endif
+#ifndef S_ISBLK
+#define S_ISBLK(mode) (((mode) & (0170000)) == (0060000))
+#endif
+#ifndef S_ISCHR
+#define S_ISCHR(mode) (((mode) & (0170000)) == (0020000))
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & (0170000)) == (0040000))
+#endif
+#ifndef S_ISFIFO
+#define S_ISFIFO(mode) (((mode) & (0170000)) == (0010000))
+#endif
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & (0170000)) == (0100000))
+#endif
+/* Do not trust WORDS_BIGENDIAN from configure since -arch compiler flag may
+ result in a different endian. Instead trust __BIG_ENDIAN__ and
+ __LITTLE_ENDIAN__ which are set correctly by -arch. */
+#undef WORDS_BIGENDIAN
+#ifdef __BIG_ENDIAN__
+#define WORDS_BIGENDIAN
+#endif
+#ifndef __APPLE__
+/* NextStep, OpenStep (but not Rhapsody) */
+#ifndef GETPGRP_VOID
+#define GETPGRP_VOID 1
+#endif
+#ifndef WNOHANG
+#define WNOHANG 01
+#endif
+#ifndef WUNTRACED
+#define WUNTRACED 02
+#endif
+#ifndef X_OK
+#define X_OK 1
+#endif
+#endif /* __APPLE__ */
+#endif /* NeXT */
+
+#ifdef _WIN32
+#include "win32/win32.h"
+#endif
+
+#if defined(__VMS)
+#include "vms.h"
+#endif
+
+#if defined(__BEOS__)
+#include <net/socket.h> /* intern.h needs fd_set definition */
+#endif
+
+#ifdef RUBY_EXPORT
+#undef RUBY_EXTERN
+#endif
+
+#ifndef RUBY_EXTERN
+#define RUBY_EXTERN extern
+#endif
+
+#ifndef EXTERN
+#define EXTERN RUBY_EXTERN /* deprecated */
+#endif
+
+#ifndef RUBY_MBCHAR_MAXSIZE
+#define RUBY_MBCHAR_MAXSIZE INT_MAX
+ /* MB_CUR_MAX will not work well in C locale */
+#endif
+
+#if defined(sparc) || defined(__sparc__)
+static inline void
+flush_register_windows(void)
+{
+ asm
+#ifdef __GNUC__
+ volatile
+#endif
+# if defined(__sparc_v9__) || defined(__sparcv9) || defined(__arch64__)
+ ("flushw")
+# else
+ ("ta 0x03")
+# endif /* trap always to flush register windows if we are on a Sparc system */
+ ;
+}
+# define FLUSH_REGISTER_WINDOWS flush_register_windows()
+#elif defined(__ia64)
+void *rb_ia64_bsp(void);
+void rb_ia64_flushrs(void);
+# define FLUSH_REGISTER_WINDOWS rb_ia64_flushrs()
+#else
+# define FLUSH_REGISTER_WINDOWS ((void)0)
+#endif
+
+#if defined(DOSISH)
+#define PATH_SEP ";"
+#elif defined(riscos)
+#define PATH_SEP ","
+#else
+#define PATH_SEP ":"
+#endif
+#define PATH_SEP_CHAR PATH_SEP[0]
+
+#if defined(__human68k__)
+#define PATH_ENV "path"
+#else
+#define PATH_ENV "PATH"
+#endif
+
+#if defined(DOSISH) && !defined(__human68k__) && !defined(__EMX__)
+#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
+
+#ifndef RUBY_PLATFORM
+#define RUBY_PLATFORM "unknown-unknown"
+#endif
+
+#endif
+/**********************************************************************
+
+ dln.h -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Wed Jan 19 16:53:09 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef DLN_H
+#define DLN_H
+
+#ifdef __cplusplus
+# ifndef HAVE_PROTOTYPES
+# define HAVE_PROTOTYPES 1
+# endif
+# ifndef HAVE_STDARG_PROTOTYPES
+# define HAVE_STDARG_PROTOTYPES 1
+# endif
+#endif
+
+#undef _
+#ifdef HAVE_PROTOTYPES
+# define _(args) args
+#else
+# define _(args) ()
+#endif
+
+char *dln_find_exe _((const char*,const char*));
+char *dln_find_file _((const char*,const char*));
+
+#ifdef USE_DLN_A_OUT
+extern char *dln_argv0;
+#endif
+
+void *dln_load _((const char*));
+#endif
+/**********************************************************************
+
+ env.h -
+
+ $Author: knu $
+ $Date: 2007-03-03 08:30:46 +0100 (Sat, 03 Mar 2007) $
+ created at: Mon Jul 11 11:53:03 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef ENV_H
+#define ENV_H
+
+extern struct FRAME {
+ VALUE self;
+ int argc;
+ ID last_func;
+ ID orig_func;
+ VALUE last_class;
+ struct FRAME *prev;
+ struct FRAME *tmp;
+ struct RNode *node;
+ int iter;
+ int flags;
+ unsigned long uniq;
+} *ruby_frame;
+
+void rb_gc_mark_frame _((struct FRAME *));
+
+#define FRAME_DMETH 1
+#define FRAME_FUNC 2
+
+extern struct SCOPE {
+ struct RBasic super;
+ ID *local_tbl;
+ VALUE *local_vars;
+ int flags;
+} *ruby_scope;
+
+#define SCOPE_ALLOCA 0
+#define SCOPE_MALLOC 1
+#define SCOPE_NOSTACK 2
+#define SCOPE_DONT_RECYCLE 4
+#define SCOPE_CLONE 8
+
+extern int ruby_in_eval;
+
+extern VALUE ruby_class;
+
+struct RVarmap {
+ struct RBasic super;
+ ID id;
+ VALUE val;
+ struct RVarmap *next;
+};
+extern struct RVarmap *ruby_dyna_vars;
+
+#endif /* ENV_H */
+/**********************************************************************
+
+ intern.h -
+
+ $Author: shyouhei $
+ $Date: 2009-01-22 07:18:48 +0100 (Thu, 22 Jan 2009) $
+ created at: Thu Jun 10 14:22:17 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+/*
+ * Functions and variables that are used by more than one source file of
+ * the kernel.
+ */
+
+#define ID_ALLOCATOR 1
+
+/* array.c */
+void rb_mem_clear _((register VALUE*, register long));
+VALUE rb_assoc_new _((VALUE, VALUE));
+VALUE rb_check_array_type _((VALUE));
+VALUE rb_ary_new _((void));
+VALUE rb_ary_new2 _((long));
+VALUE rb_ary_new3 __((long,...));
+VALUE rb_ary_new4 _((long, const VALUE *));
+VALUE rb_ary_freeze _((VALUE));
+VALUE rb_ary_aref _((int, VALUE*, VALUE));
+void rb_ary_store _((VALUE, long, VALUE));
+VALUE rb_ary_dup _((VALUE));
+VALUE rb_ary_to_ary _((VALUE));
+VALUE rb_ary_to_s _((VALUE));
+VALUE rb_ary_push _((VALUE, VALUE));
+VALUE rb_ary_pop _((VALUE));
+VALUE rb_ary_shift _((VALUE));
+VALUE rb_ary_unshift _((VALUE, VALUE));
+VALUE rb_ary_entry _((VALUE, long));
+VALUE rb_ary_each _((VALUE));
+VALUE rb_ary_join _((VALUE, VALUE));
+VALUE rb_ary_print_on _((VALUE, VALUE));
+VALUE rb_ary_reverse _((VALUE));
+VALUE rb_ary_sort _((VALUE));
+VALUE rb_ary_sort_bang _((VALUE));
+VALUE rb_ary_delete _((VALUE, VALUE));
+VALUE rb_ary_delete_at _((VALUE, long));
+VALUE rb_ary_clear _((VALUE));
+VALUE rb_ary_plus _((VALUE, VALUE));
+VALUE rb_ary_concat _((VALUE, VALUE));
+VALUE rb_ary_assoc _((VALUE, VALUE));
+VALUE rb_ary_rassoc _((VALUE, VALUE));
+VALUE rb_ary_includes _((VALUE, VALUE));
+VALUE rb_ary_cmp _((VALUE, VALUE));
+VALUE rb_protect_inspect _((VALUE(*)(ANYARGS),VALUE,VALUE));
+VALUE rb_inspecting_p _((VALUE));
+VALUE rb_check_array_value _((VALUE));
+VALUE rb_values_at _((VALUE, long, int, VALUE*, VALUE(*) _((VALUE,long))));
+/* bignum.c */
+VALUE rb_big_clone _((VALUE));
+void rb_big_2comp _((VALUE));
+VALUE rb_big_norm _((VALUE));
+VALUE rb_uint2big _((unsigned long));
+VALUE rb_int2big _((long));
+VALUE rb_uint2inum _((unsigned long));
+VALUE rb_int2inum _((long));
+VALUE rb_cstr_to_inum _((const char*, int, int));
+VALUE rb_str_to_inum _((VALUE, int, int));
+VALUE rb_cstr2inum _((const char*, int));
+VALUE rb_str2inum _((VALUE, int));
+VALUE rb_big2str _((VALUE, int));
+VALUE rb_big2str0 _((VALUE, int, int));
+long rb_big2long _((VALUE));
+#define rb_big2int(x) rb_big2long(x)
+unsigned long rb_big2ulong _((VALUE));
+#define rb_big2uint(x) rb_big2ulong(x)
+#if HAVE_LONG_LONG
+VALUE rb_ll2inum _((LONG_LONG));
+VALUE rb_ull2inum _((unsigned LONG_LONG));
+LONG_LONG rb_big2ll _((VALUE));
+unsigned LONG_LONG rb_big2ull _((VALUE));
+#endif /* HAVE_LONG_LONG */
+void rb_quad_pack _((char*,VALUE));
+VALUE rb_quad_unpack _((const char*,int));
+VALUE rb_dbl2big _((double));
+double rb_big2dbl _((VALUE));
+VALUE rb_big_plus _((VALUE, VALUE));
+VALUE rb_big_minus _((VALUE, VALUE));
+VALUE rb_big_mul _((VALUE, VALUE));
+VALUE rb_big_divmod _((VALUE, VALUE));
+VALUE rb_big_pow _((VALUE, VALUE));
+VALUE rb_big_and _((VALUE, VALUE));
+VALUE rb_big_or _((VALUE, VALUE));
+VALUE rb_big_xor _((VALUE, VALUE));
+VALUE rb_big_lshift _((VALUE, VALUE));
+VALUE rb_big_rand _((VALUE, double*));
+/* class.c */
+VALUE rb_class_boot _((VALUE));
+VALUE rb_class_new _((VALUE));
+VALUE rb_mod_init_copy _((VALUE, VALUE));
+VALUE rb_class_init_copy _((VALUE, VALUE));
+VALUE rb_singleton_class_clone _((VALUE));
+void rb_singleton_class_attached _((VALUE,VALUE));
+VALUE rb_make_metaclass _((VALUE, VALUE));
+void rb_check_inheritable _((VALUE));
+VALUE rb_class_inherited _((VALUE, VALUE));
+VALUE rb_define_class_id _((ID, VALUE));
+VALUE rb_module_new _((void));
+VALUE rb_define_module_id _((ID));
+VALUE rb_mod_included_modules _((VALUE));
+VALUE rb_mod_include_p _((VALUE, VALUE));
+VALUE rb_mod_ancestors _((VALUE));
+VALUE rb_class_instance_methods _((int, VALUE*, VALUE));
+VALUE rb_class_public_instance_methods _((int, VALUE*, VALUE));
+VALUE rb_class_protected_instance_methods _((int, VALUE*, VALUE));
+VALUE rb_big_rshift(VALUE, VALUE);
+VALUE rb_class_private_instance_methods _((int, VALUE*, VALUE));
+VALUE rb_obj_singleton_methods _((int, VALUE*, VALUE));
+void rb_define_method_id _((VALUE, ID, VALUE (*)(ANYARGS), int));
+void rb_frozen_class_p _((VALUE));
+void rb_undef _((VALUE, ID));
+void rb_define_protected_method _((VALUE, const char*, VALUE (*)(ANYARGS), int));
+void rb_define_private_method _((VALUE, const char*, VALUE (*)(ANYARGS), int));
+void rb_define_singleton_method _((VALUE, const char*, VALUE(*)(ANYARGS), int));
+VALUE rb_singleton_class _((VALUE));
+/* compar.c */
+int rb_cmpint _((VALUE, VALUE, VALUE));
+NORETURN(void rb_cmperr _((VALUE, VALUE)));
+/* enum.c */
+/* error.c */
+RUBY_EXTERN int ruby_nerrs;
+VALUE rb_exc_new _((VALUE, const char*, long));
+VALUE rb_exc_new2 _((VALUE, const char*));
+VALUE rb_exc_new3 _((VALUE, VALUE));
+NORETURN(void rb_loaderror __((const char*, ...)));
+NORETURN(void rb_name_error __((ID, const char*, ...)));
+NORETURN(void rb_invalid_str _((const char*, const char*)));
+void rb_compile_error __((const char*, ...));
+void rb_compile_error_append __((const char*, ...));
+NORETURN(void rb_load_fail _((const char*)));
+NORETURN(void rb_error_frozen _((const char*)));
+void rb_check_frozen _((VALUE));
+/* eval.c */
+RUBY_EXTERN struct RNode *ruby_current_node;
+void ruby_set_current_source _((void));
+NORETURN(void rb_exc_raise _((VALUE)));
+NORETURN(void rb_exc_fatal _((VALUE)));
+VALUE rb_f_exit _((int,VALUE*));
+VALUE rb_f_abort _((int,VALUE*));
+void rb_remove_method _((VALUE, const char*));
+#define rb_disable_super(klass, name) ((void)0)
+#define rb_enable_super(klass, name) ((void)0)
+#define HAVE_RB_DEFINE_ALLOC_FUNC 1
+void rb_define_alloc_func _((VALUE, VALUE (*)(VALUE)));
+void rb_undef_alloc_func _((VALUE));
+void rb_clear_cache _((void));
+void rb_clear_cache_by_class _((VALUE));
+void rb_alias _((VALUE, ID, ID));
+void rb_attr _((VALUE,ID,int,int,int));
+int rb_method_boundp _((VALUE, ID, int));
+VALUE rb_dvar_defined _((ID));
+VALUE rb_dvar_curr _((ID));
+VALUE rb_dvar_ref _((ID));
+void rb_dvar_asgn _((ID, VALUE));
+void rb_dvar_push _((ID, VALUE));
+VALUE *rb_svar _((int));
+VALUE rb_eval_cmd _((VALUE, VALUE, int));
+int rb_obj_respond_to _((VALUE, ID, int));
+int rb_respond_to _((VALUE, ID));
+void rb_interrupt _((void));
+VALUE rb_apply _((VALUE, ID, VALUE));
+void rb_backtrace _((void));
+ID rb_frame_last_func _((void));
+VALUE rb_obj_instance_eval _((int, VALUE*, VALUE));
+VALUE rb_mod_module_eval _((int, VALUE*, VALUE));
+void rb_load _((VALUE, int));
+void rb_load_protect _((VALUE, int, int*));
+NORETURN(void rb_jump_tag _((int)));
+int rb_provided _((const char*));
+void rb_provide _((const char*));
+VALUE rb_f_require _((VALUE, VALUE));
+VALUE rb_require_safe _((VALUE, int));
+void rb_obj_call_init _((VALUE, int, VALUE*));
+VALUE rb_class_new_instance _((int, VALUE*, VALUE));
+VALUE rb_block_proc _((void));
+VALUE rb_f_lambda _((void));
+VALUE rb_proc_new _((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE));
+VALUE rb_protect _((VALUE (*)(VALUE), VALUE, int*));
+void rb_set_end_proc _((void (*)(VALUE), VALUE));
+void rb_mark_end_proc _((void));
+void rb_exec_end_proc _((void));
+void ruby_finalize _((void));
+NORETURN(void ruby_stop _((int)));
+int ruby_cleanup _((int));
+int ruby_exec _((void));
+void rb_gc_mark_threads _((void));
+void rb_thread_start_timer _((void));
+void rb_thread_stop_timer _((void));
+void rb_thread_schedule _((void));
+void rb_thread_wait_fd _((int));
+int rb_thread_fd_writable _((int));
+void rb_thread_fd_close _((int));
+int rb_thread_alone _((void));
+void rb_thread_polling _((void));
+void rb_thread_sleep _((int));
+void rb_thread_sleep_forever _((void));
+VALUE rb_thread_stop _((void));
+VALUE rb_thread_wakeup _((VALUE));
+VALUE rb_thread_wakeup_alive _((VALUE));
+VALUE rb_thread_run _((VALUE));
+VALUE rb_thread_kill _((VALUE));
+VALUE rb_thread_alive_p _((VALUE));
+VALUE rb_thread_create _((VALUE (*)(ANYARGS), void*));
+void rb_thread_interrupt _((void));
+void rb_thread_trap_eval _((VALUE, int, int));
+void rb_thread_signal_raise _((int));
+void rb_thread_signal_exit _((void));
+int rb_thread_select _((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+void rb_thread_wait_for _((struct timeval));
+VALUE rb_thread_current _((void));
+VALUE rb_thread_main _((void));
+VALUE rb_thread_local_aref _((VALUE, ID));
+VALUE rb_thread_local_aset _((VALUE, ID, VALUE));
+void rb_thread_atfork _((void));
+VALUE rb_funcall_rescue __((VALUE, ID, int, ...));
+/* file.c */
+VALUE rb_file_s_expand_path _((int, VALUE *));
+VALUE rb_file_expand_path _((VALUE, VALUE));
+void rb_file_const _((const char*, VALUE));
+int rb_find_file_ext _((VALUE*, const char* const*));
+VALUE rb_find_file _((VALUE));
+char *rb_path_next _((const char *));
+char *rb_path_skip_prefix _((const char *));
+char *rb_path_last_separator _((const char *));
+char *rb_path_end _((const char *));
+VALUE rb_file_directory_p _((VALUE,VALUE));
+/* gc.c */
+NORETURN(void rb_memerror __((void)));
+int ruby_stack_check _((void));
+size_t ruby_stack_length _((VALUE**));
+int rb_during_gc _((void));
+char *rb_source_filename _((const char*));
+void rb_gc_mark_locations _((VALUE*, VALUE*));
+void rb_mark_tbl _((struct st_table*));
+void rb_mark_set _((struct st_table*));
+void rb_mark_hash _((struct st_table*));
+void rb_gc_mark_maybe _((VALUE));
+void rb_gc_mark _((VALUE));
+void rb_gc_force_recycle _((VALUE));
+void rb_gc _((void));
+void rb_gc_copy_finalizer _((VALUE,VALUE));
+void rb_gc_finalize_deferred _((void));
+void rb_gc_call_finalizer_at_exit _((void));
+VALUE rb_gc_enable _((void));
+VALUE rb_gc_disable _((void));
+VALUE rb_gc_start _((void));
+/* hash.c */
+void st_foreach_safe _((struct st_table *, int (*)(ANYARGS), unsigned long));
+void rb_hash_foreach _((VALUE, int (*)(ANYARGS), VALUE));
+VALUE rb_hash _((VALUE));
+VALUE rb_hash_new _((void));
+VALUE rb_hash_freeze _((VALUE));
+VALUE rb_hash_aref _((VALUE, VALUE));
+VALUE rb_hash_aset _((VALUE, VALUE, VALUE));
+VALUE rb_hash_delete_if _((VALUE));
+VALUE rb_hash_delete _((VALUE,VALUE));
+int rb_path_check _((char*));
+int rb_env_path_tainted _((void));
+/* io.c */
+#define rb_defout rb_stdout
+RUBY_EXTERN VALUE rb_fs;
+RUBY_EXTERN VALUE rb_output_fs;
+RUBY_EXTERN VALUE rb_rs;
+RUBY_EXTERN VALUE rb_default_rs;
+RUBY_EXTERN VALUE rb_output_rs;
+VALUE rb_io_write _((VALUE, VALUE));
+VALUE rb_io_gets _((VALUE));
+VALUE rb_io_getc _((VALUE));
+VALUE rb_io_ungetc _((VALUE, VALUE));
+VALUE rb_io_close _((VALUE));
+VALUE rb_io_eof _((VALUE));
+VALUE rb_io_binmode _((VALUE));
+VALUE rb_io_addstr _((VALUE, VALUE));
+VALUE rb_io_printf _((int, VALUE*, VALUE));
+VALUE rb_io_print _((int, VALUE*, VALUE));
+VALUE rb_io_puts _((int, VALUE*, VALUE));
+VALUE rb_file_open _((const char*, const char*));
+VALUE rb_gets _((void));
+void rb_write_error _((const char*));
+void rb_write_error2 _((const char*, long));
+/* marshal.c */
+VALUE rb_marshal_dump _((VALUE, VALUE));
+VALUE rb_marshal_load _((VALUE));
+/* numeric.c */
+void rb_num_zerodiv _((void));
+VALUE rb_num_coerce_bin _((VALUE, VALUE));
+VALUE rb_num_coerce_cmp _((VALUE, VALUE));
+VALUE rb_num_coerce_relop _((VALUE, VALUE));
+VALUE rb_float_new _((double));
+VALUE rb_num2fix _((VALUE));
+VALUE rb_fix2str _((VALUE, int));
+VALUE rb_dbl_cmp _((double, double));
+/* object.c */
+int rb_eql _((VALUE, VALUE));
+VALUE rb_any_to_s _((VALUE));
+VALUE rb_inspect _((VALUE));
+VALUE rb_obj_is_instance_of _((VALUE, VALUE));
+VALUE rb_obj_is_kind_of _((VALUE, VALUE));
+VALUE rb_obj_alloc _((VALUE));
+VALUE rb_obj_clone _((VALUE));
+VALUE rb_obj_dup _((VALUE));
+VALUE rb_obj_init_copy _((VALUE,VALUE));
+VALUE rb_obj_taint _((VALUE));
+VALUE rb_obj_tainted _((VALUE));
+VALUE rb_obj_untaint _((VALUE));
+VALUE rb_obj_freeze _((VALUE));
+VALUE rb_obj_id _((VALUE));
+VALUE rb_obj_class _((VALUE));
+VALUE rb_class_real _((VALUE));
+VALUE rb_class_inherited_p _((VALUE, VALUE));
+VALUE rb_convert_type _((VALUE,int,const char*,const char*));
+VALUE rb_check_convert_type _((VALUE,int,const char*,const char*));
+VALUE rb_to_int _((VALUE));
+VALUE rb_Integer _((VALUE));
+VALUE rb_Float _((VALUE));
+VALUE rb_String _((VALUE));
+VALUE rb_Array _((VALUE));
+double rb_cstr_to_dbl _((const char*, int));
+double rb_str_to_dbl _((VALUE, int));
+/* parse.y */
+RUBY_EXTERN int ruby_sourceline;
+RUBY_EXTERN char *ruby_sourcefile;
+int ruby_yyparse _((void));
+ID rb_id_attrset _((ID));
+void rb_parser_append_print _((void));
+void rb_parser_while_loop _((int, int));
+int ruby_parser_stack_on_heap _((void));
+void rb_gc_mark_parser _((void));
+int rb_is_const_id _((ID));
+int rb_is_instance_id _((ID));
+int rb_is_class_id _((ID));
+int rb_is_local_id _((ID));
+int rb_is_junk_id _((ID));
+int rb_symname_p _((const char*));
+int rb_sym_interned_p _((VALUE));
+VALUE rb_backref_get _((void));
+void rb_backref_set _((VALUE));
+VALUE rb_lastline_get _((void));
+void rb_lastline_set _((VALUE));
+VALUE rb_sym_all_symbols _((void));
+/* process.c */
+int rb_proc_exec _((const char*));
+VALUE rb_f_exec _((int,VALUE*));
+int rb_waitpid _((int,int*,int));
+void rb_syswait _((int));
+VALUE rb_proc_times _((VALUE));
+VALUE rb_detach_process _((int));
+/* range.c */
+VALUE rb_range_new _((VALUE, VALUE, int));
+VALUE rb_range_beg_len _((VALUE, long*, long*, long, int));
+VALUE rb_length_by_each _((VALUE));
+/* re.c */
+int rb_memcmp _((const void*,const void*,long));
+int rb_memcicmp _((const void*,const void*,long));
+long rb_memsearch _((const void*,long,const void*,long));
+VALUE rb_reg_nth_defined _((int, VALUE));
+VALUE rb_reg_nth_match _((int, VALUE));
+VALUE rb_reg_last_match _((VALUE));
+VALUE rb_reg_match_pre _((VALUE));
+VALUE rb_reg_match_post _((VALUE));
+VALUE rb_reg_match_last _((VALUE));
+VALUE rb_reg_new _((const char*, long, int));
+VALUE rb_reg_match _((VALUE, VALUE));
+VALUE rb_reg_match2 _((VALUE));
+int rb_reg_options _((VALUE));
+void rb_set_kcode _((const char*));
+const char* rb_get_kcode _((void));
+void rb_kcode_set_option _((VALUE));
+void rb_kcode_reset_option _((void));
+/* ruby.c */
+RUBY_EXTERN VALUE rb_argv;
+RUBY_EXTERN VALUE rb_argv0;
+void rb_load_file _((const char*));
+void ruby_script _((const char*));
+void ruby_prog_init _((void));
+void ruby_set_argv _((int, char**));
+void ruby_process_options _((int, char**));
+void ruby_load_script _((void));
+void ruby_init_loadpath _((void));
+void ruby_incpush _((const char*));
+/* signal.c */
+VALUE rb_f_kill _((int, VALUE*));
+void rb_gc_mark_trap_list _((void));
+#ifdef POSIX_SIGNAL
+#define posix_signal ruby_posix_signal
+void posix_signal _((int, RETSIGTYPE (*)(int)));
+#endif
+void rb_trap_exit _((void));
+void rb_trap_exec _((void));
+const char *ruby_signal_name _((int));
+void ruby_default_signal _((int));
+/* sprintf.c */
+VALUE rb_f_sprintf _((int, VALUE*));
+VALUE rb_str_format _((int, VALUE*, VALUE));
+/* string.c */
+VALUE rb_str_new _((const char*, long));
+VALUE rb_str_new2 _((const char*));
+VALUE rb_str_new3 _((VALUE));
+VALUE rb_str_new4 _((VALUE));
+VALUE rb_str_new5 _((VALUE, const char*, long));
+VALUE rb_tainted_str_new _((const char*, long));
+VALUE rb_tainted_str_new2 _((const char*));
+VALUE rb_str_buf_new _((long));
+VALUE rb_str_buf_new2 _((const char*));
+VALUE rb_str_buf_append _((VALUE, VALUE));
+VALUE rb_str_buf_cat _((VALUE, const char*, long));
+VALUE rb_str_buf_cat2 _((VALUE, const char*));
+VALUE rb_obj_as_string _((VALUE));
+VALUE rb_check_string_type _((VALUE));
+VALUE rb_str_dup _((VALUE));
+VALUE rb_str_locktmp _((VALUE));
+VALUE rb_str_unlocktmp _((VALUE));
+VALUE rb_str_dup_frozen _((VALUE));
+VALUE rb_str_plus _((VALUE, VALUE));
+VALUE rb_str_times _((VALUE, VALUE));
+VALUE rb_str_substr _((VALUE, long, long));
+void rb_str_modify _((VALUE));
+VALUE rb_str_freeze _((VALUE));
+VALUE rb_str_resize _((VALUE, long));
+VALUE rb_str_cat _((VALUE, const char*, long));
+VALUE rb_str_cat2 _((VALUE, const char*));
+VALUE rb_str_append _((VALUE, VALUE));
+VALUE rb_str_concat _((VALUE, VALUE));
+int rb_str_hash _((VALUE));
+int rb_str_cmp _((VALUE, VALUE));
+VALUE rb_str_upto _((VALUE, VALUE, int));
+void rb_str_update _((VALUE, long, long, VALUE));
+VALUE rb_str_inspect _((VALUE));
+VALUE rb_str_dump _((VALUE));
+VALUE rb_str_split _((VALUE, const char*));
+void rb_str_associate _((VALUE, VALUE));
+VALUE rb_str_associated _((VALUE));
+void rb_str_setter _((VALUE, ID, VALUE*));
+VALUE rb_str_intern _((VALUE));
+/* struct.c */
+VALUE rb_struct_new __((VALUE, ...));
+VALUE rb_struct_define __((const char*, ...));
+VALUE rb_struct_alloc _((VALUE, VALUE));
+VALUE rb_struct_aref _((VALUE, VALUE));
+VALUE rb_struct_aset _((VALUE, VALUE, VALUE));
+VALUE rb_struct_getmember _((VALUE, ID));
+VALUE rb_struct_iv_get _((VALUE, char*));
+VALUE rb_struct_s_members _((VALUE));
+VALUE rb_struct_members _((VALUE));
+/* time.c */
+VALUE rb_time_new _((time_t, time_t));
+/* variable.c */
+VALUE rb_mod_name _((VALUE));
+VALUE rb_class_path _((VALUE));
+void rb_set_class_path _((VALUE, VALUE, const char*));
+VALUE rb_path2class _((const char*));
+void rb_name_class _((VALUE, ID));
+VALUE rb_class_name _((VALUE));
+void rb_autoload _((VALUE, ID, const char*));
+VALUE rb_autoload_load _((VALUE, ID));
+VALUE rb_autoload_p _((VALUE, ID));
+void rb_gc_mark_global_tbl _((void));
+VALUE rb_f_trace_var _((int, VALUE*));
+VALUE rb_f_untrace_var _((int, VALUE*));
+VALUE rb_f_global_variables _((void));
+void rb_alias_variable _((ID, ID));
+struct st_table* rb_generic_ivar_table _((VALUE));
+void rb_copy_generic_ivar _((VALUE,VALUE));
+void rb_mark_generic_ivar _((VALUE));
+void rb_mark_generic_ivar_tbl _((void));
+void rb_free_generic_ivar _((VALUE));
+VALUE rb_ivar_get _((VALUE, ID));
+VALUE rb_ivar_set _((VALUE, ID, VALUE));
+VALUE rb_ivar_defined _((VALUE, ID));
+VALUE rb_iv_set _((VALUE, const char*, VALUE));
+VALUE rb_iv_get _((VALUE, const char*));
+VALUE rb_attr_get _((VALUE, ID));
+VALUE rb_obj_instance_variables _((VALUE));
+VALUE rb_obj_remove_instance_variable _((VALUE, VALUE));
+void *rb_mod_const_at _((VALUE, void*));
+void *rb_mod_const_of _((VALUE, void*));
+VALUE rb_const_list _((void*));
+VALUE rb_mod_constants _((VALUE));
+VALUE rb_mod_remove_const _((VALUE, VALUE));
+int rb_const_defined _((VALUE, ID));
+int rb_const_defined_at _((VALUE, ID));
+int rb_const_defined_from _((VALUE, ID));
+VALUE rb_const_get _((VALUE, ID));
+VALUE rb_const_get_at _((VALUE, ID));
+VALUE rb_const_get_from _((VALUE, ID));
+void rb_const_set _((VALUE, ID, VALUE));
+VALUE rb_mod_constants _((VALUE));
+VALUE rb_mod_const_missing _((VALUE,VALUE));
+VALUE rb_cvar_defined _((VALUE, ID));
+#define RB_CVAR_SET_4ARGS 1
+void rb_cvar_set _((VALUE, ID, VALUE, int));
+VALUE rb_cvar_get _((VALUE, ID));
+void rb_cv_set _((VALUE, const char*, VALUE));
+VALUE rb_cv_get _((VALUE, const char*));
+void rb_define_class_variable _((VALUE, const char*, VALUE));
+VALUE rb_mod_class_variables _((VALUE));
+VALUE rb_mod_remove_cvar _((VALUE, VALUE));
+/* version.c */
+void ruby_show_version _((void));
+void ruby_show_copyright _((void));
+/************************************************
+
+ missing.h - prototype for *.c in ./missing, and
+ for missing timeval struct
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Sat May 11 23:46:03 JST 2002
+
+************************************************/
+
+#ifndef MISSING_H
+#define MISSING_H
+
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#elif !defined(_WIN32)
+# define time_t long
+struct timeval {
+ time_t tv_sec; /* seconds */
+ time_t tv_usec; /* microseconds */
+};
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+
+#ifndef HAVE_ACOSH
+extern double acosh _((double));
+extern double asinh _((double));
+extern double atanh _((double));
+#endif
+
+#ifndef HAVE_CRYPT
+extern char *crypt _((char *, char *));
+#endif
+
+#ifndef HAVE_DUP2
+extern int dup2 _((int, int));
+#endif
+
+#ifndef HAVE_EACCESS
+extern int eaccess _((const char*, int));
+#endif
+
+#ifndef HAVE_FINITE
+extern int finite _((double));
+#endif
+
+#ifndef HAVE_FLOCK
+extern int flock _((int, int));
+#endif
+
+/*
+#ifndef HAVE_FREXP
+extern double frexp _((double, int *));
+#endif
+*/
+
+#ifndef HAVE_HYPOT
+extern double hypot _((double, double));
+#endif
+
+#ifndef HAVE_ERF
+extern double erf _((double));
+extern double erfc _((double));
+#endif
+
+#ifndef HAVE_ISINF
+# if defined(HAVE_FINITE) && defined(HAVE_ISNAN)
+# define isinf(x) (!finite(x) && !isnan(x))
+# else
+extern int isinf _((double));
+# endif
+#endif
+
+#ifndef HAVE_ISNAN
+extern int isnan _((double));
+#endif
+
+/*
+#ifndef HAVE_MEMCMP
+extern int memcmp _((char *, char *, int));
+#endif
+*/
+
+#ifndef HAVE_MEMMOVE
+extern void *memmove _((void *, void *, int));
+#endif
+
+/*
+#ifndef HAVE_MODF
+extern double modf _((double, double *));
+#endif
+*/
+
+#ifndef HAVE_STRCASECMP
+extern int strcasecmp _((char *, char *));
+#endif
+
+#ifndef HAVE_STRNCASECMP
+extern int strncasecmp _((char *, char *, int));
+#endif
+
+#ifndef HAVE_STRCHR
+extern char *strchr _((char *, int));
+extern char *strrchr _((char *, int));
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *strerror _((int));
+#endif
+
+#ifndef HAVE_STRFTIME
+extern size_t strftime _((char *, size_t, const char *, const struct tm *));
+#endif
+
+#ifndef HAVE_STRSTR
+extern char *strstr _((char *, char *));
+#endif
+
+/*
+#ifndef HAVE_STRTOL
+extern long strtol _((char *, char **, int));
+#endif
+*/
+
+#ifndef HAVE_STRTOUL
+extern unsigned long strtoul _((char *, char **, int));
+#endif
+
+#ifndef HAVE_VSNPRINTF
+# ifdef HAVE_STDARG_PROTOTYPES
+# include <stdarg.h>
+# else
+# include <varargs.h>
+# endif
+extern int snprintf __((char *, size_t n, char const *, ...));
+extern int vsnprintf _((char *, size_t n, char const *, va_list));
+#endif
+
+#endif /* MISSING_H */
+/**********************************************************************
+
+ node.h -
+
+ $Author: wyhaines $
+ $Date: 2009-07-09 20:16:38 +0200 (Thu, 09 Jul 2009) $
+ created at: Fri May 28 15:14:02 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef NODE_H
+#define NODE_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum node_type {
+ NODE_METHOD,
+ NODE_FBODY,
+ NODE_CFUNC,
+ NODE_SCOPE,
+ NODE_BLOCK,
+ NODE_IF,
+ NODE_CASE,
+ NODE_WHEN,
+ NODE_OPT_N,
+ NODE_WHILE,
+ NODE_UNTIL,
+ NODE_ITER,
+ NODE_FOR,
+ NODE_BREAK,
+ NODE_NEXT,
+ NODE_REDO,
+ NODE_RETRY,
+ NODE_BEGIN,
+ NODE_RESCUE,
+ NODE_RESBODY,
+ NODE_ENSURE,
+ NODE_AND,
+ NODE_OR,
+ NODE_NOT,
+ NODE_MASGN,
+ NODE_LASGN,
+ NODE_DASGN,
+ NODE_DASGN_CURR,
+ NODE_GASGN,
+ NODE_IASGN,
+ NODE_CDECL,
+ NODE_CVASGN,
+ NODE_CVDECL,
+ NODE_OP_ASGN1,
+ NODE_OP_ASGN2,
+ NODE_OP_ASGN_AND,
+ NODE_OP_ASGN_OR,
+ NODE_CALL,
+ NODE_FCALL,
+ NODE_VCALL,
+ NODE_SUPER,
+ NODE_ZSUPER,
+ NODE_ARRAY,
+ NODE_ZARRAY,
+ NODE_HASH,
+ NODE_RETURN,
+ NODE_YIELD,
+ NODE_LVAR,
+ NODE_DVAR,
+ NODE_GVAR,
+ NODE_IVAR,
+ NODE_CONST,
+ NODE_CVAR,
+ NODE_NTH_REF,
+ NODE_BACK_REF,
+ NODE_MATCH,
+ NODE_MATCH2,
+ NODE_MATCH3,
+ NODE_LIT,
+ NODE_STR,
+ NODE_DSTR,
+ NODE_XSTR,
+ NODE_DXSTR,
+ NODE_EVSTR,
+ NODE_DREGX,
+ NODE_DREGX_ONCE,
+ NODE_ARGS,
+ NODE_ARGSCAT,
+ NODE_ARGSPUSH,
+ NODE_SPLAT,
+ NODE_TO_ARY,
+ NODE_SVALUE,
+ NODE_BLOCK_ARG,
+ NODE_BLOCK_PASS,
+ NODE_DEFN,
+ NODE_DEFS,
+ NODE_ALIAS,
+ NODE_VALIAS,
+ NODE_UNDEF,
+ NODE_CLASS,
+ NODE_MODULE,
+ NODE_SCLASS,
+ NODE_COLON2,
+ NODE_COLON3,
+ NODE_CREF,
+ NODE_DOT2,
+ NODE_DOT3,
+ NODE_FLIP2,
+ NODE_FLIP3,
+ NODE_ATTRSET,
+ NODE_SELF,
+ NODE_NIL,
+ NODE_TRUE,
+ NODE_FALSE,
+ NODE_DEFINED,
+ NODE_NEWLINE,
+ NODE_POSTEXE,
+ NODE_ALLOCA,
+ NODE_DMETHOD,
+ NODE_BMETHOD,
+ NODE_MEMO,
+ NODE_IFUNC,
+ NODE_DSYM,
+ NODE_ATTRASGN,
+ NODE_LAST
+};
+
+typedef struct RNode {
+ unsigned long flags;
+ char *nd_file;
+ union {
+ struct RNode *node;
+ ID id;
+ VALUE value;
+ VALUE (*cfunc)(ANYARGS);
+ ID *tbl;
+ } u1;
+ union {
+ struct RNode *node;
+ ID id;
+ long argc;
+ VALUE value;
+ } u2;
+ union {
+ struct RNode *node;
+ ID id;
+ long state;
+ struct global_entry *entry;
+ long cnt;
+ VALUE value;
+ } u3;
+} NODE;
+
+extern NODE *ruby_cref;
+extern NODE *ruby_top_cref;
+
+#define RNODE(obj) (R_CAST(RNode)(obj))
+
+#define nd_type(n) ((int)(((RNODE(n))->flags>>FL_USHIFT)&0xff))
+#define nd_set_type(n,t) \
+ RNODE(n)->flags=((RNODE(n)->flags&~FL_UMASK)|(((t)<<FL_USHIFT)&FL_UMASK))
+
+#define NODE_LSHIFT (FL_USHIFT+8)
+#define NODE_LMASK (((long)1<<(sizeof(NODE*)*CHAR_BIT-NODE_LSHIFT))-1)
+#define nd_line(n) ((unsigned int)(((RNODE(n))->flags>>NODE_LSHIFT)&NODE_LMASK))
+#define nd_set_line(n,l) \
+ RNODE(n)->flags=((RNODE(n)->flags&~(-1<<NODE_LSHIFT))|(((l)&NODE_LMASK)<<NODE_LSHIFT))
+
+#define nd_head u1.node
+#define nd_alen u2.argc
+#define nd_next u3.node
+
+#define nd_cond u1.node
+#define nd_body u2.node
+#define nd_else u3.node
+
+#define nd_orig u3.value
+
+#define nd_resq u2.node
+#define nd_ensr u3.node
+
+#define nd_1st u1.node
+#define nd_2nd u2.node
+
+#define nd_stts u1.node
+
+#define nd_entry u3.entry
+#define nd_vid u1.id
+#define nd_cflag u2.id
+#define nd_cval u3.value
+
+#define nd_cnt u3.cnt
+#define nd_tbl u1.tbl
+
+#define nd_var u1.node
+#define nd_ibdy u2.node
+#define nd_iter u3.node
+
+#define nd_value u2.node
+#define nd_aid u3.id
+
+#define nd_lit u1.value
+
+#define nd_frml u1.node
+#define nd_rest u2.node
+#define nd_opt u1.node
+
+#define nd_recv u1.node
+#define nd_mid u2.id
+#define nd_args u3.node
+
+#define nd_noex u1.id
+#define nd_defn u3.node
+
+#define nd_cfnc u1.cfunc
+#define nd_argc u2.argc
+
+#define nd_cpath u1.node
+#define nd_super u3.node
+
+#define nd_modl u1.id
+#define nd_clss u1.value
+
+#define nd_beg u1.node
+#define nd_end u2.node
+#define nd_state u3.state
+#define nd_rval u2.value
+
+#define nd_nth u2.argc
+
+#define nd_tag u1.id
+#define nd_tval u2.value
+
+#define NEW_NODE(t,a0,a1,a2) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+
+#define NEW_METHOD(n,x) NEW_NODE(NODE_METHOD,x,n,0)
+#define NEW_FBODY(n,i,o) NEW_NODE(NODE_FBODY,n,i,o)
+#define NEW_DEFN(i,a,d,p) NEW_NODE(NODE_DEFN,p,i,NEW_RFUNC(a,d))
+#define NEW_DEFS(r,i,a,d) NEW_NODE(NODE_DEFS,r,i,NEW_RFUNC(a,d))
+#define NEW_CFUNC(f,c) NEW_NODE(NODE_CFUNC,f,c,0)
+#define NEW_IFUNC(f,c) NEW_NODE(NODE_IFUNC,f,c,0)
+#define NEW_RFUNC(b1,b2) NEW_SCOPE(block_append(b1,b2))
+#define NEW_SCOPE(b) NEW_NODE(NODE_SCOPE,local_tbl(),0,(b))
+#define NEW_BLOCK(a) NEW_NODE(NODE_BLOCK,a,0,0)
+#define NEW_IF(c,t,e) NEW_NODE(NODE_IF,c,t,e)
+#define NEW_UNLESS(c,t,e) NEW_IF(c,e,t)
+#define NEW_CASE(h,b) NEW_NODE(NODE_CASE,h,b,0)
+#define NEW_WHEN(c,t,e) NEW_NODE(NODE_WHEN,c,t,e)
+#define NEW_OPT_N(b) NEW_NODE(NODE_OPT_N,0,b,0)
+#define NEW_WHILE(c,b,n) NEW_NODE(NODE_WHILE,c,b,n)
+#define NEW_UNTIL(c,b,n) NEW_NODE(NODE_UNTIL,c,b,n)
+#define NEW_FOR(v,i,b) NEW_NODE(NODE_FOR,v,b,i)
+#define NEW_ITER(v,i,b) NEW_NODE(NODE_ITER,v,b,i)
+#define NEW_BREAK(s) NEW_NODE(NODE_BREAK,s,0,0)
+#define NEW_NEXT(s) NEW_NODE(NODE_NEXT,s,0,0)
+#define NEW_REDO() NEW_NODE(NODE_REDO,0,0,0)
+#define NEW_RETRY() NEW_NODE(NODE_RETRY,0,0,0)
+#define NEW_BEGIN(b) NEW_NODE(NODE_BEGIN,0,b,0)
+#define NEW_RESCUE(b,res,e) NEW_NODE(NODE_RESCUE,b,res,e)
+#define NEW_RESBODY(a,ex,n) NEW_NODE(NODE_RESBODY,n,ex,a)
+#define NEW_ENSURE(b,en) NEW_NODE(NODE_ENSURE,b,0,en)
+#define NEW_RETURN(s) NEW_NODE(NODE_RETURN,s,0,0)
+#define NEW_YIELD(a,s) NEW_NODE(NODE_YIELD,a,0,s)
+#define NEW_LIST(a) NEW_ARRAY(a)
+#define NEW_ARRAY(a) NEW_NODE(NODE_ARRAY,a,1,0)
+#define NEW_ZARRAY() NEW_NODE(NODE_ZARRAY,0,0,0)
+#define NEW_HASH(a) NEW_NODE(NODE_HASH,a,0,0)
+#define NEW_NOT(a) NEW_NODE(NODE_NOT,0,a,0)
+#define NEW_MASGN(l,r) NEW_NODE(NODE_MASGN,l,0,r)
+#define NEW_GASGN(v,val) NEW_NODE(NODE_GASGN,v,val,rb_global_entry(v))
+#define NEW_LASGN(v,val) NEW_NODE(NODE_LASGN,v,val,local_cnt(v))
+#define NEW_DASGN(v,val) NEW_NODE(NODE_DASGN,v,val,0)
+#define NEW_DASGN_CURR(v,val) NEW_NODE(NODE_DASGN_CURR,v,val,0)
+#define NEW_IASGN(v,val) NEW_NODE(NODE_IASGN,v,val,0)
+#define NEW_CDECL(v,val,path) NEW_NODE(NODE_CDECL,v,val,path)
+#define NEW_CVASGN(v,val) NEW_NODE(NODE_CVASGN,v,val,0)
+#define NEW_CVDECL(v,val) NEW_NODE(NODE_CVDECL,v,val,0)
+#define NEW_OP_ASGN1(p,id,a) NEW_NODE(NODE_OP_ASGN1,p,id,a)
+#define NEW_OP_ASGN2(r,i,o,val) NEW_NODE(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o))
+#define NEW_OP_ASGN22(i,o) NEW_NODE(NODE_OP_ASGN2,i,o,rb_id_attrset(i))
+#define NEW_OP_ASGN_OR(i,val) NEW_NODE(NODE_OP_ASGN_OR,i,val,0)
+#define NEW_OP_ASGN_AND(i,val) NEW_NODE(NODE_OP_ASGN_AND,i,val,0)
+#define NEW_GVAR(v) NEW_NODE(NODE_GVAR,v,0,rb_global_entry(v))
+#define NEW_LVAR(v) NEW_NODE(NODE_LVAR,v,0,local_cnt(v))
+#define NEW_DVAR(v) NEW_NODE(NODE_DVAR,v,0,0)
+#define NEW_IVAR(v) NEW_NODE(NODE_IVAR,v,0,0)
+#define NEW_CONST(v) NEW_NODE(NODE_CONST,v,0,0)
+#define NEW_CVAR(v) NEW_NODE(NODE_CVAR,v,0,0)
+#define NEW_NTH_REF(n) NEW_NODE(NODE_NTH_REF,0,n,local_cnt('~'))
+#define NEW_BACK_REF(n) NEW_NODE(NODE_BACK_REF,0,n,local_cnt('~'))
+#define NEW_MATCH(c) NEW_NODE(NODE_MATCH,c,0,0)
+#define NEW_MATCH2(n1,n2) NEW_NODE(NODE_MATCH2,n1,n2,0)
+#define NEW_MATCH3(r,n2) NEW_NODE(NODE_MATCH3,r,n2,0)
+#define NEW_LIT(l) NEW_NODE(NODE_LIT,l,0,0)
+#define NEW_STR(s) NEW_NODE(NODE_STR,s,0,0)
+#define NEW_DSTR(s) NEW_NODE(NODE_DSTR,s,1,0)
+#define NEW_XSTR(s) NEW_NODE(NODE_XSTR,s,0,0)
+#define NEW_DXSTR(s) NEW_NODE(NODE_DXSTR,s,0,0)
+#define NEW_DSYM(s) NEW_NODE(NODE_DSYM,s,0,0)
+#define NEW_EVSTR(n) NEW_NODE(NODE_EVSTR,0,(n),0)
+#define NEW_CALL(r,m,a) NEW_NODE(NODE_CALL,r,m,a)
+#define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
+#define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
+#define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
+#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
+#define NEW_ARGS(f,o,r) NEW_NODE(NODE_ARGS,o,r,f)
+#define NEW_ARGSCAT(a,b) NEW_NODE(NODE_ARGSCAT,a,b,0)
+#define NEW_ARGSPUSH(a,b) NEW_NODE(NODE_ARGSPUSH,a,b,0)
+#define NEW_SPLAT(a) NEW_NODE(NODE_SPLAT,a,0,0)
+#define NEW_TO_ARY(a) NEW_NODE(NODE_TO_ARY,a,0,0)
+#define NEW_SVALUE(a) NEW_NODE(NODE_SVALUE,a,0,0)
+#define NEW_BLOCK_ARG(v) NEW_NODE(NODE_BLOCK_ARG,v,0,local_cnt(v))
+#define NEW_BLOCK_PASS(b) NEW_NODE(NODE_BLOCK_PASS,0,b,0)
+#define NEW_ALIAS(n,o) NEW_NODE(NODE_ALIAS,n,o,0)
+#define NEW_VALIAS(n,o) NEW_NODE(NODE_VALIAS,n,o,0)
+#define NEW_UNDEF(i) NEW_NODE(NODE_UNDEF,0,i,0)
+#define NEW_CLASS(n,b,s) NEW_NODE(NODE_CLASS,n,NEW_SCOPE(b),(s))
+#define NEW_SCLASS(r,b) NEW_NODE(NODE_SCLASS,r,NEW_SCOPE(b),0)
+#define NEW_MODULE(n,b) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(b),0)
+#define NEW_COLON2(c,i) NEW_NODE(NODE_COLON2,c,i,0)
+#define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
+#define NEW_CREF(c,n) NEW_NODE(NODE_CREF,c,0,n)
+#define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
+#define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
+#define NEW_ATTRSET(a) NEW_NODE(NODE_ATTRSET,a,0,0)
+#define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
+#define NEW_NIL() NEW_NODE(NODE_NIL,0,0,0)
+#define NEW_TRUE() NEW_NODE(NODE_TRUE,0,0,0)
+#define NEW_FALSE() NEW_NODE(NODE_FALSE,0,0,0)
+#define NEW_DEFINED(e) NEW_NODE(NODE_DEFINED,e,0,0)
+#define NEW_NEWLINE(n) NEW_NODE(NODE_NEWLINE,0,0,n)
+#define NEW_PREEXE(b) NEW_SCOPE(b)
+#define NEW_POSTEXE() NEW_NODE(NODE_POSTEXE,0,0,0)
+#define NEW_DMETHOD(b) NEW_NODE(NODE_DMETHOD,0,0,b)
+#define NEW_BMETHOD(b) NEW_NODE(NODE_BMETHOD,0,0,b)
+#define NEW_ATTRASGN(r,m,a) NEW_NODE(NODE_ATTRASGN,r,m,a)
+
+#define NOEX_PUBLIC 0
+#define NOEX_NOSUPER 1
+#define NOEX_PRIVATE 2
+#define NOEX_PROTECTED 4
+#define NOEX_MASK 6
+
+#define NOEX_UNDEF NOEX_NOSUPER
+
+NODE *rb_compile_cstr _((const char*, const char*, int, int));
+NODE *rb_compile_string _((const char*, VALUE, int));
+NODE *rb_compile_file _((const char*, VALUE, int));
+
+void rb_add_method _((VALUE, ID, NODE *, int));
+NODE *rb_node_newnode _((enum node_type,VALUE,VALUE,VALUE));
+
+NODE* rb_method_node _((VALUE klass, ID id));
+
+struct global_entry *rb_global_entry _((ID));
+VALUE rb_gvar_get _((struct global_entry *));
+VALUE rb_gvar_set _((struct global_entry *, VALUE));
+VALUE rb_gvar_defined _((struct global_entry *));
+
+typedef unsigned int rb_event_t;
+
+#define RUBY_EVENT_NONE 0x00
+#define RUBY_EVENT_LINE 0x01
+#define RUBY_EVENT_CLASS 0x02
+#define RUBY_EVENT_END 0x04
+#define RUBY_EVENT_CALL 0x08
+#define RUBY_EVENT_RETURN 0x10
+#define RUBY_EVENT_C_CALL 0x20
+#define RUBY_EVENT_C_RETURN 0x40
+#define RUBY_EVENT_RAISE 0x80
+#define RUBY_EVENT_ALL 0xff
+
+typedef void (*rb_event_hook_func_t) _((rb_event_t,NODE*,VALUE,ID,VALUE));
+NODE *rb_copy_node_scope _((NODE *, NODE *));
+void rb_add_event_hook _((rb_event_hook_func_t,rb_event_t));
+int rb_remove_event_hook _((rb_event_hook_func_t));
+
+#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
+#include <ucontext.h>
+#define USE_CONTEXT
+#endif
+#include <setjmp.h>
+#include "st.h"
+
+#ifdef USE_CONTEXT
+typedef struct {
+ ucontext_t context;
+ volatile int status;
+} rb_jmpbuf_t[1];
+#else
+typedef jmp_buf rb_jmpbuf_t;
+#endif
+
+enum rb_thread_status {
+ THREAD_TO_KILL,
+ THREAD_RUNNABLE,
+ THREAD_STOPPED,
+ THREAD_KILLED
+};
+
+typedef struct rb_thread *rb_thread_t;
+
+struct rb_thread {
+ rb_thread_t next, prev;
+ rb_jmpbuf_t context;
+#if (defined _WIN32 && !defined _WIN32_WCE) || defined __CYGWIN__
+ unsigned long win32_exception_list;
+#endif
+
+ VALUE result;
+
+ size_t stk_len;
+ size_t stk_max;
+ VALUE *stk_ptr;
+ VALUE *stk_pos;
+#ifdef __ia64
+ size_t bstr_len;
+ size_t bstr_max;
+ VALUE *bstr_ptr;
+ VALUE *bstr_pos;
+#endif
+
+ struct FRAME *frame;
+ struct SCOPE *scope;
+ struct RVarmap *dyna_vars;
+ struct BLOCK *block;
+ struct iter *iter;
+ struct tag *tag;
+ VALUE klass;
+ VALUE wrapper;
+ NODE *cref;
+
+ int flags; /* misc. states (vmode/rb_trap_immediate/raised) */
+
+ NODE *node;
+
+ int tracing;
+ VALUE errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ int safe;
+
+ enum rb_thread_status status;
+ int wait_for;
+ int fd;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ int select_value;
+ double delay;
+ rb_thread_t join;
+
+ int abort;
+ int priority;
+ VALUE thgroup;
+
+ struct st_table *locals;
+
+ VALUE thread;
+
+ VALUE sandbox;
+};
+
+extern VALUE (*ruby_sandbox_save)_((rb_thread_t));
+extern VALUE (*ruby_sandbox_restore)_((rb_thread_t));
+extern rb_thread_t rb_curr_thread;
+extern rb_thread_t rb_main_thread;
+
+enum {
+ RAISED_EXCEPTION = 0x1000,
+ RAISED_STACKOVERFLOW = 0x2000,
+ RAISED_NOMEMORY = 0x4000,
+ RAISED_MASK = 0xf000
+};
+int rb_thread_set_raised(rb_thread_t th);
+int rb_thread_reset_raised(rb_thread_t th);
+#define rb_thread_raised_set(th, f) ((th)->flags |= (f))
+#define rb_thread_raised_reset(th, f) ((th)->flags &= ~(f))
+#define rb_thread_raised_p(th, f) (((th)->flags & (f)) != 0)
+#define rb_thread_raised_clear(th) (rb_thread_raised_reset(th, RAISED_MASK))
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif
+
+#endif
+/**********************************************************************
+
+ re.h -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Thu Sep 30 14:18:32 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef RE_H
+#define RE_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "regex.h"
+
+typedef struct re_pattern_buffer Regexp;
+
+struct RMatch {
+ struct RBasic basic;
+ VALUE str;
+ struct re_registers *regs;
+};
+
+#define RMATCH(obj) (R_CAST(RMatch)(obj))
+
+VALUE rb_reg_regcomp _((VALUE));
+long rb_reg_search _((VALUE, VALUE, long, long));
+VALUE rb_reg_regsub _((VALUE, VALUE, struct re_registers *));
+long rb_reg_adjust_startpos _((VALUE, VALUE, long, long));
+void rb_match_busy _((VALUE));
+VALUE rb_reg_quote _((VALUE));
+
+RUBY_EXTERN int ruby_ignorecase;
+
+int rb_reg_mbclen2 _((unsigned int, VALUE));
+#define mbclen2(c,re) rb_reg_mbclen2((c),(re))
+#endif
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.12.
+ Copyright (C) 1985,89,90,91,92,93,95,96,97,98 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file LGPL. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto)
+ Last change: May 21, 1993 by t^2 */
+/* modified for Ruby by matz@netlab.co.jp */
+
+#ifndef REGEX_H
+#define REGEX_H
+
+/* symbol mangling for ruby */
+#ifdef RUBY
+# define re_adjust_startpos ruby_re_adjust_startpos
+# define re_compile_fastmap ruby_re_compile_fastmap
+# define re_compile_pattern ruby_re_compile_pattern
+# define re_copy_registers ruby_re_copy_registers
+# define re_free_pattern ruby_re_free_pattern
+# define re_free_registers ruby_re_free_registers
+# define re_match ruby_re_match
+# define re_mbcinit ruby_re_mbcinit
+# define re_search ruby_re_search
+# define re_set_casetable ruby_re_set_casetable
+# define register_info_type ruby_register_info_type
+#endif
+
+#include <stddef.h>
+
+/* Define number of parens for which we record the beginnings and ends.
+ This affects how much space the `struct re_registers' type takes up. */
+#ifndef RE_NREGS
+#define RE_NREGS 10
+#endif
+
+#define BYTEWIDTH 8
+
+#define RE_REG_MAX ((1<<BYTEWIDTH)-1)
+
+/* Maximum number of duplicates an interval can allow. */
+#ifndef RE_DUP_MAX
+#define RE_DUP_MAX ((1 << 15) - 1)
+#endif
+
+
+/* If this bit is set, then character classes are supported; they are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (1L << 9)
+
+/* match will be done case insensetively */
+#define RE_OPTION_IGNORECASE (1L)
+/* perl-style extended pattern available */
+#define RE_OPTION_EXTENDED (RE_OPTION_IGNORECASE<<1)
+/* newline will be included for . */
+#define RE_OPTION_MULTILINE (RE_OPTION_EXTENDED<<1)
+/* ^ and $ ignore newline */
+#define RE_OPTION_SINGLELINE (RE_OPTION_MULTILINE<<1)
+/* search for longest match, in accord with POSIX regexp */
+#define RE_OPTION_LONGEST (RE_OPTION_SINGLELINE<<1)
+
+#define RE_MAY_IGNORECASE (RE_OPTION_LONGEST<<1)
+#define RE_OPTIMIZE_ANCHOR (RE_MAY_IGNORECASE<<1)
+#define RE_OPTIMIZE_EXACTN (RE_OPTIMIZE_ANCHOR<<1)
+#define RE_OPTIMIZE_NO_BM (RE_OPTIMIZE_EXACTN<<1)
+#define RE_OPTIMIZE_BMATCH (RE_OPTIMIZE_NO_BM<<1)
+
+/* For multi-byte char support */
+#define MBCTYPE_ASCII 0
+#define MBCTYPE_EUC 1
+#define MBCTYPE_SJIS 2
+#define MBCTYPE_UTF8 3
+
+extern
+#if defined _WIN32 && !defined __GNUC__ && !defined RUBY_EXPORT
+__declspec(dllimport)
+# endif
+const unsigned char *re_mbctab;
+#if defined(__STDC__)
+void re_mbcinit (int);
+#else
+void re_mbcinit ();
+#endif
+
+#undef ismbchar
+#define ismbchar(c) re_mbctab[(unsigned char)(c)]
+#define mbclen(c) (re_mbctab[(unsigned char)(c)]+1)
+
+/* Structure used in re_match() */
+
+typedef union
+{
+ unsigned char *word;
+ struct {
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ } bits;
+} register_info_type;
+
+/* This data structure is used to represent a compiled pattern. */
+
+struct re_pattern_buffer
+ {
+ char *buffer; /* Space holding the compiled pattern commands. */
+ int allocated; /* Size of space that `buffer' points to. */
+ int used; /* Length of portion of buffer actually occupied */
+ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
+ /* re_search uses the fastmap, if there is one,
+ to skip over totally implausible characters. */
+ char *must; /* Pointer to exact pattern which strings should have
+ to be matched. */
+ int *must_skip; /* Pointer to exact pattern skip table for bm_search */
+ long options; /* Flags for options such as extended_pattern. */
+ long re_nsub; /* Number of subexpressions found by the compiler. */
+ char fastmap_accurate;
+ /* Set to zero when a new pattern is stored,
+ set to one when the fastmap is updated from it. */
+ char can_be_null; /* Set to one by compiling fastmap
+ if this pattern might match the null string.
+ It does not necessarily match the null string
+ in that case, but if this is zero, it cannot.
+ 2 as value means can match null string
+ but at end of range or before a character
+ listed in the fastmap. */
+
+ /* stack & working area for re_match() */
+ unsigned char **regstart;
+ unsigned char **regend;
+ unsigned char **old_regstart;
+ unsigned char **old_regend;
+ register_info_type *reg_info;
+ unsigned char **best_regstart;
+ unsigned char **best_regend;
+ };
+
+typedef struct re_pattern_buffer regex_t;
+
+/* Structure to store register contents data in.
+
+ Pass the address of such a structure as an argument to re_match, etc.,
+ if you want this information back.
+
+ For i from 1 to RE_NREGS - 1, start[i] records the starting index in
+ the string of where the ith subexpression matched, and end[i] records
+ one after the ending index. start[0] and end[0] are analogous, for
+ the entire pattern. */
+
+struct re_registers
+ {
+ int allocated;
+ int num_regs;
+ int *beg;
+ int *end;
+ };
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef size_t regoff_t;
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+#ifdef __STDC__
+
+extern char *re_compile_pattern (const char *, int, struct re_pattern_buffer *);
+void re_free_pattern (struct re_pattern_buffer *);
+/* Is this really advertised? */
+extern int re_adjust_startpos (struct re_pattern_buffer *, const char*, int, int, int);
+extern void re_compile_fastmap (struct re_pattern_buffer *);
+extern int re_search (struct re_pattern_buffer *, const char*, int, int, int,
+ struct re_registers *);
+extern int re_match (struct re_pattern_buffer *, const char *, int, int,
+ struct re_registers *);
+extern void re_set_casetable (const char *table);
+extern void re_copy_registers (struct re_registers*, struct re_registers*);
+extern void re_free_registers (struct re_registers*);
+
+#ifndef RUBY
+/* 4.2 bsd compatibility. */
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+#endif
+
+#else /* !__STDC__ */
+
+extern char *re_compile_pattern ();
+void re_free_regexp ();
+/* Is this really advertised? */
+extern int re_adjust_startpos ();
+extern void re_compile_fastmap ();
+extern int re_search ();
+extern int re_match ();
+extern void re_set_casetable ();
+extern void re_copy_registers ();
+extern void re_free_registers ();
+
+#endif /* __STDC__ */
+
+#endif /* !REGEX_H */
+/**********************************************************************
+
+ ruby.h -
+
+ $Author: shyouhei $
+ created at: Thu Jun 10 14:26:32 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#ifndef RUBY_H
+#define RUBY_H
+
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+#include "config.h"
+#ifdef RUBY_EXTCONF_H
+#include RUBY_EXTCONF_H
+#endif
+
+#define NORETURN_STYLE_NEW 1
+#ifndef NORETURN
+# define NORETURN(x) x
+#endif
+#ifndef NOINLINE
+# define NOINLINE(x) x
+#endif
+
+#include "defines.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifdef HAVE_INTRINSICS_H
+# include <intrinsics.h>
+#endif
+
+#include <stddef.h>
+#include <stdio.h>
+
+/* need to include <ctype.h> to use these macros */
+#ifndef ISPRINT
+#define ISASCII(c) isascii((int)(unsigned char)(c))
+#undef ISPRINT
+#define ISPRINT(c) (ISASCII(c) && isprint((int)(unsigned char)(c)))
+#define ISSPACE(c) (ISASCII(c) && isspace((int)(unsigned char)(c)))
+#define ISUPPER(c) (ISASCII(c) && isupper((int)(unsigned char)(c)))
+#define ISLOWER(c) (ISASCII(c) && islower((int)(unsigned char)(c)))
+#define ISALNUM(c) (ISASCII(c) && isalnum((int)(unsigned char)(c)))
+#define ISALPHA(c) (ISASCII(c) && isalpha((int)(unsigned char)(c)))
+#define ISDIGIT(c) (ISASCII(c) && isdigit((int)(unsigned char)(c)))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c)))
+#endif
+
+#if defined(HAVE_ALLOCA_H)
+#include <alloca.h>
+#else
+# ifdef _AIX
+#pragma alloca
+# endif
+#endif
+
+#if defined(__VMS)
+# pragma builtins
+# define alloca __alloca
+#endif
+
+#if SIZEOF_LONG != SIZEOF_VOIDP
+# error ---->> ruby requires sizeof(void*) == sizeof(long) to be compiled. <<----
+#else
+typedef unsigned long VALUE;
+typedef unsigned long ID;
+#endif
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# ifndef LONG_MAX
+# ifdef HAVE_LIMITS_H
+# include <limits.h>
+# else
+ /* assuming 32bit(2's compliment) long */
+# define LONG_MAX 2147483647
+# endif
+# endif
+# ifndef LONG_MIN
+# define LONG_MIN (-LONG_MAX-1)
+# endif
+# ifndef CHAR_BIT
+# define CHAR_BIT 8
+# endif
+#endif
+
+#ifdef HAVE_LONG_LONG
+# ifndef LLONG_MAX
+# ifdef LONG_LONG_MAX
+# define LLONG_MAX LONG_LONG_MAX
+# else
+# ifdef _I64_MAX
+# define LLONG_MAX _I64_MAX
+# else
+ /* assuming 64bit(2's complement) long long */
+# define LLONG_MAX 9223372036854775807LL
+# endif
+# endif
+# endif
+# ifndef LLONG_MIN
+# ifdef LONG_LONG_MIN
+# define LLONG_MIN LONG_LONG_MIN
+# else
+# ifdef _I64_MIN
+# define LLONG_MIN _I64_MIN
+# else
+# define LLONG_MIN (-LLONG_MAX-1)
+# endif
+# endif
+# endif
+#endif
+
+#define FIXNUM_MAX (LONG_MAX>>1)
+#define FIXNUM_MIN RSHIFT((long)LONG_MIN,1)
+
+#define FIXNUM_FLAG 0x01
+#define INT2FIX(i) ((VALUE)(((long)(i))<<1 | FIXNUM_FLAG))
+#define LONG2FIX(i) INT2FIX(i)
+#define rb_fix_new(v) INT2FIX(v)
+VALUE rb_int2inum _((long));
+#define INT2NUM(v) rb_int2inum(v)
+#define LONG2NUM(v) INT2NUM(v)
+#define rb_int_new(v) rb_int2inum(v)
+VALUE rb_uint2inum _((unsigned long));
+#define UINT2NUM(v) rb_uint2inum(v)
+#define ULONG2NUM(v) UINT2NUM(v)
+#define rb_uint_new(v) rb_uint2inum(v)
+
+#ifdef HAVE_LONG_LONG
+VALUE rb_ll2inum _((LONG_LONG));
+#define LL2NUM(v) rb_ll2inum(v)
+VALUE rb_ull2inum _((unsigned LONG_LONG));
+#define ULL2NUM(v) rb_ull2inum(v)
+#endif
+
+#if SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_LONG_LONG)
+# define OFFT2NUM(v) LL2NUM(v)
+#elif SIZEOF_OFF_T == SIZEOF_LONG
+# define OFFT2NUM(v) LONG2NUM(v)
+#else
+# define OFFT2NUM(v) INT2NUM(v)
+#endif
+
+#define FIX2LONG(x) RSHIFT((long)x,1)
+#define FIX2ULONG(x) (((unsigned long)(x))>>1)
+#define FIXNUM_P(f) (((long)(f))&FIXNUM_FLAG)
+#define POSFIXABLE(f) ((f) < FIXNUM_MAX+1)
+#define NEGFIXABLE(f) ((f) >= FIXNUM_MIN)
+#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f))
+
+#define IMMEDIATE_MASK 0x03
+#define IMMEDIATE_P(x) ((VALUE)(x) & IMMEDIATE_MASK)
+
+#define SYMBOL_FLAG 0x0e
+#define SYMBOL_P(x) (((VALUE)(x)&0xff)==SYMBOL_FLAG)
+#define ID2SYM(x) ((VALUE)(((long)(x))<<8|SYMBOL_FLAG))
+#define SYM2ID(x) RSHIFT((unsigned long)x,8)
+
+/* special contants - i.e. non-zero and non-fixnum constants */
+#define Qfalse ((VALUE)0)
+#define Qtrue ((VALUE)2)
+#define Qnil ((VALUE)4)
+#define Qundef ((VALUE)6) /* undefined value for placeholder */
+
+#define RTEST(v) (((VALUE)(v) & ~Qnil) != 0)
+#define NIL_P(v) ((VALUE)(v) == Qnil)
+
+#define CLASS_OF(v) rb_class_of((VALUE)(v))
+
+#define T_NONE 0x00
+
+#define T_NIL 0x01
+#define T_OBJECT 0x02
+#define T_CLASS 0x03
+#define T_ICLASS 0x04
+#define T_MODULE 0x05
+#define T_FLOAT 0x06
+#define T_STRING 0x07
+#define T_REGEXP 0x08
+#define T_ARRAY 0x09
+#define T_FIXNUM 0x0a
+#define T_HASH 0x0b
+#define T_STRUCT 0x0c
+#define T_BIGNUM 0x0d
+#define T_FILE 0x0e
+
+#define T_TRUE 0x20
+#define T_FALSE 0x21
+#define T_DATA 0x22
+#define T_MATCH 0x23
+#define T_SYMBOL 0x24
+
+#define T_BLKTAG 0x3b
+#define T_UNDEF 0x3c
+#define T_VARMAP 0x3d
+#define T_SCOPE 0x3e
+#define T_NODE 0x3f
+
+#define T_MASK 0x3f
+
+#define BUILTIN_TYPE(x) (((struct RBasic*)(x))->flags & T_MASK)
+
+#define TYPE(x) rb_type((VALUE)(x))
+
+void rb_check_type _((VALUE,int));
+#define Check_Type(v,t) rb_check_type((VALUE)(v),t)
+
+VALUE rb_str_to_str _((VALUE));
+VALUE rb_string_value _((volatile VALUE*));
+char *rb_string_value_ptr _((volatile VALUE*));
+char *rb_string_value_cstr _((volatile VALUE*));
+
+#define StringValue(v) rb_string_value(&(v))
+#define StringValuePtr(v) rb_string_value_ptr(&(v))
+#define StringValueCStr(v) rb_string_value_cstr(&(v))
+
+void rb_check_safe_obj _((VALUE));
+void rb_check_safe_str _((VALUE));
+#define SafeStringValue(v) do {\
+ StringValue(v);\
+ rb_check_safe_obj(v);\
+} while (0)
+/* obsolete macro - use SafeStringValue(v) */
+#define Check_SafeStr(v) rb_check_safe_str((VALUE)(v))
+
+void rb_secure _((int));
+RUBY_EXTERN int ruby_safe_level;
+#define rb_safe_level() (ruby_safe_level)
+void rb_set_safe_level _((int));
+void rb_secure_update _((VALUE));
+
+long rb_num2long _((VALUE));
+unsigned long rb_num2ulong _((VALUE));
+#define NUM2LONG(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2long((VALUE)x))
+#define NUM2ULONG(x) rb_num2ulong((VALUE)x)
+#if SIZEOF_INT < SIZEOF_LONG
+long rb_num2int _((VALUE));
+#define NUM2INT(x) (FIXNUM_P(x)?FIX2INT(x):rb_num2int((VALUE)x))
+long rb_fix2int _((VALUE));
+#define FIX2INT(x) rb_fix2int((VALUE)x)
+unsigned long rb_num2uint _((VALUE));
+#define NUM2UINT(x) rb_num2uint(x)
+unsigned long rb_fix2uint _((VALUE));
+#define FIX2UINT(x) rb_fix2uint(x)
+#else
+#define NUM2INT(x) ((int)NUM2LONG(x))
+#define NUM2UINT(x) ((unsigned int)NUM2ULONG(x))
+#define FIX2INT(x) ((int)FIX2LONG(x))
+#define FIX2UINT(x) ((unsigned int)FIX2ULONG(x))
+#endif
+
+#ifdef HAVE_LONG_LONG
+LONG_LONG rb_num2ll _((VALUE));
+unsigned LONG_LONG rb_num2ull _((VALUE));
+# define NUM2LL(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2ll((VALUE)x))
+# define NUM2ULL(x) rb_num2ull((VALUE)x)
+#endif
+
+#if defined(HAVE_LONG_LONG) && SIZEOF_OFF_T > SIZEOF_LONG
+# define NUM2OFFT(x) ((off_t)NUM2LL(x))
+#else
+# define NUM2OFFT(x) NUM2LONG(x)
+#endif
+
+double rb_num2dbl _((VALUE));
+#define NUM2DBL(x) rb_num2dbl((VALUE)(x))
+
+/* obsolete API - use StringValue() */
+char *rb_str2cstr _((VALUE,long*));
+/* obsolete API - use StringValuePtr() */
+#define STR2CSTR(x) rb_str2cstr((VALUE)(x),0)
+
+#define NUM2CHR(x) (((TYPE(x) == T_STRING)&&(RSTRING(x)->len>=1))?\
+ RSTRING(x)->ptr[0]:(char)(NUM2INT(x)&0xff))
+#define CHR2FIX(x) INT2FIX((long)((x)&0xff))
+
+VALUE rb_newobj _((void));
+#define NEWOBJ(obj,type) type *obj = (type*)rb_newobj()
+#define OBJSETUP(obj,c,t) do {\
+ RBASIC(obj)->flags = (t);\
+ RBASIC(obj)->klass = (c);\
+ if (rb_safe_level() >= 3) FL_SET(obj, FL_TAINT);\
+} while (0)
+#define CLONESETUP(clone,obj) do {\
+ OBJSETUP(clone,rb_singleton_class_clone((VALUE)obj),RBASIC(obj)->flags);\
+ rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);\
+ if (FL_TEST(obj, FL_EXIVAR)) rb_copy_generic_ivar((VALUE)clone,(VALUE)obj);\
+} while (0)
+#define DUPSETUP(dup,obj) do {\
+ OBJSETUP(dup,rb_obj_class(obj),(RBASIC(obj)->flags)&(T_MASK|FL_EXIVAR|FL_TAINT));\
+ if (FL_TEST(obj, FL_EXIVAR)) rb_copy_generic_ivar((VALUE)dup,(VALUE)obj);\
+} while (0)
+
+struct RBasic {
+ unsigned long flags;
+ VALUE klass;
+};
+
+struct RObject {
+ struct RBasic basic;
+ struct st_table *iv_tbl;
+};
+
+struct RClass {
+ struct RBasic basic;
+ struct st_table *iv_tbl;
+ struct st_table *m_tbl;
+ VALUE super;
+};
+
+struct RFloat {
+ struct RBasic basic;
+ double value;
+};
+
+#define ELTS_SHARED FL_USER2
+
+struct RString {
+ struct RBasic basic;
+ long len;
+ char *ptr;
+ union {
+ long capa;
+ VALUE shared;
+ } aux;
+};
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#define RSTRING_LEN(s) (RSTRING(s)->len)
+
+struct RArray {
+ struct RBasic basic;
+ long len;
+ union {
+ long capa;
+ VALUE shared;
+ } aux;
+ VALUE *ptr;
+};
+#define RARRAY_PTR(s) (RARRAY(s)->ptr)
+#define RARRAY_LEN(s) (RARRAY(s)->len)
+
+struct RRegexp {
+ struct RBasic basic;
+ struct re_pattern_buffer *ptr;
+ long len;
+ char *str;
+};
+
+struct RHash {
+ struct RBasic basic;
+ struct st_table *tbl;
+ int iter_lev;
+ VALUE ifnone;
+};
+
+struct RFile {
+ struct RBasic basic;
+ struct OpenFile *fptr;
+};
+
+struct RData {
+ struct RBasic basic;
+ void (*dmark) _((void*));
+ void (*dfree) _((void*));
+ void *data;
+};
+
+#define DATA_PTR(dta) (RDATA(dta)->data)
+
+/*
+#define RUBY_DATA_FUNC(func) ((void (*)_((void*)))func)
+*/
+typedef void (*RUBY_DATA_FUNC) _((void*));
+
+VALUE rb_data_object_alloc _((VALUE,void*,RUBY_DATA_FUNC,RUBY_DATA_FUNC));
+
+#define Data_Wrap_Struct(klass,mark,free,sval)\
+ rb_data_object_alloc(klass,sval,(RUBY_DATA_FUNC)mark,(RUBY_DATA_FUNC)free)
+
+#define Data_Make_Struct(klass,type,mark,free,sval) (\
+ sval = ALLOC(type),\
+ memset(sval, 0, sizeof(type)),\
+ Data_Wrap_Struct(klass,mark,free,sval)\
+)
+
+#define Data_Get_Struct(obj,type,sval) do {\
+ Check_Type(obj, T_DATA); \
+ sval = (type*)DATA_PTR(obj);\
+} while (0)
+
+struct RStruct {
+ struct RBasic basic;
+ long len;
+ VALUE *ptr;
+};
+#define RSTRUCT_LEN(st) (RSTRUCT(st)->len)
+#define RSTRUCT_PTR(st) (RSTRUCT(st)->ptr)
+
+struct RBignum {
+ struct RBasic basic;
+ char sign;
+ long len;
+ void *digits;
+};
+
+#define R_CAST(st) (struct st*)
+#define RBASIC(obj) (R_CAST(RBasic)(obj))
+#define ROBJECT(obj) (R_CAST(RObject)(obj))
+#define RCLASS(obj) (R_CAST(RClass)(obj))
+#define RMODULE(obj) RCLASS(obj)
+#define RFLOAT(obj) (R_CAST(RFloat)(obj))
+#define RSTRING(obj) (R_CAST(RString)(obj))
+#define RREGEXP(obj) (R_CAST(RRegexp)(obj))
+#define RARRAY(obj) (R_CAST(RArray)(obj))
+#define RHASH(obj) (R_CAST(RHash)(obj))
+#define RDATA(obj) (R_CAST(RData)(obj))
+#define RSTRUCT(obj) (R_CAST(RStruct)(obj))
+#define RBIGNUM(obj) (R_CAST(RBignum)(obj))
+#define RFILE(obj) (R_CAST(RFile)(obj))
+
+#define FL_SINGLETON FL_USER0
+#define FL_MARK (1<<6)
+#define FL_FINALIZE (1<<7)
+#define FL_TAINT (1<<8)
+#define FL_EXIVAR (1<<9)
+#define FL_FREEZE (1<<10)
+
+#define FL_USHIFT 11
+
+#define FL_USER0 (1<<(FL_USHIFT+0))
+#define FL_USER1 (1<<(FL_USHIFT+1))
+#define FL_USER2 (1<<(FL_USHIFT+2))
+#define FL_USER3 (1<<(FL_USHIFT+3))
+#define FL_USER4 (1<<(FL_USHIFT+4))
+#define FL_USER5 (1<<(FL_USHIFT+5))
+#define FL_USER6 (1<<(FL_USHIFT+6))
+#define FL_USER7 (1<<(FL_USHIFT+7))
+
+#define FL_UMASK (0xff<<FL_USHIFT)
+
+#define SPECIAL_CONST_P(x) (IMMEDIATE_P(x) || !RTEST(x))
+
+#define FL_ABLE(x) (!SPECIAL_CONST_P(x))
+#define FL_TEST(x,f) (FL_ABLE(x)?(RBASIC(x)->flags&(f)):0)
+#define FL_SET(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags |= (f);} while (0)
+#define FL_UNSET(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags &= ~(f);} while (0)
+#define FL_REVERSE(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags ^= (f);} while (0)
+
+#define OBJ_TAINTED(x) FL_TEST((x), FL_TAINT)
+#define OBJ_TAINT(x) FL_SET((x), FL_TAINT)
+#define OBJ_INFECT(x,s) do {if (FL_ABLE(x) && FL_ABLE(s)) RBASIC(x)->flags |= RBASIC(s)->flags & FL_TAINT;} while (0)
+
+#define OBJ_FROZEN(x) FL_TEST((x), FL_FREEZE)
+#define OBJ_FREEZE(x) FL_SET((x), FL_FREEZE)
+
+#define ALLOC_N(type,n) (type*)xmalloc(sizeof(type)*(n))
+#define ALLOC(type) (type*)xmalloc(sizeof(type))
+#define REALLOC_N(var,type,n) (var)=(type*)xrealloc((char*)(var),sizeof(type)*(n))
+
+#define ALLOCA_N(type,n) (type*)alloca(sizeof(type)*(n))
+
+#define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n))
+#define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n))
+#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n))
+#define MEMCMP(p1,p2,type,n) memcmp((p1), (p2), sizeof(type)*(n))
+
+void rb_obj_infect _((VALUE,VALUE));
+
+typedef int ruby_glob_func(const char*,VALUE);
+void rb_glob _((const char*,void(*)(const char*,VALUE),VALUE));
+void rb_globi _((const char*,void(*)(const char*,VALUE),VALUE));
+int ruby_brace_expand _((const char*,int,ruby_glob_func*,VALUE));
+int ruby_brace_glob _((const char*,int,ruby_glob_func*,VALUE));
+
+VALUE rb_define_class _((const char*,VALUE));
+VALUE rb_define_module _((const char*));
+VALUE rb_define_class_under _((VALUE, const char*, VALUE));
+VALUE rb_define_module_under _((VALUE, const char*));
+
+void rb_include_module _((VALUE,VALUE));
+void rb_extend_object _((VALUE,VALUE));
+
+void rb_define_variable _((const char*,VALUE*));
+void rb_define_virtual_variable _((const char*,VALUE(*)(ANYARGS),void(*)(ANYARGS)));
+void rb_define_hooked_variable _((const char*,VALUE*,VALUE(*)(ANYARGS),void(*)(ANYARGS)));
+int ruby_glob _((const char*,int,int(*)(const char*,VALUE),VALUE));
+int ruby_globi _((const char*,int,int(*)(const char*,VALUE),VALUE));
+void rb_define_readonly_variable _((const char*,VALUE*));
+void rb_define_const _((VALUE,const char*,VALUE));
+void rb_define_global_const _((const char*,VALUE));
+
+#define RUBY_METHOD_FUNC(func) ((VALUE (*)(ANYARGS))func)
+void rb_define_method _((VALUE,const char*,VALUE(*)(ANYARGS),int));
+void rb_define_module_function _((VALUE,const char*,VALUE(*)(ANYARGS),int));
+void rb_define_global_function _((const char*,VALUE(*)(ANYARGS),int));
+
+void rb_undef_method _((VALUE,const char*));
+void rb_define_alias _((VALUE,const char*,const char*));
+void rb_define_attr _((VALUE,const char*,int,int));
+
+void rb_global_variable _((VALUE*));
+void rb_gc_register_address _((VALUE*));
+void rb_gc_unregister_address _((VALUE*));
+
+ID rb_intern _((const char*));
+char *rb_id2name _((ID));
+ID rb_to_id _((VALUE));
+
+char *rb_class2name _((VALUE));
+char *rb_obj_classname _((VALUE));
+
+void rb_p _((VALUE));
+
+VALUE rb_eval_string _((const char*));
+VALUE rb_eval_string_protect _((const char*, int*));
+VALUE rb_eval_string_wrap _((const char*, int*));
+VALUE rb_funcall __((VALUE, ID, int, ...));
+VALUE rb_funcall2 _((VALUE, ID, int, const VALUE*));
+VALUE rb_funcall3 _((VALUE, ID, int, const VALUE*));
+int rb_scan_args __((int, const VALUE*, const char*, ...));
+VALUE rb_call_super _((int, const VALUE*));
+
+VALUE rb_gv_set _((const char*, VALUE));
+VALUE rb_gv_get _((const char*));
+VALUE rb_iv_get _((VALUE, const char*));
+VALUE rb_iv_set _((VALUE, const char*, VALUE));
+
+VALUE rb_equal _((VALUE,VALUE));
+
+RUBY_EXTERN VALUE ruby_verbose, ruby_debug;
+
+NORETURN(void rb_raise __((VALUE, const char*, ...)));
+NORETURN(void rb_fatal __((const char*, ...)));
+NORETURN(void rb_bug __((const char*, ...)));
+NORETURN(void rb_sys_fail _((const char*)));
+NORETURN(void rb_iter_break _((void)));
+NORETURN(void rb_exit _((int)));
+NORETURN(void rb_notimplement _((void)));
+
+void rb_warning __((const char*, ...)); /* reports if `-w' specified */
+void rb_sys_warning __((const char*, ...)); /* reports if `-w' specified */
+void rb_warn __((const char*, ...)); /* reports always */
+
+VALUE rb_each _((VALUE));
+VALUE rb_yield _((VALUE));
+VALUE rb_yield_values __((int n, ...));
+VALUE rb_yield_splat _((VALUE));
+int rb_block_given_p _((void));
+void rb_need_block _((void));
+VALUE rb_iterate _((VALUE(*)(VALUE),VALUE,VALUE(*)(ANYARGS),VALUE));
+VALUE rb_rescue _((VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE));
+VALUE rb_rescue2 __((VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE,...));
+VALUE rb_ensure _((VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE));
+VALUE rb_catch _((const char*,VALUE(*)(ANYARGS),VALUE));
+NORETURN(void rb_throw _((const char*,VALUE)));
+
+VALUE rb_require _((const char*));
+
+#ifdef __ia64
+void ruby_init_stack(VALUE*, void*);
+#define RUBY_INIT_STACK \
+ VALUE variable_in_this_stack_frame; \
+ ruby_init_stack(&variable_in_this_stack_frame, rb_ia64_bsp());
+#else
+void ruby_init_stack(VALUE*);
+#define RUBY_INIT_STACK \
+ VALUE variable_in_this_stack_frame; \
+ ruby_init_stack(&variable_in_this_stack_frame);
+#endif
+void ruby_init _((void));
+void ruby_options _((int, char**));
+NORETURN(void ruby_run _((void)));
+
+RUBY_EXTERN VALUE rb_mKernel;
+RUBY_EXTERN VALUE rb_mComparable;
+RUBY_EXTERN VALUE rb_mEnumerable;
+RUBY_EXTERN VALUE rb_mPrecision;
+RUBY_EXTERN VALUE rb_mErrno;
+RUBY_EXTERN VALUE rb_mFileTest;
+RUBY_EXTERN VALUE rb_mGC;
+RUBY_EXTERN VALUE rb_mMath;
+RUBY_EXTERN VALUE rb_mProcess;
+
+RUBY_EXTERN VALUE rb_cObject;
+RUBY_EXTERN VALUE rb_cArray;
+RUBY_EXTERN VALUE rb_cBignum;
+RUBY_EXTERN VALUE rb_cBinding;
+RUBY_EXTERN VALUE rb_cClass;
+RUBY_EXTERN VALUE rb_cCont;
+RUBY_EXTERN VALUE rb_cDir;
+RUBY_EXTERN VALUE rb_cData;
+RUBY_EXTERN VALUE rb_cFalseClass;
+RUBY_EXTERN VALUE rb_cFile;
+RUBY_EXTERN VALUE rb_cFixnum;
+RUBY_EXTERN VALUE rb_cFloat;
+RUBY_EXTERN VALUE rb_cHash;
+RUBY_EXTERN VALUE rb_cInteger;
+RUBY_EXTERN VALUE rb_cIO;
+RUBY_EXTERN VALUE rb_cMatch;
+RUBY_EXTERN VALUE rb_cMethod;
+RUBY_EXTERN VALUE rb_cModule;
+RUBY_EXTERN VALUE rb_cNameErrorMesg;
+RUBY_EXTERN VALUE rb_cNilClass;
+RUBY_EXTERN VALUE rb_cNumeric;
+RUBY_EXTERN VALUE rb_cProc;
+RUBY_EXTERN VALUE rb_cRange;
+RUBY_EXTERN VALUE rb_cRegexp;
+RUBY_EXTERN VALUE rb_cStat;
+RUBY_EXTERN VALUE rb_cString;
+RUBY_EXTERN VALUE rb_cStruct;
+RUBY_EXTERN VALUE rb_cSymbol;
+RUBY_EXTERN VALUE rb_cThread;
+RUBY_EXTERN VALUE rb_cTime;
+RUBY_EXTERN VALUE rb_cTrueClass;
+RUBY_EXTERN VALUE rb_cUnboundMethod;
+
+RUBY_EXTERN VALUE rb_eException;
+RUBY_EXTERN VALUE rb_eStandardError;
+RUBY_EXTERN VALUE rb_eSystemExit;
+RUBY_EXTERN VALUE rb_eInterrupt;
+RUBY_EXTERN VALUE rb_eSignal;
+RUBY_EXTERN VALUE rb_eFatal;
+RUBY_EXTERN VALUE rb_eArgError;
+RUBY_EXTERN VALUE rb_eEOFError;
+RUBY_EXTERN VALUE rb_eIndexError;
+RUBY_EXTERN VALUE rb_eRangeError;
+RUBY_EXTERN VALUE rb_eIOError;
+RUBY_EXTERN VALUE rb_eRuntimeError;
+RUBY_EXTERN VALUE rb_eSecurityError;
+RUBY_EXTERN VALUE rb_eSystemCallError;
+RUBY_EXTERN VALUE rb_eThreadError;
+RUBY_EXTERN VALUE rb_eTypeError;
+RUBY_EXTERN VALUE rb_eZeroDivError;
+RUBY_EXTERN VALUE rb_eNotImpError;
+RUBY_EXTERN VALUE rb_eNoMemError;
+RUBY_EXTERN VALUE rb_eNoMethodError;
+RUBY_EXTERN VALUE rb_eFloatDomainError;
+RUBY_EXTERN VALUE rb_eLocalJumpError;
+RUBY_EXTERN VALUE rb_eSysStackError;
+RUBY_EXTERN VALUE rb_eRegexpError;
+
+RUBY_EXTERN VALUE rb_eScriptError;
+RUBY_EXTERN VALUE rb_eNameError;
+RUBY_EXTERN VALUE rb_eSyntaxError;
+RUBY_EXTERN VALUE rb_eLoadError;
+
+RUBY_EXTERN VALUE rb_stdin, rb_stdout, rb_stderr;
+RUBY_EXTERN VALUE ruby_errinfo;
+
+static inline VALUE
+#if defined(HAVE_PROTOTYPES)
+rb_class_of(VALUE obj)
+#else
+rb_class_of(obj)
+ VALUE obj;
+#endif
+{
+ if (FIXNUM_P(obj)) return rb_cFixnum;
+ if (obj == Qnil) return rb_cNilClass;
+ if (obj == Qfalse) return rb_cFalseClass;
+ if (obj == Qtrue) return rb_cTrueClass;
+ if (SYMBOL_P(obj)) return rb_cSymbol;
+
+ return RBASIC(obj)->klass;
+}
+
+static inline int
+#if defined(HAVE_PROTOTYPES)
+rb_type(VALUE obj)
+#else
+rb_type(obj)
+ VALUE obj;
+#endif
+{
+ if (FIXNUM_P(obj)) return T_FIXNUM;
+ if (obj == Qnil) return T_NIL;
+ if (obj == Qfalse) return T_FALSE;
+ if (obj == Qtrue) return T_TRUE;
+ if (obj == Qundef) return T_UNDEF;
+ if (SYMBOL_P(obj)) return T_SYMBOL;
+ return BUILTIN_TYPE(obj);
+}
+
+static inline int
+#if defined(HAVE_PROTOTYPES)
+rb_special_const_p(VALUE obj)
+#else
+rb_special_const_p(obj)
+ VALUE obj;
+#endif
+{
+ if (SPECIAL_CONST_P(obj)) return Qtrue;
+ return Qfalse;
+}
+
+#include "missing.h"
+#include "intern.h"
+
+#if defined(EXTLIB) && defined(USE_DLN_A_OUT)
+/* hook for external modules */
+static char *dln_libs_to_be_linked[] = { EXTLIB, 0 };
+#endif
+
+#if defined(HAVE_LIBPTHREAD)
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+typedef pthread_t rb_nativethread_t;
+# define NATIVETHREAD_CURRENT() pthread_self()
+# define NATIVETHREAD_EQUAL(t1,t2) pthread_equal((t1),(t2))
+# define HAVE_NATIVETHREAD
+
+# define NATIVETHREAD_KILL(th,sig) pthread_kill((th),(sig))
+# define HAVE_NATIVETHREAD_KILL
+#elif defined(_WIN32) || defined(_WIN32_WCE)
+typedef DWORD rb_nativethread_t;
+# define NATIVETHREAD_CURRENT() GetCurrentThreadId()
+# define NATIVETHREAD_EQUAL(t1,t2) ((t1) == (t2))
+# define HAVE_NATIVETHREAD
+#endif
+#ifdef HAVE_NATIVETHREAD
+int is_ruby_native_thread _((void));
+#else
+#define is_ruby_native_thread() (1)
+#endif
+#ifdef HAVE_NATIVETHREAD_KILL
+void ruby_native_thread_kill _((int));
+#endif
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* ifndef RUBY_H */
+/**********************************************************************
+
+ rubyio.h -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Fri Nov 12 16:47:09 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef RUBYIO_H
+#define RUBYIO_H
+
+#include <stdio.h>
+#include <errno.h>
+
+#if defined(HAVE_STDIO_EXT_H)
+#include <stdio_ext.h>
+#endif
+
+typedef struct OpenFile {
+ FILE *f; /* stdio ptr for read/write */
+ FILE *f2; /* additional ptr for rw pipes */
+ int mode; /* mode flags */
+ int pid; /* child's pid (for pipes) */
+ int lineno; /* number of lines read */
+ char *path; /* pathname for file */
+ void (*finalize) _((struct OpenFile*,int)); /* finalize proc */
+} OpenFile;
+
+#define FMODE_READABLE 1
+#define FMODE_WRITABLE 2
+#define FMODE_READWRITE 3
+#define FMODE_APPEND 64
+#define FMODE_CREATE 128
+#define FMODE_BINMODE 4
+#define FMODE_SYNC 8
+#define FMODE_WBUF 16
+#define FMODE_RBUF 32
+#define FMODE_WSPLIT 0x200
+#define FMODE_WSPLIT_INITIALIZED 0x400
+
+#define GetOpenFile(obj,fp) rb_io_check_closed((fp) = RFILE(rb_io_taint_check(obj))->fptr)
+
+#define MakeOpenFile(obj, fp) do {\
+ if (RFILE(obj)->fptr) {\
+ rb_io_close(obj);\
+ free(RFILE(obj)->fptr);\
+ RFILE(obj)->fptr = 0;\
+ }\
+ fp = 0;\
+ fp = RFILE(obj)->fptr = ALLOC(OpenFile);\
+ fp->f = fp->f2 = NULL;\
+ fp->mode = 0;\
+ fp->pid = 0;\
+ fp->lineno = 0;\
+ fp->path = NULL;\
+ fp->finalize = 0;\
+} while (0)
+
+#define GetReadFile(fptr) ((fptr)->f)
+#define GetWriteFile(fptr) (((fptr)->f2) ? (fptr)->f2 : (fptr)->f)
+
+FILE *rb_fopen _((const char*, const char*));
+FILE *rb_fdopen _((int, const char*));
+int rb_getc _((FILE*));
+long rb_io_fread _((char *, long, FILE *));
+long rb_io_fwrite _((const char *, long, FILE *));
+int rb_io_mode_flags _((const char*));
+int rb_io_modenum_flags _((int));
+void rb_io_check_writable _((OpenFile*));
+void rb_io_check_readable _((OpenFile*));
+void rb_io_fptr_finalize _((OpenFile*));
+void rb_io_synchronized _((OpenFile*));
+void rb_io_check_initialized _((OpenFile*));
+void rb_io_check_closed _((OpenFile*));
+int rb_io_wait_readable _((int));
+int rb_io_wait_writable _((int));
+void rb_io_set_nonblock(OpenFile *fptr);
+
+VALUE rb_io_taint_check _((VALUE));
+NORETURN(void rb_eof_error _((void)));
+
+void rb_read_check _((FILE*));
+int rb_read_pending _((FILE*));
+#endif
+/**********************************************************************
+
+ rubysig.h -
+
+ $Author: shyouhei $
+ $Date: 2009-01-05 03:13:40 +0100 (Mon, 05 Jan 2009) $
+ created at: Wed Aug 16 01:15:38 JST 1995
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef SIG_H
+#define SIG_H
+#include <errno.h>
+
+#ifdef _WIN32
+typedef LONG rb_atomic_t;
+
+# define ATOMIC_TEST(var) InterlockedExchange(&(var), 0)
+# define ATOMIC_SET(var, val) InterlockedExchange(&(var), (val))
+# define ATOMIC_INC(var) InterlockedIncrement(&(var))
+# define ATOMIC_DEC(var) InterlockedDecrement(&(var))
+
+/* Windows doesn't allow interrupt while system calls */
+# define TRAP_BEG do {\
+ int saved_errno = 0;\
+ rb_atomic_t trap_immediate = ATOMIC_SET(rb_trap_immediate, 1)
+# define TRAP_END\
+ ATOMIC_SET(rb_trap_immediate, trap_immediate);\
+ saved_errno = errno;\
+ CHECK_INTS;\
+ errno = saved_errno;\
+} while (0)
+# define RUBY_CRITICAL(statements) do {\
+ rb_w32_enter_critical();\
+ statements;\
+ rb_w32_leave_critical();\
+} while (0)
+#else
+typedef int rb_atomic_t;
+
+# define ATOMIC_TEST(var) ((var) ? ((var) = 0, 1) : 0)
+# define ATOMIC_SET(var, val) ((var) = (val))
+# define ATOMIC_INC(var) (++(var))
+# define ATOMIC_DEC(var) (--(var))
+
+# define TRAP_BEG do {\
+ int saved_errno = 0;\
+ int trap_immediate = rb_trap_immediate;\
+ rb_trap_immediate = 1
+# define TRAP_END rb_trap_immediate = trap_immediate;\
+ saved_errno = errno;\
+ CHECK_INTS;\
+ errno = saved_errno;\
+} while (0)
+
+# define RUBY_CRITICAL(statements) do {\
+ int trap_immediate = rb_trap_immediate;\
+ rb_trap_immediate = 0;\
+ statements;\
+ rb_trap_immediate = trap_immediate;\
+} while (0)
+#endif
+RUBY_EXTERN rb_atomic_t rb_trap_immediate;
+
+RUBY_EXTERN int rb_prohibit_interrupt;
+#define DEFER_INTS (rb_prohibit_interrupt++)
+#define ALLOW_INTS do {\
+ rb_prohibit_interrupt--;\
+ CHECK_INTS;\
+} while (0)
+#define ENABLE_INTS (rb_prohibit_interrupt--)
+
+VALUE rb_with_disable_interrupt _((VALUE(*)(ANYARGS),VALUE));
+
+RUBY_EXTERN rb_atomic_t rb_trap_pending;
+void rb_trap_restore_mask _((void));
+
+RUBY_EXTERN int rb_thread_critical;
+RUBY_EXTERN int rb_thread_pending;
+void rb_thread_schedule _((void));
+#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
+# define CHECK_INTS do {\
+ if (!(rb_prohibit_interrupt || rb_thread_critical)) {\
+ if (rb_thread_pending) rb_thread_schedule();\
+ if (rb_trap_pending) rb_trap_exec();\
+ }\
+} while (0)
+#else
+/* pseudo preemptive thread switching */
+RUBY_EXTERN int rb_thread_tick;
+#define THREAD_TICK 500
+#define CHECK_INTS do {\
+ if (!(rb_prohibit_interrupt || rb_thread_critical)) {\
+ if (rb_thread_pending || rb_thread_tick-- <= 0) {\
+ rb_thread_tick = THREAD_TICK;\
+ rb_thread_schedule();\
+ }\
+ }\
+ if (rb_trap_pending) rb_trap_exec();\
+} while (0)
+#endif
+
+#endif
+/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
+
+/* @(#) st.h 5.1 89/12/14 */
+
+#ifndef ST_INCLUDED
+
+#define ST_INCLUDED
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+typedef unsigned long st_data_t;
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+typedef unsigned LONG_LONG st_data_t;
+#else
+# error ---->> st.c requires sizeof(void*) == sizeof(long) to be compiled. <<---
+-
+#endif
+#define ST_DATA_T_DEFINED
+
+typedef struct st_table st_table;
+
+struct st_hash_type {
+ int (*compare)();
+ int (*hash)();
+};
+
+struct st_table {
+ struct st_hash_type *type;
+ int num_bins;
+ int num_entries;
+ struct st_table_entry **bins;
+};
+
+#define st_is_member(table,key) st_lookup(table,key,(st_data_t *)0)
+
+enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK};
+
+#ifndef _
+# define _(args) args
+#endif
+#ifndef ANYARGS
+# ifdef __cplusplus
+# define ANYARGS ...
+# else
+# define ANYARGS
+# endif
+#endif
+
+st_table *st_init_table _((struct st_hash_type *));
+st_table *st_init_table_with_size _((struct st_hash_type *, int));
+st_table *st_init_numtable _((void));
+st_table *st_init_numtable_with_size _((int));
+st_table *st_init_strtable _((void));
+st_table *st_init_strtable_with_size _((int));
+int st_delete _((st_table *, st_data_t *, st_data_t *));
+int st_delete_safe _((st_table *, st_data_t *, st_data_t *, st_data_t));
+int st_insert _((st_table *, st_data_t, st_data_t));
+int st_lookup _((st_table *, st_data_t, st_data_t *));
+int st_foreach _((st_table *, int (*)(ANYARGS), st_data_t));
+void st_add_direct _((st_table *, st_data_t, st_data_t));
+void st_free_table _((st_table *));
+void st_cleanup_safe _((st_table *, st_data_t));
+st_table *st_copy _((st_table *));
+
+#define ST_NUMCMP ((int (*)()) 0)
+#define ST_NUMHASH ((int (*)()) -2)
+
+#define st_numcmp ST_NUMCMP
+#define st_numhash ST_NUMHASH
+
+int st_strhash();
+
+#endif /* ST_INCLUDED */
+/**********************************************************************
+
+ util.h -
+
+ $Author: shyouhei $
+ $Date: 2007-02-13 00:01:19 +0100 (Tue, 13 Feb 2007) $
+ created at: Thu Mar 9 11:55:53 JST 1995
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifndef _
+#ifdef __cplusplus
+# ifndef HAVE_PROTOTYPES
+# define HAVE_PROTOTYPES 1
+# endif
+# ifndef HAVE_STDARG_PROTOTYPES
+# define HAVE_STDARG_PROTOTYPES 1
+# endif
+#endif
+#ifdef HAVE_PROTOTYPES
+# define _(args) args
+#else
+# define _(args) ()
+#endif
+#ifdef HAVE_STDARG_PROTOTYPES
+# define __(args) args
+#else
+# define __(args) ()
+#endif
+#endif
+
+#define scan_oct ruby_scan_oct
+unsigned long scan_oct _((const char*, int, int*));
+#define scan_hex ruby_scan_hex
+unsigned long scan_hex _((const char*, int, int*));
+
+#if defined(MSDOS) || defined(__CYGWIN32__) || defined(_WIN32)
+void ruby_add_suffix();
+#endif
+
+void ruby_qsort _((void*, const int, const int, int (*)(), void*));
+#define qsort(b,n,s,c,d) ruby_qsort(b,n,s,c,d)
+
+void ruby_setenv _((const char*, const char*));
+void ruby_unsetenv _((const char*));
+#undef setenv
+#undef unsetenv
+#define setenv(name,val) ruby_setenv(name,val)
+#define unsetenv(name,val) ruby_unsetenv(name);
+
+char *ruby_strdup _((const char*));
+#undef strdup
+#define strdup(s) ruby_strdup(s)
+
+char *ruby_getcwd _((void));
+#define my_getcwd() ruby_getcwd()
+
+double ruby_strtod _((const char*, char **));
+#undef strtod
+#define strtod(s,e) ruby_strtod(s,e)
+
+#endif /* UTIL_H */
+#define RUBY_VERSION "1.8.6"
+#define RUBY_RELEASE_DATE "2009-09-09"
+#define RUBY_VERSION_CODE 186
+#define RUBY_RELEASE_CODE 20090909
+#define RUBY_PATCHLEVEL 388
+
+#define RUBY_VERSION_MAJOR 1
+#define RUBY_VERSION_MINOR 8
+#define RUBY_VERSION_TEENY 6
+#define RUBY_RELEASE_YEAR 2009
+#define RUBY_RELEASE_MONTH 9
+#define RUBY_RELEASE_DAY 9
+
+#ifdef RUBY_EXTERN
+RUBY_EXTERN const char ruby_version[];
+RUBY_EXTERN const char ruby_release_date[];
+RUBY_EXTERN const char ruby_platform[];
+RUBY_EXTERN const int ruby_patchlevel;
+#endif
+