cvs: php4 /ext/standard file.c filters.c user_filters.c /main php_streams.h /main/streams cast.c context.h filter.c filter_api.h
php_stream_context.h php_stream_filter_api.h php_stream_plain_wrapper.h php_stream_userspace.h plain_wrapper.h streams.c userspace.h
| From: | Wez Furlong | Date: | Tue, 18 Feb 2003 01:22:22 +0000 |
| Subject: | cvs: php4 /ext/standard file.c filters.c user_filters.c /main php_streams.h /main/streams cast.c context.h filter.c filter_api.h php_stream_context.h php_stream_filter_api.h php_stream_plain_wrapper.h php_stream_userspace.h plain_wrapper.h streams.c userspace.h |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-18927@lists.php.net to get a copy of this message | ||
wez Mon Feb 17 20:22:22 2003 EDT
Added files:
/php4/main/streams php_stream_context.h php_stream_filter_api.h
php_stream_plain_wrapper.h
php_stream_userspace.h
Removed files:
/php4/main/streams context.h filter_api.h plain_wrapper.h
userspace.h
Modified files:
/php4/ext/standard file.c filters.c user_filters.c
/php4/main php_streams.h
/php4/main/streams cast.c filter.c streams.c
Log:
Implement new filter API, stage 1.
This breaks user-space filters (for the time being), and those
weird convert.* filters in ext/standard/filters.c
The filters stack has been separated into one chain for read and one chain
for write.
The user-space stream_filter_append() type functions currently only operate
on the read chain. They need extending to work with the write chain too.
Index: php4/ext/standard/file.c diff -u php4/ext/standard/file.c:1.304 php4/ext/standard/file.c:1.305 --- php4/ext/standard/file.c:1.304 Mon Feb 17 08:29:57 2003 +++ php4/ext/standard/file.c Mon Feb 17 20:22:21 2003 @@ -21,7 +21,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: file.c,v 1.304 2003/02/17 13:29:57 zeev Exp $ */ +/* $Id: file.c,v 1.305 2003/02/18 01:22:21 wez Exp $ */ /* Synced with php 3.0 revision 1.218 1999-06-16 [ssb] */ @@ -627,6 +627,7 @@ } add_assoc_string(return_value, "stream_type", (char *)stream->ops->label, 1); +#if 0 /* TODO: needs updating for new filter API */ if (stream->filterhead) { php_stream_filter *filter; @@ -639,6 +640,7 @@ add_assoc_zval(return_value, "filters", newval); } +#endif add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos); @@ -1266,9 +1268,9 @@ } if (append) { - php_stream_filter_append(stream, filter); + php_stream_filter_append(&stream->readfilters, filter); } else { - php_stream_filter_prepend(stream, filter); + php_stream_filter_prepend(&stream->readfilters, filter); } RETURN_TRUE; Index: php4/ext/standard/filters.c diff -u php4/ext/standard/filters.c:1.23 php4/ext/standard/filters.c:1.24 --- php4/ext/standard/filters.c:1.23 Sat Jan 18 19:45:49 2003 +++ php4/ext/standard/filters.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filters.c,v 1.23 2003/01/19 00:45:49 iliaa Exp $ */ +/* $Id: filters.c,v 1.24 2003/02/18 01:22:21 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -25,61 +25,40 @@ #include "ext/standard/file.h" #include "ext/standard/php_string.h" -/* {{{ common "no-opperation" methods */ -static int commonfilter_nop_flush(php_stream *stream, php_stream_filter *thisfilter, int closing TSRMLS_DC) -{ - return php_stream_filter_flush_next(stream, thisfilter, closing); -} - -static int commonfilter_nop_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - return php_stream_filter_eof_next(stream, thisfilter); -} -/* }}} */ - /* {{{ rot13 stream filter implementation */ static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"; -static size_t strfilter_rot13_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char rotbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(rotbuf)) - chunk = sizeof(rotbuf); - - PHP_STRLCPY(rotbuf, buf, sizeof(rotbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(rotbuf, chunk, rot13_from, rot13_to, 52); - wrote += php_stream_filter_write_next(stream, thisfilter, rotbuf, chunk); +static php_stream_filter_status_t strfilter_rot13_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, rot13_from, rot13_to, 52); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); } - return wrote; -} - -static size_t strfilter_rot13_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, rot13_from, rot13_to, 52); - - return read; + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; } static php_stream_filter_ops strfilter_rot13_ops = { - strfilter_rot13_write, - strfilter_rot13_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_rot13_filter, NULL, "string.rot13" }; @@ -99,88 +78,70 @@ static char lowercase[] = "abcdefghijklmnopqrstuvwxyz"; static char uppercase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static size_t strfilter_toupper_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char tmpbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(tmpbuf)) - chunk = sizeof(tmpbuf); - - PHP_STRLCPY(tmpbuf, buf, sizeof(tmpbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(tmpbuf, chunk, lowercase, uppercase, 26); - wrote += php_stream_filter_write_next(stream, thisfilter, tmpbuf, chunk); +static php_stream_filter_status_t strfilter_toupper_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, lowercase, uppercase, 26); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); } - return wrote; -} - -static size_t strfilter_tolower_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char tmpbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(tmpbuf)) - chunk = sizeof(tmpbuf); - - PHP_STRLCPY(tmpbuf, buf, sizeof(tmpbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(tmpbuf, chunk, uppercase, lowercase, 26); - wrote += php_stream_filter_write_next(stream, thisfilter, tmpbuf, chunk); + if (bytes_consumed) { + *bytes_consumed = consumed; } - - return wrote; -} - -static size_t strfilter_toupper_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, lowercase, uppercase, 26); - - return read; + + return PSFS_PASS_ON; } -static size_t strfilter_tolower_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, uppercase, lowercase, 26); +static php_stream_filter_status_t strfilter_tolower_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, uppercase, lowercase, 26); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + } - return read; + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; } static php_stream_filter_ops strfilter_toupper_ops = { - strfilter_toupper_write, - strfilter_toupper_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_toupper_filter, NULL, "string.toupper" }; static php_stream_filter_ops strfilter_tolower_ops = { - strfilter_tolower_write, - strfilter_tolower_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_tolower_filter, NULL, "string.tolower" }; @@ -1400,6 +1361,54 @@ } } +static php_stream_filter_status_t strfilter_convert_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + php_conv_err_t err; + php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract; + + while (thisfilter->buffer.head || buckets_in->head) { + /* take head off buffer first, then input brigade */ + bucket = thisfilter->buffer.head ? thisfilter->buffer.head : buckets_in->head; + php_stream_bucket_unlink(bucket TSRMLS_CC); + +#if 0 + err = php_conv_convert(inst->write_cd, ... ) + + /* update consumed by the number of bytes just used */ + + /* give output bucket to next in chain */ + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + + if (only used part of buffer) { + bucket *left, *right; + php_stream_buffer_split(bucket, &left, &right, used_len TSRMLS_CC); + php_stream_buffer_delref(left); /* delete the part we consumed */ + php_stream_buffer_append(&filter->buffer, right TSRMLS_CC); + break; + } +#endif + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; +} + + + + +#if 0 static size_t strfilter_convert_write(php_stream *stream, php_stream_filter *thisfilter, const char *buf, size_t count TSRMLS_DC) { @@ -1537,11 +1546,7 @@ } return php_stream_filter_flush_next(stream, thisfilter, closing); } - -static int strfilter_convert_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - return php_stream_filter_eof_next(stream, thisfilter); -} +#endif static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC) { @@ -1552,10 +1557,7 @@ } static php_stream_filter_ops strfilter_convert_ops = { - strfilter_convert_write, - strfilter_convert_read, - strfilter_convert_flush, - strfilter_convert_eof, + strfilter_convert_filter, strfilter_convert_dtor, "convert.*" }; Index: php4/ext/standard/user_filters.c diff -u php4/ext/standard/user_filters.c:1.9 php4/ext/standard/user_filters.c:1.10 --- php4/ext/standard/user_filters.c:1.9 Tue Jan 7 11:43:26 2003 +++ php4/ext/standard/user_filters.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,28 @@ +----------------------------------------------------------------------+ */ -/* $Id: user_filters.c,v 1.9 2003/01/07 16:43:26 pollita Exp $ */ +/* $Id: user_filters.c,v 1.10 2003/02/18 01:22:21 wez Exp $ */ + +/* + * TODO: Rewrite for buckets. + * Concept: + * The user defined filter class should implement a method named + * "filter" with the following proto: + * long filter(object brigade_in, object brigade_out, long &consumed, long flags); + * + * brigade_in and brigade_out are overloaded objects that wrap around + * the php_stream_bucket_brigades passed to the underlying filter method. + * The brigades have methods for retrieving the head of the brigade as + * an overloaded bucket object, a method for appending a + * bucket object to the end of the brigade, and a method for creating a new + * bucket at the end of the brigade. + * + * The bucket object has methods to unlink it from it's containing brigade, + * split into two buckets, and retrieve the buffer from a bucket. + * + * This approach means that there doesn't need to be very much magic between + * userspace and the real C interface. + */ #include "php.h" #include "php_globals.h" @@ -44,77 +65,14 @@ /* define the base filter class */ -/* Descendants call this function to actually send the data on to the next - * filter (or the stream itself). - * The intention is to invoke it as parent::write($data) - * */ -PHP_FUNCTION(user_filter_write) -{ - char *data; - int data_len; - size_t wrote = 0; - php_stream_filter *filter; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == FAILURE) { - RETURN_FALSE; - } - - wrote = php_stream_filter_write_next(filter->stream, filter, data, data_len); - - RETURN_LONG(wrote); -} - -PHP_FUNCTION(user_filter_read) -{ - long data_to_read; - char *data; - size_t didread = 0; - php_stream_filter *filter; - - RETVAL_FALSE; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &data_to_read) == FAILURE) { - RETURN_FALSE; - } - - data = emalloc(data_to_read + 1); - didread = php_stream_filter_read_next(filter->stream, filter, data, data_to_read); - - if (didread > 0) { - data = erealloc(data, didread + 1); - RETURN_STRINGL(data, didread, 0); - } else { - efree(data); - RETURN_FALSE; - } -} - -PHP_FUNCTION(user_filter_flush) -{ - zend_bool closing; - php_stream_filter *filter; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &closing) == FAILURE) { - RETURN_FALSE; - } - - RETURN_LONG(php_stream_filter_flush_next(filter->stream, filter, closing)); -} - PHP_FUNCTION(user_filter_nop) { } static zend_function_entry user_filter_class_funcs[] = { - PHP_NAMED_FE(write, PHP_FN(user_filter_write), NULL) - PHP_NAMED_FE(read, PHP_FN(user_filter_read), NULL) - PHP_NAMED_FE(flush, PHP_FN(user_filter_flush), NULL) + PHP_NAMED_FE(write, PHP_FN(user_filter_nop), NULL) + PHP_NAMED_FE(read, PHP_FN(user_filter_nop), NULL) + PHP_NAMED_FE(flush, PHP_FN(user_filter_nop), NULL) PHP_NAMED_FE(oncreate, PHP_FN(user_filter_nop), NULL) PHP_NAMED_FE(onclose, PHP_FN(user_filter_nop), NULL) { NULL, NULL, NULL } @@ -140,106 +98,42 @@ return SUCCESS; } -static size_t userfilter_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) +static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC) { - size_t wrote = 0; zval *obj = (zval*)thisfilter->abstract; zval func_name; zval *retval = NULL; - zval **args[1]; - zval *zbuf; - int call_result; - - ZVAL_STRINGL(&func_name, "write", sizeof("write")-1, 0); - - ALLOC_INIT_ZVAL(zbuf); - ZVAL_STRINGL(zbuf, (char*)buf, count, 1); + zval **tmp; - args[0] = &zbuf; + ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0); - call_result = call_user_function_ex(NULL, + call_user_function_ex(NULL, &obj, &func_name, &retval, - 1, args, + 0, NULL, 0, NULL TSRMLS_CC); - if (call_result == SUCCESS && retval != NULL) { - convert_to_long(retval); - wrote = Z_LVAL_P(retval); - } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user-filter write function!?"); - } - - /* beware of buffer overruns */ - if (wrote > count) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "wrote %d bytes more data than requested (%d written, %d max)", - wrote - count, - wrote, - count); - wrote = count; - } - if (retval) zval_ptr_dtor(&retval); - if (zbuf) - zval_ptr_dtor(&zbuf); - - return wrote; -} - -static size_t userfilter_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t didread = 0; - zval *obj = (zval*)thisfilter->abstract; - zval func_name; - zval *retval = NULL; - zval **args[1]; - zval *zcount; - int call_result; - - ZVAL_STRINGL(&func_name, "read", sizeof("read")-1, 0); - - ALLOC_INIT_ZVAL(zcount); - ZVAL_LONG(zcount, count); - args[0] = &zcount; - - call_result = call_user_function_ex(NULL, - &obj, - &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, - "read %d bytes more data than requested (%d read, %d max) - excess data will be lost", - 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, "failed to call read function!"); - } - - if (retval) - zval_ptr_dtor(&retval); + if (SUCCESS == zend_hash_index_find(Z_OBJPROP_P(obj), 0, (void**)&tmp)) { + zend_list_delete(Z_LVAL_PP(tmp)); + FREE_ZVAL(*tmp); + } - zval_ptr_dtor(&zcount); - - return didread; + /* kill the object */ + zval_ptr_dtor(&obj); } -static int userfilter_flush(php_stream *stream, php_stream_filter *thisfilter, int closing TSRMLS_DC) +php_stream_filter_status_t userfilter_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) { int ret = EOF; zval *obj = (zval*)thisfilter->abstract; @@ -249,10 +143,10 @@ zval *zcount; int call_result; - ZVAL_STRINGL(&func_name, "flush", sizeof("flush")-1, 0); + ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0); ALLOC_INIT_ZVAL(zcount); - ZVAL_BOOL(zcount, closing); + ZVAL_BOOL(zcount, flags & PSFS_FLAG_FLUSH_CLOSE); args[0] = &zcount; call_result = call_user_function_ex(NULL, @@ -266,55 +160,18 @@ convert_to_long(retval); ret = Z_LVAL_P(retval); } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call flush function"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function"); } if (retval) zval_ptr_dtor(&retval); zval_ptr_dtor(&zcount); - return ret; -} - -static int userfilter_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - /* TODO: does this actually ever get called!? */ - return php_stream_filter_eof_next(stream, thisfilter); -} - -static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC) -{ - zval *obj = (zval*)thisfilter->abstract; - zval func_name; - zval *retval = NULL; - zval **tmp; - - ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0); - - call_user_function_ex(NULL, - &obj, - &func_name, - &retval, - 0, NULL, - 0, NULL TSRMLS_CC); - - if (retval) - zval_ptr_dtor(&retval); - - if (SUCCESS == zend_hash_index_find(Z_OBJPROP_P(obj), 0, (void**)&tmp)) { - zend_list_delete(Z_LVAL_PP(tmp)); - FREE_ZVAL(*tmp); - } - - /* kill the object */ - zval_ptr_dtor(&obj); + return PSFS_ERR_FATAL; } static php_stream_filter_ops userfilter_ops = { - userfilter_write, - userfilter_read, - userfilter_flush, - userfilter_eof, + userfilter_filter, userfilter_dtor, "user-filter" }; @@ -356,9 +213,6 @@ fdat->ce = *(zend_class_entry**)fdat->ce; #endif - /* the class *must* be a descendant of the user-space filter - * base class, otherwise it will never work */ - /* TODO: make this sanity check */ } filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0); Index: php4/main/php_streams.h diff -u php4/main/php_streams.h:1.70 php4/main/php_streams.h:1.71 --- php4/main/php_streams.h:1.70 Sun Feb 16 19:20:38 2003 +++ php4/main/php_streams.h Mon Feb 17 20:22:21 2003 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_streams.h,v 1.70 2003/02/17 00:20:38 wez Exp $ */ +/* $Id: php_streams.h,v 1.71 2003/02/18 01:22:21 wez Exp $ */ #ifndef PHP_STREAMS_H #define PHP_STREAMS_H @@ -97,8 +97,8 @@ typedef struct _php_stream_context php_stream_context; typedef struct _php_stream_filter php_stream_filter; -#include "streams/context.h" -#include "streams/filter_api.h" +#include "streams/php_stream_context.h" +#include "streams/php_stream_filter_api.h" typedef struct _php_stream_statbuf { #if defined(NETWARE) && defined(CLIB_STAT_PATCH) @@ -174,9 +174,8 @@ php_stream_ops *ops; void *abstract; /* convenience pointer for abstraction */ - php_stream_filter *filterhead; - php_stream_filter *filtertail; - + php_stream_filter_chain readfilters, writefilters; + php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */ void *wrapperthis; /* convenience pointer for a instance of a wrapper */ zval *wrapperdata; /* fgetwrapperdata retrieves this */ @@ -287,6 +286,7 @@ #define php_stream_gets(stream, buf, maxlen) _php_stream_get_line((stream), (buf), (maxlen), NULL TSRMLS_CC) #define php_stream_get_line(stream, buf, maxlen, retlen) _php_stream_get_line((stream), (buf), (maxlen), (retlen) TSRMLS_CC) +PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC); /* CAREFUL! this is equivalent to puts NOT fputs! */ PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC); @@ -347,8 +347,8 @@ PHPAPI size_t _php_stream_passthru(php_stream * src STREAMS_DC TSRMLS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC TSRMLS_CC) -#include "streams/plain_wrapper.h" -#include "streams/userspace.h" +#include "streams/php_stream_plain_wrapper.h" +#include "streams/php_stream_userspace.h" /* coerce the stream into some other form */ /* cast as a stdio FILE * */ Index: php4/main/streams/cast.c diff -u php4/main/streams/cast.c:1.1 php4/main/streams/cast.c:1.2 --- php4/main/streams/cast.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/cast.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: cast.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: cast.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -173,7 +173,7 @@ * first, to avoid doubling up the layers of stdio with an fopencookie */ if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && stream->ops->cast && - stream->filterhead == NULL && + !php_stream_is_filtered(stream) && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { goto exit_success; @@ -235,7 +235,7 @@ } } - if (stream->filterhead) { + if (php_stream_is_filtered(stream)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system"); return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { Index: php4/main/streams/filter.c diff -u php4/main/streams/filter.c:1.1 php4/main/streams/filter.c:1.2 --- php4/main/streams/filter.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/filter.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filter.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: filter.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -46,6 +46,177 @@ return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern)); } +/* Buckets */ + +PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC) +{ + int is_persistent = php_stream_is_persistent(stream); + php_stream_bucket *bucket; + + bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent); + + if (bucket == NULL) { + return NULL; + } + + bucket->next = bucket->prev = NULL; + + if (is_persistent && !buf_persistent) { + /* all data in a persistent bucket must also be persistent */ + bucket->buf = pemalloc(buflen, 1); + + if (bucket->buf == NULL) { + pefree(bucket, 1); + return NULL; + } + + memcpy(bucket->buf, buf, buflen); + bucket->buflen = buflen; + bucket->own_buf = 1; + } else { + bucket->buf = buf; + bucket->buflen = buflen; + bucket->own_buf = own_buf; + } + bucket->is_persistent = is_persistent; + bucket->refcount = 1; + + return bucket; +} + +/* Given a bucket, returns a version of that bucket with a writeable buffer. + * If the original bucket has a refcount of 1 and owns its buffer, then it + * is returned unchanged. + * Otherwise, a copy of the buffer is made. + * In both cases, the original bucket is unlinked from its brigade. + * If a copy is made, the original bucket is delref'd. + * */ +PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC) +{ + php_stream_bucket *retval; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + + if (bucket->refcount == 1 && bucket->own_buf) { + return bucket; + } + + retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent); + memcpy(retval, bucket, sizeof(*retval)); + + retval->buf = pemalloc(retval->buflen, retval->is_persistent); + memcpy(retval->buf, bucket->buf, retval->buflen); + + retval->refcount = 1; + retval->own_buf = 1; + + php_stream_bucket_delref(bucket); + + return retval; +} + +PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC) +{ + *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + + if (*left == NULL || *right == NULL) { + goto exit_fail; + } + + (*left)->buf = pemalloc(length, in->is_persistent); + (*left)->buflen = length; + memcpy((*left)->buf, in->buf, length); + (*left)->refcount = 1; + (*left)->own_buf = 1; + (*left)->is_persistent = in->is_persistent; + + (*right)->buflen = in->buflen - length; + (*right)->buf = pemalloc((*right)->buflen, in->is_persistent); + memcpy((*right)->buf, in->buf + length, (*right)->buflen); + (*right)->refcount = 1; + (*right)->own_buf = 1; + (*right)->is_persistent = in->is_persistent; + + return SUCCESS; + +exit_fail: + if (*right) { + if ((*right)->buf) { + pefree((*right)->buf, in->is_persistent); + } + pefree(*right, in->is_persistent); + } + if (*left) { + if ((*left)->buf) { + pefree((*left)->buf, in->is_persistent); + } + pefree(*left, in->is_persistent); + } + return FAILURE; +} + +PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC) +{ + if (--bucket->refcount == 0) { + if (bucket->own_buf) { + pefree(bucket->buf, bucket->is_persistent); + } + pefree(bucket, bucket->is_persistent); + } +} + +PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + bucket->next = brigade->head; + bucket->prev = NULL; + + if (brigade->head) { + brigade->head->prev = bucket; + } else { + brigade->tail = bucket; + } + brigade->head = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + bucket->prev = brigade->tail; + bucket->next = NULL; + + if (brigade->tail) { + brigade->tail->next = bucket; + } else { + brigade->head = bucket; + } + brigade->tail = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC) +{ + if (bucket->prev) { + bucket->prev->next = bucket->next; + } else { + bucket->brigade->head = bucket->next; + } + if (bucket->next) { + bucket->next->prev = bucket->prev; + } else { + bucket->brigade->tail = bucket->prev; + } + bucket->brigade = NULL; + bucket->next = bucket->prev = NULL; +} + + + + + + + + /* We allow very simple pattern matching for filter factories: * if "charset.utf-8/sjis" is requested, we search first for an exact * match. If that fails, we try "charset.*". @@ -106,46 +277,44 @@ pefree(filter, filter->is_persistent); } -PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter) +PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter) { - filter->next = stream->filterhead; + filter->next = chain->head; filter->prev = NULL; - if (stream->filterhead) { - stream->filterhead->prev = filter; + if (chain->head) { + chain->head->prev = filter; } else { - stream->filtertail = filter; + chain->tail = filter; } - stream->filterhead = filter; - filter->stream = stream; + chain->head = filter; + filter->chain = chain; } -PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter) +PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter) { - filter->prev = stream->filtertail; + filter->prev = chain->tail; filter->next = NULL; - if (stream->filtertail) { - stream->filtertail->next = filter; + if (chain->tail) { + chain->tail->next = filter; } else { - stream->filterhead = filter; + chain->head = filter; } - stream->filtertail = filter; - filter->stream = stream; + chain->tail = filter; + filter->chain = chain; } -PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC) +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC) { - assert(stream == filter->stream); - if (filter->prev) { filter->prev->next = filter->next; } else { - stream->filterhead = filter->next; + filter->chain->head = filter->next; } if (filter->next) { filter->next->prev = filter->prev; } else { - stream->filtertail = filter->prev; + filter->chain->tail = filter->prev; } if (call_dtor) { php_stream_filter_free(filter TSRMLS_CC); Index: php4/main/streams/streams.c diff -u php4/main/streams/streams.c:1.1 php4/main/streams/streams.c:1.2 --- php4/main/streams/streams.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/streams.c Mon Feb 17 20:22:21 2003 @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: streams.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: streams.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -310,8 +310,11 @@ if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { - while (stream->filterhead) { - php_stream_filter_remove_head(stream, 1); + while (stream->readfilters.head) { + php_stream_filter_remove(stream->readfilters.head, 1); + } + while (stream->writefilters.head) { + php_stream_filter_remove(stream->writefilters.head, 1); } if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { @@ -363,44 +366,129 @@ { /* allocate/fill the buffer */ - /* is there enough data in the buffer ? */ - if (stream->writepos - stream->readpos < (off_t)size) { - size_t justread = 0; - - /* ignore eof here; the underlying state might have changed */ - - /* no; so lets fetch more data */ - - /* reduce buffer memory consumption if possible, to avoid a realloc */ - if (stream->readbuf && 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; - } + if (stream->readfilters.head) { + char *chunk_buf; + int err_flag = 0; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + + /* allocate a buffer for reading chunks */ + chunk_buf = emalloc(stream->chunk_size); + + while (!err_flag && (stream->writepos - stream->readpos < (off_t)size)) { + size_t justread = 0; + int flags; + php_stream_bucket *bucket; + php_stream_filter_status_t status; + php_stream_filter *filter; + + /* read a chunk into a bucket */ + justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC); + if (justread > 0) { + bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC); + + /* after this call, bucket is owned by the brigade */ + php_stream_bucket_append(brig_inp, bucket); + + flags = PSFS_FLAG_NORMAL; + } else { + flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC; + } - /* 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); + /* wind the handle... */ + for (filter = stream->readfilters.head; filter; filter = filter->next) { + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* we get here when the last filter in the chain has data to pass on. + * in this situation, we are passing the brig_in brigade into the + * stream read buffer */ + while (brig_inp->head) { + bucket = brig_inp->head; + /* grow buffer to hold this bucket + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < bucket->buflen) { + stream->readbuflen += bucket->buflen; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); + stream->writepos += bucket->buflen; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + break; + + case PSFS_FEED_ME: + /* when a filter needs feeding, there is no brig_out to deal with. + * we simply continue the loop; if the caller needs more data, + * we will read again, otherwise out job is done here */ + if (justread == 0) { + /* there is no data */ + err_flag = 1; + break; + } + continue; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further reads should fail. */ + err_flag = 1; + break; + } + + if (justread == 0) { + break; + } } - - if (stream->filterhead) { - justread = stream->filterhead->fops->read(stream, stream->filterhead, - stream->readbuf + stream->writepos, - stream->readbuflen - stream->writepos - TSRMLS_CC); - } else { + + efree(chunk_buf); + + } else { + /* is there enough data in the buffer ? */ + if (stream->writepos - stream->readpos < (off_t)size) { + size_t justread = 0; + + /* reduce buffer memory consumption if possible, to avoid a realloc */ + if (stream->readbuf && 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 + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < stream->chunk_size) { + stream->readbuflen += stream->chunk_size; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + justread = stream->ops->read(stream, stream->readbuf + stream->writepos, stream->readbuflen - stream->writepos TSRMLS_CC); - } - if (justread != (size_t)-1) { - stream->writepos += justread; + if (justread != (size_t)-1) { + stream->writepos += justread; + } } } - } PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) @@ -431,14 +519,8 @@ break; } - if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1) { - if (stream->filterhead) { - toread = stream->filterhead->fops->read(stream, stream->filterhead, - buf, size - TSRMLS_CC); - } else { - toread = stream->ops->read(stream, buf, size TSRMLS_CC); - } + if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) { + toread = stream->ops->read(stream, buf, size TSRMLS_CC); } else { php_stream_fill_read_buffer(stream, size TSRMLS_CC); @@ -715,38 +797,18 @@ } } -PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) -{ - int ret = 0; - - if (stream->filterhead) - stream->filterhead->fops->flush(stream, stream->filterhead, closing TSRMLS_CC); - - if (stream->ops->flush) { - ret = stream->ops->flush(stream TSRMLS_CC); - } - - return ret; -} - -PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +/* Writes a buffer directly to a stream, using multiple of the chunk size */ +static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC) { size_t didwrite = 0, towrite, justwrote; - - assert(stream); - if (buf == NULL || count == 0 || stream->ops->write == NULL) - return 0; while (count > 0) { towrite = count; if (towrite > stream->chunk_size) towrite = stream->chunk_size; - if (stream->filterhead) { - justwrote = stream->filterhead->fops->write(stream, stream->filterhead, buf, towrite TSRMLS_CC); - } else { - justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); - } + justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); + if (justwrote > 0) { buf += justwrote; count -= justwrote; @@ -754,7 +816,7 @@ /* Only screw with the buffer if we can seek, otherwise we lose data * buffered from fifos and sockets */ - if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && !php_stream_is_filtered(stream)) { stream->position += justwrote; stream->writepos = 0; stream->readpos = 0; @@ -764,6 +826,100 @@ } } return didwrite; + +} + +/* push some data through the write filter chain. + * buf may be NULL, if flags are set to indicate a flush. + * This may trigger a real write to the stream. + * Returns the number of bytes consumed from buf by the first filter in the chain. + * */ +static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC) +{ + size_t consumed = 0; + php_stream_bucket *bucket; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + php_stream_filter_status_t status; + php_stream_filter *filter; + + if (buf) { + bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC); + php_stream_bucket_append(&brig_in, bucket); + } + + for (filter = stream->writefilters.head; filter; filter = filter->next) { + /* for our return value, we are interested in the number of bytes consumed from + * the first filter in the chain */ + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, + filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* filter chain generated some output; push it through to the + * underlying stream */ + while (brig_inp->head) { + bucket = brig_inp->head; + _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC); + /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade + * hanging around and try to write it later. + * At the moment, we just drop it on the floor + * */ + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + break; + case PSFS_FEED_ME: + /* need more data before we can push data through to the stream */ + break; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further writes should fail. */ + break; + } + + return consumed; +} + +PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) +{ + int ret = 0; + + if (stream->writefilters.head) { + _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC TSRMLS_CC); + } + + if (stream->ops->flush) { + ret = stream->ops->flush(stream TSRMLS_CC); + } + + return ret; +} + +PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + if (buf == NULL || count == 0 || stream->ops->write == NULL) + return 0; + + if (stream->writefilters.head) { + return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC); + } else { + return _php_stream_write_buffer(stream, buf, count TSRMLS_CC); + } } PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) @@ -825,8 +981,9 @@ if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { int ret; - if (stream->filterhead) - stream->filterhead->fops->flush(stream, stream->filterhead, 0 TSRMLS_CC); + if (stream->writefilters.head) { + _php_stream_flush(stream, 0 TSRMLS_CC); + } switch(whence) { case SEEK_CUR: @@ -910,7 +1067,7 @@ #ifdef HAVE_MMAP if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET) - && stream->filterhead == NULL + && !php_stream_is_filtered(stream) && php_stream_tell(stream) == 0 && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0)) { @@ -975,7 +1132,7 @@ * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && - src->filterhead == NULL && + !php_stream_is_filtered(src) && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { @@ -1057,7 +1214,7 @@ * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && - src->filterhead == NULL && + !php_stream_is_filtered(src) && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { Index: php4/main/streams/php_stream_context.h +++ php4/main/streams/php_stream_context.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_context.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* Stream context and status notification related definitions */ /* callback for status notifications */ typedef void (*php_stream_notification_func)(php_stream_context *context, int notifycode, int severity, char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC); #define PHP_STREAM_NOTIFIER_PROGRESS 1 typedef struct _php_stream_notifier { php_stream_notification_func func; void *ptr; int mask; size_t progress, progress_max; /* position for progress notification */ } php_stream_notifier; struct _php_stream_context { php_stream_notifier *notifier; zval *options; /* hash keyed by wrapper family or specific wrapper */ }; PHPAPI void php_stream_context_free(php_stream_context *context); PHPAPI php_stream_context *php_stream_context_alloc(void); PHPAPI int php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname, zval ***optionvalue); PHPAPI int php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); /* not all notification codes are implemented */ #define PHP_STREAM_NOTIFY_RESOLVE 1 #define PHP_STREAM_NOTIFY_CONNECT 2 #define PHP_STREAM_NOTIFY_AUTH_REQUIRED 3 #define PHP_STREAM_NOTIFY_MIME_TYPE_IS 4 #define PHP_STREAM_NOTIFY_FILE_SIZE_IS 5 #define PHP_STREAM_NOTIFY_REDIRECTED 6 #define PHP_STREAM_NOTIFY_PROGRESS 7 #define PHP_STREAM_NOTIFY_COMPLETED 8 #define PHP_STREAM_NOTIFY_FAILURE 9 #define PHP_STREAM_NOTIFY_AUTH_RESULT 10 #define PHP_STREAM_NOTIFY_SEVERITY_INFO 0 #define PHP_STREAM_NOTIFY_SEVERITY_WARN 1 #define PHP_STREAM_NOTIFY_SEVERITY_ERR 2 PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity, char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC); PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context); #define php_stream_notify_info(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_INFO, \ (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while (0) #define php_stream_notify_progress(context, bsofar, bmax) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), PHP_STREAM_NOTIFY_PROGRESS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ NULL, 0, (bsofar), (bmax), NULL TSRMLS_CC); } } while(0) #define php_stream_notify_progress_init(context, sofar, bmax) do { if ((context) && (context)->notifier) { \ (context)->notifier->progress = (sofar); \ (context)->notifier->progress_max = (bmax); \ (context)->notifier->mask |= PHP_STREAM_NOTIFIER_PROGRESS; \ php_stream_notify_progress((context), (sofar), (bmax)); } } while (0) #define php_stream_notify_progress_increment(context, dsofar, dmax) do { if ((context) && (context)->notifier && (context)->notifier->mask & PHP_STREAM_NOTIFIER_PROGRESS) { \ (context)->notifier->progress += (dsofar); \ (context)->notifier->progress_max += (dmax); \ php_stream_notify_progress((context), (context)->notifier->progress, (context)->notifier->progress_max); } } while (0) #define php_stream_notify_file_size(context, file_size, xmsg, xcode) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), PHP_STREAM_NOTIFY_FILE_SIZE_IS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ (xmsg), (xcode), 0, (file_size), NULL TSRMLS_CC); } } while(0) #define php_stream_notify_error(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) {\ php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_ERR, \ (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while(0) /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_filter_api.h +++ php4/main/streams/php_stream_filter_api.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | | With suggestions from: | | Moriyoshi Koizumi <moriyoshi@at.wakwak.com> | | Sara Golemon <pollita@php.net> | +----------------------------------------------------------------------+ */ /* $Id: php_stream_filter_api.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* The filter API works on the principle of "Bucket-Brigades". This is * partially inspired by the Apache 2 method of doing things, although * it is intentially a light-weight implementation. * * Each stream can have a chain of filters for reading and another for writing. * * When data is written to the stream, is is placed into a bucket and placed at * the start of the input brigade. * * The first filter in the chain is invoked on the brigade and (depending on * it's return value), the next filter is invoked and so on. * */ typedef struct _php_stream_bucket php_stream_bucket; typedef struct _php_stream_bucket_brigade php_stream_bucket_brigade; struct _php_stream_bucket { php_stream_bucket *next, *prev; php_stream_bucket_brigade *brigade; char *buf; size_t buflen; /* if non-zero, buf should be pefreed when the bucket is destroyed */ int own_buf; int is_persistent; /* destroy this struct when refcount falls to zero */ int refcount; }; struct _php_stream_bucket_brigade { php_stream_bucket *head, *tail; }; typedef enum { PSFS_ERR_FATAL, /* error in data stream */ PSFS_FEED_ME, /* filter needs more data; stop processing chain until more is available */ PSFS_PASS_ON, /* filter generated output buckets; pass them on to next in chain */ } php_stream_filter_status_t; /* Buckets API. */ PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC); PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC); PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC); #define php_stream_bucket_addref(bucket) (bucket)->refcount++ PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC); PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC); #define PSFS_FLAG_NORMAL 0 /* regular read/write */ #define PSFS_FLAG_FLUSH_INC 1 /* an incremental flush */ #define PSFS_FLAG_FLUSH_CLOSE 2 /* final flush prior to closing */ typedef struct _php_stream_filter_ops { php_stream_filter_status_t (*filter)( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags TSRMLS_DC); void (*dtor)(php_stream_filter *thisfilter TSRMLS_DC); const char *label; } php_stream_filter_ops; typedef struct _php_stream_filter_chain { php_stream_filter *head, *tail; } php_stream_filter_chain; struct _php_stream_filter { php_stream_filter_ops *fops; void *abstract; /* for use by filter implementation */ php_stream_filter *next; php_stream_filter *prev; int is_persistent; /* link into stream and chain */ php_stream_filter_chain *chain; /* buffered buckets */ php_stream_bucket_brigade buffer; }; /* stack filter onto a stream */ PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC); PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC); PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC); #define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC) #define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC) #define php_stream_is_filtered(stream) ((stream)->readfilters.head || (stream)->writefilters.head) typedef struct _php_stream_filter_factory { php_stream_filter *(*create_filter)(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); } php_stream_filter_factory; PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC); PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC); PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_plain_wrapper.h +++ php4/main/streams/php_stream_plain_wrapper.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_plain_wrapper.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* definitions for the plain files wrapper */ /* operations for a plain file; use the php_stream_fopen_XXX funcs below */ PHPAPI extern php_stream_ops php_stream_stdio_ops; /* like fopen, but returns a stream */ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC); #define php_stream_fopen(filename, mode, opened) _php_stream_fopen((filename), (mode), (opened), 0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC); #define php_stream_fopen_with_path(filename, mode, path, opened) _php_stream_fopen_with_path((filename), (mode), (path), (opened) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_file(file, mode) _php_stream_fopen_from_file((file), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_fd(fd, mode) _php_stream_fopen_from_fd((fd), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_pipe(file, mode) _php_stream_fopen_from_pipe((file), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC); #define php_stream_fopen_tmpfile() _php_stream_fopen_tmpfile(0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC); #define php_stream_fopen_temporary_file(dir, pfx, opened_path) _php_stream_fopen_temporary_file((dir), (pfx), (opened_path) STREAMS_CC TSRMLS_CC) /* This is a utility API for extensions that are opening a stream, converting it * to a FILE* and then closing it again. Be warned that fileno() on the result * will most likely fail on systems with fopencookie. */ PHPAPI FILE * _php_stream_open_wrapper_as_file(char * path, char * mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); #define php_stream_open_wrapper_as_file(path, mode, options, opened_path) _php_stream_open_wrapper_as_file((path), (mode), (options), (opened_path) STREAMS_CC TSRMLS_CC) /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_userspace.h +++ php4/main/streams/php_stream_userspace.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_userspace.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* for user-space streams */ PHPAPI extern php_stream_ops php_stream_userspace_ops; PHPAPI extern php_stream_ops php_stream_userspace_dir_ops; #define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops #define PHP_STREAM_IS_USERSPACE_DIR &php_stream_userspace_dir_ops /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */
Index: php4/ext/standard/file.c diff -u php4/ext/standard/file.c:1.304 php4/ext/standard/file.c:1.305 --- php4/ext/standard/file.c:1.304 Mon Feb 17 08:29:57 2003 +++ php4/ext/standard/file.c Mon Feb 17 20:22:21 2003 @@ -21,7 +21,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: file.c,v 1.304 2003/02/17 13:29:57 zeev Exp $ */ +/* $Id: file.c,v 1.305 2003/02/18 01:22:21 wez Exp $ */ /* Synced with php 3.0 revision 1.218 1999-06-16 [ssb] */ @@ -627,6 +627,7 @@ } add_assoc_string(return_value, "stream_type", (char *)stream->ops->label, 1); +#if 0 /* TODO: needs updating for new filter API */ if (stream->filterhead) { php_stream_filter *filter; @@ -639,6 +640,7 @@ add_assoc_zval(return_value, "filters", newval); } +#endif add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos); @@ -1266,9 +1268,9 @@ } if (append) { - php_stream_filter_append(stream, filter); + php_stream_filter_append(&stream->readfilters, filter); } else { - php_stream_filter_prepend(stream, filter); + php_stream_filter_prepend(&stream->readfilters, filter); } RETURN_TRUE; Index: php4/ext/standard/filters.c diff -u php4/ext/standard/filters.c:1.23 php4/ext/standard/filters.c:1.24 --- php4/ext/standard/filters.c:1.23 Sat Jan 18 19:45:49 2003 +++ php4/ext/standard/filters.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filters.c,v 1.23 2003/01/19 00:45:49 iliaa Exp $ */ +/* $Id: filters.c,v 1.24 2003/02/18 01:22:21 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -25,61 +25,40 @@ #include "ext/standard/file.h" #include "ext/standard/php_string.h" -/* {{{ common "no-opperation" methods */ -static int commonfilter_nop_flush(php_stream *stream, php_stream_filter *thisfilter, int closing TSRMLS_DC) -{ - return php_stream_filter_flush_next(stream, thisfilter, closing); -} - -static int commonfilter_nop_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - return php_stream_filter_eof_next(stream, thisfilter); -} -/* }}} */ - /* {{{ rot13 stream filter implementation */ static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"; -static size_t strfilter_rot13_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char rotbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(rotbuf)) - chunk = sizeof(rotbuf); - - PHP_STRLCPY(rotbuf, buf, sizeof(rotbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(rotbuf, chunk, rot13_from, rot13_to, 52); - wrote += php_stream_filter_write_next(stream, thisfilter, rotbuf, chunk); +static php_stream_filter_status_t strfilter_rot13_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, rot13_from, rot13_to, 52); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); } - return wrote; -} - -static size_t strfilter_rot13_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, rot13_from, rot13_to, 52); - - return read; + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; } static php_stream_filter_ops strfilter_rot13_ops = { - strfilter_rot13_write, - strfilter_rot13_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_rot13_filter, NULL, "string.rot13" }; @@ -99,88 +78,70 @@ static char lowercase[] = "abcdefghijklmnopqrstuvwxyz"; static char uppercase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static size_t strfilter_toupper_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char tmpbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(tmpbuf)) - chunk = sizeof(tmpbuf); - - PHP_STRLCPY(tmpbuf, buf, sizeof(tmpbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(tmpbuf, chunk, lowercase, uppercase, 26); - wrote += php_stream_filter_write_next(stream, thisfilter, tmpbuf, chunk); +static php_stream_filter_status_t strfilter_toupper_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, lowercase, uppercase, 26); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); } - return wrote; -} - -static size_t strfilter_tolower_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) -{ - char tmpbuf[1024]; - size_t chunk; - size_t wrote = 0; - - while (count > 0) { - chunk = count; - if (chunk > sizeof(tmpbuf)) - chunk = sizeof(tmpbuf); - - PHP_STRLCPY(tmpbuf, buf, sizeof(tmpbuf), chunk); - buf += chunk; - count -= chunk; - - php_strtr(tmpbuf, chunk, uppercase, lowercase, 26); - wrote += php_stream_filter_write_next(stream, thisfilter, tmpbuf, chunk); + if (bytes_consumed) { + *bytes_consumed = consumed; } - - return wrote; -} - -static size_t strfilter_toupper_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, lowercase, uppercase, 26); - - return read; + + return PSFS_PASS_ON; } -static size_t strfilter_tolower_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t read; - - read = php_stream_filter_read_next(stream, thisfilter, buf, count); - php_strtr(buf, read, uppercase, lowercase, 26); +static php_stream_filter_status_t strfilter_tolower_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + + while (buckets_in->head) { + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + php_strtr(bucket->buf, bucket->buflen, uppercase, lowercase, 26); + consumed += bucket->buflen; + + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + } - return read; + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; } static php_stream_filter_ops strfilter_toupper_ops = { - strfilter_toupper_write, - strfilter_toupper_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_toupper_filter, NULL, "string.toupper" }; static php_stream_filter_ops strfilter_tolower_ops = { - strfilter_tolower_write, - strfilter_tolower_read, - commonfilter_nop_flush, - commonfilter_nop_eof, + strfilter_tolower_filter, NULL, "string.tolower" }; @@ -1400,6 +1361,54 @@ } } +static php_stream_filter_status_t strfilter_convert_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + size_t consumed = 0; + php_conv_err_t err; + php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract; + + while (thisfilter->buffer.head || buckets_in->head) { + /* take head off buffer first, then input brigade */ + bucket = thisfilter->buffer.head ? thisfilter->buffer.head : buckets_in->head; + php_stream_bucket_unlink(bucket TSRMLS_CC); + +#if 0 + err = php_conv_convert(inst->write_cd, ... ) + + /* update consumed by the number of bytes just used */ + + /* give output bucket to next in chain */ + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + + if (only used part of buffer) { + bucket *left, *right; + php_stream_buffer_split(bucket, &left, &right, used_len TSRMLS_CC); + php_stream_buffer_delref(left); /* delete the part we consumed */ + php_stream_buffer_append(&filter->buffer, right TSRMLS_CC); + break; + } +#endif + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; +} + + + + +#if 0 static size_t strfilter_convert_write(php_stream *stream, php_stream_filter *thisfilter, const char *buf, size_t count TSRMLS_DC) { @@ -1537,11 +1546,7 @@ } return php_stream_filter_flush_next(stream, thisfilter, closing); } - -static int strfilter_convert_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - return php_stream_filter_eof_next(stream, thisfilter); -} +#endif static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC) { @@ -1552,10 +1557,7 @@ } static php_stream_filter_ops strfilter_convert_ops = { - strfilter_convert_write, - strfilter_convert_read, - strfilter_convert_flush, - strfilter_convert_eof, + strfilter_convert_filter, strfilter_convert_dtor, "convert.*" }; Index: php4/ext/standard/user_filters.c diff -u php4/ext/standard/user_filters.c:1.9 php4/ext/standard/user_filters.c:1.10 --- php4/ext/standard/user_filters.c:1.9 Tue Jan 7 11:43:26 2003 +++ php4/ext/standard/user_filters.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,28 @@ +----------------------------------------------------------------------+ */ -/* $Id: user_filters.c,v 1.9 2003/01/07 16:43:26 pollita Exp $ */ +/* $Id: user_filters.c,v 1.10 2003/02/18 01:22:21 wez Exp $ */ + +/* + * TODO: Rewrite for buckets. + * Concept: + * The user defined filter class should implement a method named + * "filter" with the following proto: + * long filter(object brigade_in, object brigade_out, long &consumed, long flags); + * + * brigade_in and brigade_out are overloaded objects that wrap around + * the php_stream_bucket_brigades passed to the underlying filter method. + * The brigades have methods for retrieving the head of the brigade as + * an overloaded bucket object, a method for appending a + * bucket object to the end of the brigade, and a method for creating a new + * bucket at the end of the brigade. + * + * The bucket object has methods to unlink it from it's containing brigade, + * split into two buckets, and retrieve the buffer from a bucket. + * + * This approach means that there doesn't need to be very much magic between + * userspace and the real C interface. + */ #include "php.h" #include "php_globals.h" @@ -44,77 +65,14 @@ /* define the base filter class */ -/* Descendants call this function to actually send the data on to the next - * filter (or the stream itself). - * The intention is to invoke it as parent::write($data) - * */ -PHP_FUNCTION(user_filter_write) -{ - char *data; - int data_len; - size_t wrote = 0; - php_stream_filter *filter; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == FAILURE) { - RETURN_FALSE; - } - - wrote = php_stream_filter_write_next(filter->stream, filter, data, data_len); - - RETURN_LONG(wrote); -} - -PHP_FUNCTION(user_filter_read) -{ - long data_to_read; - char *data; - size_t didread = 0; - php_stream_filter *filter; - - RETVAL_FALSE; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &data_to_read) == FAILURE) { - RETURN_FALSE; - } - - data = emalloc(data_to_read + 1); - didread = php_stream_filter_read_next(filter->stream, filter, data, data_to_read); - - if (didread > 0) { - data = erealloc(data, didread + 1); - RETURN_STRINGL(data, didread, 0); - } else { - efree(data); - RETURN_FALSE; - } -} - -PHP_FUNCTION(user_filter_flush) -{ - zend_bool closing; - php_stream_filter *filter; - - GET_FILTER_FROM_OBJ(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &closing) == FAILURE) { - RETURN_FALSE; - } - - RETURN_LONG(php_stream_filter_flush_next(filter->stream, filter, closing)); -} - PHP_FUNCTION(user_filter_nop) { } static zend_function_entry user_filter_class_funcs[] = { - PHP_NAMED_FE(write, PHP_FN(user_filter_write), NULL) - PHP_NAMED_FE(read, PHP_FN(user_filter_read), NULL) - PHP_NAMED_FE(flush, PHP_FN(user_filter_flush), NULL) + PHP_NAMED_FE(write, PHP_FN(user_filter_nop), NULL) + PHP_NAMED_FE(read, PHP_FN(user_filter_nop), NULL) + PHP_NAMED_FE(flush, PHP_FN(user_filter_nop), NULL) PHP_NAMED_FE(oncreate, PHP_FN(user_filter_nop), NULL) PHP_NAMED_FE(onclose, PHP_FN(user_filter_nop), NULL) { NULL, NULL, NULL } @@ -140,106 +98,42 @@ return SUCCESS; } -static size_t userfilter_write(php_stream *stream, php_stream_filter *thisfilter, - const char *buf, size_t count TSRMLS_DC) +static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC) { - size_t wrote = 0; zval *obj = (zval*)thisfilter->abstract; zval func_name; zval *retval = NULL; - zval **args[1]; - zval *zbuf; - int call_result; - - ZVAL_STRINGL(&func_name, "write", sizeof("write")-1, 0); - - ALLOC_INIT_ZVAL(zbuf); - ZVAL_STRINGL(zbuf, (char*)buf, count, 1); + zval **tmp; - args[0] = &zbuf; + ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0); - call_result = call_user_function_ex(NULL, + call_user_function_ex(NULL, &obj, &func_name, &retval, - 1, args, + 0, NULL, 0, NULL TSRMLS_CC); - if (call_result == SUCCESS && retval != NULL) { - convert_to_long(retval); - wrote = Z_LVAL_P(retval); - } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user-filter write function!?"); - } - - /* beware of buffer overruns */ - if (wrote > count) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "wrote %d bytes more data than requested (%d written, %d max)", - wrote - count, - wrote, - count); - wrote = count; - } - if (retval) zval_ptr_dtor(&retval); - if (zbuf) - zval_ptr_dtor(&zbuf); - - return wrote; -} - -static size_t userfilter_read(php_stream *stream, php_stream_filter *thisfilter, - char *buf, size_t count TSRMLS_DC) -{ - size_t didread = 0; - zval *obj = (zval*)thisfilter->abstract; - zval func_name; - zval *retval = NULL; - zval **args[1]; - zval *zcount; - int call_result; - - ZVAL_STRINGL(&func_name, "read", sizeof("read")-1, 0); - - ALLOC_INIT_ZVAL(zcount); - ZVAL_LONG(zcount, count); - args[0] = &zcount; - - call_result = call_user_function_ex(NULL, - &obj, - &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, - "read %d bytes more data than requested (%d read, %d max) - excess data will be lost", - 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, "failed to call read function!"); - } - - if (retval) - zval_ptr_dtor(&retval); + if (SUCCESS == zend_hash_index_find(Z_OBJPROP_P(obj), 0, (void**)&tmp)) { + zend_list_delete(Z_LVAL_PP(tmp)); + FREE_ZVAL(*tmp); + } - zval_ptr_dtor(&zcount); - - return didread; + /* kill the object */ + zval_ptr_dtor(&obj); } -static int userfilter_flush(php_stream *stream, php_stream_filter *thisfilter, int closing TSRMLS_DC) +php_stream_filter_status_t userfilter_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) { int ret = EOF; zval *obj = (zval*)thisfilter->abstract; @@ -249,10 +143,10 @@ zval *zcount; int call_result; - ZVAL_STRINGL(&func_name, "flush", sizeof("flush")-1, 0); + ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0); ALLOC_INIT_ZVAL(zcount); - ZVAL_BOOL(zcount, closing); + ZVAL_BOOL(zcount, flags & PSFS_FLAG_FLUSH_CLOSE); args[0] = &zcount; call_result = call_user_function_ex(NULL, @@ -266,55 +160,18 @@ convert_to_long(retval); ret = Z_LVAL_P(retval); } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call flush function"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function"); } if (retval) zval_ptr_dtor(&retval); zval_ptr_dtor(&zcount); - return ret; -} - -static int userfilter_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) -{ - /* TODO: does this actually ever get called!? */ - return php_stream_filter_eof_next(stream, thisfilter); -} - -static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC) -{ - zval *obj = (zval*)thisfilter->abstract; - zval func_name; - zval *retval = NULL; - zval **tmp; - - ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0); - - call_user_function_ex(NULL, - &obj, - &func_name, - &retval, - 0, NULL, - 0, NULL TSRMLS_CC); - - if (retval) - zval_ptr_dtor(&retval); - - if (SUCCESS == zend_hash_index_find(Z_OBJPROP_P(obj), 0, (void**)&tmp)) { - zend_list_delete(Z_LVAL_PP(tmp)); - FREE_ZVAL(*tmp); - } - - /* kill the object */ - zval_ptr_dtor(&obj); + return PSFS_ERR_FATAL; } static php_stream_filter_ops userfilter_ops = { - userfilter_write, - userfilter_read, - userfilter_flush, - userfilter_eof, + userfilter_filter, userfilter_dtor, "user-filter" }; @@ -356,9 +213,6 @@ fdat->ce = *(zend_class_entry**)fdat->ce; #endif - /* the class *must* be a descendant of the user-space filter - * base class, otherwise it will never work */ - /* TODO: make this sanity check */ } filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0); Index: php4/main/php_streams.h diff -u php4/main/php_streams.h:1.70 php4/main/php_streams.h:1.71 --- php4/main/php_streams.h:1.70 Sun Feb 16 19:20:38 2003 +++ php4/main/php_streams.h Mon Feb 17 20:22:21 2003 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_streams.h,v 1.70 2003/02/17 00:20:38 wez Exp $ */ +/* $Id: php_streams.h,v 1.71 2003/02/18 01:22:21 wez Exp $ */ #ifndef PHP_STREAMS_H #define PHP_STREAMS_H @@ -97,8 +97,8 @@ typedef struct _php_stream_context php_stream_context; typedef struct _php_stream_filter php_stream_filter; -#include "streams/context.h" -#include "streams/filter_api.h" +#include "streams/php_stream_context.h" +#include "streams/php_stream_filter_api.h" typedef struct _php_stream_statbuf { #if defined(NETWARE) && defined(CLIB_STAT_PATCH) @@ -174,9 +174,8 @@ php_stream_ops *ops; void *abstract; /* convenience pointer for abstraction */ - php_stream_filter *filterhead; - php_stream_filter *filtertail; - + php_stream_filter_chain readfilters, writefilters; + php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */ void *wrapperthis; /* convenience pointer for a instance of a wrapper */ zval *wrapperdata; /* fgetwrapperdata retrieves this */ @@ -287,6 +286,7 @@ #define php_stream_gets(stream, buf, maxlen) _php_stream_get_line((stream), (buf), (maxlen), NULL TSRMLS_CC) #define php_stream_get_line(stream, buf, maxlen, retlen) _php_stream_get_line((stream), (buf), (maxlen), (retlen) TSRMLS_CC) +PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC); /* CAREFUL! this is equivalent to puts NOT fputs! */ PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC); @@ -347,8 +347,8 @@ PHPAPI size_t _php_stream_passthru(php_stream * src STREAMS_DC TSRMLS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC TSRMLS_CC) -#include "streams/plain_wrapper.h" -#include "streams/userspace.h" +#include "streams/php_stream_plain_wrapper.h" +#include "streams/php_stream_userspace.h" /* coerce the stream into some other form */ /* cast as a stdio FILE * */ Index: php4/main/streams/cast.c diff -u php4/main/streams/cast.c:1.1 php4/main/streams/cast.c:1.2 --- php4/main/streams/cast.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/cast.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: cast.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: cast.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -173,7 +173,7 @@ * first, to avoid doubling up the layers of stdio with an fopencookie */ if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && stream->ops->cast && - stream->filterhead == NULL && + !php_stream_is_filtered(stream) && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { goto exit_success; @@ -235,7 +235,7 @@ } } - if (stream->filterhead) { + if (php_stream_is_filtered(stream)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system"); return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { Index: php4/main/streams/filter.c diff -u php4/main/streams/filter.c:1.1 php4/main/streams/filter.c:1.2 --- php4/main/streams/filter.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/filter.c Mon Feb 17 20:22:21 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filter.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: filter.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -46,6 +46,177 @@ return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern)); } +/* Buckets */ + +PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC) +{ + int is_persistent = php_stream_is_persistent(stream); + php_stream_bucket *bucket; + + bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent); + + if (bucket == NULL) { + return NULL; + } + + bucket->next = bucket->prev = NULL; + + if (is_persistent && !buf_persistent) { + /* all data in a persistent bucket must also be persistent */ + bucket->buf = pemalloc(buflen, 1); + + if (bucket->buf == NULL) { + pefree(bucket, 1); + return NULL; + } + + memcpy(bucket->buf, buf, buflen); + bucket->buflen = buflen; + bucket->own_buf = 1; + } else { + bucket->buf = buf; + bucket->buflen = buflen; + bucket->own_buf = own_buf; + } + bucket->is_persistent = is_persistent; + bucket->refcount = 1; + + return bucket; +} + +/* Given a bucket, returns a version of that bucket with a writeable buffer. + * If the original bucket has a refcount of 1 and owns its buffer, then it + * is returned unchanged. + * Otherwise, a copy of the buffer is made. + * In both cases, the original bucket is unlinked from its brigade. + * If a copy is made, the original bucket is delref'd. + * */ +PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC) +{ + php_stream_bucket *retval; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + + if (bucket->refcount == 1 && bucket->own_buf) { + return bucket; + } + + retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent); + memcpy(retval, bucket, sizeof(*retval)); + + retval->buf = pemalloc(retval->buflen, retval->is_persistent); + memcpy(retval->buf, bucket->buf, retval->buflen); + + retval->refcount = 1; + retval->own_buf = 1; + + php_stream_bucket_delref(bucket); + + return retval; +} + +PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC) +{ + *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + + if (*left == NULL || *right == NULL) { + goto exit_fail; + } + + (*left)->buf = pemalloc(length, in->is_persistent); + (*left)->buflen = length; + memcpy((*left)->buf, in->buf, length); + (*left)->refcount = 1; + (*left)->own_buf = 1; + (*left)->is_persistent = in->is_persistent; + + (*right)->buflen = in->buflen - length; + (*right)->buf = pemalloc((*right)->buflen, in->is_persistent); + memcpy((*right)->buf, in->buf + length, (*right)->buflen); + (*right)->refcount = 1; + (*right)->own_buf = 1; + (*right)->is_persistent = in->is_persistent; + + return SUCCESS; + +exit_fail: + if (*right) { + if ((*right)->buf) { + pefree((*right)->buf, in->is_persistent); + } + pefree(*right, in->is_persistent); + } + if (*left) { + if ((*left)->buf) { + pefree((*left)->buf, in->is_persistent); + } + pefree(*left, in->is_persistent); + } + return FAILURE; +} + +PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC) +{ + if (--bucket->refcount == 0) { + if (bucket->own_buf) { + pefree(bucket->buf, bucket->is_persistent); + } + pefree(bucket, bucket->is_persistent); + } +} + +PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + bucket->next = brigade->head; + bucket->prev = NULL; + + if (brigade->head) { + brigade->head->prev = bucket; + } else { + brigade->tail = bucket; + } + brigade->head = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + bucket->prev = brigade->tail; + bucket->next = NULL; + + if (brigade->tail) { + brigade->tail->next = bucket; + } else { + brigade->head = bucket; + } + brigade->tail = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC) +{ + if (bucket->prev) { + bucket->prev->next = bucket->next; + } else { + bucket->brigade->head = bucket->next; + } + if (bucket->next) { + bucket->next->prev = bucket->prev; + } else { + bucket->brigade->tail = bucket->prev; + } + bucket->brigade = NULL; + bucket->next = bucket->prev = NULL; +} + + + + + + + + /* We allow very simple pattern matching for filter factories: * if "charset.utf-8/sjis" is requested, we search first for an exact * match. If that fails, we try "charset.*". @@ -106,46 +277,44 @@ pefree(filter, filter->is_persistent); } -PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter) +PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter) { - filter->next = stream->filterhead; + filter->next = chain->head; filter->prev = NULL; - if (stream->filterhead) { - stream->filterhead->prev = filter; + if (chain->head) { + chain->head->prev = filter; } else { - stream->filtertail = filter; + chain->tail = filter; } - stream->filterhead = filter; - filter->stream = stream; + chain->head = filter; + filter->chain = chain; } -PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter) +PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter) { - filter->prev = stream->filtertail; + filter->prev = chain->tail; filter->next = NULL; - if (stream->filtertail) { - stream->filtertail->next = filter; + if (chain->tail) { + chain->tail->next = filter; } else { - stream->filterhead = filter; + chain->head = filter; } - stream->filtertail = filter; - filter->stream = stream; + chain->tail = filter; + filter->chain = chain; } -PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC) +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC) { - assert(stream == filter->stream); - if (filter->prev) { filter->prev->next = filter->next; } else { - stream->filterhead = filter->next; + filter->chain->head = filter->next; } if (filter->next) { filter->next->prev = filter->prev; } else { - stream->filtertail = filter->prev; + filter->chain->tail = filter->prev; } if (call_dtor) { php_stream_filter_free(filter TSRMLS_CC); Index: php4/main/streams/streams.c diff -u php4/main/streams/streams.c:1.1 php4/main/streams/streams.c:1.2 --- php4/main/streams/streams.c:1.1 Sun Feb 16 17:19:28 2003 +++ php4/main/streams/streams.c Mon Feb 17 20:22:21 2003 @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: streams.c,v 1.1 2003/02/16 22:19:28 wez Exp $ */ +/* $Id: streams.c,v 1.2 2003/02/18 01:22:21 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -310,8 +310,11 @@ if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { - while (stream->filterhead) { - php_stream_filter_remove_head(stream, 1); + while (stream->readfilters.head) { + php_stream_filter_remove(stream->readfilters.head, 1); + } + while (stream->writefilters.head) { + php_stream_filter_remove(stream->writefilters.head, 1); } if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { @@ -363,44 +366,129 @@ { /* allocate/fill the buffer */ - /* is there enough data in the buffer ? */ - if (stream->writepos - stream->readpos < (off_t)size) { - size_t justread = 0; - - /* ignore eof here; the underlying state might have changed */ - - /* no; so lets fetch more data */ - - /* reduce buffer memory consumption if possible, to avoid a realloc */ - if (stream->readbuf && 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; - } + if (stream->readfilters.head) { + char *chunk_buf; + int err_flag = 0; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + + /* allocate a buffer for reading chunks */ + chunk_buf = emalloc(stream->chunk_size); + + while (!err_flag && (stream->writepos - stream->readpos < (off_t)size)) { + size_t justread = 0; + int flags; + php_stream_bucket *bucket; + php_stream_filter_status_t status; + php_stream_filter *filter; + + /* read a chunk into a bucket */ + justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC); + if (justread > 0) { + bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC); + + /* after this call, bucket is owned by the brigade */ + php_stream_bucket_append(brig_inp, bucket); + + flags = PSFS_FLAG_NORMAL; + } else { + flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC; + } - /* 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); + /* wind the handle... */ + for (filter = stream->readfilters.head; filter; filter = filter->next) { + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* we get here when the last filter in the chain has data to pass on. + * in this situation, we are passing the brig_in brigade into the + * stream read buffer */ + while (brig_inp->head) { + bucket = brig_inp->head; + /* grow buffer to hold this bucket + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < bucket->buflen) { + stream->readbuflen += bucket->buflen; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); + stream->writepos += bucket->buflen; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + break; + + case PSFS_FEED_ME: + /* when a filter needs feeding, there is no brig_out to deal with. + * we simply continue the loop; if the caller needs more data, + * we will read again, otherwise out job is done here */ + if (justread == 0) { + /* there is no data */ + err_flag = 1; + break; + } + continue; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further reads should fail. */ + err_flag = 1; + break; + } + + if (justread == 0) { + break; + } } - - if (stream->filterhead) { - justread = stream->filterhead->fops->read(stream, stream->filterhead, - stream->readbuf + stream->writepos, - stream->readbuflen - stream->writepos - TSRMLS_CC); - } else { + + efree(chunk_buf); + + } else { + /* is there enough data in the buffer ? */ + if (stream->writepos - stream->readpos < (off_t)size) { + size_t justread = 0; + + /* reduce buffer memory consumption if possible, to avoid a realloc */ + if (stream->readbuf && 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 + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < stream->chunk_size) { + stream->readbuflen += stream->chunk_size; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + justread = stream->ops->read(stream, stream->readbuf + stream->writepos, stream->readbuflen - stream->writepos TSRMLS_CC); - } - if (justread != (size_t)-1) { - stream->writepos += justread; + if (justread != (size_t)-1) { + stream->writepos += justread; + } } } - } PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) @@ -431,14 +519,8 @@ break; } - if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1) { - if (stream->filterhead) { - toread = stream->filterhead->fops->read(stream, stream->filterhead, - buf, size - TSRMLS_CC); - } else { - toread = stream->ops->read(stream, buf, size TSRMLS_CC); - } + if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) { + toread = stream->ops->read(stream, buf, size TSRMLS_CC); } else { php_stream_fill_read_buffer(stream, size TSRMLS_CC); @@ -715,38 +797,18 @@ } } -PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) -{ - int ret = 0; - - if (stream->filterhead) - stream->filterhead->fops->flush(stream, stream->filterhead, closing TSRMLS_CC); - - if (stream->ops->flush) { - ret = stream->ops->flush(stream TSRMLS_CC); - } - - return ret; -} - -PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +/* Writes a buffer directly to a stream, using multiple of the chunk size */ +static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC) { size_t didwrite = 0, towrite, justwrote; - - assert(stream); - if (buf == NULL || count == 0 || stream->ops->write == NULL) - return 0; while (count > 0) { towrite = count; if (towrite > stream->chunk_size) towrite = stream->chunk_size; - if (stream->filterhead) { - justwrote = stream->filterhead->fops->write(stream, stream->filterhead, buf, towrite TSRMLS_CC); - } else { - justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); - } + justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); + if (justwrote > 0) { buf += justwrote; count -= justwrote; @@ -754,7 +816,7 @@ /* Only screw with the buffer if we can seek, otherwise we lose data * buffered from fifos and sockets */ - if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && !php_stream_is_filtered(stream)) { stream->position += justwrote; stream->writepos = 0; stream->readpos = 0; @@ -764,6 +826,100 @@ } } return didwrite; + +} + +/* push some data through the write filter chain. + * buf may be NULL, if flags are set to indicate a flush. + * This may trigger a real write to the stream. + * Returns the number of bytes consumed from buf by the first filter in the chain. + * */ +static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC) +{ + size_t consumed = 0; + php_stream_bucket *bucket; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + php_stream_filter_status_t status; + php_stream_filter *filter; + + if (buf) { + bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC); + php_stream_bucket_append(&brig_in, bucket); + } + + for (filter = stream->writefilters.head; filter; filter = filter->next) { + /* for our return value, we are interested in the number of bytes consumed from + * the first filter in the chain */ + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, + filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* filter chain generated some output; push it through to the + * underlying stream */ + while (brig_inp->head) { + bucket = brig_inp->head; + _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC); + /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade + * hanging around and try to write it later. + * At the moment, we just drop it on the floor + * */ + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + break; + case PSFS_FEED_ME: + /* need more data before we can push data through to the stream */ + break; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further writes should fail. */ + break; + } + + return consumed; +} + +PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) +{ + int ret = 0; + + if (stream->writefilters.head) { + _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC TSRMLS_CC); + } + + if (stream->ops->flush) { + ret = stream->ops->flush(stream TSRMLS_CC); + } + + return ret; +} + +PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + if (buf == NULL || count == 0 || stream->ops->write == NULL) + return 0; + + if (stream->writefilters.head) { + return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC); + } else { + return _php_stream_write_buffer(stream, buf, count TSRMLS_CC); + } } PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) @@ -825,8 +981,9 @@ if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { int ret; - if (stream->filterhead) - stream->filterhead->fops->flush(stream, stream->filterhead, 0 TSRMLS_CC); + if (stream->writefilters.head) { + _php_stream_flush(stream, 0 TSRMLS_CC); + } switch(whence) { case SEEK_CUR: @@ -910,7 +1067,7 @@ #ifdef HAVE_MMAP if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET) - && stream->filterhead == NULL + && !php_stream_is_filtered(stream) && php_stream_tell(stream) == 0 && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0)) { @@ -975,7 +1132,7 @@ * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && - src->filterhead == NULL && + !php_stream_is_filtered(src) && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { @@ -1057,7 +1214,7 @@ * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && - src->filterhead == NULL && + !php_stream_is_filtered(src) && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { Index: php4/main/streams/php_stream_context.h +++ php4/main/streams/php_stream_context.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_context.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* Stream context and status notification related definitions */ /* callback for status notifications */ typedef void (*php_stream_notification_func)(php_stream_context *context, int notifycode, int severity, char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC); #define PHP_STREAM_NOTIFIER_PROGRESS 1 typedef struct _php_stream_notifier { php_stream_notification_func func; void *ptr; int mask; size_t progress, progress_max; /* position for progress notification */ } php_stream_notifier; struct _php_stream_context { php_stream_notifier *notifier; zval *options; /* hash keyed by wrapper family or specific wrapper */ }; PHPAPI void php_stream_context_free(php_stream_context *context); PHPAPI php_stream_context *php_stream_context_alloc(void); PHPAPI int php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname, zval ***optionvalue); PHPAPI int php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); /* not all notification codes are implemented */ #define PHP_STREAM_NOTIFY_RESOLVE 1 #define PHP_STREAM_NOTIFY_CONNECT 2 #define PHP_STREAM_NOTIFY_AUTH_REQUIRED 3 #define PHP_STREAM_NOTIFY_MIME_TYPE_IS 4 #define PHP_STREAM_NOTIFY_FILE_SIZE_IS 5 #define PHP_STREAM_NOTIFY_REDIRECTED 6 #define PHP_STREAM_NOTIFY_PROGRESS 7 #define PHP_STREAM_NOTIFY_COMPLETED 8 #define PHP_STREAM_NOTIFY_FAILURE 9 #define PHP_STREAM_NOTIFY_AUTH_RESULT 10 #define PHP_STREAM_NOTIFY_SEVERITY_INFO 0 #define PHP_STREAM_NOTIFY_SEVERITY_WARN 1 #define PHP_STREAM_NOTIFY_SEVERITY_ERR 2 PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity, char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC); PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context); #define php_stream_notify_info(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_INFO, \ (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while (0) #define php_stream_notify_progress(context, bsofar, bmax) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), PHP_STREAM_NOTIFY_PROGRESS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ NULL, 0, (bsofar), (bmax), NULL TSRMLS_CC); } } while(0) #define php_stream_notify_progress_init(context, sofar, bmax) do { if ((context) && (context)->notifier) { \ (context)->notifier->progress = (sofar); \ (context)->notifier->progress_max = (bmax); \ (context)->notifier->mask |= PHP_STREAM_NOTIFIER_PROGRESS; \ php_stream_notify_progress((context), (sofar), (bmax)); } } while (0) #define php_stream_notify_progress_increment(context, dsofar, dmax) do { if ((context) && (context)->notifier && (context)->notifier->mask & PHP_STREAM_NOTIFIER_PROGRESS) { \ (context)->notifier->progress += (dsofar); \ (context)->notifier->progress_max += (dmax); \ php_stream_notify_progress((context), (context)->notifier->progress, (context)->notifier->progress_max); } } while (0) #define php_stream_notify_file_size(context, file_size, xmsg, xcode) do { if ((context) && (context)->notifier) { \ php_stream_notification_notify((context), PHP_STREAM_NOTIFY_FILE_SIZE_IS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ (xmsg), (xcode), 0, (file_size), NULL TSRMLS_CC); } } while(0) #define php_stream_notify_error(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) {\ php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_ERR, \ (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while(0) /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_filter_api.h +++ php4/main/streams/php_stream_filter_api.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | | With suggestions from: | | Moriyoshi Koizumi <moriyoshi@at.wakwak.com> | | Sara Golemon <pollita@php.net> | +----------------------------------------------------------------------+ */ /* $Id: php_stream_filter_api.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* The filter API works on the principle of "Bucket-Brigades". This is * partially inspired by the Apache 2 method of doing things, although * it is intentially a light-weight implementation. * * Each stream can have a chain of filters for reading and another for writing. * * When data is written to the stream, is is placed into a bucket and placed at * the start of the input brigade. * * The first filter in the chain is invoked on the brigade and (depending on * it's return value), the next filter is invoked and so on. * */ typedef struct _php_stream_bucket php_stream_bucket; typedef struct _php_stream_bucket_brigade php_stream_bucket_brigade; struct _php_stream_bucket { php_stream_bucket *next, *prev; php_stream_bucket_brigade *brigade; char *buf; size_t buflen; /* if non-zero, buf should be pefreed when the bucket is destroyed */ int own_buf; int is_persistent; /* destroy this struct when refcount falls to zero */ int refcount; }; struct _php_stream_bucket_brigade { php_stream_bucket *head, *tail; }; typedef enum { PSFS_ERR_FATAL, /* error in data stream */ PSFS_FEED_ME, /* filter needs more data; stop processing chain until more is available */ PSFS_PASS_ON, /* filter generated output buckets; pass them on to next in chain */ } php_stream_filter_status_t; /* Buckets API. */ PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC); PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC); PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC); #define php_stream_bucket_addref(bucket) (bucket)->refcount++ PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC); PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC); #define PSFS_FLAG_NORMAL 0 /* regular read/write */ #define PSFS_FLAG_FLUSH_INC 1 /* an incremental flush */ #define PSFS_FLAG_FLUSH_CLOSE 2 /* final flush prior to closing */ typedef struct _php_stream_filter_ops { php_stream_filter_status_t (*filter)( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags TSRMLS_DC); void (*dtor)(php_stream_filter *thisfilter TSRMLS_DC); const char *label; } php_stream_filter_ops; typedef struct _php_stream_filter_chain { php_stream_filter *head, *tail; } php_stream_filter_chain; struct _php_stream_filter { php_stream_filter_ops *fops; void *abstract; /* for use by filter implementation */ php_stream_filter *next; php_stream_filter *prev; int is_persistent; /* link into stream and chain */ php_stream_filter_chain *chain; /* buffered buckets */ php_stream_bucket_brigade buffer; }; /* stack filter onto a stream */ PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC); PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC); PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC); #define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC) #define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC) #define php_stream_is_filtered(stream) ((stream)->readfilters.head || (stream)->writefilters.head) typedef struct _php_stream_filter_factory { php_stream_filter *(*create_filter)(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); } php_stream_filter_factory; PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC); PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC); PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_plain_wrapper.h +++ php4/main/streams/php_stream_plain_wrapper.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_plain_wrapper.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* definitions for the plain files wrapper */ /* operations for a plain file; use the php_stream_fopen_XXX funcs below */ PHPAPI extern php_stream_ops php_stream_stdio_ops; /* like fopen, but returns a stream */ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC); #define php_stream_fopen(filename, mode, opened) _php_stream_fopen((filename), (mode), (opened), 0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC); #define php_stream_fopen_with_path(filename, mode, path, opened) _php_stream_fopen_with_path((filename), (mode), (path), (opened) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_file(file, mode) _php_stream_fopen_from_file((file), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_fd(fd, mode) _php_stream_fopen_from_fd((fd), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_pipe(file, mode) _php_stream_fopen_from_pipe((file), (mode) STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC); #define php_stream_fopen_tmpfile() _php_stream_fopen_tmpfile(0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC); #define php_stream_fopen_temporary_file(dir, pfx, opened_path) _php_stream_fopen_temporary_file((dir), (pfx), (opened_path) STREAMS_CC TSRMLS_CC) /* This is a utility API for extensions that are opening a stream, converting it * to a FILE* and then closing it again. Be warned that fileno() on the result * will most likely fail on systems with fopencookie. */ PHPAPI FILE * _php_stream_open_wrapper_as_file(char * path, char * mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); #define php_stream_open_wrapper_as_file(path, mode, options, opened_path) _php_stream_open_wrapper_as_file((path), (mode), (options), (opened_path) STREAMS_CC TSRMLS_CC) /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/main/streams/php_stream_userspace.h +++ php4/main/streams/php_stream_userspace.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ /* $Id: php_stream_userspace.h,v 1.1 2003/02/18 01:22:21 wez Exp $ */ /* for user-space streams */ PHPAPI extern php_stream_ops php_stream_userspace_ops; PHPAPI extern php_stream_ops php_stream_userspace_dir_ops; #define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops #define PHP_STREAM_IS_USERSPACE_DIR &php_stream_userspace_dir_ops /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */