From: Wez Furlong Date: Sat, 5 May 2001 18:36:22 +0000 (+0000) Subject: Nuke buffering from php_streams, move connect_nonb() from fsock.c to network.c X-Git-Tag: php-4.0.6RC1~129 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3ffb8e3800db8733d1bba602200a51a47985c17a;p=php Nuke buffering from php_streams, move connect_nonb() from fsock.c to network.c and rename to php_connect_nonb(). Use php_connect_nonb() instead of connect() in php_hostconnect() -> timeouts should now work in php_hostconnect(). sock streams abstraction now uses php_sockbuf as the "abstract" pointer. --- diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 014bb4ffe7..ebd176f9e4 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -70,6 +70,8 @@ #include "url.h" #include "fsock.h" +#include "php_network.h" + #ifdef ZTS static int fsock_globals_id; #else @@ -131,77 +133,6 @@ PHPAPI int php_is_persistent_sock(int sock) return 0; } /* }}} */ -/* {{{ connect_nonb */ -PHPAPI int connect_nonb(int sockfd, - struct sockaddr *addr, - socklen_t addrlen, - struct timeval *timeout) -{ -/* probably won't work on Win32, someone else might try it (read: fix it ;) */ - -#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY)) - -#ifndef O_NONBLOCK -#define O_NONBLOCK O_NDELAY -#endif - - int flags; - int n; - int error = 0; - socklen_t len; - int ret = 0; - fd_set rset; - fd_set wset; - - flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - - if ((n = connect(sockfd, addr, addrlen)) < 0) { - if (errno != EINPROGRESS) { - return -1; - } - } - - if (n == 0) { - goto ok; - } - - FD_ZERO(&rset); - FD_SET(sockfd, &rset); - - wset = rset; - - if ((n = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) { - error = ETIMEDOUT; - } - - if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { - len = sizeof(error); - /* - BSD-derived systems set errno correctly - Solaris returns -1 from getsockopt in case of error - */ - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - ret = -1; - } - } else { - /* whoops: sockfd has disappeared */ - ret = -1; - } - - ok: - fcntl(sockfd, F_SETFL, flags); - - if(error) { - errno = error; - ret = -1; - } - return ret; -#else /* !defined(PHP_WIN32) && ... */ - return connect(sockfd, addr, addrlen); -#endif -} -/* }}} */ /* {{{ php_fsockopen() */ /* @@ -281,7 +212,7 @@ static void php_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) { server.sin_port = htons(portno); - if (connect_nonb(socketd, (struct sockaddr *)&server, sizeof(server), &timeout) == SOCK_CONN_ERR) { + if (php_connect_nonb(socketd, (struct sockaddr *)&server, sizeof(server), &timeout) == SOCK_CONN_ERR) { CLOSE_SOCK(1); if (arg_count>2) { @@ -308,7 +239,7 @@ static void php_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) { unix_addr.sun_family = AF_UNIX; strlcpy(unix_addr.sun_path, (*args[0])->value.str.val, sizeof(unix_addr.sun_path)); - if (connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), &timeout) == SOCK_CONN_ERR) { + if (php_connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), &timeout) == SOCK_CONN_ERR) { CLOSE_SOCK(1); if (arg_count>2) { zval_dtor(*args[2]); @@ -607,12 +538,11 @@ PHPAPI void php_sockset_timeout(int socket, struct timeval *timeout) /* * FIXME: fgets depends on '\n' as line delimiter */ -PHPAPI char *php_sock_fgets(char *buf, size_t maxlen, int socket) +static char * php_sock_fgets_internal(char * buf, size_t maxlen, php_sockbuf * sock) { char *p = NULL; char *ret = NULL; size_t amount = 0; - SOCK_FIND(sock, socket); if (maxlen==0) { buf[0] = 0; @@ -656,6 +586,12 @@ PHPAPI char *php_sock_fgets(char *buf, size_t maxlen, int socket) return ret; } +PHPAPI char *php_sock_fgets(char *buf, size_t maxlen, int socket) +{ + SOCK_FIND(sock, socket); + return php_sock_fgets_internal(buf, maxlen, sock); +} + /* }}} */ @@ -693,116 +629,63 @@ PHPAPI int php_sock_feof(int socket) return ret; } -/* {{{ php_sock_fread() */ - -PHPAPI size_t php_sock_fread(char *ptr, size_t size, int socket) -{ - size_t ret = 0; - SOCK_FIND_AND_READ_MAX(size); - - if(size < 0) - return ret; - - ret = MIN(TOREAD(sock), size); - if(ret) { - memcpy(ptr, READPTR(sock), ret); - sock->readpos += ret; - } - - return ret; -} - -/* }}} */ -/* {{{ module start/shutdown functions */ - - /* {{{ php_msock_destroy */ -PHPAPI void php_msock_destroy(int *data) -{ - close(*data); -} -/* }}} */ - - /* {{{ stream abstraction */ #if HAVE_PHP_STREAM static size_t php_sockop_write(php_stream * stream, const char * buf, size_t count) { - int socket = (int)stream->abstract; - return send(socket, buf, count, 0); + php_sockbuf * sock = (php_sockbuf*)stream->abstract; + return send(sock->socket, buf, count, 0); } -static void php_stream_sockwait_for_data(php_stream * stream) +static size_t php_sockop_read(php_stream * stream, char * buf, size_t count) { - fd_set fdr, tfdr; - int retval, socket; - struct timeval timeout, *ptimeout; - - socket = (int)stream->abstract; - - FD_ZERO(&fdr); - FD_SET(socket, &fdr); - stream->timeout_event = 0; + php_sockbuf * sock = (php_sockbuf*)stream->abstract; + size_t ret = 0; - if (stream->timeout.tv_sec == -1) - ptimeout = NULL; + if (sock->is_blocked) + php_sockread_total(sock, count); else - ptimeout = &timeout; - - while(1) { - tfdr = fdr; - timeout = stream->timeout; - - retval = select(socket + 1, &tfdr, NULL, NULL, ptimeout); + php_sockread(sock); - if (retval == 0) - stream->timeout_event = 1; + if(count < 0) + return ret; - if (retval >= 0) - break; + ret = MIN(TOREAD(sock), count); + if (ret) { + memcpy(buf, READPTR(sock), ret); + sock->readpos += ret; } -} -static size_t php_sockop_read(php_stream * stream, char * buf, size_t count) -{ - int socket = (int)stream->abstract; - - /* 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. - */ - - if (stream->is_blocked) { - php_stream_sockwait_for_data(stream); - if (stream->timeout_event) - return 0; - } - return recv(socket, buf, count, 0); + return ret; } static int php_sockop_close(php_stream * stream) { - int socket = (int)stream->abstract; - SOCK_CLOSE(socket); + php_sockbuf * sock = (php_sockbuf*)stream->abstract; + + SOCK_CLOSE(sock->socket); + SOCK_DESTROY(sock); + return 0; } static int php_sockop_flush(php_stream * stream) { - int socket = (int)stream->abstract; - return fsync(socket); + php_sockbuf * sock = (php_sockbuf*)stream->abstract; + return fsync(sock->socket); } static int php_sockop_cast(php_stream * stream, int castas, void ** ret) { - int socket = (int)stream->abstract; + php_sockbuf * sock = (php_sockbuf*)stream->abstract; switch(castas) { case PHP_STREAM_AS_STDIO: if (ret) { /* DANGER!: data buffered in stream->readbuf will be forgotten! */ - *ret = fdopen(socket, stream->mode); + if (TOREAD(sock) > 0) + zend_error(E_WARNING, "%s(): buffered data lost during conversion to FILE*!", get_active_function_name()); + *ret = fdopen(sock->socket, stream->mode); if (*ret) return SUCCESS; return FAILURE; @@ -811,23 +694,59 @@ static int php_sockop_cast(php_stream * stream, int castas, void ** ret) case PHP_STREAM_AS_FD: case PHP_STREAM_AS_SOCKETD: if (ret) - *ret = (void*)socket; + *ret = (void*)sock->socket; return SUCCESS; default: return FAILURE; } } +static char * php_sockop_gets(php_stream * stream, char *buf, size_t size) +{ + php_sockbuf * sock = (php_sockbuf*)stream->abstract; + return php_sock_fgets_internal(buf, size, sock); +} + php_stream_ops php_stream_socket_ops = { php_sockop_write, php_sockop_read, php_sockop_close, php_sockop_flush, - NULL, NULL, + NULL, php_sockop_gets, php_sockop_cast, "socket" }; #endif +/* }}} */ + +/* {{{ php_sock_fread() */ + +PHPAPI size_t php_sock_fread(char *ptr, size_t size, int socket) +{ + size_t ret = 0; + SOCK_FIND_AND_READ_MAX(size); + + if(size < 0) + return ret; + + ret = MIN(TOREAD(sock), size); + if(ret) { + memcpy(ptr, READPTR(sock), ret); + sock->readpos += ret; + } + + return ret; +} /* }}} */ +/* {{{ module start/shutdown functions */ + + /* {{{ php_msock_destroy */ +PHPAPI void php_msock_destroy(int *data) +{ + close(*data); +} +/* }}} */ + + PHP_RSHUTDOWN_FUNCTION(fsock) { @@ -836,10 +755,11 @@ PHP_RSHUTDOWN_FUNCTION(fsock) php_cleanup_sockbuf(0 FLS_CC); return SUCCESS; } - +/* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: + * vim: sw=4 ts=4 tw=78 */ diff --git a/ext/standard/fsock.h b/ext/standard/fsock.h index f285306836..6d35af9674 100644 --- a/ext/standard/fsock.h +++ b/ext/standard/fsock.h @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Authors: Paul Panotzki - Bunyip Information Systems | | Jim Winstead (jimw@php.net) | + | Wez Furlong | +----------------------------------------------------------------------+ */ @@ -53,6 +54,7 @@ extern php_stream_ops php_stream_socket_ops; #endif +/* stream->abstract points to an instance of this */ struct php_sockbuf { int socket; unsigned char *readbuf; @@ -67,6 +69,9 @@ struct php_sockbuf { size_t chunk_size; struct timeval timeout; char timeout_event; +#if HAVE_PHP_STREAM + php_stream * stream; +#endif }; typedef struct php_sockbuf php_sockbuf; @@ -88,9 +93,15 @@ PHPAPI size_t php_sock_set_def_chunk_size(size_t size); PHPAPI void php_msock_destroy(int *data); PHPAPI void php_cleanup_sockbuf(int persistent FLS_DC); -PHPAPI int connect_nonb(int sockfd, struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout); PHPAPI struct php_sockbuf *php_get_socket(int socket); PHP_RSHUTDOWN_FUNCTION(fsock); +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim: sw=4 ts=4 tw=78 + */ #endif /* FSOCK_H */ diff --git a/main/network.c b/main/network.c index 6d1822ad01..b71225dfa0 100644 --- a/main/network.c +++ b/main/network.c @@ -185,6 +185,83 @@ static int php_network_getaddresses(const char *host, struct sockaddr ***sal) return 0; } +/* {{{ php_connect_nonb */ +PHPAPI int php_connect_nonb(int sockfd, + struct sockaddr *addr, + socklen_t addrlen, + struct timeval *timeout) +{ + /* probably won't work on Win32, someone else might try it (read: fix it ;) */ + +#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY)) + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + + int flags; + int n; + int error = 0; + socklen_t len; + int ret = 0; + fd_set rset; + fd_set wset; + + if (timeout == NULL) { + /* blocking mode */ + return connect(sockfd, addr, addrlen); + } + + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + + if ((n = connect(sockfd, addr, addrlen)) < 0) { + if (errno != EINPROGRESS) { + return -1; + } + } + + if (n == 0) { + goto ok; + } + + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + + wset = rset; + + if ((n = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) { + error = ETIMEDOUT; + } + + if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + /* + BSD-derived systems set errno correctly + Solaris returns -1 from getsockopt in case of error + */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + ret = -1; + } + } else { + /* whoops: sockfd has disappeared */ + ret = -1; + } + +ok: + fcntl(sockfd, F_SETFL, flags); + + if(error) { + errno = error; + ret = -1; + } + return ret; +#else /* !defined(PHP_WIN32) && ... */ + return connect(sockfd, addr, addrlen); +#endif +} +/* }}} */ + /* * Creates a socket of type socktype and connects to the given host and * port, returns the created socket on success, else returns -1. @@ -194,38 +271,48 @@ int php_hostconnect(char *host, unsigned short port, int socktype, int timeout) { int s; struct sockaddr **sal, **psal; - + struct timeval timeoutval; + if (php_network_getaddresses(host, &sal)) return -1; + if (timeout) { + timeoutval.tv_sec = timeout; + timeoutval.tv_usec = 0; + } + psal = sal; while (*sal != NULL) { s = socket((*sal)->sa_family, socktype, 0); if (s != SOCK_ERR) { switch ((*sal)->sa_family) { #if defined( HAVE_GETADDRINFO ) && defined( HAVE_IPV6 ) - case AF_INET6: { - struct sockaddr_in6 *sa = - (struct sockaddr_in6 *)*sal; - - sa->sin6_family = (*sal)->sa_family; - sa->sin6_port = htons(port); - if (connect(s, (struct sockaddr *) sa, - sizeof(*sa)) != SOCK_CONN_ERR) - goto ok; - } break; + case AF_INET6: + { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *)*sal; + + sa->sin6_family = (*sal)->sa_family; + sa->sin6_port = htons(port); + if (php_connect_nonb(s, (struct sockaddr *) sa, + sizeof(*sa), timeout ? &timeoutval : NULL) != SOCK_CONN_ERR) + goto ok; + } + break; #endif - case AF_INET: { - struct sockaddr_in *sa = - (struct sockaddr_in *)*sal; + case AF_INET: + { + struct sockaddr_in *sa = + (struct sockaddr_in *)*sal; - sa->sin_family = (*sal)->sa_family; - sa->sin_port = htons(port); - if (connect(s, (struct sockaddr *) sa, - sizeof(*sa)) != SOCK_CONN_ERR) - goto ok; + sa->sin_family = (*sal)->sa_family; + sa->sin_port = htons(port); + if (php_connect_nonb(s, (struct sockaddr *) sa, + sizeof(*sa), timeout ? &timeoutval : NULL) != SOCK_CONN_ERR) + goto ok; - } break; + } + break; } close (s); } @@ -245,4 +332,5 @@ int php_hostconnect(char *host, unsigned short port, int socktype, int timeout) * tab-width: 8 * c-basic-offset: 8 * End: + * vim: ts=4 sw=4 tw=78 */ diff --git a/main/php_network.h b/main/php_network.h index cf2851d914..eaacbc8450 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -20,7 +20,27 @@ #ifndef _PHP_NETWORK_H #define _PHP_NETWORK_H +#ifdef PHP_WIN32 +# ifndef WINNT +# define WINNT 1 +# endif +# undef FD_SETSIZE +# include "arpa/inet.h" +# define socklen_t unsigned int +#endif + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + + + int php_hostconnect(char *host, unsigned short port, int socktype, int timeout); +PHPAPI int php_connect_nonb(int sockfd, struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout); #endif /* _PHP_NETWORK_H */ diff --git a/main/php_streams.h b/main/php_streams.h index cf8d9aa478..640aebe11f 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -26,73 +26,46 @@ #include #endif +/* See README.STREAMS in php4 root dir for more info about this stuff */ typedef struct _php_stream php_stream; typedef struct _php_stream_ops { -/* stdio like functions - these are mandatory! */ + /* stdio like functions - these are mandatory! */ size_t (*write)(php_stream * stream, const char * buf, size_t count); size_t (*read)(php_stream * stream, char * buf, size_t count); int (*close)(php_stream * stream); int (*flush)(php_stream * stream); -/* these are optional */ + /* these are optional */ int (*seek)(php_stream * stream, off_t offset, int whence); -/* used only in unbuffered mode */ char * (*gets)(php_stream * stream, char * buf, size_t size); int (*cast)(php_stream * stream, int castas, void ** ret); const char * label; /* label for this ops structure */ } php_stream_ops; -typedef struct _php_stream_buffer { - char * buffer; - size_t buflen; - - int dirty; /* 1 if we need to commit data */ - - off_t readpos; - off_t writepos; - - size_t chunksize; /* amount to commit in one operation */ - int persistent; -} php_stream_buffer; - -PHPAPI int php_stream_buf_init(php_stream_buffer * buffer, int persistent, size_t chunksize); -PHPAPI int php_stream_buf_cleanup(php_stream_buffer * buffer); -/* add data into buffer, growing it if required */ -PHPAPI int php_stream_buf_append(php_stream_buffer * buffer, const char * buf, size_t size); -/* read data out of buffer */ -PHPAPI size_t php_stream_buf_read(php_stream_buffer * buffer, char * buf, size_t size); -PHPAPI int php_stream_buf_overwrite(php_stream_buffer * buffer, const char * buf, size_t size); - struct _php_stream { php_stream_ops * ops; void * abstract; /* convenience pointer for abstraction */ - int eof; - - /* for convenience for sockets */ - int is_blocked; - struct timeval timeout; - int timeout_event; - - int readahead; /* number of chunks to read-ahead */ - int is_persistent; char mode[16]; /* "rwb" etc. ala stdio */ - /* the stream can be buffered */ - int is_buffered; - php_stream_buffer readbuf; - + /* so we know how to clean it up correctly. This should be set to + * PHP_STREAM_FCLOSE_XXX as appropriate */ + int fclose_stdiocast; FILE * stdiocast; /* cache this, otherwise we might leak! */ }; /* php_stream */ +#define PHP_STREAM_FCLOSE_NONE 0 +#define PHP_STREAM_FCLOSE_FDOPEN 1 +#define PHP_STREAM_FCLOSE_FOPENCOOKIE 2 + /* allocate a new stream for a particular ops */ -PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, size_t bufsize, int persistent, const char * mode); +PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode); PHPAPI int php_stream_free(php_stream * stream, int call_dtor); #define php_stream_close(stream) php_stream_free(stream, 1) -/* seeking is only supported for reading! */ PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence); +#define php_stream_rewind(stream) php_stream_seek(stream, 0L, SEEK_SET) PHPAPI off_t php_stream_tell(php_stream * stream); PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count); PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count); @@ -114,14 +87,21 @@ PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode); /* cast as a socketd */ #define PHP_STREAM_AS_SOCKETD 2 -/* warning: once you have cast a stream as a FILE*, you probably should not use - the php_stream_XXX api after that point, or you will confuse the buffering - in FILE* and/or php_stream * -*/ PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err); /* use this to check if a stream can be cast into another form */ #define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0) +/* use this to check if a stream is of a particular type: + * PHPAPI int php_stream_is(php_stream * stream, php_stream_ops * ops); */ +#define php_stream_is(stream, anops) (stream->ops == anops) + #endif /* HAVE_PHP_STREAM */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim: sw=4 ts=4 tw=78 + */ #endif diff --git a/main/streams.c b/main/streams.c index c25d0db73b..df0350d266 100755 --- a/main/streams.c +++ b/main/streams.c @@ -22,6 +22,12 @@ #if HAVE_PHP_STREAM +#ifdef PHP_WIN32 +#define EWOULDBLOCK WSAEWOULDBLOCK +#else +#include "build-defs.h" +#endif + #define MAX_CHUNK_SIZE 8192 #define TOREAD(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos) @@ -32,92 +38,8 @@ #define READ_MAX(stream, max) if (stream->is_blocked) stream_read_total(sock, max); else stream_readahead(sock) - -PHPAPI int php_stream_buf_init(php_stream_buffer * buffer, int persistent, size_t chunksize) -{ - memset(buffer, 0, sizeof(php_stream_buffer)); - - /* defer memory allocation until first use */ - buffer->persistent = persistent; - buffer->chunksize = chunksize; - - return SUCCESS; -} - -PHPAPI int php_stream_buf_cleanup(php_stream_buffer * buffer) -{ - if (buffer->buffer) { - pefree(buffer->buffer, buffer->persistent); - buffer->buffer = NULL; - } - return SUCCESS; -} - -/* append data to the buffer ready for reading */ -PHPAPI int php_stream_buf_append(php_stream_buffer * buffer, const char * buf, size_t size) -{ - if (!buffer->dirty && buffer->buffer && (buffer->writepos + size > buffer->buflen)) { - /* if a lot of memory is sitting idle, reclaim it, but only if we are "clean" */ - if (buffer->readpos > 4 * buffer->chunksize) { - memmove(buffer->buffer + buffer->readpos, buffer->buffer, buffer->writepos - buffer->readpos); - - buffer->writepos -= buffer->readpos; - buffer->readpos = 0; - } - } - while (buffer->writepos + size > buffer->buflen) { - /* grow it */ - buffer->buflen += buffer->chunksize; - buffer->buffer = perealloc(buffer->buffer, buffer->buflen, buffer->persistent); - } - memcpy(buffer->buffer + buffer->writepos, buf, size); - buffer->writepos += size; - return SUCCESS; -} - -/* write data into the buffer at the present read position. - When done, if we overlapped the writepos, move it to so that - it occurs just after the zone we wrote. -*/ -PHPAPI int php_stream_buf_overwrite(php_stream_buffer * buffer, const char * buf, size_t size) -{ - /* ensure that there it enough memory */ - while (buffer->readpos + size > buffer->buflen) { - buffer->buflen += buffer->chunksize; - buffer->buffer = perealloc(buffer->buffer, buffer->buflen, buffer->persistent); - } - memcpy(buffer->buffer + buffer->readpos, buf, size); - if (buffer->readpos + size > buffer->writepos) - buffer->writepos = buffer->readpos + size; - - buffer->dirty = 1; - - return SUCCESS; -} - -/* read data out of buffer */ -PHPAPI size_t php_stream_buf_read(php_stream_buffer * buffer, char * buf, size_t size) -{ - size_t ret; - - ret = MIN(size, buffer->writepos - buffer->readpos); - - if (ret == 0) { - if (buf) - buf[0] = 0; - } - else { - if (buf) - memcpy(buf, buffer->buffer + buffer->readpos, ret); - buffer->readpos += ret; - } - return ret; -} - - - /* allocate a new stream for a particular ops */ -PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, size_t bufsize, int persistent, const char * mode) +PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode) { php_stream * ret; @@ -131,10 +53,6 @@ PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, size strncpy(ret->mode, mode, sizeof(ret->mode)); - if (bufsize) { - ret->is_buffered = 1; - php_stream_buf_init(&ret->readbuf, persistent, bufsize); - } return ret; } @@ -143,97 +61,44 @@ PHPAPI int php_stream_free(php_stream * stream, int call_dtor) int ret = 1; if (call_dtor) { + + if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { + /* calling fclose on an fopencookied stream will ultimately + call this very same function. If we were called via fclose, + the cookie_closer unsets the fclose_stdiocast flags, so + we can be sure that we only reach here when PHP code calls + php_stream_free. + Lets let the cookie code clean it all up. + */ + return fclose(stream->stdiocast); + } + + php_stream_flush(stream); ret = stream->ops->close(stream); + + /* tidy up any FILE* that might have been fdopened */ + if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) + { + fclose(stream->stdiocast); + stream->stdiocast = NULL; + } } - php_stream_buf_cleanup(&stream->readbuf); pefree(stream, stream->is_persistent); return ret; } -/* get a chunk into the stream read buffer */ -static size_t stream_read_chunk(php_stream * stream) -{ - size_t nr, ret = 0; - char buf[MAX_CHUNK_SIZE]; - - /* do timeout check here ? */ - - nr = stream->ops->read(stream, buf, stream->readbuf.chunksize); - - if (nr > 0) { - if (php_stream_buf_append(&stream->readbuf, buf, nr)) - ret = nr; - } - else if (nr == 0 || (nr < 0 && errno != EWOULDBLOCK)) { - stream->eof = 1; - } - return ret; -} - - -/* read 1 + readahead chunks into buffer, if possible */ -static size_t stream_readahead(php_stream * stream) -{ - size_t nr_bytes; - size_t nr_read = 0; - int i; - - for(i = 0; !stream->eof && i < (stream->readahead + 1); i++) { - nr_bytes = stream_read_chunk(stream); - if(nr_bytes == 0) - break; - nr_read += nr_bytes; - } - return nr_read; -} - -static void stream_read_total(php_stream * stream, size_t size) -{ - while(!stream->eof && TOREAD(stream) < size && !stream->timeout_event) { - stream_readahead(stream); - } -} - - PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t size) { - size_t ret = 0; - - if (stream->is_buffered) { - /* fill the buffer with enough bytes */ - stream_read_total(stream, size); - - if(size < 0) - return ret; - - ret = php_stream_buf_read(&stream->readbuf, buf, size); - } - else - ret = stream->ops->read(stream, buf, size); - - return ret; + return stream->ops->read(stream, buf, size); } PHPAPI int php_stream_eof(php_stream * stream) { - int ret = 0; - - if (stream->is_buffered) { - - if(!stream->is_blocked) - stream_read_chunk(stream); - - if(!TOREAD(stream) && stream->eof) - ret = 1; - } - else { - /* we will 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 */ - ret = stream->ops->read(stream, NULL, 0) == EOF ? 1 : 0; - } - return ret; + /* 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 */ + return stream->ops->read(stream, NULL, 0) == EOF ? 1 : 0; } PHPAPI int php_stream_getc(php_stream * stream) @@ -245,9 +110,6 @@ PHPAPI int php_stream_getc(php_stream * stream) return EOF; } - -#define SEARCHCR() p = memchr(READPTR(stream), '\n', MIN(TOREAD(stream), maxlen)) - PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen) { @@ -256,46 +118,7 @@ PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen) return buf; } - if (stream->is_buffered) { - /* buffered fgets */ - char * p = NULL; - size_t amount = 0; - - SEARCHCR(); - - if (!p) { - if (stream->is_blocked) { - while (!p && !stream->eof && !stream->timeout_event && TOREAD(stream) < maxlen) - { - stream_read_chunk(stream); - SEARCHCR(); - } - } - else { - stream_read_chunk(stream); - SEARCHCR(); - } - } - - if (p) - amount = (ptrdiff_t)p - (ptrdiff_t)READPTR(stream) + 1; - else - amount = TOREAD(stream); - - amount = MIN(amount, maxlen); - php_stream_buf_read(&stream->readbuf, buf, amount); - buf[amount] = '\0'; - - /* signal error only if we don't return data from this call - and there is not data to read and if the eof flag is set */ - - if (amount || TOREAD(stream) || !stream->eof) { - return buf; - } - - return NULL; - } - else if (stream->ops->gets) { + if (stream->ops->gets) { return stream->ops->gets(stream, buf, maxlen); } else { @@ -318,47 +141,19 @@ PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen) } } -static int stream_commit(php_stream * stream) -{ - zend_error(E_WARNING, "buffered writes not yet implemented!"); - return FAILURE; -} - PHPAPI int php_stream_flush(php_stream * stream) { - if (!stream->is_buffered && stream->ops->flush) - { - return stream->ops->flush(stream); - } - zend_error(E_WARNING, "php_stream_flush is not yet implemented on buffered streams!"); - return EOF; + return stream->ops->flush(stream); } PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count) { - size_t ret = 0; - if (strchr(stream->mode, 'w') == NULL) { zend_error(E_WARNING, "%s(): stream was not opened for writing", get_active_function_name()); return 0; } - if (stream->is_buffered) { - /* commit buffer before appending, to preserve memory */ - stream_commit(stream); - - /* dump it into the buffer */ - php_stream_buf_overwrite(&stream->readbuf, buf, count); - - /* commit if it makes sense */ - stream_commit(stream); - - ret = count; - } - else - ret = stream->ops->write(stream, buf, count); - - return ret; + return stream->ops->write(stream, buf, count); } PHPAPI off_t php_stream_tell(php_stream * stream) @@ -372,26 +167,13 @@ PHPAPI off_t php_stream_tell(php_stream * stream) PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence) { - if (stream->is_buffered) { - /*TODO: implement! - stream_commit(stream); - stream->readbuf.readpos = 0; - stream->readbuf.writepos = 0; - if (stream->ops->seek) - return stream->ops->seek(stream, offset, whence); - */ - goto cant_seek; - } - else if (stream->ops->seek) { + if (stream->ops->seek) return stream->ops->seek(stream, offset, whence); - } -cant_seek: - zend_error(E_ERROR, "streams of type %s do not support seeking", stream->ops->label); + zend_error(E_WARNING, "streams of type %s do not support seeking", stream->ops->label); return -1; } - /*------- STDIO stream implementation -------*/ static size_t php_stdiop_write(php_stream * stream, const char * buf, size_t count) @@ -464,7 +246,7 @@ PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode) FILE * fp = fopen(filename, mode); if (fp) { - php_stream * ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode); + php_stream * ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, mode); if (ret) return ret; @@ -489,7 +271,10 @@ static int stream_cookie_seeker(void *cookie, off_t position, int whence) { } static int stream_cookie_closer(void *cookie) { - return php_stream_close(((php_stream *)cookie)); + php_stream * stream = (php_stream*)cookie; + /* prevent recursion */ + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + return php_stream_close(stream); } static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = @@ -497,6 +282,8 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = stream_cookie_reader, stream_cookie_writer, stream_cookie_seeker, stream_cookie_closer }; +#else +/* TODO: use socketpair() to emulate fopecookie, as suggested by Hartmut ? */ #endif PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err) @@ -519,8 +306,10 @@ PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int sho *ret = fopencookie(stream, stream->mode, stream_cookie_functions); - if (*ret != NULL) + if (*ret != NULL) { + stream->fclose_stdiocast = 1; goto exit_success; + } /* must be either: a) programmer error @@ -539,7 +328,10 @@ PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int sho exit_fail: if (show_err) { - const char * cast_names[3] = { "STDIO FILE*", "File Descriptor", "Socket Descriptor" }; + /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ + static const char * cast_names[3] = { + "STDIO FILE*", "File Descriptor", "Socket Descriptor" + }; zend_error(E_WARNING, "%s(): cannot represent a stream of type %s as a %s", get_active_function_name(), stream->ops->label, @@ -558,3 +350,10 @@ exit_success: } #endif +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim: tw=78 sw=4 ts=4 + */