wez Sun Sep 22 21:47:05 2002 EDT Modified files: /php4/ext/curl curlstreams.c /php4/ext/standard file.c http_fopen_wrapper.c php_fopen_wrapper.c /php4/ext/zlib zlib_fopen_wrapper.c /php4/ext/bz2 bz2.c /php4/main memory_streams.c network.c php_network.h php_streams.h streams.c user_streams.c Log: Implement read buffering in streams. 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.
Index: php4/ext/curl/curlstreams.c diff -u php4/ext/curl/curlstreams.c:1.1 php4/ext/curl/curlstreams.c:1.2 --- php4/ext/curl/curlstreams.c:1.1 Mon Aug 12 11:56:13 2002 +++ php4/ext/curl/curlstreams.c Sun Sep 22 21:47:00 2002 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: curlstreams.c,v 1.1 2002/08/12 15:56:13 wez Exp $ */ +/* $Id: curlstreams.c,v 1.2 2002/09/23 01:47:00 wez Exp $ */ /* This file implements cURL based wrappers. * NOTE: If you are implementing your own streams that are intended to @@ -145,7 +145,8 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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: php4/ext/standard/file.c diff -u php4/ext/standard/file.c:1.254 php4/ext/standard/file.c:1.255 --- php4/ext/standard/file.c:1.254 Fri Sep 13 09:34:06 2002 +++ php4/ext/standard/file.c Sun Sep 22 21:47:01 2002 @@ -21,7 +21,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: file.c,v 1.254 2002/09/13 13:34:06 iliaa Exp $ */ +/* $Id: file.c,v 1.255 2002/09/23 01:47:01 wez Exp $ */ /* Synced with php 3.0 revision 1.218 1999-06-16 [ssb] */ @@ -1094,26 +1094,25 @@ 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: php4/ext/standard/http_fopen_wrapper.c diff -u php4/ext/standard/http_fopen_wrapper.c:1.47 php4/ext/standard/http_fopen_wrapper.c:1.48 --- php4/ext/standard/http_fopen_wrapper.c:1.47 Sat Sep 14 08:09:47 2002 +++ php4/ext/standard/http_fopen_wrapper.c Sun Sep 22 21:47:01 2002 @@ -18,7 +18,7 @@ | Wez Furlong <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: http_fopen_wrapper.c,v 1.47 2002/09/14 12:09:47 jan Exp $ */ +/* $Id: http_fopen_wrapper.c,v 1.48 2002/09/23 01:47:01 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -388,6 +388,9 @@ 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: php4/ext/standard/php_fopen_wrapper.c diff -u php4/ext/standard/php_fopen_wrapper.c:1.23 php4/ext/standard/php_fopen_wrapper.c:1.24 --- php4/ext/standard/php_fopen_wrapper.c:1.23 Sat Sep 7 16:54:14 2002 +++ php4/ext/standard/php_fopen_wrapper.c Sun Sep 22 21:47:01 2002 @@ -17,7 +17,7 @@ | Hartmut Holzgraefe <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: php_fopen_wrapper.c,v 1.23 2002/09/07 20:54:14 wez Exp $ */ +/* $Id: php_fopen_wrapper.c,v 1.24 2002/09/23 01:47:01 wez Exp $ */ #include <stdio.h> #include <stdlib.h> @@ -59,10 +59,10 @@ 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: php4/ext/zlib/zlib_fopen_wrapper.c diff -u php4/ext/zlib/zlib_fopen_wrapper.c:1.28 php4/ext/zlib/zlib_fopen_wrapper.c:1.29 --- php4/ext/zlib/zlib_fopen_wrapper.c:1.28 Thu Aug 22 13:40:25 2002 +++ php4/ext/zlib/zlib_fopen_wrapper.c Sun Sep 22 21:47:01 2002 @@ -16,7 +16,7 @@ | Hartmut Holzgraefe <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: zlib_fopen_wrapper.c,v 1.28 2002/08/22 17:40:25 wez Exp $ */ +/* $Id: zlib_fopen_wrapper.c,v 1.29 2002/09/23 01:47:01 wez Exp $ */ #define IS_EXT_MODULE #define _GNU_SOURCE @@ -42,28 +42,23 @@ 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_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: php4/ext/bz2/bz2.c diff -u php4/ext/bz2/bz2.c:1.57 php4/ext/bz2/bz2.c:1.58 --- php4/ext/bz2/bz2.c:1.57 Thu Aug 29 07:45:27 2002 +++ php4/ext/bz2/bz2.c Sun Sep 22 21:47:02 2002 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: bz2.c,v 1.57 2002/08/29 11:45:27 iliaa Exp $ */ +/* $Id: bz2.c,v 1.58 2002/09/23 01:47:02 wez Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -132,8 +132,10 @@ php_bz2iop_write, php_bz2iop_read, php_bz2iop_close, php_bz2iop_flush, "BZip2", - NULL, NULL, - NULL, NULL + NULL, /* seek */ + NULL, /* cast */ + NULL, /* stat */ + NULL /* set_option */ }; /* {{{ Bzip2 stream openers */ Index: php4/main/memory_streams.c diff -u php4/main/memory_streams.c:1.16 php4/main/memory_streams.c:1.17 --- php4/main/memory_streams.c:1.16 Mon Jul 1 14:48:47 2002 +++ php4/main/memory_streams.c Sun Sep 22 21:47:03 2002 @@ -138,7 +138,7 @@ /* {{{ */ -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 @@ 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 @@ ms->fpos = ms->fpos + offset; } } + *newoffs = ms->fpos; return 0; case SEEK_SET: if (ms->fsize < (size_t)(offset)) { @@ -173,6 +172,7 @@ } else { ms->fpos = offset; } + *newoffs = ms->fpos; return 0; case SEEK_END: if (offset > 0) { @@ -184,6 +184,7 @@ } else { ms->fpos = ms->fsize + offset; } + *newoffs = ms->fpos; return 0; default: return 0; @@ -192,35 +193,6 @@ } /* }}} */ - -/* {{{ */ -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_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_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_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: php4/main/network.c diff -u php4/main/network.c:1.66 php4/main/network.c:1.67 --- php4/main/network.c:1.66 Thu Sep 12 17:52:09 2002 +++ php4/main/network.c Sun Sep 22 21:47:04 2002 @@ -16,7 +16,7 @@ | Streams work by Wez Furlong <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: network.c,v 1.66 2002/09/12 21:52:09 sesser Exp $ */ +/* $Id: network.c,v 1.67 2002/09/23 01:47:04 wez Exp $ */ /*#define DEBUG_MAIN_NETWORK 1*/ #define MAX_CHUNKS_PER_READ 10 @@ -540,7 +540,6 @@ 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 @@ if (!block) { flags |= myflag; } else { - flags &= ~myflag; + flags &= ~myflag; } fcntl(socketd, F_SETFL, flags); #endif @@ -707,13 +706,9 @@ 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: 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 @@ } } #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 @@ 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 @@ 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 @@ } } -#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: php4/main/php_network.h diff -u php4/main/php_network.h:1.27 php4/main/php_network.h:1.28 --- php4/main/php_network.h:1.27 Thu Sep 12 17:52:09 2002 +++ php4/main/php_network.h Sun Sep 22 21:47:04 2002 @@ -15,7 +15,7 @@ | Author: Stig Venaas <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: php_network.h,v 1.27 2002/09/12 21:52:09 sesser Exp $ */ +/* $Id: php_network.h,v 1.28 2002/09/23 01:47:04 wez Exp $ */ #ifndef _PHP_NETWORK_H #define _PHP_NETWORK_H @@ -103,13 +103,8 @@ 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: php4/main/php_streams.h diff -u php4/main/php_streams.h:1.43 php4/main/php_streams.h:1.44 --- php4/main/php_streams.h:1.43 Sat Sep 7 16:58:30 2002 +++ php4/main/php_streams.h Sun Sep 22 21:47:04 2002 @@ -148,8 +148,7 @@ 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 @@ (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 @@ #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: php4/main/streams.c diff -u php4/main/streams.c:1.74 php4/main/streams.c:1.75 --- php4/main/streams.c:1.74 Sat Sep 7 16:58:30 2002 +++ php4/main/streams.c Sun Sep 22 21:47:04 2002 @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: streams.c,v 1.74 2002/09/07 20:58:30 wez Exp $ */ +/* $Id: streams.c,v 1.75 2002/09/23 01:47:04 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -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 @@ 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 @@ 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 @@ } /* }}} */ +/* {{{ 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 @@ } 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 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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_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: php4/main/user_streams.c diff -u php4/main/user_streams.c:1.19 php4/main/user_streams.c:1.20 --- php4/main/user_streams.c:1.19 Wed Sep 18 06:15:40 2002 +++ php4/main/user_streams.c Sun Sep 22 21:47:04 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: user_streams.c,v 1.19 2002/09/18 10:15:40 wez Exp $ */ +/* $Id: user_streams.c,v 1.20 2002/09/23 01:47:04 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -80,7 +80,6 @@ #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_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 @@ 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); - - return 0; -} + ret = -1; -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_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 */ };
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php