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.
/* 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;
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;
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;
php_curl_stream_flush,
"cURL",
NULL, /* seek */
- php_curl_stream_gets, /* gets */
php_curl_stream_cast, /* cast */
php_curl_stream_stat /* stat */
};
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;
}
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 {
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) {
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)
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)
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,
/* {{{ */
-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;
ms = stream->abstract;
assert(ms != NULL);
- if (offset == 0 && whence == SEEK_CUR)
- return ms->fpos;
switch(whence) {
case SEEK_CUR:
if (offset < 0) {
ms->fpos = ms->fpos + offset;
}
}
+ *newoffs = ms->fpos;
return 0;
case SEEK_SET:
if (ms->fsize < (size_t)(offset)) {
} else {
ms->fpos = offset;
}
+ *newoffs = ms->fpos;
return 0;
case SEEK_END:
if (offset > 0) {
} else {
ms->fpos = ms->fsize + offset;
}
+ *newoffs = ms->fpos;
return 0;
default:
return 0;
}
/* }}} */
-
-/* {{{ */
-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)
{
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 */
};
/* {{{ */
-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)
{
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 */
};
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;
if (!block) {
flags |= myflag;
} else {
- flags &= ~myflag;
+ flags &= ~myflag;
}
fcntl(socketd, F_SETFL, flags);
#endif
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;
}
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
}
}
#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;
closesocket(sock->socket);
}
- if (sock->readbuf)
- pefree(sock->readbuf, php_stream_is_persistent(stream));
pefree(sock, php_stream_is_persistent(stream));
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;
}
}
-#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
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
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);
(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;
#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 */
#endif
#define STREAM_DEBUG 0
+#define SANITY_CHECK_SEEK 1
#define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1)
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));
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
}
/* }}} */
+/* {{{ filter API */
static HashTable stream_filters_hash;
PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
}
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);
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 */
}
php_error_docref(NULL TSRMLS_CC, E_WARNING, "streams of type %s do not support seeking", stream->ops->label);
+
return -1;
}
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);
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;
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
};
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) {
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;
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;
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,
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)) {
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;
#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"
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)
{
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;
}
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 */
};