LUE result, match;
    struct re_registers *regs;
    long i;

    if (rb_reg_search(pat, str, *start, 0) >= 0) {
	match = rb_backref_get();
	regs = RMATCH(match)->regs;
	if (BEG(0) == END(0)) {
	    /*
	     * Always consume at least one character of the input string
	     */
	    *start = END(0)+mbclen2(RSTRING(str)->ptr[END(0)],pat);
	}
	else {
	    *start = END(0);
	}
	if (regs->num_regs == 1) {
	    return rb_reg_nth_match(0, match);
	}
	result = rb_ary_new2(regs->num_regs);
	for (i=1; i < regs->num_regs; i++) {
	    rb_ary_push(result, rb_reg_nth_match(i, match));
	}

	return result;
    }
    return Qnil;
}

static VALUE
rb_str_scan(str, pat)
    VALUE str, pat;
{
    VALUE result;
    long start = 0;
    VALUE match = Qnil;

    pat = get_pat(pat);
    if (!rb_block_given_p()) {
	VALUE ary = rb_ary_new();

	while (!NIL_P(result = scan_once(str, pat, &start))) {
	    match = rb_backref_get();
	    rb_ary_push(ary, result);
	}
	rb_backref_set(match);
	return ary;
    }
    
    while (!NIL_P(result = scan_once(str, pat, &start))) {
	match = rb_backref_get();
	rb_match_busy(match);
	rb_yield(result);
	rb_backref_set(match);	/* restore $~ value */
    }
    rb_backref_set(match);
    return str;
}

static VALUE
rb_f_scan(self, pat)
    VALUE self, pat;
{
    return rb_str_scan(uscore_get(), pat);
}

static VALUE
rb_str_hex(str)
    VALUE str;
{
    return rb_str2inum(str, 16);
}

static VALUE
rb_str_oct(str)
    VALUE str;
{
    int base = 8;

    if (RSTRING(str)->len > 2 && RSTRING(str)->ptr[0] == '0') {
	switch (RSTRING(str)->ptr[1]) {
	  case 'x':
	  case 'X':
	    base = 16;
	    break;
	  case 'b':
	  case 'B':
	    base = 2;
	    break;
	}
    }
    return rb_str2inum(str, base);
}

static VALUE
rb_str_crypt(str, salt)
    VALUE str, salt;
{
    extern char *crypt();
    VALUE result;

    if (TYPE(salt) != T_STRING) salt = rb_str_to_str(salt);
    if (RSTRING(salt)->len < 2)
	rb_raise(rb_eArgError, "salt too short(need >=2 bytes)");

    result = rb_str_new2(crypt(RSTRING(str)->ptr, RSTRING(salt)->ptr));
    OBJ_INFECT(result, str);
    OBJ_INFECT(result, salt);
    return result;
}

static VALUE
rb_str_intern(str)
    VALUE str;
{
    ID id;

    if (strlen(RSTRING(str)->ptr) != RSTRING(str)->len)
	rb_raise(rb_eArgError, "string contains `\\0'");
    id = rb_intern(RSTRING(str)->ptr);
    return ID2SYM(id);
}

static VALUE
rb_str_sum(argc, argv, str)
    int argc;
    VALUE *argv;
    VALUE str;
{
    VALUE vbits;
    int   bits;
    char *p, *pend;

    if (rb_scan_args(argc, argv, "01", &vbits) == 0) {
	bits = 16;
    }
    else bits = NUM2INT(vbits);

    p = RSTRING(str)->ptr; pend = p + RSTRING(str)->len;
    if (bits > sizeof(long)*CHAR_BIT) {
	VALUE res = INT2FIX(0);
	VALUE mod;

	mod = rb_funcall(INT2FIX(1), rb_intern("<<"), 1, INT2FIX(bits));
	mod = rb_funcall(mod, '-', 1, INT2FIX(1));

	while (p < pend) {
	    res = rb_funcall(res, '+', 1, INT2FIX((unsigned int)*p));
	    p++;
	}
	res = rb_funcall(res, '&', 1, mod);
	return res;
    }
    else {
	unsigned int res = 0;
	unsigned int mod = (1<<bits)-1;

	if (mod == 0) {
	    mod = -1;
	}
	while (p < pend) {
	    res += (unsigned int)*p;
	    p++;
	}
	res &= mod;
	return rb_int2inum(res);
    }
}

static VALUE
rb_str_ljust(str, w)
    VALUE str;
    VALUE w;
{
    long width = NUM2LONG(w);
    VALUE res;
    char *p, *pend;

    if (width < 0 || RSTRING(str)->len >= width) return rb_str_dup(str);
    res = rb_str_new(0, width);
    RBASIC(res)->klass = rb_obj_class(str);
    memcpy(RSTRING(res)->ptr, RSTRING(str)->ptr, RSTRING(str)->len);
    p = RSTRING(res)->ptr + RSTRING(str)->len; pend = RSTRING(res)->ptr + width;
    while (p < pend) {
	*p++ = ' ';
    }
    OBJ_INFECT(res, str);
    return res;
}

static VALUE
rb_str_rjust(str, w)
    VALUE str;
    VALUE w;
{
    long width = NUM2LONG(w);
    VALUE res;
    char *p, *pend;

    if (width < 0 || RSTRING(str)->len >= width) return rb_str_dup(str);
    res = rb_str_new(0, width);
    RBASIC(res)->klass = rb_obj_class(str);
    p = RSTRING(res)->ptr; pend = p + width - RSTRING(str)->len;
    while (p < pend) {
	*p++ = ' ';
    }
    memcpy(pend, RSTRING(str)->ptr, RSTRING(str)->len);
    OBJ_INFECT(res, str);
    return res;
}

static VALUE
rb_str_center(str, w)
    VALUE str;
    VALUE w;
{
    long width = NUM2LONG(w);
    VALUE res;
    char *p, *pend;
    long n;

    if (width < 0 || RSTRING(str)->len >= width) return rb_str_dup(str);
    res = rb_str_new(0, width);
    RBASIC(res)->klass = rb_obj_class(str);
    n = (width - RSTRING(str)->len)/2;
    p = RSTRING(res)->ptr; pend = p + n;
    while (p < pend) {
	*p++ = ' ';
    }
    memcpy(pend, RSTRING(str)->ptr, RSTRING(str)->len);
    p = pend + RSTRING(str)->len; pend = RSTRING(res)->ptr + width;
    while (p < pend) {
	*p++ = ' ';
    }
    OBJ_INFECT(res, str);
    return res;
}

void
rb_str_setter(val, id, var)
    VALUE val;
    ID id;
    VALUE *var;
{
    if (!NIL_P(val) && TYPE(val) != T_STRING) {
	rb_raise(rb_eTypeError, "value of %s must be String", rb_id2name(id));
    }
    *var = val;
}

