diff options
Diffstat (limited to 'test/scanners/c/ruby.in.c')
-rw-r--r-- | test/scanners/c/ruby.in.c | 96308 |
1 files changed, 0 insertions, 96308 deletions
diff --git a/test/scanners/c/ruby.in.c b/test/scanners/c/ruby.in.c deleted file mode 100644 index 868ea59..0000000 --- a/test/scanners/c/ruby.in.c +++ /dev/null @@ -1,96308 +0,0 @@ -/********************************************************************** - - 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(¤t_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, "e->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(®s, struct re_registers, 1); - result = re_search(RREGEXP(re)->ptr,RSTRING(str)->ptr,RSTRING(str)->len, - pos, range, ®s); - - 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(®s); - 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, ®s); - re_free_registers(®s); - 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(®_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 - |