]> granicus.if.org Git - php/commitdiff
Implement read buffering in streams.
authorWez Furlong <wez@php.net>
Mon, 23 Sep 2002 01:47:04 +0000 (01:47 +0000)
committerWez Furlong <wez@php.net>
Mon, 23 Sep 2002 01:47:04 +0000 (01:47 +0000)
Eliminate similar code from network.c.
Implement fgets equivalent at the streams level, which can detect
the mac, dos and unix line endings and handle them appropriately.
The default behaviour is unix (and dos) line endings.
An ini option to control this behaviour will follow.
# Don't forget to make clean!
# I've done some testing but would appreciate feedback from
# people with scripts/extensions that seek around a lot.

ext/curl/curlstreams.c
ext/standard/file.c
ext/standard/http_fopen_wrapper.c
ext/standard/php_fopen_wrapper.c
ext/zlib/zlib_fopen_wrapper.c
main/memory_streams.c
main/network.c
main/php_network.h
main/php_streams.h
main/streams.c
main/user_streams.c

index dcffdfaf5f774254fd4890fabce0953bbb2420c4..eaee95409dad498059087d18f393a3331d164bbe 100644 (file)
@@ -145,7 +145,8 @@ static size_t php_curl_stream_read(php_stream *stream, char *buf, size_t count T
                /* check for EOF */
 
                /* if we have buffered data, then we are not at EOF */
-               if (curlstream->readbuffer.writepos > 0 && curlstream->readbuffer.readpos < curlstream->readbuffer.writepos)
+               if (curlstream->readbuffer.writepos > 0
+                               && curlstream->readbuffer.readpos < curlstream->readbuffer.writepos)
                        return 0;
                
 
@@ -172,7 +173,8 @@ static size_t php_curl_stream_read(php_stream *stream, char *buf, size_t count T
                        tv.tv_sec = 15; /* TODO: allow this to be configured from the script */
 
                        /* wait for data */
-                       switch (select(curlstream->maxfd+1, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &tv)) {
+                       switch (select(curlstream->maxfd+1, &curlstream->readfds,
+                                               &curlstream->writefds, &curlstream->excfds, &tv)) {
                                case -1:
                                        /* error */
                                        return 0;
@@ -233,21 +235,6 @@ static int php_curl_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRM
        return -1;
 }
 
-static char *php_curl_stream_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC)
-{
-       php_curl_stream *curlstream = (php_curl_stream*)stream->abstract;
-       char *ret;
-       
-       php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.readpos, SEEK_SET);
-       ret = php_stream_gets(curlstream->readbuffer.buf, buf, size);
-       curlstream->readbuffer.readpos = php_stream_tell(curlstream->readbuffer.buf);
-
-       if (ret > 0)
-               return buf;
-
-       return NULL;
-}
-
 static int php_curl_stream_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
 {
        php_curl_stream *curlstream = (php_curl_stream*)stream->abstract;
@@ -262,7 +249,6 @@ PHPAPI php_stream_ops php_curl_stream_ops = {
        php_curl_stream_flush,
        "cURL",
        NULL, /* seek */
-       php_curl_stream_gets, /* gets */
        php_curl_stream_cast, /* cast */
        php_curl_stream_stat  /* stat */
 };
@@ -357,7 +343,24 @@ PHPAPI php_stream *php_curl_stream_opener(php_stream_wrapper *wrapper, char *fil
                
                curl_easy_perform(curlstream->curl);
        }
+       else
 #endif
+       {
+               /* fire up the connection; we need to detect a connection error here,
+                * otherwise the curlstream we return ends up doing nothing useful. */
+               CURLMcode m;
+
+               while (CURLM_CALL_MULTI_PERFORM == 
+                               (m = curl_multi_perform(curlstream->multi, &curlstream->pending))
+                         ) {
+                       ; /* spin */
+               }
+
+               if (m != CURLM_OK) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error mcode=%d", m);
+               }
+
+       }
        
        return stream;
 }
index 33eb58da95382a8faa15cbeb16b2a5db7594018a..544cea3b4f1a5e62e3356fd3b080008be0bad11f 100644 (file)
@@ -1094,26 +1094,25 @@ PHP_FUNCTION(socket_set_timeout)
 PHP_FUNCTION(socket_get_status)
 {
        zval **socket;
-       int type;
-       void *what;
+       php_stream *stream;
 
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &socket) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
 
-       what = zend_fetch_resource(socket TSRMLS_CC, -1, "File-Handle", &type, 1, le_stream);
-       ZEND_VERIFY_RESOURCE(what);
+       php_stream_from_zval(stream, socket);
+       
 
        array_init(return_value);
 
