From: Wez Furlong Date: Sun, 17 Mar 2002 14:21:01 +0000 (+0000) Subject: Fix for php_stream_gets when the implementation does not support it X-Git-Tag: php-4.3.0dev-ZendEngine2-Preview1~1311 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=12a00923769aa26e18f3af6b11b4e6f37d6163d0;p=php Fix for php_stream_gets when the implementation does not support it natively (Thanks Marcus). Implement php_stream_make_seekable() and add STREAM_MUST_SEEK as an option to php_stream_open_wrapper(). See README.STREAMS for usage. --- diff --git a/README.STREAMS b/README.STREAMS index 897d40db4e..6100c8b272 100644 --- a/README.STREAMS +++ b/README.STREAMS @@ -51,7 +51,16 @@ Where: IGNORE_URL - do not use plugin wrappers REPORT_ERRORS - show errors in a standard format if something goes wrong. - opened_path is used to return the path of the actual file opened. + STREAM_MUST_SEEK - If you really need to be able to seek the stream + and don't need to be able to write to the original + file/URL, use this option to arrange for the stream + to be copied (if needed) into a stream that can + be seek()ed. + + opened_path is used to return the path of the actual file opened, + but if you used STREAM_MUST_SEEK, may not be valid. You are + responsible for efree()ing opened_path. opened_path may be (and usually + is) NULL. If you need to open a specific stream, or convert standard resources into streams there are a range of functions to do this defined in php_streams.h. @@ -109,6 +118,39 @@ The buffer is allocated using pemalloc(); you need to call pefree() to release the memory when you are done. As with copy_to_stream, this function will try use mmap where it can. +If you have an existing stream and need to be able to seek() it, you +can use this function to copy the contents into a new stream that can +be seek()ed: + +PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream); + +It returns one of the following values: +#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */ +#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */ +#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */ +#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */ + +make_seekable will always set newstream to be the stream that is valid +if the function succeeds. +When you have finished, remember to close the stream. + +NOTE: If you only need to seek forwards, there is no need to call this +function, as the php_stream_seek can emulate forward seeking when the +whence parameter is SEEK_CUR. + +NOTE: Writing to the stream may not affect the original source, so it +only makes sense to use this for read-only use. + +NOTE: If the origstream is network based, this function will block +until the whole contents have been downloaded. + +NOTE: Never call this function with an origstream that is referenced +as a resource! It will close the origstream on success, and this +can lead to a crash when the resource is later used/released. + +NOTE: If you are opening a stream and need it to be seekable, use the +STREAM_MUST_SEEK option to php_stream_open_wrapper(); + Casting Streams =============== What if your extension needs to access the FILE* of a user level file pointer? diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h index f64efd40c8..1a9ca6cc3a 100644 --- a/main/fopen_wrappers.h +++ b/main/fopen_wrappers.h @@ -22,19 +22,6 @@ #include "php_globals.h" -#define IGNORE_PATH 0 -#define USE_PATH 1 -#define IGNORE_URL 2 -/* There's no USE_URL. */ -#define ENFORCE_SAFE_MODE 4 -#define REPORT_ERRORS 8 - -#ifdef PHP_WIN32 -# define IGNORE_URL_WIN IGNORE_URL -#else -# define IGNORE_URL_WIN 0 -#endif - PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC); PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC); diff --git a/main/php_streams.h b/main/php_streams.h index 65a83c8036..402156c335 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -146,13 +146,36 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_ #define php_stream_is_persistent(stream) (stream)->is_persistent /* Wrappers support */ + +#define IGNORE_PATH 0 +#define USE_PATH 1 +#define IGNORE_URL 2 +/* There's no USE_URL. */ +#define ENFORCE_SAFE_MODE 4 +#define REPORT_ERRORS 8 +/* If you don't need to write to the stream, but really need to + * be able to seek, use this flag in your options. */ +#define STREAM_MUST_SEEK 16 + +#ifdef PHP_WIN32 +# define IGNORE_URL_WIN IGNORE_URL +#else +# define IGNORE_URL_WIN 0 +#endif + int php_init_stream_wrappers(TSRMLS_D); int php_shutdown_stream_wrappers(TSRMLS_D); PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC); PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC); - PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options, char **opened_path TSRMLS_DC); +#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */ +#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */ +#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */ +#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */ +/* DO NOT call this on streams that are referenced by resources! */ +PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream); + #endif /* diff --git a/main/streams.c b/main/streams.c index e84f6804b3..b67bc837fe 100755 --- a/main/streams.c +++ b/main/streams.c @@ -166,7 +166,7 @@ PHPAPI char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen) return stream->ops->gets(stream, buf, maxlen); } else { /* unbuffered fgets - poor performance ! */ - size_t n = 0; + size_t n = 1; char *c = buf; /* TODO: look at error returns? */ @@ -944,7 +944,7 @@ PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options, goto out; } - if ((options & USE_PATH) && PG(include_path) != NULL) { + if ((options & USE_PATH) && PG(include_path) != NULL) { stream = php_stream_fopen_with_path(path, mode, PG(include_path), opened_path TSRMLS_CC); goto out; } @@ -954,7 +954,29 @@ PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options, stream = php_stream_fopen(path, mode, opened_path TSRMLS_CC); out: - if (stream == NULL && (options & REPORT_ERRORS)) { + if (stream != NULL && (options & STREAM_MUST_SEEK)) { + php_stream *newstream; + + switch(php_stream_make_seekable(stream, &newstream)) { + case PHP_STREAM_UNCHANGED: + return stream; + case PHP_STREAM_RELEASED: + return newstream; + default: + php_stream_close(stream); + stream = NULL; + if (options & REPORT_ERRORS) { + char *tmp = estrdup(path); + php_strip_url_passwd(tmp); + zend_error(E_WARNING, "%s(\"%s\") - could not make seekable - %s", + get_active_function_name(TSRMLS_C), tmp, strerror(errno)); + efree(tmp); + + options ^= REPORT_ERRORS; + } + } + } + if (stream == NULL && (options & REPORT_ERRORS)) { char *tmp = estrdup(path); php_strip_url_passwd(tmp); zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, strerror(errno)); @@ -963,6 +985,34 @@ out: return stream; } +PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream) +{ + assert(newstream != NULL); + + *newstream = NULL; + + if (origstream->ops->seek != NULL) { + *newstream = origstream; + return PHP_STREAM_UNCHANGED; + } + + /* Use a tmpfile and copy the old streams contents into it */ + + *newstream = php_stream_fopen_tmpfile(); + + if (*newstream == NULL) + return PHP_STREAM_FAILED; + + if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) { + php_stream_close(*newstream); + *newstream = NULL; + return PHP_STREAM_CRITICAL; + } + + php_stream_close(origstream); + + return PHP_STREAM_RELEASED; +} /* * Local variables: