/*
 *  call-seq:
 *     str.each(separator=$/) {|substr| block }        => str
 *     str.each_line(separator=$/) {|substr| block }   => str
 *  
 *  Splits <i>str</i> using the supplied parameter as the record separator
 *  (<code>$/</code> by default), passing each substring in turn to the supplied
 *  block. If a zero-length record separator is supplied, the string is split on
 *  <code>\n</code> characters, except that multiple successive newlines are
 *  appended together.
 *     
 *     print "Example one\n"
 *     "hello\nworld".each {|s| p s}
 *     print "Example two\n"
 *     "hello\nworld".each('l') {|s| p s}
 *     print "Example three\n"
 *     "hello\n\n\nworld".each('') {|s| p s}
 *     
 *  <em>produces:</em>
 *     
 *     Example one
 *     "hello\n"
 *     "world"
 *     Example two
 *     "hel"
 *     "l"
 *     "o\nworl"
 *     "d"
 *     Example three
 *     "hello\n\n\n"
 *     "world"
 */

static VALUE
rb_str_each_line(argc, argv, str)
    int argc;
    VALUE *argv;
    VALUE str;
{
    VALUE rs;
    int newline;
    char *p = RSTRING(str)->ptr, *pend = p + RSTRING(str)->len, *s;
    char *ptr = p;
    long len = RSTRING(str)->len, rslen;
    VALUE line;

    if (rb_scan_args(argc, argv, "01", &rs) == 0) {
        rs = rb_rs;
    }

    if (NIL_P(rs)) {
        rb_yield(str);
        return str;
    }
    StringValue(rs);
    rslen = RSTRING(rs)->len;
    if (rslen == 0) {
        newline = '\n';
    }
    else {
        newline = RSTRING(rs)->ptr[rslen-1];
    }

    for (s = p, p += rslen; p < pend; p++) {
        if (rslen == 0 && *p == '\n') {
            if (*++p != '\n') continue;
            while (*p == '\n') p++;
        }
        if (RSTRING(str)->ptr < p && p[-1] == newline &&
            (rslen <= 1 ||
             rb_memcmp(RSTRING(rs)->ptr, p-rslen, rslen) == 0)) {
            line = rb_str_new5(str, s, p - s);
            OBJ_INFECT(line, str);
            rb_yield(line);
            str_mod_check(str, ptr, len);
            s = p;
        }
    }

    if (s != pend) {
        if (p > pend) p = pend;
        line = rb_str_new5(str, s, p - s);
        OBJ_INFECT(line, str);
        rb_yield(line);
    }

    return str;
}