Commit: 9a460497da3cc2b755f4628350756427fc0a1051
Author: Gustavo André dos Santos Lopes(cataphr...@php.net) Mon, 19 Mar
2012 16:28:10 +0000
Committer: Gustavo André dos Santos Lopes(cataphr...@php.net) Mon, 19 Mar
2012 16:34:31 +0000
Parents: 53e3467ff233af4a40626f86ea8a61880722beb8
Link:
http://git.php.net/?p=php-src.git;a=commitdiff;h=9a460497da3cc2b755f4628350756427fc0a1051
Log:
MFH: 45a6f8d for 5.4.
- Further fix for bug #60455 (stream_get_line misbehaves if EOF is not detected
together with the last read).
- Fixed bug #60817 (stream_get_line() reads from stream even when there is
already sufficient data buffered). stream_get_line() now behaves more like
fgets(), as is documented.
Bugs:
https://bugs.php.net/60455
https://bugs.php.net/60817
Changed paths:
M ext/standard/tests/streams/bug60455_02.phpt
M ext/standard/tests/streams/bug60455_03.phpt
A ext/standard/tests/streams/bug60455_04.phpt
A ext/standard/tests/streams/bug60817.phpt
M main/streams/streams.c
9a460497da3cc2b755f4628350756427fc0a1051
diff --git a/ext/standard/tests/streams/bug60455_02.phpt
b/ext/standard/tests/streams/bug60455_02.phpt
index 6e06e9f..0ddf346 100644
--- a/ext/standard/tests/streams/bug60455_02.phpt
+++ b/ext/standard/tests/streams/bug60455_02.phpt
@@ -28,3 +28,4 @@ while (!feof($f)) {
}
--EXPECT--
string(1) "a"
+bool(false)
diff --git a/ext/standard/tests/streams/bug60455_03.phpt
b/ext/standard/tests/streams/bug60455_03.phpt
index 5d7ba1f..2429d31 100644
--- a/ext/standard/tests/streams/bug60455_03.phpt
+++ b/ext/standard/tests/streams/bug60455_03.phpt
@@ -47,7 +47,9 @@ while (!feof($f)) {
--EXPECT--
string(1) "a"
string(1) "b"
+bool(false)
string(1) "a"
string(0) ""
+bool(false)
string(1) "a"
string(0) ""
diff --git a/ext/standard/tests/streams/bug60455_04.phpt
b/ext/standard/tests/streams/bug60455_04.phpt
new file mode 100644
index 0000000..3a82298
--- /dev/null
+++ b/ext/standard/tests/streams/bug60455_04.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #60455: stream_get_line and 1-line with maxlen size followed by 0-length
+read with EOL indication
+--FILE--
+<?php
+class TestStream {
+ private $s = 0;
+ function stream_open($path, $mode, $options, &$opened_path) {
+ return true;
+ }
+ function stream_read($count) {
+ if ($this->s++ == 0)
+ return "a\n";
+
+ return "";
+ }
+ function stream_eof() {
+ return $this->s >= 2;
+ }
+
+}
+
+stream_wrapper_register("test", "TestStream");
+
+$f = fopen("test://", "r");
+while (!feof($f)) {
+ $line = stream_get_line($f, 2, "\n");
+ var_dump($line);
+}
+--EXPECT--
+string(1) "a"
+bool(false)
diff --git a/ext/standard/tests/streams/bug60817.phpt
b/ext/standard/tests/streams/bug60817.phpt
new file mode 100644
index 0000000..2d4cf26
--- /dev/null
+++ b/ext/standard/tests/streams/bug60817.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Bug #60817: stream_get_line() reads from stream even when there is already
sufficient data buffered
+--FILE--
+<?php
+class TestStream { //data, empty data, empty data + eof
+ private $s = 0;
+ function stream_open($path, $mode, $options, &$opened_path) {
+ return true;
+ }
+ function stream_read($count) {
+ echo "Read done\n";
+ if ($this->s++ == 0)
+ return "a\nbb\ncc";
+
+ return "";
+ }
+ function stream_eof() {
+ return $this->s >= 2;
+ }
+
+}
+
+stream_wrapper_register("test", "TestStream");
+
+$f = fopen("test://", "r");
+while (!feof($f)) {
+ $line = stream_get_line($f, 99, "\n");
+ var_dump($line);
+}
+
+--EXPECT--
+Read done
+string(1) "a"
+string(2) "bb"
+Read done
+string(2) "cc"
diff --git a/main/streams/streams.c b/main/streams/streams.c
index 1614307..89fa364 100755
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -996,77 +996,111 @@ PHPAPI char *_php_stream_get_line(php_stream *stream,
char *buf, size_t maxlen,
return bufstart;
}
+#define STREAM_BUFFERED_AMOUNT(stream) \
+ ((size_t)(((stream)->writepos) - (stream)->readpos))
+
+static char *_php_stream_search_delim(php_stream *stream,
+
size_t maxlen,
+
size_t skiplen,
+ char
*delim, /* non-empty! */
+
size_t delim_len TSRMLS_DC)
+{
+ size_t seek_len;
+
+ /* set the maximum number of bytes we're allowed to read from buffer */
+ seek_len = MIN(STREAM_BUFFERED_AMOUNT(stream), maxlen);
+ if (seek_len <= skiplen) {
+ return NULL;
+ }
+
+ if (delim_len == 1) {
+ return memchr(&stream->readbuf[stream->readpos + skiplen],
+ delim[0], seek_len - skiplen);
+ } else {
+ return php_memnstr((char*)&stream->readbuf[stream->readpos +
skiplen],
+ delim, delim_len,
+ (char*)&stream->readbuf[stream->readpos +
seek_len]);
+ }
+}
+
PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t
*returned_len, char *delim, size_t delim_len TSRMLS_DC)
{
- char *e, *buf;
- size_t toread, len;
- int skip = 0;
+ char *ret_buf, /* returned buffer */
+ *found_delim = NULL;
+ size_t buffered_len,
+ tent_ret_len; /* tentative returned
length*/
+ int has_delim = delim_len > 0 && delim[0] != '\0';
+
+ if (maxlen == 0) {
+ return NULL;
+ }
- len = stream->writepos - stream->readpos;
+ if (has_delim) {
+ found_delim = _php_stream_search_delim(
+ stream, maxlen, 0, delim, delim_len TSRMLS_CC);
+ }
- /* make sure the stream read buffer has maxlen bytes */
- while (len < maxlen) {
+ buffered_len = STREAM_BUFFERED_AMOUNT(stream);
+ /* try to read up to maxlen length bytes while we don't find the delim
*/
+ while (!found_delim && buffered_len < maxlen) {
+ size_t just_read,
+ to_read_now;
- size_t just_read;
- toread = MIN(maxlen - len, stream->chunk_size);
+ to_read_now = MIN(maxlen - buffered_len, stream->chunk_size);
- php_stream_fill_read_buffer(stream, len + toread TSRMLS_CC);
+ php_stream_fill_read_buffer(stream, buffered_len + to_read_now
TSRMLS_CC);
- just_read = (stream->writepos - stream->readpos) - len;
- len += just_read;
+ just_read = STREAM_BUFFERED_AMOUNT(stream) - buffered_len;
/* Assume the stream is temporarily or permanently out of data
*/
if (just_read == 0) {
break;
}
- }
- if (delim_len == 0 || !delim) {
- toread = maxlen;
- } else {
- size_t seek_len;
-
- /* set the maximum number of bytes we're allowed to read from
buffer */
- seek_len = stream->writepos - stream->readpos;
- if (seek_len > maxlen) {
- seek_len = maxlen;
- }
-
- if (delim_len == 1) {
- e = memchr(stream->readbuf + stream->readpos, *delim,
seek_len);
- } else {
- e = php_memnstr(stream->readbuf + stream->readpos,
delim, delim_len, (stream->readbuf + stream->readpos + seek_len));
- }
-
- if (!e) {
- /* return with error if the delimiter string was not
found, we
- * could not completely fill the read buffer with
maxlen bytes
- * and we don't know we've reached end of file. Added
with
- * non-blocking streams in mind, where this situation
is frequent */
- if (seek_len < maxlen && !stream->eof) {
- return NULL;
+ if (has_delim) {
+ /* search for delimiter, but skip buffered_len (the
number of bytes
+ * buffered before this loop iteration), as they have
already been
+ * searched for the delimiter */
+ found_delim = _php_stream_search_delim(
+ stream, maxlen, buffered_len, delim, delim_len
TSRMLS_CC);
+ if (found_delim) {
+ break;
}
- toread = maxlen;
- } else {
- toread = e - (char *) stream->readbuf - stream->readpos;
- /* we found the delimiter, so advance the read pointer
past it */
- skip = 1;
}
+ buffered_len += just_read;
}
- if (toread > maxlen && maxlen > 0) {
- toread = maxlen;
+ if (has_delim && found_delim) {
+ tent_ret_len = found_delim -
(char*)&stream->readbuf[stream->readpos];
+ } else if (!has_delim && STREAM_BUFFERED_AMOUNT(stream) >= maxlen) {
+ tent_ret_len = maxlen;
+ } else {
+ /* return with error if the delimiter string (if any) was not
found, we
+ * could not completely fill the read buffer with maxlen bytes
and we
+ * don't know we've reached end of file. Added with
non-blocking streams
+ * in mind, where this situation is frequent */
+ if (STREAM_BUFFERED_AMOUNT(stream) < maxlen && !stream->eof) {
+ return NULL;
+ } else if (STREAM_BUFFERED_AMOUNT(stream) == 0 && stream->eof) {
+ /* refuse to return an empty string just because by
accident
+ * we knew of EOF in a read that returned no data */
+ return NULL;
+ } else {
+ tent_ret_len = MIN(STREAM_BUFFERED_AMOUNT(stream),
maxlen);
+ }
}
- buf = emalloc(toread + 1);
- *returned_len = php_stream_read(stream, buf, toread);
+ ret_buf = emalloc(tent_ret_len + 1);
+ /* php_stream_read will not call ops->read here because the necessary
+ * data is guaranteedly buffered */
+ *returned_len = php_stream_read(stream, ret_buf, tent_ret_len);
- if (skip) {
+ if (found_delim) {
stream->readpos += delim_len;
stream->position += delim_len;
}
- buf[*returned_len] = '\0';
- return buf;
+ ret_buf[*returned_len] = '\0';
+ return ret_buf;
}
/* Writes a buffer directly to a stream, using multiple of the chunk size */
--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php