mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
This seems to resolve the issues with fgets.
I've moved EOF detection into the streams layer; a stream reader implementation should set stream->eof when it detects EOF. Fixed test for user streams - it still fails but that is due to an output buffering bug.
This commit is contained in:
parent
945ccfa76a
commit
077fe52d8b
8 changed files with 151 additions and 132 deletions
|
@ -39,6 +39,7 @@ static size_t php_stream_output_write(php_stream *stream, const char *buf, size_
|
|||
|
||||
static size_t php_stream_output_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
||||
{
|
||||
stream->eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -243,11 +243,12 @@ foreach($line_lengths as $line_length) {
|
|||
|
||||
printf("\n--[%d] whence=%s offset=%d line_length=%d position_should_be=%d --\n",
|
||||
$j, $whence_names[$whence], $offset, $line_length, $position);
|
||||
printf("REAL: pos=(%d,%d,%d) ret=%d line=`%s'\n", $rpb, $rpa, ftell($tf), $rr, $rline);
|
||||
printf("USER: pos=(%d,%d,%d) ret=%d line=`%s'\n", $upb, $upa, ftell($fp), $ur, $uline);
|
||||
printf("REAL: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $rpb, $rpa, ftell($tf), $rr, strlen($rline), $rline);
|
||||
printf("USER: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $upb, $upa, ftell($fp), $ur, strlen($uline), $uline);
|
||||
|
||||
if ($rr != $ur || $rline != $uline || $rpa != $position || $upa != $position) {
|
||||
$fail_count++;
|
||||
echo "###################################### FAIL!\n";
|
||||
$dat = stream_get_meta_data($fp);
|
||||
var_dump($dat);
|
||||
break;
|
||||
|
@ -273,7 +274,7 @@ fseek($tf, $DATALEN / 2, SEEK_SET);
|
|||
|
||||
while(!feof($fp)) {
|
||||
$uline = fgets($fp, 1024);
|
||||
$rline = fgets($fp, 1024);
|
||||
$rline = fgets($tf, 1024);
|
||||
|
||||
if ($uline != $rline) {
|
||||
echo "FGETS: FAIL\nuser=$uline\nreal=$rline\n";
|
||||
|
|
|
@ -32,14 +32,14 @@ struct php_gz_stream_data_t {
|
|||
static size_t php_gziop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
||||
{
|
||||
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
if (gzeof(self->gz_file))
|
||||
return EOF;
|
||||
return 0;
|
||||
}
|
||||
size_t ret;
|
||||
|
||||
return gzread(self->gz_file, buf, count);
|
||||
ret = gzread(self->gz_file, buf, count);
|
||||
|
||||
if (ret == 0 && gzeof(self->gz_file))
|
||||
stream->eof = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t php_gziop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
|
||||
|
|
|
@ -89,14 +89,6 @@ static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count
|
|||
ms = stream->abstract;
|
||||
assert(ms != NULL);
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
/* check for EOF condition */
|
||||
if (ms->fpos >= ms->fsize) {
|
||||
return EOF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ms->fpos + count > ms->fsize) {
|
||||
count = ms->fsize - ms->fpos;
|
||||
}
|
||||
|
@ -105,6 +97,8 @@ static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count
|
|||
assert(buf!= NULL);
|
||||
memcpy(buf, ms->data+ms->fpos, count);
|
||||
ms->fpos += count;
|
||||
} else {
|
||||
stream->eof = 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -767,32 +767,6 @@ static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS
|
|||
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
|
||||
size_t nr_bytes = 0;
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
/* check for EOF condition */
|
||||
|
||||
DUMP_SOCK_STATE("check for EOF", sock);
|
||||
|
||||
if (sock->eof)
|
||||
return EOF;
|
||||
|
||||
/* no data in the buffer - lets examine the socket */
|
||||
#if HAVE_SYS_POLL_H && HAVE_POLL
|
||||
{
|
||||
struct pollfd topoll;
|
||||
|
||||
topoll.fd = sock->socket;
|
||||
topoll.events = POLLIN;
|
||||
topoll.revents = 0;
|
||||
|
||||
if (poll(&topoll, 1, 0) == 1) {
|
||||
return topoll.revents & POLLHUP ? EOF : 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* presume that we are not yet at the eof */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(sock->is_blocked) {
|
||||
php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
|
||||
if (sock->timeout_event)
|
||||
|
@ -811,7 +785,7 @@ DUMP_SOCK_STATE("check for EOF", sock);
|
|||
php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
|
||||
|
||||
if(nr_bytes == 0 || (nr_bytes < 0 && streams_socket_errno != EWOULDBLOCK)) {
|
||||
sock->eof = 1;
|
||||
stream->eof = 1;
|
||||
}
|
||||
|
||||
return nr_bytes;
|
||||
|
|
|
@ -269,6 +269,8 @@ struct _php_stream {
|
|||
/* how much data to read when filling buffer */
|
||||
size_t chunk_size;
|
||||
|
||||
int eof;
|
||||
|
||||
}; /* php_stream */
|
||||
/* state definitions when closing down; these are private to streams.c */
|
||||
#define PHP_STREAM_FCLOSE_NONE 0
|
||||
|
|
131
main/streams.c
131
main/streams.c
|
@ -462,8 +462,8 @@ static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_D
|
|||
/* allocate/fill the buffer */
|
||||
|
||||
/* is there enough data in the buffer ? */
|
||||
while (stream->writepos - stream->readpos < (off_t)size) {
|
||||
size_t justread;
|
||||
if (stream->writepos - stream->readpos < (off_t)size) {
|
||||
size_t justread = 0;
|
||||
|
||||
/* no; so lets fetch more data */
|
||||
|
||||
|
@ -491,21 +491,21 @@ static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_D
|
|||
stream->readbuflen - stream->writepos
|
||||
TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (justread <= 0)
|
||||
break;
|
||||
|
||||
stream->writepos += justread;
|
||||
|
||||
if (stream->flags & PHP_STREAM_FLAG_AVOID_BLOCKING)
|
||||
break;
|
||||
|
||||
if (justread > 0) {
|
||||
stream->writepos += justread;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
|
||||
{
|
||||
size_t toread, didread = 0;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
/* take from the read buffer first.
|
||||
* It is possible that a buffered stream was switched to non-buffered, so we
|
||||
* drain the remainder of the buffer before using the "raw" read mode for
|
||||
|
@ -525,6 +525,10 @@ PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS
|
|||
|
||||
if (size == 0) {
|
||||
stream->position += didread;
|
||||
|
||||
if (didread == 0)
|
||||
stream->eof = 1;
|
||||
|
||||
return didread;
|
||||
}
|
||||
|
||||
|
@ -549,6 +553,10 @@ PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS
|
|||
}
|
||||
}
|
||||
stream->position += size;
|
||||
|
||||
if (didread == 0)
|
||||
stream->eof = 1;
|
||||
|
||||
return didread;
|
||||
}
|
||||
|
||||
|
@ -558,6 +566,8 @@ PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
|
|||
if (stream->writepos - stream->readpos > 0)
|
||||
return 0;
|
||||
|
||||
return stream->eof;
|
||||
|
||||
/* 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 */
|
||||
|
@ -657,7 +667,7 @@ PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRML
|
|||
{
|
||||
size_t avail = 0;
|
||||
int did_copy = 0;
|
||||
|
||||
|
||||
if (maxlen == 0)
|
||||
return NULL;
|
||||
|
||||
|
@ -779,6 +789,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_
|
|||
if (offset > 0 && offset < stream->writepos - stream->readpos) {
|
||||
stream->readpos += offset;
|
||||
stream->position += offset;
|
||||
stream->eof = 0;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
@ -787,6 +798,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_
|
|||
offset < stream->position + stream->writepos - stream->readpos) {
|
||||
stream->readpos += offset - stream->position;
|
||||
stream->position = offset;
|
||||
stream->eof = 0;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
@ -809,8 +821,11 @@ PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_
|
|||
}
|
||||
ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
|
||||
|
||||
if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0)
|
||||
if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
|
||||
if (ret == 0)
|
||||
stream->eof = 0;
|
||||
return ret;
|
||||
}
|
||||
/* else the stream has decided that it can't support seeking after all;
|
||||
* fall through to attempt emulation */
|
||||
}
|
||||
|
@ -827,6 +842,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_
|
|||
if (php_stream_read(stream, tmp, offset) == 0)
|
||||
return -1;
|
||||
}
|
||||
stream->eof = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1100,6 +1116,7 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
|
|||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
int fd; /* underlying file descriptor */
|
||||
int is_process_pipe; /* use pclose instead of fclose */
|
||||
int is_pipe; /* don't try and seek */
|
||||
#if HAVE_FLUSHIO
|
||||
|
@ -1149,21 +1166,19 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC)
|
|||
PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC)
|
||||
{
|
||||
php_stdio_stream_data *self;
|
||||
#ifdef S_ISFIFO
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
self = emalloc_rel_orig(sizeof(*self));
|
||||
self->file = file;
|
||||
self->is_pipe = 0;
|
||||
self->is_process_pipe = 0;
|
||||
|
||||
self->fd = fileno(file);
|
||||
|
||||
#ifdef S_ISFIFO
|
||||
/* detect if this is a pipe */
|
||||
fd = fileno(file);
|
||||
if (fd >= 0) {
|
||||
if (self->fd >= 0) {
|
||||
struct stat sb;
|
||||
self->is_pipe = (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0;
|
||||
self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1178,6 +1193,8 @@ PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STRE
|
|||
self->file = file;
|
||||
self->is_pipe = 1;
|
||||
self->is_process_pipe = 1;
|
||||
self->fd = fileno(file);
|
||||
|
||||
return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
|
||||
}
|
||||
|
||||
|
@ -1187,37 +1204,48 @@ static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count
|
|||
|
||||
assert(data != NULL);
|
||||
|
||||
if (data->fd >= 0) {
|
||||
|
||||
return write(data->fd, buf, count);
|
||||
|
||||
} else {
|
||||
|
||||
#if HAVE_FLUSHIO
|
||||
if (!data->is_pipe && data->last_op == 'r') {
|
||||
fseek(data->file, 0, SEEK_CUR);
|
||||
}
|
||||
data->last_op = 'w';
|
||||
if (!data->is_pipe && data->last_op == 'r') {
|
||||
fseek(data->file, 0, SEEK_CUR);
|
||||
}
|
||||
data->last_op = 'w';
|
||||
#endif
|
||||
|
||||
return fwrite(buf, 1, count, data->file);
|
||||
return fwrite(buf, 1, count, data->file);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
||||
{
|
||||
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
|
||||
size_t ret;
|
||||
|
||||
assert(data != NULL);
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
/* check for EOF condition */
|
||||
if (feof(data->file)) {
|
||||
return EOF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->fd >= 0) {
|
||||
ret = read(data->fd, buf, count);
|
||||
|
||||
if (ret == 0 || (ret < 0 && errno != EWOULDBLOCK))
|
||||
stream->eof = 1;
|
||||
|
||||
} else {
|
||||
#if HAVE_FLUSHIO
|
||||
if (!data->is_pipe && data->last_op == 'w')
|
||||
fseek(data->file, 0, SEEK_CUR);
|
||||
data->last_op = 'r';
|
||||
if (!data->is_pipe && data->last_op == 'w')
|
||||
fseek(data->file, 0, SEEK_CUR);
|
||||
data->last_op = 'r';
|
||||
#endif
|
||||
|
||||
return fread(buf, 1, count, data->file);
|
||||
ret = fread(buf, 1, count, data->file);
|
||||
|
||||
if (ret == 0 && feof(data->file))
|
||||
stream->eof = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC)
|
||||
|
@ -1249,7 +1277,11 @@ static int php_stdiop_flush(php_stream *stream TSRMLS_DC)
|
|||
|
||||
assert(data != NULL);
|
||||
|
||||
return fflush(data->file);
|
||||
if (data->fd >= 0) {
|
||||
return fsync(data->fd);
|
||||
} else {
|
||||
return fflush(data->file);
|
||||
}
|
||||
}
|
||||
|
||||
static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
|
||||
|
@ -1263,10 +1295,22 @@ static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *
|
|||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot seek on a pipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fseek(data->file, offset, whence);
|
||||
*newoffset = ftell(data->file);
|
||||
return ret;
|
||||
|
||||
if (data->fd >= 0) {
|
||||
off_t result;
|
||||
|
||||
result = lseek(data->fd, offset, whence);
|
||||
if (result == (off_t)-1)
|
||||
return -1;
|
||||
|
||||
*newoffset = result;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
ret = fseek(data->file, offset, whence);
|
||||
*newoffset = ftell(data->file);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
|
||||
|
@ -1275,15 +1319,22 @@ static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
|
|||
php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
|
||||
|
||||
assert(data != NULL);
|
||||
|
||||
/* as soon as someone touches the stdio layer, buffering may ensue,
|
||||
* so we need to stop using the fd directly in that case */
|
||||
|
||||
switch (castas) {
|
||||
case PHP_STREAM_AS_STDIO:
|
||||
if (ret) {
|
||||
*ret = data->file;
|
||||
data->fd = -1;
|
||||
}
|
||||
return SUCCESS;
|
||||
|
||||
case PHP_STREAM_AS_FD:
|
||||
/* fetch the fileno rather than using data->fd, since we may
|
||||
* have zeroed that member if someone requested the FILE*
|
||||
* first (see above case) */
|
||||
fd = fileno(data->file);
|
||||
if (fd < 0) {
|
||||
return FAILURE;
|
||||
|
|
|
@ -431,65 +431,61 @@ static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
|
|||
int call_result;
|
||||
size_t didread = 0;
|
||||
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
||||
zval *zcount;
|
||||
|
||||
assert(us != NULL);
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
|
||||
ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0);
|
||||
|
||||
call_result = call_user_function_ex(NULL,
|
||||
MAKE_STD_ZVAL(zcount);
|
||||
ZVAL_LONG(zcount, count);
|
||||
args[0] = &zcount;
|
||||
|
||||
call_result = call_user_function_ex(NULL,
|
||||
&us->object,
|
||||
&func_name,
|
||||
&retval,
|
||||
1, args,
|
||||
0, NULL TSRMLS_CC);
|
||||
|
||||
if (call_result == SUCCESS && retval != NULL) {
|
||||
convert_to_string(retval);
|
||||
didread = Z_STRLEN_P(retval);
|
||||
if (didread > count) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %d bytes more data than requested (%d read, %d max) - excess data will be lost",
|
||||
us->wrapper->classname, didread - count, didread, count);
|
||||
didread = count;
|
||||
}
|
||||
if (didread > 0)
|
||||
memcpy(buf, Z_STRVAL_P(retval), didread);
|
||||
} else if (call_result == FAILURE) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
|
||||
us->wrapper->classname);
|
||||
}
|
||||
zval_ptr_dtor(&zcount);
|
||||
|
||||
if (retval)
|
||||
zval_ptr_dtor(&retval);
|
||||
|
||||
/* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
|
||||
|
||||
ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
|
||||
|
||||
call_result = call_user_function_ex(NULL,
|
||||
&us->object,
|
||||
&func_name,
|
||||
&retval,
|
||||
0, NULL, 0, NULL TSRMLS_CC);
|
||||
|
||||
if (call_result == SUCCESS && retval != NULL && zval_is_true(retval))
|
||||
didread = 0;
|
||||
else {
|
||||
if (call_result == FAILURE) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
|
||||
us->wrapper->classname);
|
||||
}
|
||||
|
||||
didread = EOF;
|
||||
}
|
||||
|
||||
} else {
|
||||
zval *zcount;
|
||||
|
||||
ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0);
|
||||
|
||||
MAKE_STD_ZVAL(zcount);
|
||||
ZVAL_LONG(zcount, count);
|
||||
args[0] = &zcount;
|
||||
|
||||
call_result = call_user_function_ex(NULL,
|
||||
&us->object,
|
||||
&func_name,
|
||||
&retval,
|
||||
1, args,
|
||||
0, NULL TSRMLS_CC);
|
||||
|
||||
if (call_result == SUCCESS && retval != NULL) {
|
||||
convert_to_string(retval);
|
||||
didread = Z_STRLEN_P(retval);
|
||||
if (didread > count) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %d bytes more data than requested (%d read, %d max) - excess data will be lost",
|
||||
us->wrapper->classname, didread - count, didread, count);
|
||||
didread = count;
|
||||
}
|
||||
if (didread > 0)
|
||||
memcpy(buf, Z_STRVAL_P(retval), didread);
|
||||
} else if (call_result == FAILURE) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
|
||||
if (!(call_result == SUCCESS && retval != NULL && zval_is_true(retval))) {
|
||||
if (call_result == FAILURE) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
|
||||
us->wrapper->classname);
|
||||
}
|
||||
zval_ptr_dtor(&zcount);
|
||||
|
||||
stream->eof = 1;
|
||||
}
|
||||
|
||||
if (retval)
|
||||
zval_ptr_dtor(&retval);
|
||||
|
||||
|
||||
return didread;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue