wez Sat Sep 28 09:05:48 2002 EDT Modified files: /php4/main php_streams.h streams.c user_streams.c Log: Allow user streams/wrappers to implement fstat(), opendir() and stat().
Index: php4/main/php_streams.h diff -u php4/main/php_streams.h:1.49 php4/main/php_streams.h:1.50 --- php4/main/php_streams.h:1.49 Thu Sep 26 06:14:41 2002 +++ php4/main/php_streams.h Sat Sep 28 09:05:47 2002 @@ -514,7 +514,9 @@ /* 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 PHPAPI void php_stream_context_free(php_stream_context *context); PHPAPI php_stream_context *php_stream_context_alloc(void); Index: php4/main/streams.c diff -u php4/main/streams.c:1.84 php4/main/streams.c:1.85 --- php4/main/streams.c:1.84 Thu Sep 26 12:22:28 2002 +++ php4/main/streams.c Sat Sep 28 09:05:47 2002 @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: streams.c,v 1.84 2002/09/26 16:22:28 wez Exp $ */ +/* $Id: streams.c,v 1.85 2002/09/28 13:05:47 wez Exp $ */ #define _GNU_SOURCE #include "php.h" @@ -133,6 +133,72 @@ return PHP_STREAM_PERSISTENT_NOT_EXIST; } +static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, +const char *caption TSRMLS_DC) +{ + char *tmp = estrdup(path); + char *msg; + int free_msg = 0; + + if (wrapper) { + if (wrapper->err_count > 0) { + int i; + size_t l; + int brlen; + char *br; + + if (PG(html_errors)) { + brlen = 7; + br = "<br />\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (i = 0, l = 0; i < wrapper->err_count; i++) { + l += strlen(wrapper->err_stack[i]); + if (i < wrapper->err_count - 1) + l += brlen; + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (i = 0; i < wrapper->err_count; i++) { + strcat(msg, wrapper->err_stack[i]); + if (i < wrapper->err_count - 1) + strcat(msg, br); + } + + free_msg = 1; + } else { + msg = strerror(errno); + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg); + efree(tmp); + if (free_msg) + efree(msg); +} + +static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper) { + /* tidy up the error stack */ + int i; + + for (i = 0; i < wrapper->err_count; i++) + efree(wrapper->err_stack[i]); + if (wrapper->err_stack) + efree(wrapper->err_stack); + wrapper->err_stack = NULL; + wrapper->err_count = 0; + } +} + + + /* allocate a new stream for a particular ops */ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */ { @@ -1858,28 +1924,21 @@ if (wrapper && wrapper->wops->dir_opener) { stream = wrapper->wops->dir_opener(wrapper, - path_to_open, "r", options, NULL, + path_to_open, "r", options ^ REPORT_ERRORS, NULL, context STREAMS_REL_CC TSRMLS_CC); if (stream) { stream->wrapper = wrapper; stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; } + } else if (wrapper) { + php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS +TSRMLS_CC, "not implemented"); } - if (stream == NULL && (options & REPORT_ERRORS)) { - char *tmp = estrdup(path); - char *msg; - - if (wrapper) - msg = strerror(errno); - else - msg = "no suitable wrapper could be found"; - - php_strip_url_passwd(tmp); - php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s", msg); - efree(tmp); + display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC); } + tidy_wrapper_error_log(wrapper TSRMLS_CC); + return stream; } /* }}} */ @@ -1983,63 +2042,9 @@ } } if (stream == NULL && (options & REPORT_ERRORS)) { - char *tmp = estrdup(path); - char *msg; - int free_msg = 0; - - if (wrapper) { - if (wrapper->err_count > 0) { - int i; - size_t l; - int brlen; - char *br; - - if (PG(html_errors)) { - brlen = 7; - br = "<br />\n"; - } else { - brlen = 1; - br = "\n"; - } - - for (i = 0, l = 0; i < wrapper->err_count; i++) { - l += strlen(wrapper->err_stack[i]); - if (i < wrapper->err_count - 1) - l += brlen; - } - msg = emalloc(l + 1); - msg[0] = '\0'; - for (i = 0; i < wrapper->err_count; i++) { - strcat(msg, wrapper->err_stack[i]); - if (i < wrapper->err_count - 1) - strcat(msg, br); - } - - free_msg = 1; - } else { - msg = strerror(errno); - } - } else { - msg = "no suitable wrapper could be found"; - } - - php_strip_url_passwd(tmp); - php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "failed to create stream: %s", msg); - efree(tmp); - if (free_msg) - efree(msg); - } - if (wrapper) { - /* tidy up the error stack */ - int i; - - for (i = 0; i < wrapper->err_count; i++) - efree(wrapper->err_stack[i]); - if (wrapper->err_stack) - efree(wrapper->err_stack); - wrapper->err_stack = NULL; - wrapper->err_count = 0; + display_wrapper_errors(wrapper, path, "failed to create stream" +TSRMLS_CC); } + tidy_wrapper_error_log(wrapper TSRMLS_CC); #if ZEND_DEBUG if (stream == NULL && copy_of_path != NULL) efree(copy_of_path); Index: php4/main/user_streams.c diff -u php4/main/user_streams.c:1.23 php4/main/user_streams.c:1.24 --- php4/main/user_streams.c:1.23 Thu Sep 26 08:12:27 2002 +++ php4/main/user_streams.c Sat Sep 28 09:05:47 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: user_streams.c,v 1.23 2002/09/26 12:12:27 wez Exp $ */ +/* $Id: user_streams.c,v 1.24 2002/09/28 13:05:47 wez Exp $ */ #include "php.h" #include "php_globals.h" @@ -33,12 +33,17 @@ }; static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); +static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, +php_stream_statbuf *ssb TSRMLS_DC); +static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, +char *mode, + int options, char **opened_path, php_stream_context *context +STREAMS_DC TSRMLS_DC); static php_stream_wrapper_ops user_stream_wops = { user_wrapper_opener, - NULL, - NULL, - NULL + NULL, /* close - the streams themselves know how */ + NULL, /* stat - the streams themselves know how */ + user_wrapper_stat_url, + user_wrapper_opendir, + "user-space" }; @@ -83,8 +88,14 @@ #define USERSTREAM_SEEK "stream_seek" #define USERSTREAM_TELL "stream_tell" #define USERSTREAM_EOF "stream_eof" +#define USERSTREAM_STAT "stream_stat" +#define USERSTREAM_STATURL "url_stat" +#define USERSTREAM_DIR_OPEN "dir_opendir" +#define USERSTREAM_DIR_READ "dir_readdir" +#define USERSTREAM_DIR_REWIND "dir_rewinddir" +#define USERSTREAM_DIR_CLOSE "dir_closedir" -/* class should have methods like these: +/* {{{ class should have methods like these: function stream_open($path, $mode, $options, &$opened_path) { @@ -126,8 +137,38 @@ { return true/false; } + + function stream_stat() + { + return array( just like that returned by fstat() ); + } + + function url_stat(string $url) + { + return array( just like that returned by stat() ); + } + + function dir_opendir(string $url, int $options) + { + return true / false; + } + + function dir_readdir() + { + return string next filename in dir ; + } + + function dir_closedir() + { + release dir related resources; + } + + function dir_rewinddir() + { + reset to start of dir list; + } - **/ + }}} **/ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) { @@ -138,7 +179,7 @@ int call_result; php_stream *stream = NULL; - /* Try to catch bad usage without prevent flexibility */ + /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented"); return NULL; @@ -219,6 +260,81 @@ return stream; } +static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, +char *mode, + int options, char **opened_path, php_stream_context *context +STREAMS_DC TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct +php_user_stream_wrapper*)wrapper->abstract; + php_userstream_data_t *us; + zval *zfilename, *zoptions, *zretval = NULL, *zfuncname; + zval **args[2]; + int call_result; + php_stream *stream = NULL; + + /* Try to catch bad usage without preventing flexibility */ + if (FG(user_stream_current_filename) != NULL && strcmp(filename, +FG(user_stream_current_filename)) == 0) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite +recursion prevented"); + return NULL; + } + FG(user_stream_current_filename) = filename; + + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; + + /* create an instance of our class */ + ALLOC_ZVAL(us->object); + object_init_ex(us->object, uwrap->ce); + ZVAL_REFCOUNT(us->object) = 1; + PZVAL_IS_REF(us->object) = 1; + + /* call it's dir_open method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, filename, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[1] = &zoptions; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1); + + call_result = call_user_function_ex(NULL, + &us->object, + zfuncname, + &zretval, + 2, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) { + /* the stream is now open! */ + stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, +mode); + + /* set wrapper data to be a reference to our object */ + stream->wrapperdata = us->object; + zval_add_ref(&stream->wrapperdata); + } else { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" +USERSTREAM_DIR_OPEN "\" call failed", + us->wrapper->classname); + } + + /* destroy everything else */ + if (stream == NULL) { + zval_ptr_dtor(&us->object); + efree(us); + } + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zoptions); + zval_ptr_dtor(&zfilename); + + FG(user_stream_current_filename) = NULL; + + return stream; +} + + /* {{{ proto bool file_register_wrapper(string protocol, string classname) Registers a custom URL protocol handler class */ PHP_FUNCTION(file_register_wrapper) @@ -286,16 +402,16 @@ didwrite = 0; if (call_result == SUCCESS && retval != NULL) { - convert_to_long_ex(&retval); + convert_to_long(retval); didwrite = Z_LVAL_P(retval); } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " - is not implemented!", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " +is not implemented!", us->wrapper->classname); } /* don't allow strange buffer overruns due to bogus return */ if (didwrite > count) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " - wrote %d bytes more data than requested (%d written, %d max)", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " +wrote %d bytes more data than requested (%d written, %d max)", us->wrapper->classname, didwrite - count, didwrite, count); didwrite = count; @@ -331,7 +447,7 @@ didread = 0; else { if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_EOF " - is not implemented! Assuming EOF", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" +USERSTREAM_EOF " is not implemented! Assuming EOF", us->wrapper->classname); } @@ -355,7 +471,7 @@ 0, NULL TSRMLS_CC); if (call_result == SUCCESS && retval != NULL) { - convert_to_string_ex(&retval); + convert_to_string(retval); didread = Z_STRLEN_P(retval); if (didread > count) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %d bytes more data than requested (%d read, %d max) - excess data will be lost", @@ -365,7 +481,7 @@ if (didread > 0) memcpy(buf, Z_STRVAL_P(retval), didread); } else if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - is not implemented!", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" +USERSTREAM_READ " is not implemented!", us->wrapper->classname); } zval_ptr_dtor(&zcount); @@ -488,7 +604,7 @@ if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) *newoffs = Z_LVAL_P(retval); else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " - is not implemented!", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " +is not implemented!", us->wrapper->classname); if (retval) @@ -497,11 +613,228 @@ return 0; } + +/* parse the return value from one of the stat functions and store the + * relevant fields into the statbuf provided */ +static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC) +{ + zval *elem; + +#define STAT_PROP_ENTRY(name) \ + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), +(void**)&elem)) { \ + convert_to_long(elem); + \ + ssb->sb.st_##name = Z_LVAL_P(elem); + \ + } + + STAT_PROP_ENTRY(dev); + STAT_PROP_ENTRY(ino); + STAT_PROP_ENTRY(mode); + STAT_PROP_ENTRY(nlink); + STAT_PROP_ENTRY(uid); + STAT_PROP_ENTRY(gid); +#if HAVE_ST_RDEV + STAT_PROP_ENTRY(rdev); +#endif + STAT_PROP_ENTRY(size); + STAT_PROP_ENTRY(atime); + STAT_PROP_ENTRY(mtime); + STAT_PROP_ENTRY(ctime); +#ifdef HAVE_ST_BLKSIZE + STAT_PROP_ENTRY(blksize); +#endif +#ifdef HAVE_ST_BLOCKS + STAT_PROP_ENTRY(blocks); +#endif + +#undef STAT_PROP_ENTRY + return SUCCESS; +} + +static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb +TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + int ret = -1; + + ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) { + if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC)) + ret = 0; + } else { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" +USERSTREAM_STAT " is not implemented!", + us->wrapper->classname); + } + } + return ret; +} + +static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, +php_stream_statbuf *ssb TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct +php_user_stream_wrapper*)wrapper->abstract; + zval *zfilename, *zfuncname, *zretval; + zval **args[1]; + int call_result; + zval *object; + int ret = -1; + + /* create an instance of our class */ + ALLOC_ZVAL(object); + object_init_ex(object, uwrap->ce); + ZVAL_REFCOUNT(object) = 1; + PZVAL_IS_REF(object) = 1; + + /* call the stat_url method */ + + /* call it's stream_open method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1); + + call_result = call_user_function_ex(NULL, + &object, + zfuncname, + &zretval, + 1, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == +IS_ARRAY) { + /* We got the info we needed */ + if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC)) + ret = 0; + } else { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" +USERSTREAM_STATURL " is not implemented!", + uwrap->classname); + } + } + + /* clean up */ + zval_ptr_dtor(&object); + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zfilename); + + return ret; + +} + +static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count +TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + php_stream_dirent *ent = (php_stream_dirent*)buf; + + /* avoid problems if someone mis-uses the stream */ + if (count != sizeof(php_stream_dirent)) + return 0; + + ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, +0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) { + convert_to_string(retval); + PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), +Z_STRLEN_P(retval)); + + didread = sizeof(php_stream_dirent); + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ +" is not implemented!", + us->wrapper->classname); + } + + if (retval) + zval_ptr_dtor(&retval); + + return didread; +} + +static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, +0); + + call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + zval_ptr_dtor(&us->object); + + efree(us); + + return 0; +} + +static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, +off_t *newoffs TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, +sizeof(USERSTREAM_DIR_REWIND)-1, 0); + + call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + return 0; + +} + php_stream_ops php_stream_userspace_ops = { php_userstreamop_write, php_userstreamop_read, php_userstreamop_close, php_userstreamop_flush, "user-space", php_userstreamop_seek, + NULL, /* cast */ + php_userstreamop_stat, /* stat */ + NULL /* set_option */ +}; + +php_stream_ops php_stream_userspace_dir_ops = { + NULL, /* write */ + php_userstreamop_readdir, + php_userstreamop_closedir, + NULL, /* flush */ + "user-space-dir", + php_userstreamop_rewinddir, NULL, /* cast */ NULL, /* stat */ NULL /* set_option */
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php