/* * 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; }