-       if (php_stream_is((php_stream*)what, PHP_STREAM_IS_SOCKET))     {
+       if (php_stream_is(stream, PHP_STREAM_IS_SOCKET))        {
 
-               php_netstream_data_t *sock = PHP_NETSTREAM_DATA_FROM_STREAM((php_stream*)what);
+               php_netstream_data_t *sock = PHP_NETSTREAM_DATA_FROM_STREAM(stream);
 
                add_assoc_bool(return_value, "timed_out", sock->timeout_event);
                add_assoc_bool(return_value, "blocked", sock->is_blocked);
                add_assoc_bool(return_value, "eof", sock->eof);
-               add_assoc_long(return_value, "unread_bytes", sock->writepos - sock->readpos);
+               add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
 
        }
        else    {
index 82cffcf8eceed2e97828925775a748a48d9a2795..5841cff75ca630985c6ea559a07ff70c191b4468 100644 (file)
@@ -388,6 +388,9 @@ out:
                php_stream_notify_progress_init(context, 0, file_size);
                if (options & STREAM_WILL_CAST)
                        php_stream_sock_set_chunk_size(stream, chunk_size TSRMLS_CC);
+               /* as far as streams are concerned, we are now at the start of
+                * the stream */
+               stream->position = 0;
        }
 
        if (response_header)    {
index 83dd2cca8077d3952878d884294eef1e461303e0..6f077a38131b47d15611a519f31d00a4b28c4258 100644 (file)
@@ -59,10 +59,10 @@ php_stream_ops php_stream_output_ops = {
        php_stream_output_close,
        php_stream_output_flush,
        "Output",
-       NULL,
-       NULL,
-       NULL,
-       NULL
+       NULL, /* seek */
+       NULL, /* cast */
+       NULL, /* stat */
+       NULL  /* set_option */
 };
 
 php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
index c90a77259f53c5fac9f540f7b8bb248557310de7..ee2222306d58e7365b6d138a647475f2f07c4d67 100644 (file)
@@ -42,28 +42,23 @@ static size_t php_gziop_read(php_stream *stream, char *buf, size_t count TSRMLS_
        return gzread(self->gz_file, buf, count); 
 }
 
-static char *php_gziop_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC)
-{
-       struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
-       return gzgets(self->gz_file, buf, size);
-}
-
-
 static size_t php_gziop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
 {
        struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
        return gzwrite(self->gz_file, (char*)buf, count); 
 }
 
-static int php_gziop_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+static int php_gziop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
 {
        struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
-
+       int ret;
+       
        assert(self != NULL);
-       if (offset == 0 && whence == SEEK_CUR)
-               return gztell(self->gz_file);
        
-       return gzseek(self->gz_file, offset, whence);
+       ret = gzseek(self->gz_file, offset, whence);
+       *newoffs = gztell(self->gz_file);
+       
+       return ret;
 }
 
 static int php_gziop_close(php_stream *stream, int close_handle TSRMLS_DC)
@@ -89,8 +84,10 @@ php_stream_ops php_stream_gzio_ops = {
        php_gziop_write, php_gziop_read,
        php_gziop_close, php_gziop_flush,
        "ZLIB",
-       php_gziop_seek, php_gziop_gets,
-       NULL, NULL
+       php_gziop_seek, 
+       NULL, /* cast */
+       NULL, /* stat */
+       NULL  /* set_option */
 };
 
 php_stream *php_stream_gzopen(php_stream_wrapper *wrapper, char *path, char *mode,
index 1af7bb9eeb5f1c391d4ff53247576a5f9da3ab27..e48d5faa2149dfd58842ac42e8bb284338bca406 100644 (file)
@@ -138,7 +138,7 @@ static int php_stream_memory_flush(php_stream *stream TSRMLS_DC) {
 
 
 /* {{{ */
-static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
 {
        php_stream_memory_data *ms;
 
@@ -146,8 +146,6 @@ static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence T
        ms = stream->abstract;
        assert(ms != NULL);
 
-       if (offset == 0 && whence == SEEK_CUR)
-               return ms->fpos;
        switch(whence) {
                case SEEK_CUR:
                        if (offset < 0) {
@@ -165,6 +163,7 @@ static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence T
                                        ms->fpos = ms->fpos + offset;
                                }
                        }
+                       *newoffs = ms->fpos;
                        return 0;
                case SEEK_SET:
                        if (ms->fsize < (size_t)(offset)) {
@@ -173,6 +172,7 @@ static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence T
                        } else {
                                ms->fpos = offset;
                        }
+                       *newoffs = ms->fpos;
                        return 0;
                case SEEK_END:
                        if (offset > 0) {
@@ -184,6 +184,7 @@ static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence T
                        } else {
                                ms->fpos = ms->fsize + offset;
                        }
+                       *newoffs = ms->fpos;
                        return 0;
                default:
                        return 0;
@@ -192,35 +193,6 @@ static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence T
 }
 /* }}} */
 
-
-/* {{{ */
-static char *php_stream_memory_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
-{
-       size_t n = 1;
-       char *c = buf;
-
-       php_stream_memory_data *ms;
-
-       assert(stream != NULL);
-       ms = stream->abstract;
-       assert(ms != NULL);
-       assert(buf!= NULL);
-       assert(ms->data!= NULL);
-
-       while(n < maxlen && ms->fpos<ms->fsize) {
-               n++;
-               if ((*c = ms->data[ms->fpos++]) == '\n') {
-                       c++;
-                       break;
-               }
-               c++;
-       }
-       *c = 0;
-       return buf;
-}
-/* }}} */
-
-
 /* {{{ */
 static int php_stream_memory_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
 {
@@ -234,9 +206,9 @@ php_stream_ops      php_stream_memory_ops = {
        php_stream_memory_close, php_stream_memory_flush,
        "MEMORY",
        php_stream_memory_seek,
-       php_stream_memory_gets,
        php_stream_memory_cast,
-       NULL
+       NULL, /* stat */
+       NULL  /* set_option */
 };
 
 
@@ -380,33 +352,22 @@ static int php_stream_temp_flush(php_stream *stream TSRMLS_DC)
 
 
 /* {{{ */
-static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
-{
-       php_stream_temp_data *ts;
-
-       assert(stream != NULL);
-       ts = stream->abstract;
-       assert(ts != NULL);
-
-       return php_stream_seek(ts->innerstream, offset, whence);
-}
-/* }}} */
-
-
-/* {{{ */
-char *php_stream_temp_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
+static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
 {
        php_stream_temp_data *ts;
+       int ret;
 
        assert(stream != NULL);
        ts = stream->abstract;
        assert(ts != NULL);
 
-       return php_stream_gets(ts->innerstream, buf, maxlen);
+       ret = php_stream_seek(ts->innerstream, offset, whence);
+       *newoffs = php_stream_tell(ts->innerstream);
+       
+       return ret;
 }
 /* }}} */
 
-
 /* {{{ */
 static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
 {
@@ -457,9 +418,9 @@ php_stream_ops      php_stream_temp_ops = {
        php_stream_temp_close, php_stream_temp_flush,
        "TEMP",
        php_stream_temp_seek,
-       php_stream_temp_gets,
        php_stream_temp_cast,
-       NULL
+       NULL, /* stat */
+       NULL /* set_option */
 };
 
 
index 22d7a2a95a31dc647830e23f34a2259b743f98b6..4f51d6cb1659d88ffd9caae525da51a40f071227 100644 (file)
@@ -540,7 +540,6 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, int persistent
        memset(sock, 0, sizeof(php_netstream_data_t));
 
        sock->is_blocked = 1;
-       sock->chunk_size = FG(def_chunk_size);
        sock->timeout.tv_sec = SOCKET_DEFAULT_TIMEOUT;
        sock->socket = socket;
 
@@ -697,7 +696,7 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
       if (!block) {
               flags |= myflag;
       } else {
-              flags &= ~myflag;
+               flags &= ~myflag;
       }
       fcntl(socketd, F_SETFL, flags);
 #endif
@@ -707,13 +706,9 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
 PHPAPI size_t php_stream_sock_set_chunk_size(php_stream *stream, size_t size TSRMLS_DC)
 {
        size_t oldsize;
-       php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
 
-       if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET))
-               return 0;
-       
-       oldsize = sock->chunk_size;
-       sock->chunk_size = size;
+       oldsize = stream->chunk_size;
+       stream->chunk_size = size;
 
        return oldsize;
 }
@@ -782,105 +777,18 @@ DUMP_SOCK_STATE("wait_for_data", sock);
 DUMP_SOCK_STATE("wait_for_data: done", sock);
 }
 
-
-static size_t php_sock_stream_read_internal(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
-{
-       char buf[PHP_SOCK_CHUNK_SIZE];
-       int nr_bytes;
-       size_t nr_read = 0;
-       
-       /* For blocking sockets, we wait until there is some
-          data to read (real data or EOF)
-
-          Otherwise, recv() may time out and return 0 and
-          therefore sock->eof would be set errornously.
-        */
-
-DUMP_SOCK_STATE("read_internal entry", sock);
-
-       if(sock->is_blocked) {
-               php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
-               if (sock->timeout_event)
-                       return 0;
-       }
-
-DUMP_SOCK_STATE("read_internal about to recv/SSL_read", sock);
-
-       /* read at a maximum sock->chunk_size */
-#if HAVE_OPENSSL_EXT
-       if (sock->ssl_active)
-               nr_bytes = SSL_read(sock->ssl_handle, buf, sock->chunk_size);
-       else
-#endif
-       nr_bytes = recv(sock->socket, buf, sock->chunk_size, 0);
-DUMP_SOCK_STATE("read_internal after recv/SSL_read", sock);
-
-#if DEBUG_MAIN_NETWORK
-printf("read_internal read %d/%d bytes\n", nr_bytes, sock->chunk_size);
-#endif
-
-       if(nr_bytes > 0) {
-
-               php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
-               
-               /* try to avoid an ever-expanding buffer */
-               if (sock->readpos > 0) {
-                       memmove(sock->readbuf, READPTR(sock), sock->readbuflen - sock->readpos);
-                       sock->writepos -= sock->readpos;
-                       sock->readpos = 0;
-               }
-               
-               if(sock->writepos + nr_bytes > sock->readbuflen) {
-                       sock->readbuflen += sock->chunk_size;
-                       sock->readbuf = perealloc(sock->readbuf, sock->readbuflen,
-                                       php_stream_is_persistent(stream));
-               }
-               memcpy(WRITEPTR(sock), buf, nr_bytes);
-               sock->writepos += nr_bytes;
-               nr_read = nr_bytes;
-       } else if(nr_bytes == 0 || (nr_bytes < 0 && streams_socket_errno != EWOULDBLOCK)) {
-               sock->eof = 1;
-       }
-
-       return nr_read;
-
-}
-
-static size_t php_sock_stream_read(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
-{
-       size_t nr_bytes;
-       size_t nr_read = 0;
-       int i;
-
-       for(i = 0; !sock->eof && i < MAX_CHUNKS_PER_READ; i++) {
-DUMP_SOCK_STATE("read about to read_internal", sock);
-               nr_bytes = php_sock_stream_read_internal(stream, sock TSRMLS_CC);
-               if(nr_bytes == 0)
-                       break;
-               
-               
-               nr_read += nr_bytes;
-       }
-
-       return nr_read;
-}
-
 static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
 {
        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
-       size_t ret = 0;
+       size_t nr_bytes = 0;
 
        if (buf == NULL && count == 0) {
                /* check for EOF condition */
-               int save_blocked;
                
 DUMP_SOCK_STATE("check for EOF", sock);
                
                if (sock->eof)
                        return EOF;
-               
-               if (TOREAD(sock))
-                       return 0;
 
                /* no data in the buffer - lets examine the socket */
 #if HAVE_SYS_POLL_H && HAVE_POLL
@@ -896,45 +804,34 @@ DUMP_SOCK_STATE("check for EOF", sock);
                        }
                }
 #endif
-               
-               /* in the absence of other methods of checking if the
-                * socket is still active, try to read a chunk of data,
-                * but lets not block. */
-               sock->timeout_event = 0;
-               save_blocked = php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0, NULL);
-               php_sock_stream_read_internal(stream, sock TSRMLS_CC);
-               php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, save_blocked, NULL);
-               
-               if (sock->eof)
-                       return EOF;
-               
+               /* presume that we are not yet at the eof */
                return 0;
        }
-       
-       if (sock->is_blocked) {
-               sock->timeout_event = 0;
-               while(!sock->eof && TOREAD(sock) < count && !sock->timeout_event)
-                       if (php_sock_stream_read_internal(stream, sock TSRMLS_CC) == 0)
-                               break;
-       } else {
-               php_sock_stream_read(stream, sock TSRMLS_CC);
+
+       if(sock->is_blocked) {
+               php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
+               if (sock->timeout_event)
+                       return 0;
        }
 
-       if(count < 0)
-               return ret;
+#if HAVE_OPENSSL_EXT
+       if (sock->ssl_active)
+               nr_bytes = SSL_read(sock->ssl_handle, buf, count);
+       else
+#endif
+
+       nr_bytes = recv(sock->socket, buf, count, 0);
+
+       php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
 
-       ret = MIN(TOREAD(sock), count);
-       if (ret) {
-               memcpy(buf, READPTR(sock), ret);
-               sock->readpos += ret;
+       if(nr_bytes == 0 || (nr_bytes < 0 && streams_socket_errno != EWOULDBLOCK)) {
+               sock->eof = 1;
        }
-#if DEBUG_MAIN_NETWORK
-       DUMP_SOCK_STATE("sockop_read", sock);
-       printf("sockop_read returning with %d bytes read\n", ret);
-#endif
-       return ret;
+
+       return nr_bytes;
 }
 
+
 static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
 {
        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
@@ -955,8 +852,6 @@ static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
                closesocket(sock->socket);
 
        }
-       if (sock->readbuf)
-               pefree(sock->readbuf, php_stream_is_persistent(stream));
 
        pefree(sock, php_stream_is_persistent(stream));
        
@@ -1012,9 +907,6 @@ static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                                return FAILURE;
 #endif
                        if (ret)        {
-                               /* DANGER!: data buffered in stream->readbuf will be forgotten! */
-                               if (TOREAD(sock) > 0)
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d bytes of buffered data lost during conversion to FILE*!", TOREAD(sock));
                                *ret = fdopen(sock->socket, stream->mode);
                                if (*ret)
                                        return SUCCESS;
@@ -1035,75 +927,11 @@ static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
        }
 }
 
-#define SEARCHCR() do {                                                                                                \
-       if (TOREAD(sock)) {                                                                                             \
-               for (p = READPTR(sock), pe = p + MIN(TOREAD(sock), maxlen);     \
-                               *p != '\n'; )                                                                           \
-                       if (++p >= pe) {                                                                                \
-                               p = NULL;                                                                                       \
-                               break;                                                                                          \
-                       }                                                                                                               \
-       } else                                                                                                                  \
-               p = NULL;                                                                                                       \
-} while (0)
-
-
-static char *php_sockop_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
-{
-       php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
-       char *p = NULL, *pe;
-       char *ret = NULL;
-       size_t amount = 0;
-
-       if (maxlen==0) {
-               buf[0] = 0;
-               return buf;
-       }
-
-       SEARCHCR();
-
-       if(!p) {
-               if(sock->is_blocked) {
-                       sock->timeout_event = 0;
-                       
-                       while(!p && !sock->eof && !sock->timeout_event && TOREAD(sock) < maxlen) {
-                               php_sock_stream_read_internal(stream, sock TSRMLS_CC);
-                               SEARCHCR();
-                       }
-               } else {
-                       php_sock_stream_read(stream, sock TSRMLS_CC);
-                       SEARCHCR();
-               }
-       }
-
-       if(p) {
-               amount = (ptrdiff_t) p - (ptrdiff_t) READPTR(sock) + 1;
-       } else {
-               amount = TOREAD(sock);
-       }
-
-       amount = MIN(amount, maxlen);
-
-       if(amount > 0) {
-               memcpy(buf, READPTR(sock), amount);
-               sock->readpos += amount;
-       }
-       buf[amount] = '\0';
-
-       /* signal error only, if we don't return data from this call and
-          if there is no data to read and if the eof flag is set */
-       if(amount || TOREAD(sock) || !sock->eof) {
-               ret = buf;
-       }
-
-       return ret;
-}
-
 php_stream_ops php_stream_socket_ops = {
        php_sockop_write, php_sockop_read,
        php_sockop_close, php_sockop_flush,
        "socket",
-       NULL, php_sockop_gets,
+       NULL, /* seek */
        php_sockop_cast,
        php_sockop_stat,
        php_sockop_set_option
index 5279377986670f9f039c7a1bc26ed45fe5df6206..ffb23cc3fa8c98e74ae1356a5ba92d833672b0a7 100644 (file)
@@ -103,13 +103,8 @@ int php_sockaddr_size(php_sockaddr_storage *addr);
 
 struct _php_netstream_data_t   {
        int socket;
-       unsigned char *readbuf;
-       size_t readbuflen;
-       size_t readpos;
-       size_t writepos;
        char eof;
        char is_blocked;
-       size_t chunk_size;
        struct timeval timeout;
        char timeout_event;
 #if HAVE_OPENSSL_EXT
index fa57b5aff34685e72d05fbe30e2094d9d44b6be5..5cd12839f0dbdf8ad9319f8b6d73ab64113720df 100755 (executable)
@@ -148,8 +148,7 @@ typedef struct _php_stream_ops  {
        const char *label; /* label for this ops structure */
        
        /* these are optional */
-       int    (*seek)(php_stream *stream, off_t offset, int whence TSRMLS_DC);
-       char *(*gets)(php_stream *stream, char *buf, size_t size TSRMLS_DC);
+       int (*seek)(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
        int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC);
        int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
        int (*set_option)(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
@@ -217,7 +216,12 @@ struct _php_stream_filter {
        (thisfilter)->next ? (thisfilter)->next->fops->eof((stream), (thisfilter) TSRMLS_CC) \
        : (stream)->ops->read((stream), NULL, 0 TSRMLS_CC) == EOF ? 1 : 0
 
+#define PHP_STREAM_FLAG_NO_SEEK                                                1
+#define PHP_STREAM_FLAG_NO_BUFFER                                      2
 
+#define PHP_STREAM_FLAG_EOL_UNIX                                       0 /* also includes DOS */
+#define PHP_STREAM_FLAG_DETECT_EOL                                     4
+#define PHP_STREAM_FLAG_EOL_MAC                                                8
        
 struct _php_stream  {
        php_stream_ops *ops;
@@ -245,6 +249,17 @@ struct _php_stream  {
 #endif
 
        php_stream_context *context;
+       int flags;      /* PHP_STREAM_FLAG_XXX */
+
+       /* buffer */
+       off_t position; /* of underlying stream */
+       unsigned char *readbuf;
+       size_t readbuflen;
+       size_t readpos;
+       size_t writepos;
+       
+       /* how much data to read when filling buffer */
+       size_t chunk_size;
 
 }; /* php_stream */
 /* state definitions when closing down; these are private to streams.c */
index 26462c6f428a74807f45619925c90dd968a856f0..e81d1bd305098a61703c8766d2714d72a4df36cd 100755 (executable)
@@ -48,6 +48,7 @@
 #endif
 
 #define STREAM_DEBUG 0
+#define SANITY_CHECK_SEEK 1
 
 #define STREAM_WRAPPER_PLAIN_FILES     ((php_stream_wrapper*)-1)
 
@@ -91,6 +92,7 @@ fprintf(stderr, "stream_alloc: %s:%p\n", ops->label, ret);
        ret->ops = ops;
        ret->abstract = abstract;
        ret->is_persistent = persistent;
+       ret->chunk_size = FG(def_chunk_size);
        ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, php_file_le_stream());
        strlcpy(ret->mode, mode, sizeof(ret->mode));
 
@@ -156,6 +158,11 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label,
                        stream->wrapperdata = NULL;
                }
 
+               if (stream->readbuf) {
+                       pefree(stream->readbuf, stream->is_persistent);
+                       stream->readbuf = NULL;
+               }
+               
 #if ZEND_DEBUG
                if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
                        /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
@@ -184,6 +191,7 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label,
 }
 /* }}} */
 
+/* {{{ filter API */
 static HashTable stream_filters_hash;
 
 PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
@@ -303,22 +311,76 @@ PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_strea
        }
        return filter;
 }
+/* }}} */
 
 /* {{{ generic stream operations */
-PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
+
+static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC)
 {
-       if (stream->filterhead)
-               return stream->filterhead->fops->read(stream, stream->filterhead, buf, size TSRMLS_CC);
+       /* allocate/fill the buffer */
        
-       return stream->ops->read(stream, buf, size TSRMLS_CC);
+       /* is there enough data in the buffer ? */
+       while (stream->writepos - stream->readpos < size) {
+               size_t justread;
+               
+               /* no; so lets fetch more data */
+               
+               /* reduce buffer memory consumption if possible, to avoid a realloc */
+               if (stream->readbuflen - stream->writepos < stream->chunk_size) {
+                       memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos);
+                       stream->writepos -= stream->readpos;
+                       stream->readpos = 0;
+               }
+               
+               /* grow the buffer if required */
+               if (stream->readbuflen - stream->writepos < stream->chunk_size) {
+                       stream->readbuflen += stream->chunk_size;
+                       stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
+                                       stream->is_persistent);
+               }
+               
+               if (stream->filterhead) {
+                       justread = stream->filterhead->fops->read(stream, stream->filterhead,
+                                       stream->readbuf + stream->writepos,
+                                       stream->readbuflen - stream->writepos
+                                       TSRMLS_CC);
+               } else {
+                       justread = stream->ops->read(stream, stream->readbuf + stream->writepos,
+                                       stream->readbuflen - stream->writepos
+                                       TSRMLS_CC);
+               }
+               if (justread == 0)
+                       break;
+               stream->writepos += justread;
+       }
+}
+
+PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
+{
+       if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1) {
+               return stream->ops->read(stream, buf, size TSRMLS_CC);
+       } else {
+               php_stream_fill_read_buffer(stream, size TSRMLS_CC);
+
+               if (size > stream->writepos - stream->readpos)
+                       size = stream->writepos - stream->readpos;
+
+               memcpy(buf, stream->readbuf + stream->readpos, size);
+               stream->readpos += size;
+               stream->position += size;
+               return size;
+       }
 }
 
 PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
 {
+       /* if there is data in the buffer, it's not EOF */
+       if (stream->writepos - stream->readpos > 0)
+               return 0;
+
        /* we define our stream reading function so that it
           must return EOF when an EOF condition occurs, when
           working in unbuffered mode and called with these args */
-
        if (stream->filterhead)
                return stream->filterhead->fops->eof(stream, stream->filterhead TSRMLS_CC);
        
@@ -379,70 +441,151 @@ PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_D
 
 PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
 {
+       char *cr, *lf, *eol;
+       size_t toread = 0, didread = 0, justread = 0, avail = 0;
+       char *readptr;
+       
        if (maxlen == 0)
                return NULL;
+       
+       while (didread < maxlen - 1) {
+               toread = maxlen - 1;
+               if (toread > stream->chunk_size)
+                       toread = stream->chunk_size;
 
-       if (stream->filterhead || stream->ops->gets == NULL) {
-               /* unbuffered fgets - performance not so good! */
-               char *c = buf;
+               php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
 
-               /* TODO: look at error returns? */
+               readptr = stream->readbuf + stream->readpos;
+               avail = stream->writepos - stream->readpos;
 
-               while (--maxlen > 0 && php_stream_read(stream, buf, 1) == 1 && *buf++ != '\n')
-                       ;
-               *buf = '\0';
+               if (avail == 0)
+                       break;
+               
+               /* Look for EOL */
+               if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
+                       cr = memchr(readptr, '\r', avail);
+                       lf = memchr(readptr, '\n', avail);
+
+                       if (cr && lf != cr + 1) {
+                               /* mac */
+                               stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
+                               stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
+                               eol = cr;
+                       } else if ((cr && lf && cr == lf - 1) || (lf)) {
+                               /* dos or unix endings */
+                               stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
+                               eol = lf;
+                       }
+               } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
+                       eol = memchr(readptr, '\r', avail);
+               } else {
+                       /* unix (and dos) line endings */
+                       eol = memchr(readptr, '\n', avail);
+               }
 
-               return c == buf && maxlen > 0 ? NULL : c;
+               if (eol && (eol + 1 - readptr) <= maxlen - 1) {
+                       justread = eol + 1 - readptr;
+               } else {
+                       eol = NULL;
+                       justread = toread;
+                       if (justread > avail)
+                               justread = avail;
+               }
+
+               memcpy(buf, readptr, justread);
+               didread += justread;
+               buf += justread;
+               stream->readpos += justread;
 
-       } else if (stream->ops->gets) {
-               return stream->ops->gets(stream, buf, maxlen TSRMLS_CC);
+               if (eol)
+                       break;
        }
-       /* should not happen */
-       return NULL;
+
+       if (didread == 0)
+               return NULL;
+       
+       /* terminate the buffer */
+       *buf = '\0';
+       stream->position += didread;
+       
+       return buf;
 }
 
 PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC)
 {
+       int ret;
+       
        if (stream->filterhead)
                stream->filterhead->fops->flush(stream, stream->filterhead, closing TSRMLS_CC);
-
+       
        if (stream->ops->flush) {
-               return stream->ops->flush(stream TSRMLS_CC);
+               ret = stream->ops->flush(stream TSRMLS_CC);
        }
-       return 0;
+       
+       return ret;
 }
 
 PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
 {
+       size_t didwrite;
+       
        assert(stream);
        if (buf == NULL || count == 0 || stream->ops->write == NULL)
                return 0;
 
        if (stream->filterhead) {
-               return stream->filterhead->fops->write(stream, stream->filterhead, buf, count TSRMLS_CC);
+               didwrite = stream->filterhead->fops->write(stream, stream->filterhead, buf, count TSRMLS_CC);
        } else {
-               return stream->ops->write(stream, buf, count TSRMLS_CC);
+               didwrite = stream->ops->write(stream, buf, count TSRMLS_CC);
        }
+       stream->position += didwrite;
+       return didwrite;
 }
 
 PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC)
 {
-       off_t ret = -1;
-
-       if (stream->ops->seek) {
-               ret = stream->ops->seek(stream, 0, SEEK_CUR TSRMLS_CC);
-       }
-       return ret;
+       return stream->position;
 }
 
 PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
 {
-       if (stream->ops->seek) {
+       /* not moving anywhere */
+       if (offset == 0 && whence == SEEK_CUR)
+               return 0;
 
+       /* handle the case where we are in the buffer */
+       switch(whence) {
+               case SEEK_CUR:
+                       if (offset > 0 && offset < stream->writepos - stream->readpos) {
+                               stream->readpos += offset;
+                               stream->position += offset;
+                               return 0;
+                       }
+                       break;
+               case SEEK_SET:
+                       if (offset > stream->position &&
+                                       offset < stream->position + stream->writepos - stream->readpos) {
+                               stream->readpos += offset - stream->position;
+                               stream->position = offset;
+                               return 0;
+                       }
+                       break;
+       }
+       
+       /* invalidate the buffer contents */
+       stream->readpos = stream->writepos = 0;
+
+       if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
                if (stream->filterhead)
                        stream->filterhead->fops->flush(stream, stream->filterhead, 0 TSRMLS_CC);
                
-               return stream->ops->seek(stream, offset, whence TSRMLS_CC);
+               switch(whence) {
+                       case SEEK_CUR:
+                               offset = stream->position + offset;
+                               whence = SEEK_SET;
+                               break;
+               }
+               return stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
        }
 
        /* emulate forward moving seeks with reads */
@@ -461,6 +604,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_
        }
 
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "streams of type %s do not support seeking", stream->ops->label);
+
        return -1;
 }
 
