diff --git a/src/builtin.cpp b/src/builtin.cpp index 70eef46c3..6ee68a005 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -2024,20 +2024,19 @@ static int read_in_chunks(int fd, wcstring &buff, bool split_null) { break; } - long i; - for (i = 0; i < bytes_read && !finished; i++) { - if ((!split_null && inbuf[i] == L'\n') || (split_null && inbuf[i] == L'\0')) { - finished = true; - } else { - str.push_back(inbuf[i]); - } - } - buff += str2wcstring(str); - if (i < bytes_read) { - CHECK(lseek(fd, i - bytes_read, SEEK_CUR) != -1, STATUS_BUILTIN_ERROR) + const char *end = std::find(inbuf, inbuf + bytes_read, split_null ? L'\0' : L'\n'); + long bytes_consumed = end - inbuf; // note: must be signed for use in lseek + assert(bytes_consumed <= bytes_read); + str.append(inbuf, bytes_consumed); + if (bytes_consumed < bytes_read) { + // We found a splitter + // +1 because we need to treat the splitter as consumed, but not append it to the string + CHECK(lseek(fd, bytes_consumed - bytes_read + 1, SEEK_CUR) != -1, STATUS_BUILTIN_ERROR) + finished = true; } } + buff = str2wcstring(str); if (buff.empty() && eof) { exit_res = STATUS_BUILTIN_ERROR; } diff --git a/tests/read.in b/tests/read.in index 893797a1b..78be3047a 100644 --- a/tests/read.in +++ b/tests/read.in @@ -117,4 +117,14 @@ echo -ne 'foo\nbar\0baz\nquux' | while read -lza foo print_vars foo end +echo +echo '# chunked read tests' +set -l path /tmp/fish_chunked_read_test.txt +set -l longstr (seq 1024 | string join ',') +echo -n $longstr > $path +read -l longstr2 < $path +test "$longstr" = "$longstr2" +and echo "Chunked reads test pass" +or echo "Chunked reads test failure: long strings don't match!" + true diff --git a/tests/read.out b/tests/read.out index 43b016db3..d35a9146a 100644 --- a/tests/read.out +++ b/tests/read.out @@ -55,3 +55,6 @@ newline 1 'foo' 1 'bar' 2 'foo' 'bar' 2 'baz' 'quux' + +# chunked read tests +Chunked reads test pass