From 077fe52d8b650b5d1739aa55ab90f6ab6ad8461b Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 5 Oct 2002 10:35:13 +0000 Subject: 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. --- ext/standard/php_fopen_wrapper.c | 1 + ext/standard/tests/file/userstreams.phpt | 7 +- ext/zlib/zlib_fopen_wrapper.c | 12 +-- main/memory_streams.c | 10 +-- main/network.c | 28 +------ main/php_streams.h | 2 + main/streams.c | 131 +++++++++++++++++++++---------- main/user_streams.c | 88 ++++++++++----------- 8 files changed, 149 insertions(+), 130 deletions(-) diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 0ca83d356a..6299e28ac1 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -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; } diff --git a/ext/standard/tests/file/userstreams.phpt b/ext/standard/tests/file/userstreams.phpt index 6fc7da5faa..f71333efd9 100644 --- a/ext/standard/tests/file/userstreams.phpt +++ b/ext/standard/tests/file/userstreams.phpt @@ -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"; diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c index 1ccccc615a..b97f6a691b 100644 --- a/ext/zlib/zlib_fopen_wrapper.c +++ b/ext/zlib/zlib_fopen_wrapper.c @@ -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; + size_t ret; + + ret = gzread(self->gz_file, buf, count); - if (buf == NULL && count == 0) { - if (gzeof(self->gz_file)) - return EOF; - return 0; - } + if (ret == 0 && gzeof(self->gz_file)) + stream->eof = 1; - return gzread(self->gz_file, buf, count); + return ret; } static size_t php_gziop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) diff --git a/main/memory_streams.c b/main/memory_streams.c index 520225c87c..ca5055da8b 100644 --- a/main/memory_streams.c +++ b/main/memory_streams.c @@ -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; } diff --git a/main/network.c b/main/network.c index b3fa16c542..64ee9864a3 100644 --- a/main/network.c +++ b/main/network.c @@ -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; diff --git a/main/php_streams.h b/main/php_streams.h index 3acd52624f..9a5f12c8fb 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -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 diff --git a/main/streams.c b/main/streams.c index 8a23938aaf..1620628801 100755 --- a/main/streams.c +++ b/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; diff --git a/main/user_streams.c b/main/user_streams.c index 0688fbd596..bf7d141126 100644 --- a/main/user_streams.c +++ b/main/user_streams.c @@ -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, - 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); - } + 1, args, + 0, NULL TSRMLS_CC); - didread = EOF; + 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); - } 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 (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))) { + 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; } -- cgit v1.2.1