Skip to content

Commit

Permalink
Fix handling of chomp with paragraph separator
Browse files Browse the repository at this point in the history
Try to mirror IO behavior, where chomp takes out the entire paragraph
separators between entries, but does not chomp a single line separator
at the end of the string.

Partially Fixes [Bug #18768]
  • Loading branch information
jeremyevans committed May 30, 2022
1 parent 1edc885 commit a83ddbb
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 24 deletions.
27 changes: 15 additions & 12 deletions ext/stringio/stringio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,7 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else if ((n = RSTRING_LEN(str)) == 0) {
const char *paragraph_end = NULL;
p = s;
while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') {
p += *p == '\r';
Expand All @@ -1213,19 +1214,21 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
}
s = p;
while ((p = memchr(p, '\n', e - p)) && (p != e)) {
if (*++p == '\n') {
e = p + 1;
w = (arg->chomp ? 1 : 0);
break;
}
else if (*p == '\r' && p < e && p[1] == '\n') {
e = p + 2;
w = (arg->chomp ? 2 : 0);
break;
}
p++;
if (!((p < e && *p == '\n') ||
(p + 1 < e && *p == '\r' && *(p+1) == '\n'))) {
continue;
}
paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1);
while ((p < e && *p == '\n') ||
(p + 1 < e && *p == '\r' && *(p+1) == '\n')) {
p += (*p == '\r') ? 2 : 1;
}
e = p;
break;
}
if (!w && arg->chomp) {
w = chomp_newline_width(s, e);
if (arg->chomp && paragraph_end) {
w = e - paragraph_end;
}
str = strio_substr(ptr, s - RSTRING_PTR(ptr->string), e - s - w, enc);
}
Expand Down
24 changes: 12 additions & 12 deletions test/stringio/test_stringio.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ def test_gets_chomp
assert_equal("a", StringIO.new("a\nb").gets(chomp: true))
assert_equal("abc", StringIO.new("abc\n\ndef\n").gets(chomp: true))
assert_equal("abc\n\ndef\n", StringIO.new("abc\n\ndef\n").gets(nil, chomp: true))
assert_equal("abc\n", StringIO.new("abc\n\ndef\n").gets("", chomp: true))
assert_equal("abc", StringIO.new("abc\n\ndef\n").gets("", chomp: true))
stringio = StringIO.new("abc\n\ndef\n")
assert_equal("abc\n", stringio.gets("", chomp: true))
assert_equal("def", stringio.gets("", chomp: true))
assert_equal("abc", stringio.gets("", chomp: true))
assert_equal("def\n", stringio.gets("", chomp: true))

assert_string("", Encoding::UTF_8, StringIO.new("\n").gets(chomp: true))
end
Expand All @@ -110,10 +110,10 @@ def test_gets_chomp_eol
assert_equal("a", StringIO.new("a\r\nb").gets(chomp: true))
assert_equal("abc", StringIO.new("abc\r\n\r\ndef\r\n").gets(chomp: true))
assert_equal("abc\r\n\r\ndef\r\n", StringIO.new("abc\r\n\r\ndef\r\n").gets(nil, chomp: true))
assert_equal("abc\r\n", StringIO.new("abc\r\n\r\ndef\r\n").gets("", chomp: true))
assert_equal("abc", StringIO.new("abc\r\n\r\ndef\r\n").gets("", chomp: true))
stringio = StringIO.new("abc\r\n\r\ndef\r\n")
assert_equal("abc\r\n", stringio.gets("", chomp: true))
assert_equal("def", stringio.gets("", chomp: true))
assert_equal("abc", stringio.gets("", chomp: true))
assert_equal("def\r\n", stringio.gets("", chomp: true))
end

def test_readlines
Expand Down Expand Up @@ -596,15 +596,15 @@ def test_each
assert_equal(["foo\n", "bar\n", "baz\n"], f.each.to_a)
f.rewind
assert_equal(["foo", "bar", "baz"], f.each(chomp: true).to_a)
f = StringIO.new("foo\nbar\n\nbaz\n")
assert_equal(["foo\nbar\n\n", "baz\n"], f.each("").to_a)
f = StringIO.new("foo\nbar\n\n\nbaz\n")
assert_equal(["foo\nbar\n\n\n", "baz\n"], f.each("").to_a)
f.rewind
assert_equal(["foo\nbar\n", "baz"], f.each("", chomp: true).to_a)
assert_equal(["foo\nbar", "baz\n"], f.each("", chomp: true).to_a)

f = StringIO.new("foo\r\nbar\r\n\r\nbaz\r\n")
assert_equal(["foo\r\nbar\r\n\r\n", "baz\r\n"], f.each("").to_a)
f = StringIO.new("foo\r\nbar\r\n\r\n\r\nbaz\r\n")
assert_equal(["foo\r\nbar\r\n\r\n\r\n", "baz\r\n"], f.each("").to_a)
f.rewind
assert_equal(["foo\r\nbar\r\n", "baz"], f.each("", chomp: true).to_a)
assert_equal(["foo\r\nbar", "baz\r\n"], f.each("", chomp: true).to_a)

f = StringIO.new("abc\n\ndef\n")
assert_equal(["ab", "c\n", "\nd", "ef", "\n"], f.each(nil, 2, chomp: true).to_a)
Expand Down

0 comments on commit a83ddbb

Please sign in to comment.