@@ -855,9 +999,10 @@ static int php_stdiop_flush(php_stream *stream TSRMLS_DC)
        return fflush(data->file);
 }
 
-static int php_stdiop_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
 {
        php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
+       int ret;
 
        assert(data != NULL);
 
@@ -866,26 +1011,11 @@ static int php_stdiop_seek(php_stream *stream, off_t offset, int whence TSRMLS_D
                return -1;
        }
        
-       if (offset == 0 && whence == SEEK_CUR)
-               return ftell(data->file);
-
-       return fseek(data->file, offset, whence);
+       ret = fseek(data->file, offset, whence);
+       *newoffset = ftell(data->file);
+       return ret;
 }
 
-static char *php_stdiop_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC)
-{
-       php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
-
-       assert(data != NULL);
-#if HAVE_FLUSHIO
-       if (!data->is_pipe && data->last_op == 'w') {
-               fseek(data->file, 0, SEEK_CUR);
-       }
-       data->last_op = 'r';
-#endif
-
-       return fgets(buf, size, data->file);
-}
 static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
 {
        int fd;
@@ -989,7 +1119,7 @@ PHPAPI php_stream_ops      php_stream_stdio_ops = {
        php_stdiop_close, php_stdiop_flush,
        "STDIO",
        php_stdiop_seek,
-       php_stdiop_gets, php_stdiop_cast,
+       php_stdiop_cast,
        php_stdiop_stat,
        php_stdiop_set_option
 };
@@ -1224,6 +1354,17 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show
        int flags = castas & PHP_STREAM_CAST_MASK;
        castas &= ~PHP_STREAM_CAST_MASK;
 
+       /* synchronize our buffer (if possible) */
+       if (ret) {
+               php_stream_flush(stream);
+               if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
+                       off_t dummy;
+
+                       stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC);
+                       stream->readpos = stream->writepos = 0;
+               }
+       }
+       
        /* filtered streams can only be cast as stdio, and only when fopencookie is present */
        
        if (castas == PHP_STREAM_AS_STDIO) {
@@ -1299,6 +1440,17 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show
        return FAILURE;
 
 exit_success:
+
+       if (stream->writepos && stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE) {
+               /* the data we have buffered will be lost to the third party library that
+                * will be accessing the stream.  Emit a warning so that the end-user will
+                * know that they should try something else */
+               
+               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                               "%d bytes of buffered data lost during conversion to FILE*!",
+                               stream->writepos - stream->readpos);
+       }
+       
        if (castas == PHP_STREAM_AS_STDIO && ret)
                stream->stdiocast = *ret;
        