void
Init_String()
{
    rb_cString  = rb_define_class("String", rb_cObject);
    rb_include_module(rb_cString, rb_mComparable);
    rb_include_module(rb_cString, rb_mEnumerable);
    rb_define_singleton_method(rb_cString, "new", rb_str_s_new, -1);
    rb_define_method(rb_cString, "initialize", rb_str_replace_m, 1);
    rb_define_method(rb_cString, "clone", rb_str_clone, 0);
    rb_define_method(rb_cString, "dup", rb_str_dup, 0);
    rb_define_method(rb_cString, "<=>", rb_str_cmp_m, 1);
    rb_define_method(rb_cString, "==", rb_str_equal, 1);
    rb_define_method(rb_cString, "===", rb_str_equal, 1);
    rb_define_method(rb_cString, "eql?", rb_str_equal, 1);
    rb_define_method(rb_cString, "hash", rb_str_hash_m, 0);
    rb_define_method(rb_cString, "+", rb_str_plus, 1);
    rb_define_method(rb_cString, "*", rb_str_times, 1);
    rb_define_method(rb_cString, "%", rb_str_format, 1);
    rb_define_method(rb_cString, "[]", rb_str_aref_m, -1);
    rb_define_method(rb_cString, "[]=", rb_str_aset_m, -1);
    rb_define_method(rb_cString, "length", rb_str_length, 0);
    rb_define_method(rb_cString, "size", rb_str_length, 0);
    rb_define_method(rb_cString, "empty?", rb_str_empty, 0);
    rb_define_method(rb_cString, "=~", rb_str_match, 1);
    rb_define_method(rb_cString, "~", rb_str_match2, 0);
    rb_define_method(rb_cString, "succ", rb_str_succ, 0);
    rb_define_method(rb_cString, "succ!", rb_str_succ_bang, 0);
    rb_define_method(rb_cString, "next", rb_str_succ, 0);
    rb_define_method(rb_cString, "next!", rb_str_succ_bang, 0);
    rb_define_method(rb_cString, "upto", rb_str_upto_m, 1);
    rb_define_method(rb_cString, "index", rb_str_index_m, -1);
    rb_define_method(rb_cString, "rindex", rb_str_rindex, -1);
    rb_define_method(rb_cString, "replace", rb_str_replace_m, 1);

    rb_define_method(rb_cString, "to_i", rb_str_to_i, 0);
    rb_define_method(rb_cString, "to_f", rb_str_to_f, 0);
    rb_define_method(rb_cString, "to_s", rb_str_to_s, 0);
    rb_define_method(rb_cString, "to_str", rb_str_to_s, 0);
    rb_define_method(rb_cString, "inspect", rb_str_inspect, 0);
    rb_define_method(rb_cString, "dump", rb_str_dump, 0);

    rb_define_method(rb_cString, "upcase", rb_str_upcase, 0);
    rb_define_method(rb_cString, "downcase", rb_str_downcase, 0);
    rb_define_method(rb_cString, "capitalize", rb_str_capitalize, 0);
    rb_define_method(rb_cString, "swapcase", rb_str_swapcase, 0);

    rb_define_method(rb_cString, "upcase!", rb_str_upcase_bang, 0);
    rb_define_method(rb_cString, "downcase!", rb_str_downcase_bang, 0);
    rb_define_method(rb_cString, "capitalize!", rb_str_capitalize_bang, 0);
    rb_define_method(rb_cString, "swapcase!", rb_str_swapcase_bang, 0);

    rb_define_method(rb_cString, "hex", rb_str_hex, 0);
    rb_define_method(rb_cString, "oct", rb_str_oct, 0);
    rb_define_method(rb_cString, "split", rb_str_split_m, -1);
    rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
    rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
    rb_define_method(rb_cString, "concat", rb_str_concat, 1);
    rb_define_method(rb_cString, "<<", rb_str_concat, 1);
    rb_define_method(rb_cString, "crypt", rb_str_crypt, 1);
    rb_define_method(rb_cString, "intern", rb_str_intern, 0);

    rb_define_method(rb_cString, "include?", rb_str_include, 1);

    rb_define_method(rb_cString, "scan", rb_str_scan, 1);

    rb_define_method(rb_cString, "ljust", rb_str_ljust, 1);
    rb_define_method(rb_cString, "rjust", rb_str_rjust, 1);
    rb_define_method(rb_cString, "center", rb_str_center, 1);

    rb_define_method(rb_cString, "sub", rb_str_sub, -1);
    rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);
    rb_define_method(rb_cString, "chop", rb_str_chop, 0);
    rb_define_method(rb_cString, "chomp", rb_str_chomp, -1);
    rb_define_method(rb_cString, "strip", rb_str_strip, 0);

    rb_define_method(rb_cString, "sub!", rb_str_sub_bang, -1);
    rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1);
    rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0);
    rb_define_method(rb_cString, "chop!", rb_str_chop_bang, 0);
    rb_define_method(rb_cString, "chomp!", rb_str_chomp_bang, -1);

    rb_define_method(rb_cString, "tr", rb_str_tr, 2);
    rb_define_method(rb_cString, "tr_s", rb_str_tr_s, 2);
    rb_define_method(rb_cString, "delete", rb_str_delete, -1);
    rb_define_method(rb_cString, "squeeze", rb_str_squeeze, -1);
    rb_define_method(rb_cString, "count", rb_str_count, -1);

    rb_define_method(rb_cString, "tr!", rb_str_tr_bang, 2);
    rb_define_method(rb_cString, "tr_s!", rb_str_tr_s_bang, 2);
    rb_define_method(rb_cString, "delete!", rb_str_delete_bang, -1);
    rb_define_method(rb_cString, "squeeze!", rb_str_squeeze_bang, -1);

    rb_define_method(rb_cString, "each_line", rb_str_each_line, -1);
    rb_define_method(rb_cString, "each", rb_str_each_line, -1);
    rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);

    rb_define_method(rb_cString, "sum", rb_str_sum, -1);

    rb_define_global_function("sub", rb_f_sub, -1);
    rb_define_global_function("gsub", rb_f_gsub, -1);

    rb_define_global_function("sub!", rb_f_sub_bang, -1);
    rb_define_global_function("gsub!", rb_f_gsub_bang, -1);

    rb_define_global_function("chop", rb_f_chop, 0);
    rb_define_global_function("chop!", rb_f_chop_bang, 0);

    rb_define_global_function("chomp", rb_f_chomp, -1);
    rb_define_global_function("chomp!", rb_f_chomp_bang, -1);

    rb_define_global_function("split", rb_f_split, -1);
    rb_define_global_function("scan", rb_f_scan, 1);

    rb_define_method(rb_cString, "slice", rb_str_aref_m, -1);
    rb_define_method(rb_cString, "slice!", rb_str_slice_bang, -1);

    id_to_s = rb_intern("to_s");

    rb_fs = Qnil;
    rb_define_hooked_variable("$;", &rb_fs, 0, rb_str_setter);
    rb_define_hooked_variable("$-F", &rb_fs, 0, rb_str_setter);
}
