#include #include #include #include "codegen.h" #include "symboltable.h" #include "stringbuffer.h" extern void yyerror(char* msg); static stringBuffer* staticVariableBuffer; static stringBuffer* classInitBuffer; static stringBuffer* currentMethodBuffer; static stringBuffer* finishedMethodsBuffer; static stringBuffer* mainBuffer; static int currentMethodBufferIndex; static int currentMethodStackSize; static int currentMethodStackSizeMax; static int currentMethodNumberOfLocals; static int classInitBufferIndex; static int classInitStackSize; static int classInitStackSizeMax; static int labelCounter = 0; static int global = 1; char tempString[MAX_LENGTH_OF_COMMAND]; extern char* className; /* from minako-syntax.y */ /* forward declarations */ static void increaseStackby(int stackdiff); char convertType(int type); void codegenInit() { staticVariableBuffer = newStringBuffer(); classInitBuffer = newStringBuffer(); currentMethodBuffer = 0; finishedMethodsBuffer = newStringBuffer(); mainBuffer = newStringBuffer(); stringBufferAppend(mainBuffer, "; ------- Header --------------------------------------------"); sprintf(tempString, ".class public synchronized %s", className); stringBufferAppend(mainBuffer, tempString); stringBufferAppend(mainBuffer, ".super java/lang/Object"); stringBufferAppend(mainBuffer, "; -----------------------------------------------------------"); stringBufferAppend(mainBuffer, ""); stringBufferAppend(finishedMethodsBuffer, "; ------- Constructor ---------------------------------------"); stringBufferAppend(finishedMethodsBuffer, ".method public ()V"); stringBufferAppend(finishedMethodsBuffer, "\t.limit stack 1"); stringBufferAppend(finishedMethodsBuffer, "\t.limit locals 1"); stringBufferAppend(finishedMethodsBuffer, "\taload_0"); stringBufferAppend(finishedMethodsBuffer, "\tinvokenonvirtual java/lang/Object/()V"); stringBufferAppend(finishedMethodsBuffer, "\treturn"); stringBufferAppend(finishedMethodsBuffer, ".end method"); stringBufferAppend(finishedMethodsBuffer, "; -----------------------------------------------------------"); stringBufferAppend(finishedMethodsBuffer, ""); stringBufferAppend(staticVariableBuffer, "; ------- Class Variables -----------------------------------"); stringBufferAppend(classInitBuffer, "; ------- Class Initializer ---------------------------------"); stringBufferAppend(classInitBuffer, ".method static ()V"); classInitBufferIndex = classInitBuffer->numberOfNextElement; stringBufferAppend(classInitBuffer, "\t.limit locals 0"); } void codegenAppendCommand(char* cmd, int stackdiff) { char tempString[MAX_LENGTH_OF_COMMAND]; sprintf(tempString, "\t%s", cmd); if (global) stringBufferAppend(classInitBuffer, tempString); else stringBufferAppend(currentMethodBuffer, tempString); increaseStackby(stackdiff); } void codegenInsertCommand(int address, char* cmd, int stackdiff) { char tempString[MAX_LENGTH_OF_COMMAND]; sprintf(tempString, "\t%s", cmd); if (global) stringBufferInsert(classInitBuffer, address, tempString); else stringBufferInsert(currentMethodBuffer, address, tempString); increaseStackby(stackdiff); } void codegenAppendLabel(int label) { char tempString[MAX_LENGTH_OF_COMMAND]; sprintf(tempString, "Label%d:", label); if (global) stringBufferAppend(classInitBuffer, tempString); else stringBufferAppend(currentMethodBuffer, tempString); } void codegenAddVariable(char* name, int type) { /*fprintf(stderr, "add variable %s(%d) global=%d ", name, convertType(type), global);*/ if (global) { if (type == TYPE_INT) sprintf(tempString, ".field static %s %c", name, 'I'); else if (type == TYPE_FLOAT) sprintf(tempString, ".field static %s %c", name, 'F'); else if (type == TYPE_BOOLEAN) sprintf(tempString, ".field static %s %c", name, 'Z'); else yyerror("compiler-intern error in codegenAddGlobalVariable().\n"); stringBufferAppend(staticVariableBuffer, tempString); } else { currentMethodNumberOfLocals++; } } int codegenGetNextLabel() { return labelCounter++; } int codegenGetCurrentAddress() { if (global) return classInitBuffer->numberOfNextElement; else return currentMethodBuffer->numberOfNextElement; } void codegenEnterFunction(symtabEntry* entry) { currentMethodBuffer = newStringBuffer(); currentMethodStackSize = 0; currentMethodStackSizeMax = 0; labelCounter = 1; global = 0; if (strcmp(entry->name, "main") == 0) { if (entry->idtype != TYPE_VOID) yyerror("main has to be void.\n"); currentMethodNumberOfLocals = 1; symtabInsert(strdup("#main-param#"), TYPE_VOID, CLASS_FUNC); stringBufferAppend(currentMethodBuffer, "; ------- Methode ---- void main() --------------------------"); stringBufferAppend(currentMethodBuffer, ".method public static main([Ljava/lang/String;)V"); } else { int i; currentMethodNumberOfLocals = entry->paramIndex; stringBufferAppend(currentMethodBuffer, "; ------- Methode -------------------------------------------"); sprintf(tempString, ".method public static %s(", entry->name); for (i=entry->paramIndex-1; i>=0; i--) { int type = entry->params[i]->idtype; tempString[strlen(tempString)+1] = 0; tempString[strlen(tempString)] = convertType(type); } tempString[strlen(tempString)+2] = 0; tempString[strlen(tempString)+1] = convertType(entry->idtype); tempString[strlen(tempString)] = ')'; stringBufferAppend(currentMethodBuffer, tempString); } currentMethodBufferIndex = currentMethodBuffer->numberOfNextElement; } void codegenLeaveFunction() { global = 1; sprintf(tempString, "\t.limit locals %d", currentMethodNumberOfLocals); stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString); sprintf(tempString, "\t.limit stack %d", currentMethodStackSizeMax); stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString); stringBufferAppend(currentMethodBuffer, "\treturn"); stringBufferAppend(currentMethodBuffer, ".end method"); stringBufferAppend(currentMethodBuffer, "; -----------------------------------------------------------"); stringBufferAppend(currentMethodBuffer, ""); stringBufferConcatenate(finishedMethodsBuffer, currentMethodBuffer); } void codegenFinishCode() { stringBufferAppend(staticVariableBuffer, "; -----------------------------------------------------------"); stringBufferAppend(staticVariableBuffer, ""); sprintf(tempString, "\t.limit stack %d", classInitStackSizeMax); stringBufferInsert(classInitBuffer, classInitBufferIndex, tempString); stringBufferAppend(classInitBuffer, "\treturn"); stringBufferAppend(classInitBuffer, ".end method"); stringBufferAppend(classInitBuffer, "; -----------------------------------------------------------"); stringBufferConcatenate(mainBuffer, staticVariableBuffer); stringBufferConcatenate(mainBuffer, finishedMethodsBuffer); stringBufferConcatenate(mainBuffer, classInitBuffer); stringBufferPrint(mainBuffer); } static void increaseStackby(int stackdiff) { if (global) { classInitStackSize += stackdiff; if (classInitStackSize > classInitStackSizeMax) classInitStackSizeMax = classInitStackSize; } else { currentMethodStackSize += stackdiff; if (currentMethodStackSize > currentMethodStackSizeMax) currentMethodStackSizeMax = currentMethodStackSize; } } char convertType(int type) { switch(type) { case TYPE_VOID: return 'V'; case TYPE_INT: return 'I'; case TYPE_FLOAT: return 'F'; case TYPE_BOOLEAN: return 'Z'; default: yyerror("compiler-intern error in convertType().\n"); } return 0; /* to avoid compiler-warning */ } //#include //#include int main() { int a = 12, b = 44; while (a != b) { if (a > b) a -= b; else b -= a; } printf("%d\n%d", a, 0X0);\ } /********************************************************************** array.c - $Author: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ 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" #include "node.h" VALUE rb_cArray, rb_cValues; static ID id_cmp; #define ARY_DEFAULT_SIZE 16 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 true 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; if (len < 0) { rb_raise(rb_eArgError, "negative array size (or size too big)"); } if (len > 0 && len * sizeof(VALUE) <= len) { rb_raise(rb_eArgError, "array size too big"); } if (len == 0) len++; ary = ary_alloc(klass); 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 #define va_init_list(a,b) va_start(a,b) #else #include #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; iptr[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); } RARRAY(ary)->len = n; return ary; } VALUE #ifdef HAVE_STDARG_PROTOTYPES rb_values_new(long n, ...) #else rb_values_new(n, va_alist) long n; va_dcl #endif { va_list ar; VALUE val; long i; val = ary_new(rb_cValues, n); va_init_list(ar, n); for (i=0; iptr[i] = va_arg(ar, VALUE); } va_end(ar); RARRAY(val)->len = n; return val; } VALUE rb_values_new2(n, elts) long n; const VALUE *elts; { VALUE val; val = ary_new(rb_cValues, n); if (n > 0 && elts) { RARRAY(val)->len = n; MEMCPY(RARRAY(val)->ptr, elts, VALUE, n); } return val; } 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; } } static VALUE ary_shared_array(klass, ary) VALUE klass, ary; { VALUE val = ary_alloc(klass); ary_make_shared(ary); RARRAY(val)->ptr = RARRAY(ary)->ptr; RARRAY(val)->len = RARRAY(ary)->len; RARRAY(val)->aux.shared = RARRAY(ary)->aux.shared; FL_SET(val, ELTS_SHARED); return val; } VALUE rb_values_from_ary(ary) VALUE ary; { return ary_shared_array(rb_cValues, ary); } VALUE rb_ary_from_values(val) VALUE val; { return ary_shared_array(rb_cArray, val); } VALUE rb_assoc_new(car, cdr) VALUE car, cdr; { return rb_values_new(2, car, cdr); } static VALUE to_ary(ary) VALUE ary; { return rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); } static VALUE to_a(ary) VALUE ary; { return rb_convert_type(ary, T_ARRAY, "Array", "to_a"); } 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; 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 > 0 && len * (long)sizeof(VALUE) <= len) { rb_raise(rb_eArgError, "array size too big"); } rb_ary_modify(ary); 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; ilen = 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); } } 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; } new_capa += idx; if (new_capa * (long)sizeof(VALUE) <= new_capa) { rb_raise(rb_eArgError, "index too big"); } 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; } static VALUE ary_shared_first(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE nv, result; long n; rb_scan_args(argc, argv, "1", &nv); n = NUM2LONG(nv); if (n > RARRAY(ary)->len) { n = RARRAY(ary)->len; } else if (n < 0) { rb_raise(rb_eArgError, "negative array size"); } result = ary_shared_array(rb_cArray, ary); RARRAY(result)->len = n; return result; } static VALUE ary_shared_last(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE result = ary_shared_first(argc, argv, ary); RARRAY(result)->ptr += RARRAY(ary)->len - RARRAY(result)->len; return result; } /* * 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; } 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]; } /* * call-seq: * array.pop -> obj or nil * * Removes the last element from self and returns it, or * nil if the array is empty. * * a = [ "a", "b", "c", "d" ] * a.pop #=> "d" * a.pop(2) #=> ["b", "c"] * a #=> ["a"] */ static VALUE rb_ary_pop_m(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE result; if (argc == 0) { return rb_ary_pop(ary); } rb_ary_modify_check(ary); result = ary_shared_last(argc, argv, ary); RARRAY(ary)->len -= RARRAY(result)->len; return result; } 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]; ary_make_shared(ary); RARRAY(ary)->ptr++; /* shift ptr */ RARRAY(ary)->len--; return top; } /* * call-seq: * array.shift -> obj or nil * * Returns the first element of self and removes it (shifting all * other elements down by one). Returns nil if the array * is empty. * * args = [ "-m", "-q", "filename" ] * args.shift #=> "-m" * args #=> ["-q", "filename"] * * args = [ "-m", "-q", "filename" ] * args.shift(2) #=> ["-m", "-q"] * args #=> ["filename"] */ static VALUE rb_ary_shift_m(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE result; long n; if (argc == 0) { return rb_ary_shift(ary); } rb_ary_modify_check(ary); result = ary_shared_first(argc, argv, ary); n = RARRAY(result)->len; RARRAY(ary)->ptr += n; RARRAY(ary)->len -= n; return result; } 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 array. * 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 (beg + len > RARRAY(ary)->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) { 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)); } /* 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 Array#[]. * (Array#at is slightly faster than Array#[], * 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 of the array. If the array is empty, * returns nil. * * a = [ "q", "r", "s", "t" ] * a.first #=> "q" * a.first(2) #=> ["q", "r"] */ 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 { return ary_shared_first(argc, argv, ary); } } /* * call-seq: * array.last -> obj or nil * array.last(n) -> an_array * * Returns the last element(s) of self. If the array is empty, * the first form returns nil. * * a = [ "w", "x", "y", "z" ] * a.last #=> "z" * a.last(2) #=> ["y", "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 { return ary_shared_last(argc, argv, ary); } } /* * call-seq: * array.fetch(index) -> obj * array.fetch(index, default ) -> obj * array.fetch(index) {|index| block } -> obj * * Tries to return the element at position index. If the index * lies outside the array, the first form throws an * IndexError exception, the second form returns * default, and the third form returns the value of invoking * the block, passing in the index. Negative values of index * 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 * array.index {|item| block} -> int or nil * * Returns the index of the first object in self such that is * == to obj. If a block is given instead of an * argument, returns first object for which block is true. * Returns nil if no match is found. * * a = [ "a", "b", "c" ] * a.index("b") #=> 1 * a.index("z") #=> nil * a.index{|x|x=="b"} #=> 1 */ static VALUE rb_ary_index(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE val; long i; if (rb_scan_args(argc, argv, "01", &val) == 0) { for (i=0; ilen; i++) { if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) { return LONG2NUM(i); } } } else { for (i=0; ilen; 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 array * == to obj. If a block is given instead of an * argument, returns first object for which block is * true. Returns nil if no match is found. * * a = [ "a", "b", "b", "b", "c" ] * a.rindex("b") #=> 3 * a.rindex("z") #=> nil * a.rindex{|x|x=="b"} #=> 3 */ static VALUE rb_ary_rindex(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE val; long i = RARRAY(ary)->len; if (rb_scan_args(argc, argv, "01", &val) == 0) { while (i--) { if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) return LONG2NUM(i); if (i > RARRAY(ary)->len) { i = RARRAY(ary)->len; } } } else { while (i--) { if (rb_equal(RARRAY(ary)->ptr[i], val)) return LONG2NUM(i); if (i > RARRAY(ary)->len) { i = RARRAY(ary)->len; } } } return Qnil; } 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 to_ary(obj); } 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 (beg + len > RARRAY(ary)->len) { len = RARRAY(ary)->len - beg; } if (rpl == Qundef) { rlen = 0; } else { rpl = rb_ary_to_ary(rpl); rlen = RARRAY(rpl)->len; } rb_ary_modify(ary); if (beg >= RARRAY(ary)->len) { 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. An +IndexError+ is raised if a negative index points * past the beginning of the array. See also * Array#push, and Array#unshift. * * 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", nil] * a[1..-1] = [] #=> ["A"] */ static VALUE rb_ary_aset(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { long offset, beg, len; if (argc == 3) { 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 (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) { rb_raise(rb_eArgError, "wrong number of arguments (at least 1)"); } pos = NUM2LONG(argv[0]); if (pos == -1) { pos = RARRAY(ary)->len; } else if (pos < 0) { pos++; } if (argc == 1) return ary; rb_ary_splice(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1)); return ary; } /* * call-seq: * array.each {|item| block } -> array * * Calls block once for each element in self, 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; ilen; i++) { rb_yield(RARRAY(ary)->ptr[i]); } return ary; } /* * call-seq: * array.each_index {|index| block } -> array * * Same as Array#each, 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; ilen; i++) { rb_yield(LONG2NUM(i)); } return ary; } /* * call-seq: * array.reverse_each {|item| block } * * Same as Array#each, but traverses self 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 self. 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 true if self 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 recursive_join(ary, arg, recur) VALUE ary; VALUE *arg; int recur; { if (recur) { return rb_str_new2("[...]"); } 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; ilen; 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; ilen; i++) { tmp = RARRAY(ary)->ptr[i]; switch (TYPE(tmp)) { case T_STRING: break; case T_ARRAY: { VALUE args[2]; args[0] = tmp; args[1] = sep; tmp = rb_exec_recursive(recursive_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 sep. * * [ "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_.join. * * [ "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 VALUE inspect_ary(ary, dummy, recur) VALUE ary; VALUE dummy; int recur; { int tainted = OBJ_TAINTED(ary); long i; VALUE s, str; if (recur) return rb_tainted_str_new2("[...]"); str = rb_str_buf_new2("["); for (i=0; ilen; 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 array. */ static VALUE rb_ary_inspect(ary) VALUE ary; { if (RARRAY(ary)->len == 0) return rb_str_new2("[]"); return rb_exec_recursive(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 self'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_eRuntimeError, "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 && 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 <=> operator or using * an optional code block. The block implements a comparison between * a and b, returning -1, 0, or +1. See also * Enumerable#sort_by. * * 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 self. Comparisons for * the sort will be done using the <=> operator or using * an optional code block. The block implements a comparison between * a and b, returning -1, 0, or +1. See also * Enumerable#sort_by. * * 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 block once for each element of self. Creates a * new array containing the values returned by the block. * See also Enumerable#collect. * * 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 Enumerable#collect. * * 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_get_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 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 Array#select. * * 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_get_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 array, * returning an array containing those elements for which the block * returns a true value (equivalent to Enumerable#select). * * 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 self that are equal to obj. If * the item is not found, returns nil. If the optional * code block is given, returns the result of block 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 nil if the index is out of range. See also * Array#slice!. * * 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 * nil 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, Qundef); /* 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 Array#delete_if, deleting elements from * _self_ for which the block evaluates to true, but returns * nil if no changes were made. Also see * Enumerable#reject. */ 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 self for which block evaluates * to true. * * 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 * self with corresponding elements from each argument. This * generates a sequence of self.size n-element * arrays, where n is one more that the count of arguments. If * the size of any argument is less than enumObj.size, * nil 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; ilen; i++) { VALUE tmp = rb_ary_new2(argc+1); rb_ary_push(tmp, rb_ary_elt(ary, i)); for (j=0; jlen; result = rb_ary_new2(len); for (i=0; i an_array * * Assumes that self 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; ilen; result = rb_ary_new2(elen); for (j=0; jlen) { rb_raise(rb_eIndexError, "element size differs (%d should be %d)", RARRAY(tmp)->len, elen); } for (j=0; j array * * Replaces the contents of self with the contents of * other_array, 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 self (which * may be the entire array) to obj. A start of * nil is equivalent to zero. A length of * nil is equivalent to self.length. 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, end, len; 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); 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; } if (beg > RARRAY(ary)->len) { 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=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 (LONG_MAX/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; ilen) { 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 Array#rassoc. * * 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 * key with the second element of each contained array using * ==. Returns the first contained array that matches. See * also Array#assoc. * * 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; } /* * 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; { long i; 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; for (i=0; ilen; i++) { if (!rb_equal(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i))) return Qfalse; } return Qtrue; } /* * call-seq: * array.eql?(other) -> true or false * * Returns true 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; { long i; if (ary1 == ary2) return Qtrue; if (TYPE(ary2) != T_ARRAY) return Qfalse; if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse; for (i=0; ilen; i++) { if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i))) return Qfalse; } return Qtrue; } static VALUE recursive_hash(ary, dummy, recur) VALUE ary, dummy; int recur; { long i, h; VALUE n; if (recur) { return LONG2FIX(0); } h = RARRAY(ary)->len; for (i=0; ilen; 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 eql?). */ static VALUE rb_ary_hash(ary) VALUE ary; { return rb_exec_recursive(recursive_hash, ary, 0); } /* * call-seq: * array.include?(obj) -> true or false * * Returns true if the given object is present in * self (that is, if any object == anObject), * false 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; ilen; i++) { if (rb_equal(RARRAY(ary)->ptr[i], item)) { return Qtrue; } } return Qfalse; } /* * 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 Array#<=> 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 i, len; ary2 = to_ary(ary2); len = RARRAY(ary1)->len; if (len > RARRAY(ary2)->len) { len = RARRAY(ary2)->len; } for (i=0; ilen - 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; ilen; i++) { rb_hash_aset(hash, RARRAY(ary1)->ptr[i], Qtrue); } if (ary2) { for (i=0; ilen; 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, hash; long i; hash = ary_make_hash(to_ary(ary2), 0); ary3 = rb_ary_new(); for (i=0; ilen; 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; ilen; 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; ilen; 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; ilen; 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 nil 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; ilen; 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 self. * * 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-nil 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 nil if no modifications were made (i.e., * array 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 (ilen) { 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_m, -1); rb_define_method(rb_cArray, "shift", rb_ary_shift_m, -1); 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, "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("<=>"); rb_cValues = rb_define_class("Values", rb_cArray); } /********************************************************************** ascii.c - Oniguruma (regular expression library) **********************************************************************/ /*- * Copyright (c) 2002-2004 K.Kosako * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include "regenc.h" static int ascii_is_code_ctype(OnigCodePoint code, unsigned int ctype) { if (code < 128) return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype); else return FALSE; } OnigEncodingType OnigEncodingASCII = { onigenc_single_byte_mbc_enc_len, "US-ASCII", /* name */ 1, /* max byte length */ 1, /* min byte length */ ONIGENC_AMBIGUOUS_MATCH_ASCII_CASE, { (OnigCodePoint )'\\' /* esc */ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar '.' */ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anytime '*' */ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* zero or one time '?' */ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* one or more time '+' */ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar anytime */ }, onigenc_is_mbc_newline_0x0a, onigenc_single_byte_mbc_to_code, onigenc_single_byte_code_to_mbclen, onigenc_single_byte_code_to_mbc, onigenc_ascii_mbc_to_normalize, onigenc_ascii_is_mbc_ambiguous, onigenc_ascii_get_all_pair_ambig_codes, onigenc_nothing_get_all_comp_ambig_codes, ascii_is_code_ctype, onigenc_not_support_get_ctype_code_range, onigenc_single_byte_left_adjust_char_head, onigenc_always_true_is_allowed_reverse_match }; /********************************************************************** bignum.c - $Author: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ created at: Fri Jun 10 00:48:55 JST 1994 Copyright (C) 1993-2003 Yukihiro Matsumoto **********************************************************************/ #include "ruby.h" #include #include #ifdef HAVE_IEEEFP_H #include #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 || (RBIGNUM(x)->len == 1 && BDIGITS(x)[0] == 0)) static VALUE bignew_1(klass, len, sign) VALUE klass; long len; char sign; { NEWOBJ(big, struct RBignum); OBJSETUP(big, klass, T_BIGNUM); big->sign = sign; 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; } static void get2comp(x, carry) /* get 2's complement */ VALUE x; int carry; { long i = RBIGNUM(x)->len; BDIGIT *ds = BDIGITS(x); BDIGIT_DBL num; 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 (!carry) return; if ((ds[RBIGNUM(x)->len-1] & (1<<(BITSPERDIG-1))) == 0) { REALLOC_N(RBIGNUM(x)->digits, BDIGIT, ++RBIGNUM(x)->len); ds = BDIGITS(x); ds[RBIGNUM(x)->len-1] = ~0; } } void rb_big_2comp(x) /* get 2's complement */ VALUE x; { get2comp(x, Qtrue); } static VALUE bignorm(x) VALUE x; { if (!FIXNUM_P(x)) { long len = RBIGNUM(x)->len; BDIGIT *ds = BDIGITS(x); while (len-- && !ds[len]) ; RBIGNUM(x)->len = ++len; 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; } 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) { len = SIZEOF_LONG/SIZEOF_BDIGITS; } 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; 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'); --str; } 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++) { if (c == '_') { if (badcheck) { if (nondigit) goto bad; nondigit = c; } continue; } else if (!ISASCII(c)) { break; } else if (isdigit(c)) { c -= '0'; } else if (islower(c)) { c -= 'a' - 10; } else if (isupper(c)) { c -= 'A' - 10; } else { break; } if (c >= base) break; nondigit = 0; i = 0; num = c; for (;;) { while (iptr; } 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_big2str(x, base) VALUE x; int base; { volatile VALUE t; BDIGIT *ds; long i, j, hbase; VALUE ss; char *s, c; if (FIXNUM_P(x)) { return rb_fix2str(x, base); } i = RBIGNUM(x)->len; if (BIGZEROP(x)) { return rb_str_new2("0"); } j = SIZEOF_BDIGITS*CHAR_BIT*i; switch (base) { case 2: break; case 3: j = j * 647L / 1024; break; case 4: case 5: case 6: case 7: j /= 2; break; case 8: case 9: j /= 3; break; case 10: case 11: case 12: case 13: case 14: case 15: j = j * 241L / 800; 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 /= 4; break; case 32: case 33: case 34: case 35: case 36: j /= 5; break; default: rb_raise(rb_eArgError, "illegal radix %d", base); break; } j += 2; hbase = base * base; #if SIZEOF_BDIGITS > 2 hbase *= hbase; #endif t = rb_big_clone(x); ds = BDIGITS(t); ss = rb_str_new(0, j); s = RSTRING(ss)->ptr; s[0] = RBIGNUM(x)->sign ? '+' : '-'; while (i && j) { long k = i; BDIGIT_DBL num = 0; while (k--) { num = BIGUP(num) + ds[k]; ds[k] = (BDIGIT)(num / hbase); num %= hbase; } if (ds[i-1] == 0) i--; k = SIZEOF_BDIGITS; while (k--) { c = (char)(num % base); s[--j] = ruby_digitmap[(int)c]; num /= base; if (i == 0 && num == 0) break; } } while (s[j] == '0') j++; RSTRING(ss)->len -= RBIGNUM(x)->sign?j:j-1; memmove(RBIGNUM(x)->sign?s:s+1, s+j, RSTRING(ss)->len); s[RSTRING(ss)->len] = '\0'; return ss; } /* * call-seq: * big.to_s(base=10) => string * * Returns a string containing the representation of big radix * base (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, check) VALUE x; char *type; int check; { long len = RBIGNUM(x)->len; BDIGIT_DBL num; BDIGIT *ds; if (len > SIZEOF_LONG/SIZEOF_BDIGITS) { if (check) rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type); len = SIZEOF_LONG/SIZEOF_BDIGITS; } 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", Qfalse); if (!RBIGNUM(x)->sign) { return -num; } return num; } unsigned long rb_big2ulong(x) VALUE x; { unsigned long num = big2ulong(x, "unsigned long", Qtrue); 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", Qtrue); 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 big to a Float. If big doesn't * fit in a Float, 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 big is * less than, equal to, or greater than numeric. This is the * basis for the tests in Comparable. * */ 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: return rb_dbl_cmp(rb_big2dbl(x), RFLOAT(y)->value); 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 true only if obj has the same value * as big. Contrast this with Bignum#eql?, which * requires obj to be a Bignum. * * 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; b = rb_big2dbl(x); if (isnan(a) || isnan(b)) return Qfalse; 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 true only if obj is a * Bignum with the same value as big. Contrast this * with Bignum#==, 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 = RBIGNUM(x)->len; BDIGIT *ds = BDIGITS(z); if (!RBIGNUM(x)->sign) get2comp(z, Qtrue); while (i--) ds[i] = ~ds[i]; if (RBIGNUM(x)->sign) get2comp(z, Qfalse); RBIGNUM(z)->sign = !RBIGNUM(z)->sign; 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)?1: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; char 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); } } /* * call-seq: * big * other => Numeric * * Multiplies big and other, returning the result. */ VALUE rb_big_mul(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 bignorm(z); } 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); 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 big by numeric. * * -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); } /* * call-seq: * big.divmod(numeric) => array * * See Numeric#divmod. * */ 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 big by * numeric. * * -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; for (;;) { yy -= 1; if (yy == 0) break; while (yy % 2 == 0) { yy /= 2; x = rb_big_mul(x, x); } z = rb_big_mul(z, x); } 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, Qtrue); } if (!RBIGNUM(x)->sign) { x = rb_big_clone(x); get2comp(x, Qtrue); } 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; isign) get2comp(z, Qfalse); 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, Qtrue); } if (!RBIGNUM(x)->sign) { x = rb_big_clone(x); get2comp(x, Qtrue); } 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; isign) get2comp(z, Qfalse); 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, Qtrue); } if (!RBIGNUM(x)->sign) { x = rb_big_clone(x); get2comp(x, Qtrue); } 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; isign) get2comp(z, Qfalse); return bignorm(z); } static VALUE rb_big_rshift _((VALUE,VALUE)); /* * call-seq: * big << numeric => integer * * Shifts big left _numeric_ positions (right if _numeric_ is negative). */ VALUE rb_big_lshift(x, y) VALUE x, y; { BDIGIT *xds, *zds; int shift = NUM2INT(y); int s1 = shift/BITSPERDIG; int s2 = shift%BITSPERDIG; VALUE z; BDIGIT_DBL num = 0; long len, i; if (shift < 0) return rb_big_rshift(x, INT2FIX(-shift)); len = RBIGNUM(x)->len; z = bignew(len+s1+1, RBIGNUM(x)->sign); zds = BDIGITS(z); for (i=0; i> numeric => integer * * Shifts big right _numeric_ positions (left if _numeric_ is negative). */ static VALUE rb_big_rshift(x, y) VALUE x, y; { BDIGIT *xds, *zds; int shift = NUM2INT(y); long s1 = shift/BITSPERDIG; long s2 = shift%BITSPERDIG; VALUE z; BDIGIT_DBL num = 0; long i, j; if (shift < 0) return rb_big_lshift(x, INT2FIX(-shift)); if (s1 > RBIGNUM(x)->len) { if (RBIGNUM(x)->sign) return INT2FIX(0); else return INT2FIX(-1); } if (!RBIGNUM(x)->sign) { x = rb_big_clone(x); get2comp(x, Qtrue); } xds = BDIGITS(x); i = RBIGNUM(x)->len; j = i - s1; 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, Qfalse); } return bignorm(z); } /* * call-seq: * big[n] -> 0, 1 * * Bit Reference---Returns the nth bit in the (assumed) binary * representation of big, where big[0] is the least * significant bit. * * a = 9**15 * 50.downto(0) do |n| * print a[n] * end * * produces: * * 000101110110100000111000011110010100111100010111001 * */ static VALUE rb_big_aref(x, y) VALUE x, y; { BDIGIT *xds; int shift; long s1, s2; if (TYPE(y) == T_BIGNUM) { if (!RBIGNUM(y)->sign || RBIGNUM(x)->sign) return INT2FIX(0); return INT2FIX(1); } shift = NUM2INT(y); if (shift < 0) return INT2FIX(0); s1 = shift/BITSPERDIG; s2 = shift%BITSPERDIG; if (!RBIGNUM(x)->sign) { if (s1 >= RBIGNUM(x)->len) return INT2FIX(1); x = rb_big_clone(x); get2comp(x, Qtrue); } else { if (s1 >= RBIGNUM(x)->len) return INT2FIX(0); } xds = BDIGITS(x); if (xds[s1] & (1< 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 aBignum * * Returns the absolute value of big. * * -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 * big. * * (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 [], 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: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ 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 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; } 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 singleton class"); } } VALUE rb_class_new(super) VALUE super; { Check_Type(super, T_CLASS); rb_check_inheritable(super); if (super == rb_cClass) { rb_raise(rb_eTypeError, "can't make subclass of Class"); } return rb_class_boot(super); } static int clone_method(mid, body, tbl) ID mid; NODE *body; st_table *tbl; { st_insert(tbl, mid, (st_data_t)NEW_METHOD(body->nd_body, 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 = rb_singleton_class_clone(orig); } 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) { RCLASS(clone)->m_tbl = st_init_numtable(); st_foreach(RCLASS(orig)->m_tbl, clone_method, (st_data_t)RCLASS(clone)->m_tbl); } 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"); } 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); } clone->m_tbl = st_init_numtable(); st_foreach(RCLASS(klass)->m_tbl, clone_method, (st_data_t)clone->m_tbl); 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; { if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) { return RBASIC(obj)->klass = rb_cClass; } else { VALUE metasuper; VALUE klass = rb_class_boot(super); FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; rb_singleton_class_attached(klass, obj); 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; } 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 (NIL_P(module)) return; if (klass == module) return; 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 mod. * * 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 true if module is included in * mod or one of mod'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 mod (including * mod 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 false, the * instance methods in mod are returned, otherwise the methods * in mod and mod'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 * mod. If the optional parameter is not false, 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 * mod. If the optional parameter is not false, 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 mod. * If the optional parameter is not false, 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 obj. * If the optional all parameter is true, the list will include * methods in modules included in obj. * * 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; { rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PUBLIC); } 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 #define va_init_list(a,b) va_start(a,b) #else #include #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 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: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ 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); } 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 <=> * 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 <=> * 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 (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 <=> * 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 (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 <=> * 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 (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 <=> * 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 (rb_cmpint(c, x, y) <= 0) return Qtrue; return Qfalse; } /* * call-seq: * obj.between?(min, max) => true or false * * Returns false if obj <=> * min is less than zero or if anObject <=> * max is greater than zero, true 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 Comparable mixin is used by classes whose objects * may be ordered. The class must define the <=> 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. Comparable uses * <=> to implement the conventional comparison operators * (<, <=, ==, >=, * and >) and the method between?. * * 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: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ 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 #include #ifdef HAVE_UNISTD_H #include #endif #if defined HAVE_DIRENT_H && !defined _WIN32 # include # define NAMLEN(dirent) strlen((dirent)->d_name) #elif defined HAVE_DIRECT_H && !defined _WIN32 # include # 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 # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif # ifdef _WIN32 # include "win32/dir.h" # endif #endif #include #ifndef HAVE_STDLIB_H char *getenv(); #endif #ifndef HAVE_STRING_H char *strchr _((char*,char)); #endif #include #include "util.h" #if !defined HAVE_LSTAT && !defined lstat #define lstat stat #endif #define FNM_NOESCAPE 0x01 #define FNM_PATHNAME 0x02 #define FNM_DOTMATCH 0x04 #define FNM_CASEFOLD 0x08 #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(p) 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(p1, p2, nocase) 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) { 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; FilePathValue(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, open is a synonym for * Dir::new. If a block is present, it is passed * aDir as a parameter. The directory is closed at the end of * the block, and Dir::open 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"); } #define GetDIR(obj, dirp) do {\ 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(s)->ptr, 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 dir'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 dir and returns it as a string. * Returns nil 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}" } * * produces: * * 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); 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 dir. See also * Dir#seek. * * 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 dir. integer * must be a value returned by Dir#tell. * * d = Dir.new("testdir") #=> # * d.read #=> "." * i = d.tell #=> 12 * d.read #=> ".." * d.seek(i) #=> # * 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 Dir#seek, but returns the position * parameter. * * d = Dir.new("testdir") #=> # * 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 dir to the first entry. * * d = Dir.new("testdir") * d.read #=> "." * d.rewind #=> # * d.read #=> "." */ static VALUE dir_rewind(dir) VALUE dir; { struct dir_data *dirp; GetDIR(dir, dirp); rewinddir(dirp->dir); return dir; } /* * call-seq: * dir.close => nil * * Closes the directory stream. Any further attempts to access * dir will raise an IOError. * * 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 HOME, or * LOGDIR. SystemCallError (probably * Errno::ENOENT) 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 chdir is the value of the * block. chdir blocks can be nested, but in a * multi-threaded program an error will be raised if a thread attempts * to open a chdir 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 * * produces: * * /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) { FilePathValue(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; rb_secure(2); FilePathValue(*dir); 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 chroot(2) 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 string, with permissions * specified by the optional parameter anInteger. The * permissions may be modified by the value of * File::umask, and are ignored on NT. Raises a * SystemCallError if the directory cannot be created. See * also the discussion of permissions in the class documentation for * File. * */ 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 * SystemCallError 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); } /* System call with warning */ static int do_stat(path, pst) const char *path; struct stat *pst; { int ret = stat(path, pst); if (ret < 0 && errno != ENOENT) rb_sys_warning(path); return ret; } static int do_lstat(path, pst) const char *path; struct stat *pst; { int ret = lstat(path, pst); if (ret < 0 && errno != ENOENT) rb_sys_warning(path); return ret; } static DIR * do_opendir(path) const char *path; { DIR *dirp = opendir(path); if (dirp == NULL && errno != ENOENT && errno != ENOTDIR) rb_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); 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; } p = Next(p-1); } return 0; } /* Find separator in globbing pattern. */ static char * find_dirsep(s, flags) 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 baskclashes */ 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 struct glob_pattern * glob_make_pattern(p, flags) const char *p; int flags; { struct glob_pattern *list, *tmp, **tail = &list; int dirsep = 0; /* pattern is terminated with '/' */ while (*p) { tmp = ALLOC(struct glob_pattern); 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 = ALLOC_N(char, m-p+1); 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 = ALLOC(struct glob_pattern); tmp->type = dirsep ? MATCH_DIR : MATCH_ALL; tmp->str = 0; *tail = tmp; tmp->next = 0; return list; } static void glob_free_pattern(list) struct glob_pattern *list; { while (list) { struct glob_pattern *tmp = list; list = list->next; if (tmp->str) free(tmp->str); free(tmp); } } static VALUE join_path(path, dirsep, name) VALUE path; int dirsep; const char *name; { long len = RSTRING(path)->len; VALUE buf = rb_str_new(0, RSTRING(path)->len+strlen(name)+(dirsep?1:0)); memcpy(RSTRING(buf)->ptr, RSTRING(path)->ptr, len); if (dirsep) { strcpy(RSTRING(buf)->ptr+len, "/"); len++; } strcpy(RSTRING(buf)->ptr+len, name); return buf; } enum answer { YES, NO, UNKNOWN }; #ifndef S_ISDIR # define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) #endif #ifndef S_ISLNK # ifndef S_IFLNK # define S_ISLNK(m) (0) # else # define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK) # endif #endif struct glob_args { void (*func) _((VALUE, VALUE)); VALUE 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; VALUE path = args->c; OBJ_TAINT(path); (*args->func)(path, args->v); return Qnil; } static int glob_call_func(func, path, arg) void (*func) _((VALUE, VALUE)); VALUE path; VALUE arg; { int status; struct glob_args args; args.func = func; args.c = path; args.v = arg; rb_protect(glob_func_caller, (VALUE)&args, &status); return status; } static int glob_helper(path, dirsep, exist, isdir, beg, end, flags, func, arg) VALUE 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; void (*func) _((VALUE, VALUE)); 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; } } if (RSTRING(path)->len > 0) { if (match_all && exist == UNKNOWN) { if (do_lstat(RSTRING(path)->ptr, &st) == 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(RSTRING(path)->ptr, &st) == 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) { status = glob_call_func(func, join_path(path, dirsep, ""), arg); if (status) return status; } } if (exist == NO || isdir == NO) return 0; if (magical || recursive) { struct dirent *dp; DIR *dirp = do_opendir(RSTRING(path)->len > 0 ? RSTRING(path)->ptr : "."); if (dirp == NULL) return 0; for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { VALUE buf = join_path(path, dirsep, dp->d_name); enum answer new_isdir = UNKNOWN; if (recursive && strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0 && fnmatch("*", dp->d_name, flags) == 0) { #ifndef _WIN32 if (do_lstat(RSTRING(buf)->ptr, &st) == 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 = ALLOC_N(struct glob_pattern *, (end - beg) * 2); 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(new_beg); if (status) break; } closedir(dirp); } else if (plain) { struct glob_pattern **copy_beg, **copy_end, **cur2; copy_beg = copy_end = ALLOC_N(struct glob_pattern *, end - beg); for (cur = beg; cur < end; ++cur) *copy_end++ = (*cur)->type == PLAIN ? *cur : 0; for (cur = copy_beg; cur < copy_end; ++cur) { if (*cur) { VALUE buf; char *name; name = ALLOC_N(char, strlen((*cur)->str) + 1); strcpy(name, (*cur)->str); if (escape) remove_backslashes(name); new_beg = new_end = ALLOC_N(struct glob_pattern *, end - beg); *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); status = glob_helper(buf, 1, UNKNOWN, UNKNOWN, new_beg, new_end, flags, func, arg); free(new_beg); if (status) break; } } free(copy_beg); } return status; } static int rb_glob2(path, offset, flags, func, arg) VALUE path; long offset; int flags; void (*func) _((VALUE, VALUE)); VALUE arg; { struct glob_pattern *list; const char *root, *start; VALUE buf; int n; int status; if (flags & FNM_CASEFOLD) { rb_warn("Dir.glob() ignores File::FNM_CASEFOLD"); } start = root = StringValuePtr(path) + offset; #if defined DOSISH flags |= FNM_CASEFOLD; root = rb_path_skip_prefix(root); #else flags &= ~FNM_CASEFOLD; #endif if (root && *root == '/') root++; n = root - start; buf = rb_str_new(start, n); list = glob_make_pattern(root, flags); status = glob_helper(buf, 0, UNKNOWN, UNKNOWN, &list, &list + 1, flags, func, arg); glob_free_pattern(list); return status; } struct rb_glob_args { void (*func) _((const char*, VALUE)); VALUE arg; }; static VALUE rb_glob_caller(path, a) VALUE path, a; { struct rb_glob_args *args = (struct rb_glob_args *)a; (*args->func)(RSTRING(path)->ptr, args->arg); return Qnil; } void rb_glob(path, func, arg) const char *path; void (*func) _((const char*, VALUE)); VALUE arg; { struct rb_glob_args args; int status; args.func = func; args.arg = arg; status = rb_glob2(rb_str_new2(path), 0, 0, rb_glob_caller, &args); if (status) rb_jump_tag(status); } static void push_pattern(path, ary) VALUE path, ary; { rb_ary_push(ary, path); } static int push_glob(VALUE ary, VALUE s, long offset, int flags); static int push_glob(ary, str, offset, flags) VALUE ary; VALUE str; long offset; int flags; { const int escape = !(flags & FNM_NOESCAPE); const char *p = RSTRING(str)->ptr + offset; 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) { VALUE buffer = rb_str_new(0, strlen(s)); char *buf; long shift; buf = RSTRING(buffer)->ptr; 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 = push_glob(ary, buffer, offset, flags); if (status) break; } } else if (!lbrace && !rbrace) { status = rb_glob2(str, offset, flags, push_pattern, ary); } return status; } static VALUE rb_push_glob(str, flags) /* '\0' is delimiter */ VALUE str; int flags; { long offset = 0; VALUE ary; FilePathValue(str); ary = rb_ary_new(); while (offset < RSTRING(str)->len) { int status = push_glob(ary, str, offset, flags); char *p, *pend; if (status) rb_jump_tag(status); p = RSTRING(str)->ptr + offset; p += strlen(p) + 1; pend = RSTRING(str)->ptr + RSTRING(str)->len; while (p < pend && !*p) p++; offset = p - RSTRING(str)->ptr; } if (rb_block_given_p()) { rb_ary_each(ary); return Qnil; } return ary; } /* * call-seq: * Dir[ string ] => array * * Equivalent to calling * dir.glob(string,0). * */ static VALUE dir_s_aref(obj, str) VALUE obj, str; { return rb_push_glob(str, 0); } /* * call-seq: * Dir.glob( string, [flags] ) => array * Dir.glob( string, [flags] ) {| filename | block } => nil * * Returns the filenames found by expanding the pattern given in * string, either as an array or as parameters to the * block. Note that this pattern is not a regexp (it's closer to a * shell glob). See File::fnmatch for the meaning of * the flags parameter. Note that case sensitivity * depends on your system (so File::FNM_CASEFOLD is ignored) * * *:: Matches any file. Can be restricted by * other values in the glob. * * will match all files; c* will * match all files beginning with * c; *c will match * all files ending with c; and * *c* will match all files that * have c in them (including at * the beginning or end). Equivalent to * / .* /x in regexp. * **:: Matches directories recursively. * ?:: Matches any one character. Equivalent to * /.{1}/ in regexp. * [set]:: Matches any one character in +set+. * Behaves exactly like character sets in * Regexp, including set negation * ([^a-z]). * {p,q}:: Matches either literal p or * literal q. Matching literals * may be more than one character in length. * More than two literals may be specified. * Equivalent to pattern alternation in * regexp. * \:: 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; int flags; if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2) flags = NUM2INT(rflags); else flags = 0; return rb_push_glob(str, flags); } static VALUE dir_open_dir(path) VALUE path; { struct dir_data *dp; 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}" } * * produces: * * 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 SystemCallError 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 path matches against pattern The * pattern is not a regular expression; instead it follows rules * similar to shell filename globbing. It may contain the following * metacharacters: * * *:: Matches any file. Can be restricted by * other values in the glob. * * will match all files; c* will * match all files beginning with * c; *c will match * all files ending with c; and * *c* will match all files that * have c in them (including at * the beginning or end). Equivalent to * / .* /x in regexp. * **:: Matches directories recursively or files * expansively. * ?:: Matches any one character. Equivalent to * /.{1}/ in regexp. * [set]:: Matches any one character in +set+. * Behaves exactly like character sets in * Regexp, including set negation * ([^a-z]). * \:: Escapes the next metacharacter. * * flags is a bitwise OR of the FNM_xxx * parameters. The same glob pattern and flags are used by * Dir::glob. * * 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 = File.join("**", "*.rb") * 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 * * File.fnmatch('* IGNORE /*', 'dave/.profile', File::FNM_PATHNAME) #=> false * File.fnmatch('* IGNORE /*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true * * File.fnmatch('** IGNORE /foo', 'a/b/c/foo', File::FNM_PATHNAME) #=> true * File.fnmatch('** IGNORE /foo', '/a/b/c/foo', File::FNM_PATHNAME) #=> true * File.fnmatch('** IGNORE /foo', 'c:/a/b/c/foo', File::FNM_PATHNAME) #=> true * File.fnmatch('** IGNORE /foo', 'a/.b/c/foo', File::FNM_PATHNAME) #=> false * File.fnmatch('** IGNORE /foo', '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 Dir are directory streams representing * directories in the underlying file system. They provide a variety of * ways to list directories and their contents. See also * File. * * The directory used in these examples contains the two regular files * (config.h and main.rb), the parent * directory (..), and the directory itself * (.). */ 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,"inspect", dir_inspect, 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)); } /********************************************************************** dln.c - $Author: murphy $ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ 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 #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 #ifdef _AIX #pragma alloca #endif #if defined(HAVE_ALLOCA_H) #include #endif #ifdef HAVE_STRING_H # include #else # include #endif #ifndef xmalloc void *xmalloc(); void *xcalloc(); void *xrealloc(); #endif #include #if defined(_WIN32) || defined(__VMS) #include "missing/file.h" #endif #include #include #ifndef S_ISDIR # define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifndef MAXPATHLEN # define MAXPATHLEN 1024 #endif #ifdef HAVE_UNISTD_H # include #endif #ifndef _WIN32 char *getenv(); #endif #if defined(__VMS) #pragma builtins #include #endif #ifdef __MACOS__ # include # include # include # include "macruby_private.h" #endif #ifdef __BEOS__ # include #endif int eaccess(); #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 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 #include #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; symn_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') { char *p = alloca(strlen(lib) + 4); sprintf(p, "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 # include #endif #ifdef __hpux #include #include "dl.h" #endif #if defined(_AIX) #include /* for isdigit() */ #include /* for global errno */ #include #endif #ifdef NeXT #if NS_TARGET_MAJOR < 4 #include #else #include #ifndef NSLINKMODULE_OPTION_BINDNOW #define NSLINKMODULE_OPTION_BINDNOW 1 #endif #endif #else #ifdef __APPLE__ #include #endif #endif #if defined _WIN32 && !defined __CYGWIN__ #include #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