@@ -1369,7 +1521,7 @@ static int php_plain_files_dirstream_close(php_stream *stream, int close_handle
        return closedir((DIR *)stream->abstract);
 }
 
-static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
 {
        rewinddir((DIR *)stream->abstract);
        return 0;
@@ -1380,8 +1532,9 @@ static php_stream_ops     php_plain_files_dirstream_ops = {
        php_plain_files_dirstream_close, NULL,
        "dir",
        php_plain_files_dirstream_rewind,
-       NULL, NULL,
-       NULL
+       NULL, /* cast */
+       NULL, /* stat */
+       NULL  /* set_option */
 };
 
 static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char *path, char *mode,
@@ -1548,8 +1701,10 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options,
                                path_to_open, "r", options, NULL,
                                context STREAMS_REL_CC TSRMLS_CC);
 
-               if (stream)
+               if (stream) {
                        stream->wrapper = wrapper;
+                       stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
+               }
        }
 
        if (stream == NULL && (options & REPORT_ERRORS)) {
@@ -1571,6 +1726,7 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options,
 
 PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC)
 {
+
        if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent)))
                return ent;
        
index bfe81b917ad036c070b68d6c2910991842513ef2..248e0a35482e113855c3280b7d391dd01b75ef11 100644 (file)
@@ -80,7 +80,6 @@ typedef struct _php_userstream_data php_userstream_data_t;
 #define USERSTREAM_WRITE       "stream_write"
 #define USERSTREAM_FLUSH       "stream_flush"
 #define USERSTREAM_SEEK                "stream_seek"
