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

Reply via email to