-#define USERSTREAM_GETS                "stream_gets"
 #define USERSTREAM_TELL                "stream_tell"
 #define USERSTREAM_EOF         "stream_eof"
 
@@ -109,13 +108,8 @@ function stream_open($path, $mode, $options, &$opened_path)
    function stream_seek($offset, $whence)
    {
    }
-   function stream_gets($size)
-   {
-      return false on error;
-      else return string;
-   }
-   
- * */
+  
+ **/
 
 static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
 {
@@ -405,108 +399,56 @@ static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
        return call_result;
 }
 
-static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
 {
        zval func_name;
        zval *retval = NULL;
-       int call_result;
+       int call_result, ret;
        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
+       zval **args[2];
+       zval *zoffs, *zwhence;
 
        assert(us != NULL);
 
-       if (offset == 0 && whence == SEEK_CUR) {
-               ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0);
+       ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0);
 
-               call_result = call_user_function_ex(NULL,
+       MAKE_STD_ZVAL(zoffs);
+       ZVAL_LONG(zoffs, offset);
+       args[0] = &zoffs;
+
+       MAKE_STD_ZVAL(zwhence);
+       ZVAL_LONG(zwhence, whence);
+       args[1] = &zwhence;
+
+       call_result = call_user_function_ex(NULL,
                        &us->object,
                        &func_name,
                        &retval,
-                       0, NULL, 0, NULL TSRMLS_CC);
-
-
-       } else {
-               zval **args[2];
-               zval *zoffs, *zwhence;
-
-               ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0);
-
-               MAKE_STD_ZVAL(zoffs);
-               ZVAL_LONG(zoffs, offset);
-               args[0] = &zoffs;
-
-               MAKE_STD_ZVAL(zwhence);
-               ZVAL_LONG(zwhence, whence);
-               args[1] = &zwhence;
-
-               call_result = call_user_function_ex(NULL,
-                               &us->object,
-                               &func_name,
-                               &retval,
-                               2, args,
-                               0, NULL TSRMLS_CC);
-
-               zval_ptr_dtor(&zoffs);
-               zval_ptr_dtor(&zwhence);
+                       2, args,
+                       0, NULL TSRMLS_CC);
 
-       }
+       zval_ptr_dtor(&zoffs);
+       zval_ptr_dtor(&zwhence);
 
        if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG)
-               call_result = Z_LVAL_P(retval);
+               ret = Z_LVAL_P(retval);
        else
-               call_result = -1;
-       
-       if (retval)
-               zval_ptr_dtor(&retval);
+               ret = -1;
 
-       return 0;
-}
-
-static char *php_userstreamop_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC)
-{
-       zval func_name;
-       zval *retval = NULL;
-       zval *zcount;
-       zval **args[2];
-       size_t didread = 0;
-       php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
-       int call_result;
-
-       assert(us != NULL);
-
-       /* TODO: when the gets method is not available, fall back on emulated version using read */
-       
-       ZVAL_STRINGL(&func_name, USERSTREAM_GETS, sizeof(USERSTREAM_GETS)-1, 0);
-
-       MAKE_STD_ZVAL(zcount);
-       ZVAL_LONG(zcount, size);
-       args[0] = &zcount;
+       /* now determine where we are */
+       ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0);
 
        call_result = call_user_function_ex(NULL,
-                       &us->object,
-                       &func_name,
-                       &retval,
-                       1, args,
-                       0, NULL TSRMLS_CC);
-
-       if (retval && call_result == SUCCESS) {
-               convert_to_string_ex(&retval);
-               didread = Z_STRLEN_P(retval);
-               if (didread > size) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_GETS " - read more data than requested; some data will be lost",
-                                       us->wrapper->classname);
-                       didread = size;
-               }
-               if (didread > 0)
-                       memcpy(buf, Z_STRVAL_P(retval), didread);
-
-               zval_ptr_dtor(&retval);
-       }
+               &us->object,
+               &func_name,
+               &retval,
+               0, NULL, 0, NULL TSRMLS_CC);
 
+       if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG)
+               *newoffs = Z_LVAL_P(retval);
+       
        if (retval)
-               zval_ptr_dtor(&zcount);
-
-       if (didread)
-               return buf;
+               zval_ptr_dtor(&retval);
 
        return 0;
 }
@@ -515,9 +457,10 @@ php_stream_ops php_stream_userspace_ops = {
        php_userstreamop_write, php_userstreamop_read,
        php_userstreamop_close, php_userstreamop_flush,
        "user-space",
-       php_userstreamop_seek, php_userstreamop_gets,
+       php_userstreamop_seek,
        NULL, /* cast */
-       NULL /* stat */
+       NULL, /* stat */
+       NULL  /* set_option */
 };