LTP GCOV extension - code coverage report
Current view: directory - main/streams - streams.c
Test: PHP Code Coverage
Date: 2007-04-10 Instrumented lines: 909
Code covered: 48.5 % Executed lines: 441
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 5                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2007 The PHP Group                                |
       6                 :    +----------------------------------------------------------------------+
       7                 :    | This source file is subject to version 3.01 of the PHP license,      |
       8                 :    | that is bundled with this package in the file LICENSE, and is        |
       9                 :    | available through the world-wide-web at the following url:           |
      10                 :    | http://www.php.net/license/3_01.txt                                  |
      11                 :    | If you did not receive a copy of the PHP license and are unable to   |
      12                 :    | obtain it through the world-wide-web, please send a note to          |
      13                 :    | license@php.net so we can mail you a copy immediately.               |
      14                 :    +----------------------------------------------------------------------+
      15                 :    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
      16                 :    | Borrowed code from:                                                  |
      17                 :    |          Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
      18                 :    |          Jim Winstead <jimw@php.net>                                 |
      19                 :    +----------------------------------------------------------------------+
      20                 :  */
      21                 : 
      22                 : /* $Id: streams.c,v 1.82.2.6.2.12 2007/03/03 19:01:34 helly Exp $ */
      23                 : 
      24                 : #define _GNU_SOURCE
      25                 : #include "php.h"
      26                 : #include "php_globals.h"
      27                 : #include "php_network.h"
      28                 : #include "php_open_temporary_file.h"
      29                 : #include "ext/standard/file.h"
      30                 : #include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
      31                 : #include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */
      32                 : #include <stddef.h>
      33                 : #include <fcntl.h>
      34                 : #include "php_streams_int.h"
      35                 : 
      36                 : /* {{{ resource and registration code */
      37                 : /* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */
      38                 : static HashTable url_stream_wrappers_hash;
      39                 : static int le_stream = FAILURE; /* true global */
      40                 : static int le_pstream = FAILURE; /* true global */
      41                 : static int le_stream_filter = FAILURE; /* true global */
      42                 : 
      43                 : PHPAPI int php_file_le_stream(void)
      44           28459 : {
      45           28459 :         return le_stream;
      46                 : }
      47                 : 
      48                 : PHPAPI int php_file_le_pstream(void)
      49           27715 : {
      50           27715 :         return le_pstream;
      51                 : }
      52                 : 
      53                 : PHPAPI int php_file_le_stream_filter(void)
      54              13 : {
      55              13 :         return le_stream_filter;
      56                 : }
      57                 : 
      58                 : PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D)
      59               0 : {
      60               0 :         return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
      61                 : }
      62                 : 
      63                 : PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void)
      64               0 : {
      65               0 :         return &url_stream_wrappers_hash;
      66                 : }
      67                 : 
      68                 : static int _php_stream_release_context(zend_rsrc_list_entry *le, void *pContext TSRMLS_DC)
      69               0 : {
      70               0 :         if (le->ptr == pContext) {
      71               0 :                 return --le->refcount == 0;
      72                 :         }
      73               0 :         return 0;
      74                 : }
      75                 : 
      76                 : static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC)
      77               0 : {
      78                 :         php_stream *stream;
      79                 : 
      80               0 :         if (Z_TYPE_P(rsrc) != le_pstream) {
      81               0 :                 return 0;
      82                 :         }
      83                 : 
      84               0 :         stream = (php_stream*)rsrc->ptr;
      85                 : 
      86                 : #if STREAM_DEBUG
      87                 : fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
      88                 : #endif
      89                 : 
      90               0 :         stream->rsrc_id = FAILURE;
      91                 : 
      92               0 :         if (stream->context) {
      93               0 :                 zend_hash_apply_with_argument(&EG(regular_list),
      94                 :                                 (apply_func_arg_t) _php_stream_release_context,
      95                 :                                 stream->context TSRMLS_CC);
      96               0 :                 stream->context = NULL;
      97                 :         }
      98                 :         
      99               0 :         return 0;
     100                 : }
     101                 : 
     102                 : PHP_RSHUTDOWN_FUNCTION(streams)
     103             219 : {
     104             219 :         zend_hash_apply(&EG(persistent_list), (apply_func_t)forget_persistent_resource_id_numbers TSRMLS_CC);
     105             219 :         return SUCCESS;
     106                 : }
     107                 : 
     108                 : PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC)
     109               0 : {
     110                 :         zend_rsrc_list_entry *le;
     111                 : 
     112               0 :         if (zend_hash_find(&EG(persistent_list), (char*)persistent_id, strlen(persistent_id)+1, (void*) &le) == SUCCESS) {
     113               0 :                 if (Z_TYPE_P(le) == le_pstream) {
     114               0 :                         if (stream) {
     115               0 :                                 *stream = (php_stream*)le->ptr;
     116               0 :                                 le->refcount++;
     117               0 :                                 (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream);
     118                 :                         }
     119               0 :                         return PHP_STREAM_PERSISTENT_SUCCESS;
     120                 :                 }
     121               0 :                 return PHP_STREAM_PERSISTENT_FAILURE;
     122                 :         }
     123               0 :         return PHP_STREAM_PERSISTENT_NOT_EXIST;
     124                 : }
     125                 : 
     126                 : /* }}} */
     127                 : 
     128                 : /* {{{ wrapper error reporting */
     129                 : void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
     130               0 : {
     131               0 :         char *tmp = estrdup(path);
     132                 :         char *msg;
     133               0 :         int free_msg = 0;
     134                 : 
     135               0 :         if (wrapper) {
     136               0 :                 if (wrapper->err_count > 0) {
     137                 :                         int i;
     138                 :                         size_t l;
     139                 :                         int brlen;
     140                 :                         char *br;
     141                 : 
     142               0 :                         if (PG(html_errors)) {
     143               0 :                                 brlen = 7;
     144               0 :                                 br = "<br />\n";
     145                 :                         } else {
     146               0 :                                 brlen = 1;
     147               0 :                                 br = "\n";
     148                 :                         }
     149                 : 
     150               0 :                         for (i = 0, l = 0; i < wrapper->err_count; i++) {
     151               0 :                                 l += strlen(wrapper->err_stack[i]);
     152               0 :                                 if (i < wrapper->err_count - 1) {
     153               0 :                                         l += brlen;
     154                 :                                 }
     155                 :                         }
     156               0 :                         msg = emalloc(l + 1);
     157               0 :                         msg[0] = '\0';
     158               0 :                         for (i = 0; i < wrapper->err_count; i++) {
     159               0 :                                 strcat(msg, wrapper->err_stack[i]);
     160               0 :                                 if (i < wrapper->err_count - 1) { 
     161               0 :                                         strcat(msg, br);
     162                 :                                 }
     163                 :                         }
     164                 : 
     165               0 :                         free_msg = 1;
     166                 :                 } else {
     167               0 :                         msg = strerror(errno);
     168                 :                 }
     169                 :         } else {
     170               0 :                 msg = "no suitable wrapper could be found";
     171                 :         }
     172                 : 
     173               0 :         php_strip_url_passwd(tmp);
     174               0 :         php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg);
     175               0 :         efree(tmp);
     176               0 :         if (free_msg) {
     177               0 :                 efree(msg);
     178                 :         }
     179               0 : }
     180                 : 
     181                 : void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
     182             769 : {
     183             769 :         if (wrapper) {
     184                 :                 /* tidy up the error stack */
     185                 :                 int i;
     186                 : 
     187             769 :                 for (i = 0; i < wrapper->err_count; i++) {
     188               0 :                         efree(wrapper->err_stack[i]);
     189                 :                 }
     190             769 :                 if (wrapper->err_stack) {
     191               0 :                         efree(wrapper->err_stack);
     192                 :                 }
     193             769 :                 wrapper->err_stack = NULL;
     194             769 :                 wrapper->err_count = 0;
     195                 :         }
     196             769 : }
     197                 : 
     198                 : PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...)
     199               0 : {
     200                 :         va_list args;
     201               0 :         char *buffer = NULL;
     202                 : 
     203               0 :         va_start(args, fmt);
     204               0 :         vspprintf(&buffer, 0, fmt, args);
     205               0 :         va_end(args);
     206                 : 
     207               0 :         if (options & REPORT_ERRORS || wrapper == NULL) {
     208               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buffer);
     209               0 :                 efree(buffer);
     210                 :         } else {
     211                 :                 /* append to stack */
     212               0 :                 wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *));
     213               0 :                 if (wrapper->err_stack) {
     214               0 :                         wrapper->err_stack[wrapper->err_count++] = buffer;
     215                 :                 }
     216                 :         }
     217               0 : }
     218                 : 
     219                 : 
     220                 : /* }}} */
     221                 : 
     222                 : /* allocate a new stream for a particular ops */
     223                 : PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */
     224            1434 : {
     225                 :         php_stream *ret;
     226                 : 
     227            1434 :         ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0);
     228                 : 
     229            1434 :         memset(ret, 0, sizeof(php_stream));
     230                 : 
     231            1434 :         ret->readfilters.stream = ret;
     232            1434 :         ret->writefilters.stream = ret;
     233                 : 
     234                 : #if STREAM_DEBUG
     235                 : fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
     236                 : #endif
     237                 : 
     238            1434 :         ret->ops = ops;
     239            1434 :         ret->abstract = abstract;
     240            1434 :         ret->is_persistent = persistent_id ? 1 : 0;
     241            1434 :         ret->chunk_size = FG(def_chunk_size);
     242                 : 
     243            1434 :         if (FG(auto_detect_line_endings)) {
     244               0 :                 ret->flags |= PHP_STREAM_FLAG_DETECT_EOL;
     245                 :         }
     246                 : 
     247            1434 :         if (persistent_id) {
     248                 :                 zend_rsrc_list_entry le;
     249                 : 
     250               0 :                 Z_TYPE(le) = le_pstream;
     251               0 :                 le.ptr = ret;
     252               0 :                 le.refcount = 0;
     253                 : 
     254               0 :                 if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id,
     255                 :                                         strlen(persistent_id) + 1,
     256                 :                                         (void *)&le, sizeof(le), NULL)) {
     257                 :                         
     258               0 :                         pefree(ret, 1);
     259               0 :                         return NULL;
     260                 :                 }
     261                 :         }
     262                 : 
     263            1434 :         ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream);
     264            1434 :         strlcpy(ret->mode, mode, sizeof(ret->mode));
     265                 : 
     266            1434 :         return ret;
     267                 : }
     268                 : /* }}} */
     269                 : 
     270                 : static int _php_stream_free_persistent(zend_rsrc_list_entry *le, void *pStream TSRMLS_DC)
     271               0 : {
     272               0 :         return le->ptr == pStream;
     273                 : }
     274                 : 
     275                 : PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */
     276            1965 : {
     277            1965 :         int ret = 1;
     278            1965 :         int remove_rsrc = 1;
     279            1965 :         int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0;
     280            1965 :         int release_cast = 1;
     281                 : 
     282            1965 :         if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) {
     283               0 :                 preserve_handle = 1;
     284                 :         }
     285                 : 
     286                 : #if STREAM_DEBUG
     287                 : fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->label, stream, stream->orig_path, stream->in_free, close_options);
     288                 : #endif
     289                 : 
     290                 :         /* recursion protection */
     291            1965 :         if (stream->in_free) {
     292             531 :                 return 1;
     293                 :         }
     294                 : 
     295            1434 :         stream->in_free++;
     296                 : 
     297                 :         /* if we are releasing the stream only (and preserving the underlying handle),
     298                 :          * we need to do things a little differently.
     299                 :          * We are only ever called like this when the stream is cast to a FILE*
     300                 :          * for include (or other similar) purposes.
     301                 :          * */
     302            1434 :         if (preserve_handle) {
     303               0 :                 if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     304                 :                         /* If the stream was fopencookied, we must NOT touch anything
     305                 :                          * here, as the cookied stream relies on it all.
     306                 :                          * Instead, mark the stream as OK to auto-clean */
     307                 :                         php_stream_auto_cleanup(stream);
     308               0 :                         stream->in_free--;
     309               0 :                         return 0;
     310                 :                 }
     311                 :                 /* otherwise, make sure that we don't close the FILE* from a cast */
     312               0 :                 release_cast = 0;
     313                 :         }
     314                 : 
     315                 : #if STREAM_DEBUG
     316                 : fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n",
     317                 :                 stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc);
     318                 : #endif
     319                 : 
     320                 :         /* make sure everything is saved */
     321            1434 :         _php_stream_flush(stream, 1 TSRMLS_CC);
     322                 :                 
     323                 :         /* If not called from the resource dtor, remove the stream from the resource list. */
     324            1434 :         if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) {
     325             531 :                 zend_list_delete(stream->rsrc_id);
     326                 :         }
     327                 : 
     328                 :         /* Remove stream from any context link list */
     329            1434 :         if (stream->context && stream->context->links) {
     330               0 :                 php_stream_context_del_link(stream->context, stream);
     331                 :         }
     332                 : 
     333            1434 :         if (close_options & PHP_STREAM_FREE_CALL_DTOR) {
     334            1434 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     335                 :                         /* calling fclose on an fopencookied stream will ultimately
     336                 :                                 call this very same function.  If we were called via fclose,
     337                 :                                 the cookie_closer unsets the fclose_stdiocast flags, so
     338                 :                                 we can be sure that we only reach here when PHP code calls
     339                 :                                 php_stream_free.
     340                 :                                 Lets let the cookie code clean it all up.
     341                 :                          */
     342               0 :                         stream->in_free = 0;
     343               0 :                         return fclose(stream->stdiocast);
     344                 :                 }
     345                 : 
     346            1434 :                 ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);
     347            1434 :                 stream->abstract = NULL;
     348                 : 
     349                 :                 /* tidy up any FILE* that might have been fdopened */
     350            1434 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
     351               0 :                         fclose(stream->stdiocast);
     352               0 :                         stream->stdiocast = NULL;
     353               0 :                         stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
     354                 :                 }
     355                 :         }
     356                 : 
     357            1434 :         if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
     358            2869 :                 while (stream->readfilters.head) {
     359               1 :                         php_stream_filter_remove(stream->readfilters.head, 1 TSRMLS_CC);
     360                 :                 }
     361            2880 :                 while (stream->writefilters.head) {
     362              12 :                         php_stream_filter_remove(stream->writefilters.head, 1 TSRMLS_CC);
     363                 :                 }
     364                 : 
     365            1434 :                 if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
     366               0 :                         stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC);
     367               0 :                         stream->wrapper = NULL;
     368                 :                 }
     369                 : 
     370            1434 :                 if (stream->wrapperdata) {
     371               0 :                         zval_ptr_dtor(&stream->wrapperdata);
     372               0 :                         stream->wrapperdata = NULL;
     373                 :                 }
     374                 : 
     375            1434 :                 if (stream->readbuf) {
     376             447 :                         pefree(stream->readbuf, stream->is_persistent);
     377             447 :                         stream->readbuf = NULL;
     378                 :                 }
     379                 : 
     380            1434 :                 if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) {
     381                 :                         /* we don't work with *stream but need its value for comparison */
     382               0 :                         zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _php_stream_free_persistent, stream TSRMLS_CC);
     383                 :                 }
     384                 : #if ZEND_DEBUG
     385                 :                 if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
     386                 :                         /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
     387                 :                          * as leaked; it will log a warning, but lets help it out and display what kind
     388                 :                          * of stream it was. */
     389                 :                         char *leakinfo;
     390                 :                         spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path);
     391                 : 
     392                 :                         if (stream->orig_path) {
     393                 :                                 pefree(stream->orig_path, stream->is_persistent);
     394                 :                                 stream->orig_path = NULL;
     395                 :                         }
     396                 :                         
     397                 : # if defined(PHP_WIN32)
     398                 :                         OutputDebugString(leakinfo);
     399                 : # else
     400                 :                         fprintf(stderr, "%s", leakinfo);
     401                 : # endif
     402                 :                         efree(leakinfo);
     403                 :                 } else {
     404                 :                         if (stream->orig_path) {
     405                 :                                 pefree(stream->orig_path, stream->is_persistent);
     406                 :                                 stream->orig_path = NULL;
     407                 :                         }
     408                 : 
     409                 :                         pefree(stream, stream->is_persistent);
     410                 :                 }
     411                 : #else
     412            1434 :                 if (stream->orig_path) {
     413             755 :                         pefree(stream->orig_path, stream->is_persistent);
     414             755 :                         stream->orig_path = NULL;
     415                 :                 }
     416                 : 
     417            1434 :                 pefree(stream, stream->is_persistent);
     418                 : #endif
     419                 :         }
     420                 : 
     421            1434 :         return ret;
     422                 : }
     423                 : /* }}} */
     424                 : 
     425                 : /* {{{ generic stream operations */
     426                 : 
     427                 : static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC)
     428            2377 : {
     429                 :         /* allocate/fill the buffer */
     430                 : 
     431            2377 :         if (stream->readfilters.head) {
     432                 :                 char *chunk_buf;
     433               2 :                 int err_flag = 0;
     434               2 :                 php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
     435               2 :                 php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
     436                 : 
     437                 :                 /* allocate a buffer for reading chunks */
     438               2 :                 chunk_buf = emalloc(stream->chunk_size);
     439                 : 
     440               5 :                 while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) {
     441               2 :                         size_t justread = 0;
     442                 :                         int flags;
     443                 :                         php_stream_bucket *bucket;
     444               2 :                         php_stream_filter_status_t status = PSFS_ERR_FATAL;
     445                 :                         php_stream_filter *filter;
     446                 : 
     447                 :                         /* read a chunk into a bucket */
     448               2 :                         justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC);
     449               3 :                         if (justread && justread != (size_t)-1) {
     450               1 :                                 bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC);
     451                 : 
     452                 :                                 /* after this call, bucket is owned by the brigade */
     453               1 :                                 php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
     454                 : 
     455               1 :                                 flags = PSFS_FLAG_NORMAL;
     456                 :                         } else {
     457               1 :                                 flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC;
     458                 :                         }
     459                 :                 
     460                 :                         /* wind the handle... */
     461               3 :                         for (filter = stream->readfilters.head; filter; filter = filter->next) {
     462               2 :                                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC);
     463                 : 
     464               2 :                                 if (status != PSFS_PASS_ON) {
     465               1 :                                         break;
     466                 :                                 }
     467                 :                                 
     468                 :                                 /* brig_out becomes brig_in.
     469                 :                                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
     470                 :                                  * to its own brigade */
     471               1 :                                 brig_swap = brig_inp;
     472               1 :                                 brig_inp = brig_outp;
     473               1 :                                 brig_outp = brig_swap;
     474               1 :                                 memset(brig_outp, 0, sizeof(*brig_outp));
     475                 :                         }
     476                 :                         
     477               2 :                         switch (status) {
     478                 :                                 case PSFS_PASS_ON:
     479                 :                                         /* we get here when the last filter in the chain has data to pass on.
     480                 :                                          * in this situation, we are passing the brig_in brigade into the
     481                 :                                          * stream read buffer */
     482               5 :                                         while (brig_inp->head) {
     483               3 :                                                 bucket = brig_inp->head;
     484                 :                                                 /* grow buffer to hold this bucket
     485                 :                                                  * TODO: this can fail for persistent streams */
     486               3 :                                                 if (stream->readbuflen - stream->writepos < bucket->buflen) {
     487               3 :                                                         stream->readbuflen += bucket->buflen;
     488               3 :                                                         stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
     489                 :                                                                         stream->is_persistent);
     490                 :                                                 }
     491               3 :                                                 memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
     492               3 :                                                 stream->writepos += bucket->buflen;
     493                 :                                                 
     494               3 :                                                 php_stream_bucket_unlink(bucket TSRMLS_CC);
     495               3 :                                                 php_stream_bucket_delref(bucket TSRMLS_CC);
     496                 :                                         }
     497                 : 
     498               1 :                                         break;
     499                 : 
     500                 :                                 case PSFS_FEED_ME:
     501                 :                                         /* when a filter needs feeding, there is no brig_out to deal with.
     502                 :                                          * we simply continue the loop; if the caller needs more data,
     503                 :                                          * we will read again, otherwise out job is done here */
     504               1 :                                         if (justread == 0) {
     505                 :                                                 /* there is no data */
     506               1 :                                                 err_flag = 1;
     507               1 :                                                 break;
     508                 :                                         }
     509               0 :                                         continue;
     510                 : 
     511                 :                                 case PSFS_ERR_FATAL:
     512                 :                                         /* some fatal error. Theoretically, the stream is borked, so all
     513                 :                                          * further reads should fail. */
     514               0 :                                         err_flag = 1;
     515                 :                                         break;
     516                 :                         }
     517                 : 
     518               2 :                         if (justread == 0 || justread == (size_t)-1) {
     519                 :                                 break;
     520                 :                         }
     521                 :                 }
     522                 : 
     523               2 :                 efree(chunk_buf);
     524                 : 
     525                 :         } else {
     526                 :                 /* is there enough data in the buffer ? */
     527            2375 :                 if (stream->writepos - stream->readpos < (off_t)size) {
     528            2375 :                         size_t justread = 0;
     529                 : 
     530                 :                         /* reduce buffer memory consumption if possible, to avoid a realloc */
     531            2375 :                         if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) {
     532            1812 :                                 memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos);
     533            1812 :                                 stream->writepos -= stream->readpos;
     534            1812 :                                 stream->readpos = 0;
     535                 :                         }
     536                 : 
     537                 :                         /* grow the buffer if required
     538                 :                          * TODO: this can fail for persistent streams */
     539            2375 :                         if (stream->readbuflen - stream->writepos < stream->chunk_size) {
     540             446 :                                 stream->readbuflen += stream->chunk_size;
     541             446 :                                 stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
     542                 :                                                 stream->is_persistent);
     543                 :                         }
     544                 : 
     545            2375 :                         justread = stream->ops->read(stream, stream->readbuf + stream->writepos,
     546                 :                                         stream->readbuflen - stream->writepos
     547                 :                                         TSRMLS_CC);
     548                 : 
     549            2375 :                         if (justread != (size_t)-1) {
     550            2375 :                                 stream->writepos += justread;
     551                 :                         }
     552                 :                 }
     553                 :         }
     554            2377 : }
     555                 : 
     556                 : PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
     557            2784 : {
     558            2784 :         size_t toread = 0, didread = 0;
     559                 : 
     560            6417 :         while (size > 0) {
     561                 : 
     562                 :                 /* take from the read buffer first.
     563                 :                  * It is possible that a buffered stream was switched to non-buffered, so we
     564                 :                  * drain the remainder of the buffer before using the "raw" read mode for
     565                 :                  * the excess */
     566            2902 :                 if (stream->writepos > stream->readpos) {
     567                 : 
     568               0 :                         toread = stream->writepos - stream->readpos;
     569               0 :                         if (toread > size) {
     570               0 :                                 toread = size;
     571                 :                         }
     572                 : 
     573               0 :                         memcpy(buf, stream->readbuf + stream->readpos, toread);
     574               0 :                         stream->readpos += toread;
     575               0 :                         size -= toread;
     576               0 :                         buf += toread;
     577               0 :                         didread += toread;
     578                 :                 }
     579                 : 
     580                 :                 /* ignore eof here; the underlying state might have changed */
     581            2902 :                 if (size == 0) {
     582               0 :                         break;
     583                 :                 }
     584                 : 
     585            3647 :                 if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) {
     586             745 :                         toread = stream->ops->read(stream, buf, size TSRMLS_CC);
     587                 :                 } else {
     588            2157 :                         php_stream_fill_read_buffer(stream, size TSRMLS_CC);
     589                 : 
     590            2157 :                         toread = stream->writepos - stream->readpos;
     591            2157 :                         if (toread > size) {
     592               0 :                                 toread = size;
     593                 :                         }
     594                 : 
     595            2157 :                         if (toread > 0) {
     596            1703 :                                 memcpy(buf, stream->readbuf + stream->readpos, toread);
     597            1703 :                                 stream->readpos += toread;
     598                 :                         }
     599                 :                 }
     600            2902 :                 if (toread > 0) {
     601            2434 :                         didread += toread;
     602            2434 :                         buf += toread;
     603            2434 :                         size -= toread;
     604                 :                 } else {
     605                 :                         /* EOF, or temporary end of data (for non-blocking mode). */
     606             468 :                         break;
     607                 :                 }
     608                 : 
     609                 :                 /* just break anyway, to avoid greedy read */
     610            2434 :                 if (stream->wrapper != &php_plain_files_wrapper) {
     611            1585 :                         break;
     612                 :                 }
     613                 :         }
     614                 : 
     615            2784 :         if (didread > 0) {
     616            2434 :                 stream->position += didread;
     617                 :         }
     618                 : 
     619            2784 :         return didread;
     620                 : }
     621                 : 
     622                 : PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
     623            4720 : {
     624                 :         /* if there is data in the buffer, it's not EOF */
     625            4720 :         if (stream->writepos - stream->readpos > 0) {
     626            4398 :                 return 0;
     627                 :         }
     628                 : 
     629                 :         /* use the configured timeout when checking eof */
     630             322 :         if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR ==
     631                 :                         php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS,
     632                 :                         0, NULL)) {
     633               0 :                 stream->eof = 1;
     634                 :         }
     635                 : 
     636             322 :         return stream->eof;
     637                 : }
     638                 : 
     639                 : PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC)
     640               0 : {
     641               0 :         unsigned char buf = c;
     642                 : 
     643               0 :         if (php_stream_write(stream, &buf, 1) > 0) {
     644               0 :                 return 1;
     645                 :         }
     646               0 :         return EOF;
     647                 : }
     648                 : 
     649                 : PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC)
     650               0 : {
     651                 :         char buf;
     652                 : 
     653               0 :         if (php_stream_read(stream, &buf, 1) > 0) {
     654               0 :                 return buf & 0xff;
     655                 :         }
     656               0 :         return EOF;
     657                 : }
     658                 : 
     659                 : PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC)
     660               0 : {
     661                 :         int len;
     662               0 :         char newline[2] = "\n"; /* is this OK for Win? */
     663               0 :         len = strlen(buf);
     664                 : 
     665               0 :         if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) {
     666               0 :                 return 1;
     667                 :         }
     668               0 :         return 0;
     669                 : }
     670                 : 
     671                 : PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
     672              18 : {
     673              18 :         memset(ssb, 0, sizeof(*ssb));
     674                 : 
     675                 :         /* if the stream was wrapped, allow the wrapper to stat it */
     676              18 :         if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) {
     677               0 :                 return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC);
     678                 :         }
     679                 : 
     680                 :         /* if the stream doesn't directly support stat-ing, return with failure.
     681                 :          * We could try and emulate this by casting to a FD and fstat-ing it,
     682                 :          * but since the fd might not represent the actual underlying content
     683                 :          * this would give bogus results. */
     684              18 :         if (stream->ops->stat == NULL) {
     685               0 :                 return -1;
     686                 :         }
     687                 : 
     688              18 :         return (stream->ops->stat)(stream, ssb TSRMLS_CC);
     689                 : }
     690                 : 
     691                 : PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC)
     692            4509 : {
     693                 :         size_t avail;
     694            4509 :         char *cr, *lf, *eol = NULL;
     695                 :         char *readptr;
     696                 : 
     697            4509 :         if (!buf) {
     698            4508 :                 readptr = stream->readbuf + stream->readpos;
     699            4508 :                 avail = stream->writepos - stream->readpos;
     700                 :         } else {
     701               1 :                 readptr = buf;
     702               1 :                 avail = buf_len;
     703                 :         }       
     704                 : 
     705                 :         /* Look for EOL */
     706            4509 :         if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
     707               0 :                 cr = memchr(readptr, '\r', avail);
     708               0 :                 lf = memchr(readptr, '\n', avail);
     709                 : 
     710               0 :                 if (cr && lf != cr + 1 && !(lf && lf < cr)) {
     711                 :                         /* mac */
     712               0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     713               0 :                         stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
     714               0 :                         eol = cr;
     715               0 :                 } else if ((cr && lf && cr == lf - 1) || (lf)) {
     716                 :                         /* dos or unix endings */
     717               0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     718               0 :                         eol = lf;
     719                 :                 }
     720            4509 :         } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
     721               0 :                 eol = memchr(readptr, '\r', avail);
     722                 :         } else {
     723                 :                 /* unix (and dos) line endings */
     724            4509 :                 eol = memchr(readptr, '\n', avail);
     725                 :         }
     726                 : 
     727            4509 :         return eol;
     728                 : }
     729                 : 
     730                 : /* If buf == NULL, the buffer will be allocated automatically and will be of an
     731                 :  * appropriate length to hold the line, regardless of the line length, memory
     732                 :  * permitting */
     733                 : PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
     734                 :                 size_t *returned_len TSRMLS_DC)
     735            4610 : {
     736            4610 :         size_t avail = 0;
     737            4610 :         size_t current_buf_size = 0;
     738            4610 :         size_t total_copied = 0;
     739            4610 :         int grow_mode = 0;
     740            4610 :         char *bufstart = buf;
     741                 : 
     742            4610 :         if (buf == NULL) {
     743            4610 :                 grow_mode = 1;
     744               0 :         } else if (maxlen == 0) {
     745               0 :                 return NULL;
     746                 :         }
     747                 : 
     748                 :         /*
     749                 :          * If the underlying stream operations block when no new data is readable,
     750                 :          * we need to take extra precautions.
     751                 :          *
     752                 :          * If there is buffered data available, we check for a EOL. If it exists,
     753                 :          * we pass the data immediately back to the caller. This saves a call
     754                 :          * to the read implementation and will not block where blocking
     755                 :          * is not necessary at all.
     756                 :          *
     757                 :          * If the stream buffer contains more data than the caller requested,
     758                 :          * we can also avoid that costly step and simply return that data.
     759                 :          */
     760                 : 
     761                 :         for (;;) {
     762            4728 :                 avail = stream->writepos - stream->readpos;
     763                 : 
     764            4728 :                 if (avail > 0) {
     765            4508 :                         size_t cpysz = 0;
     766                 :                         char *readptr;
     767                 :                         char *eol;
     768            4508 :                         int done = 0;
     769                 : 
     770            4508 :                         readptr = stream->readbuf + stream->readpos;
     771            4508 :                         eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC);
     772                 : 
     773            4508 :                         if (eol) {
     774            4500 :                                 cpysz = eol - readptr + 1;
     775            4500 :                                 done = 1;
     776                 :                         } else {
     777               8 :                                 cpysz = avail;
     778                 :                         }
     779                 : 
     780            4508 :                         if (grow_mode) {
     781                 :                                 /* allow room for a NUL. If this realloc is really a realloc
     782                 :                                  * (ie: second time around), we get an extra byte. In most
     783                 :                                  * cases, with the default chunk size of 8K, we will only
     784                 :                                  * incur that overhead once.  When people have lines longer
     785                 :                                  * than 8K, we waste 1 byte per additional 8K or so.
     786                 :                                  * That seems acceptable to me, to avoid making this code
     787                 :                                  * hard to follow */
     788            4508 :                                 bufstart = erealloc(bufstart, current_buf_size + cpysz + 1);
     789            4508 :                                 current_buf_size += cpysz + 1;
     790            4508 :                                 buf = bufstart + total_copied;
     791                 :                         } else {
     792               0 :                                 if (cpysz >= maxlen - 1) {
     793               0 :                                         cpysz = maxlen - 1;
     794               0 :                                         done = 1;
     795                 :                                 }
     796                 :                         }
     797                 : 
     798            4508 :                         memcpy(buf, readptr, cpysz);
     799                 : 
     800            4508 :                         stream->position += cpysz;
     801            4508 :                         stream->readpos += cpysz;
     802            4508 :                         buf += cpysz;
     803            4508 :                         maxlen -= cpysz;
     804            4508 :                         total_copied += cpysz;
     805                 : 
     806            4508 :                         if (done) {
     807            4500 :                                 break;
     808                 :                         }
     809             220 :                 } else if (stream->eof) {
     810               0 :                         break;
     811                 :                 } else {
     812                 :                         /* XXX: Should be fine to always read chunk_size */
     813                 :                         size_t toread;
     814                 :                         
     815             220 :                         if (grow_mode) {
     816             220 :                                 toread = stream->chunk_size;
     817                 :                         } else {
     818               0 :                                 toread = maxlen - 1;
     819               0 :                                 if (toread > stream->chunk_size) {
     820               0 :                                         toread = stream->chunk_size;
     821                 :                                 }
     822                 :                         }
     823                 : 
     824             220 :                         php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
     825                 : 
     826             220 :                         if (stream->writepos - stream->readpos == 0) {
     827             110 :                                 break;
     828                 :                         }
     829                 :                 }
     830             118 :         }
     831                 : 
     832            4610 :         if (total_copied == 0) {
     833                 :                 if (grow_mode) {
     834                 :                         assert(bufstart == NULL);
     835                 :                 }
     836             102 :                 return NULL;
     837                 :         }
     838                 : 
     839            4508 :         buf[0] = '\0';
     840            4508 :         if (returned_len) {
     841            4508 :                 *returned_len = total_copied;
     842                 :         }
     843                 : 
     844            4508 :         return bufstart;
     845                 : }
     846                 : 
     847                 : PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC)
     848               0 : {
     849                 :         char *e, *buf;
     850                 :         size_t toread;
     851               0 :         int skip = 0;
     852                 : 
     853               0 :         php_stream_fill_read_buffer(stream, maxlen TSRMLS_CC);
     854                 : 
     855               0 :         if (delim_len == 0 || !delim) {
     856               0 :                 toread = maxlen;
     857                 :         } else {
     858               0 :                 if (delim_len == 1) {
     859               0 :                         e = memchr(stream->readbuf + stream->readpos, *delim, stream->writepos - stream->readpos);
     860                 :                 } else {
     861               0 :                         e = php_memnstr(stream->readbuf + stream->readpos, delim, delim_len, (stream->readbuf + stream->writepos));
     862                 :                 }
     863                 : 
     864               0 :                 if (!e) {
     865               0 :                         toread = maxlen;
     866                 :                 } else {
     867               0 :                         toread = e - (char *) stream->readbuf - stream->readpos;
     868               0 :                         skip = 1;
     869                 :                 }
     870                 :         }
     871                 : 
     872               0 :         if (toread > maxlen && maxlen > 0) {
     873               0 :                 toread = maxlen;
     874                 :         }
     875                 : 
     876               0 :         buf = emalloc(toread + 1);
     877               0 :         *returned_len = php_stream_read(stream, buf, toread);
     878                 : 
     879                 :         if (*returned_len >= 0) {
     880               0 :                 if (skip) {
     881               0 :                         stream->readpos += delim_len;
     882               0 :                         stream->position += delim_len;
     883                 :                 }
     884               0 :                 buf[*returned_len] = '\0';
     885               0 :                 return buf;
     886                 :         } else {
     887                 :                 efree(buf);
     888                 :                 return NULL;
     889                 :         }
     890                 : }
     891                 : 
     892                 : /* Writes a buffer directly to a stream, using multiple of the chunk size */
     893                 : static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
     894             320 : {
     895             320 :         size_t didwrite = 0, towrite, justwrote;
     896                 : 
     897                 :         /* if we have a seekable stream we need to ensure that data is written at the
     898                 :          * current stream->position. This means invalidating the read buffer and then
     899                 :          * performing a low-level seek */
     900             320 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) {
     901               0 :                 stream->readpos = stream->writepos = 0;
     902                 : 
     903               0 :                 stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position TSRMLS_CC);
     904                 :         }
     905                 : 
     906                 :  
     907            1149 :         while (count > 0) {
     908             509 :                 towrite = count;
     909             509 :                 if (towrite > stream->chunk_size)
     910             189 :                         towrite = stream->chunk_size;
     911                 : 
     912             509 :                 justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC);
     913                 : 
     914                 :                 /* convert justwrote to an integer, since normally it is unsigned */
     915             509 :                 if ((int)justwrote > 0) {
     916             509 :                         buf += justwrote;
     917             509 :                         count -= justwrote;
     918             509 :                         didwrite += justwrote;
     919                 :                         
     920                 :                         /* Only screw with the buffer if we can seek, otherwise we lose data
     921                 :                          * buffered from fifos and sockets */
     922             509 :                         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
     923             476 :                                 stream->position += justwrote;
     924                 :                         }
     925                 :                 } else {
     926               0 :                         break;
     927                 :                 }
     928                 :         }
     929             320 :         return didwrite;
     930                 : 
     931                 : }
     932                 : 
     933                 : /* push some data through the write filter chain.
     934                 :  * buf may be NULL, if flags are set to indicate a flush.
     935                 :  * This may trigger a real write to the stream.
     936                 :  * Returns the number of bytes consumed from buf by the first filter in the chain.
     937                 :  * */
     938                 : static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC)
     939              64 : {
     940              64 :         size_t consumed = 0;
     941                 :         php_stream_bucket *bucket;
     942              64 :         php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
     943              64 :         php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
     944              64 :         php_stream_filter_status_t status = PSFS_ERR_FATAL;
     945                 :         php_stream_filter *filter;
     946                 : 
     947              64 :         if (buf) {
     948              41 :                 bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC);
     949              41 :                 php_stream_bucket_append(&brig_in, bucket TSRMLS_CC);
     950                 :         }
     951                 : 
     952             119 :         for (filter = stream->writefilters.head; filter; filter = filter->next) {
     953                 :                 /* for our return value, we are interested in the number of bytes consumed from
     954                 :                  * the first filter in the chain */
     955             106 :                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp,
     956                 :                                 filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC);
     957                 : 
     958             106 :                 if (status != PSFS_PASS_ON) {
     959              51 :                         break;
     960                 :                 }
     961                 :                 /* brig_out becomes brig_in.
     962                 :                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
     963                 :                  * to its own brigade */
     964              55 :                 brig_swap = brig_inp;
     965              55 :                 brig_inp = brig_outp;
     966              55 :                 brig_outp = brig_swap;
     967              55 :                 memset(brig_outp, 0, sizeof(*brig_outp));
     968                 :         }
     969                 : 
     970              64 :         switch (status) {
     971                 :                 case PSFS_PASS_ON:
     972                 :                         /* filter chain generated some output; push it through to the
     973                 :                          * underlying stream */
     974              70 :                         while (brig_inp->head) {
     975              44 :                                 bucket = brig_inp->head;
     976              44 :                                 _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC);
     977                 :                                 /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade
     978                 :                                  * hanging around and try to write it later.
     979                 :                                  * At the moment, we just drop it on the floor
     980                 :                                  * */
     981                 : 
     982              44 :                                 php_stream_bucket_unlink(bucket TSRMLS_CC);
     983              44 :                                 php_stream_bucket_delref(bucket TSRMLS_CC);
     984                 :                         }
     985                 :                         break;
     986                 :                 case PSFS_FEED_ME:
     987                 :                         /* need more data before we can push data through to the stream */
     988                 :                         break;
     989                 : 
     990                 :                 case PSFS_ERR_FATAL:
     991                 :                         /* some fatal error.  Theoretically, the stream is borked, so all
     992                 :                          * further writes should fail. */
     993                 :                         break;
     994                 :         }
     995                 : 
     996              64 :         return consumed;
     997                 : }
     998                 : 
     999                 : PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC)
    1000            1452 : {
    1001            1452 :         int ret = 0;
    1002                 : 
    1003            1452 :         if (stream->writefilters.head) {
    1004              23 :                 _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC  TSRMLS_CC);
    1005                 :         }
    1006                 : 
    1007            1452 :         if (stream->ops->flush) {
    1008            1438 :                 ret = stream->ops->flush(stream TSRMLS_CC);
    1009                 :         }
    1010                 : 
    1011            1452 :         return ret;
    1012                 : }
    1013                 : 
    1014                 : PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
    1015             317 : {
    1016             317 :         if (buf == NULL || count == 0 || stream->ops->write == NULL) {
    1017               0 :                 return 0;
    1018                 :         }
    1019                 : 
    1020             317 :         if (stream->writefilters.head) {
    1021              41 :                 return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC);
    1022                 :         } else {
    1023             276 :                 return _php_stream_write_buffer(stream, buf, count TSRMLS_CC);
    1024                 :         }
    1025                 : }
    1026                 : 
    1027                 : PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...)
    1028               4 : {
    1029                 :         size_t count;
    1030                 :         char *buf;
    1031                 :         va_list ap;
    1032                 : 
    1033               4 :         va_start(ap, fmt);
    1034               4 :         count = vspprintf(&buf, 0, fmt, ap);
    1035               4 :         va_end(ap);
    1036                 : 
    1037               4 :         if (!buf) {
    1038               0 :                 return 0; /* error condition */
    1039                 :         }
    1040                 : 
    1041               4 :         count = php_stream_write(stream, buf, count);
    1042               4 :         efree(buf);
    1043                 : 
    1044               4 :         return count;
    1045                 : }
    1046                 : 
    1047                 : PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC)
    1048             124 : {
    1049             124 :         return stream->position;
    1050                 : }
    1051                 : 
    1052                 : PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
    1053              15 : {
    1054                 :         /* handle the case where we are in the buffer */
    1055              15 :         if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
    1056               0 :                 switch(whence) {
    1057                 :                         case SEEK_CUR:
    1058               0 :                                 if (offset > 0 && offset < stream->writepos - stream->readpos) {
    1059               0 :                                         stream->readpos += offset;
    1060               0 :                                         stream->position += offset;
    1061               0 :                                         stream->eof = 0;
    1062               0 :                                         return 0;
    1063                 :                                 }
    1064               0 :                                 break;
    1065                 :                         case SEEK_SET:
    1066               0 :                                 if (offset > stream->position &&
    1067                 :                                                 offset < stream->position + stream->writepos - stream->readpos) {
    1068               0 :                                         stream->readpos += offset - stream->position;
    1069               0 :                                         stream->position = offset;
    1070               0 :                                         stream->eof = 0;
    1071               0 :                                         return 0;
    1072                 :                                 }
    1073                 :                                 break;
    1074                 :                 }
    1075                 :         }
    1076                 : 
    1077                 : 
    1078              15 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
    1079                 :                 int ret;
    1080                 :                 
    1081              15 :                 if (stream->writefilters.head) {
    1082               0 :                         _php_stream_flush(stream, 0 TSRMLS_CC);
    1083                 :                 }
    1084                 :                 
    1085              15 :                 switch(whence) {
    1086                 :                         case SEEK_CUR:
    1087               0 :                                 offset = stream->position + offset;
    1088               0 :                                 whence = SEEK_SET;
    1089                 :                                 break;
    1090                 :                 }
    1091              15 :                 ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
    1092                 : 
    1093              15 :                 if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
    1094              15 :                         if (ret == 0) {
    1095              15 :                                 stream->eof = 0;
    1096                 :                         }
    1097                 : 
    1098                 :                         /* invalidate the buffer contents */
    1099              15 :                         stream->readpos = stream->writepos = 0;
    1100                 : 
    1101              15 :                         return ret;
    1102                 :                 }
    1103                 :                 /* else the stream has decided that it can't support seeking after all;
    1104                 :                  * fall through to attempt emulation */
    1105                 :         }
    1106                 : 
    1107                 :         /* emulate forward moving seeks with reads */
    1108               0 :         if (whence == SEEK_CUR && offset > 0) {
    1109                 :                 char tmp[1024];
    1110               0 :                 while(offset >= sizeof(tmp)) {
    1111               0 :                         if (php_stream_read(stream, tmp, sizeof(tmp)) == 0) {
    1112               0 :                                 return -1;
    1113                 :                         }
    1114               0 :                         offset -= sizeof(tmp);
    1115                 :                 }
    1116               0 :                 if (offset && (php_stream_read(stream, tmp, offset) == 0)) {
    1117               0 :                         return -1;
    1118                 :                 }
    1119               0 :                 stream->eof = 0;
    1120               0 :                 return 0;
    1121                 :         }
    1122                 : 
    1123               0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking");
    1124                 : 
    1125               0 :         return -1;
    1126                 : }
    1127                 : 
    1128                 : PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
    1129             595 : {
    1130             595 :         int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
    1131                 : 
    1132             595 :         if (stream->ops->set_option) {
    1133             595 :                 ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC);
    1134                 :         }
    1135                 : 
    1136             595 :         if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) {
    1137             212 :                 switch(option) {
    1138                 :                         case PHP_STREAM_OPTION_SET_CHUNK_SIZE:
    1139               0 :                                 ret = stream->chunk_size;
    1140               0 :                                 stream->chunk_size = value;
    1141               0 :                                 return ret;
    1142                 : 
    1143                 :                         case PHP_STREAM_OPTION_READ_BUFFER:
    1144                 :                                 /* try to match the buffer mode as best we can */
    1145               0 :                                 if (value == PHP_STREAM_BUFFER_NONE) {
    1146               0 :                                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
    1147                 :                                 } else {
    1148               0 :                                         stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
    1149                 :                                 }
    1150               0 :                                 ret = PHP_STREAM_OPTION_RETURN_OK;
    1151                 :                                 break;
    1152                 :                                 
    1153                 :                         default:
    1154                 :                                 ;
    1155                 :                 }
    1156                 :         }
    1157                 : 
    1158             595 :         return ret;
    1159                 : }
    1160                 : 
    1161                 : PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC)
    1162               0 : {
    1163               0 :         return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
    1164                 : }
    1165                 : 
    1166                 : PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
    1167               0 : {
    1168               0 :         size_t bcount = 0;
    1169                 :         char buf[8192];
    1170                 :         int b;
    1171                 : 
    1172               0 :         if (php_stream_mmap_possible(stream)) {
    1173                 :                 char *p;
    1174                 :                 size_t mapped;
    1175                 : 
    1176               0 :                 p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_COPY_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1177                 : 
    1178               0 :                 if (p) {
    1179               0 :                         PHPWRITE(p, mapped);
    1180                 : 
    1181               0 :                         php_stream_mmap_unmap(stream);
    1182                 : 
    1183               0 :                         return mapped;
    1184                 :                 }
    1185                 :         }
    1186                 : 
    1187               0 :         while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
    1188               0 :                 PHPWRITE(buf, b);
    1189               0 :                 bcount += b;
    1190                 :         }
    1191                 : 
    1192               0 :         return bcount;
    1193                 : }
    1194                 : 
    1195                 : 
    1196                 : PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC)
    1197             124 : {
    1198             124 :         size_t ret = 0;
    1199                 :         char *ptr;
    1200             124 :         size_t len = 0, max_len;
    1201             124 :         int step = CHUNK_SIZE;
    1202             124 :         int min_room = CHUNK_SIZE / 4;
    1203                 :         php_stream_statbuf ssbuf;
    1204                 : 
    1205             124 :         if (maxlen == 0) { 
    1206               0 :                 return 0;
    1207                 :         }
    1208                 : 
    1209             124 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1210             124 :                 maxlen = 0;
    1211                 :         }
    1212                 : 
    1213             124 :         if (php_stream_mmap_possible(src)) {
    1214                 :                 char *p;
    1215                 :                 size_t mapped;
    1216                 : 
    1217             124 :                 p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1218                 : 
    1219             124 :                 if (p && mapped) {
    1220             121 :                         *buf = pemalloc_rel_orig(mapped + 1, persistent);
    1221                 : 
    1222             121 :                         if (*buf) {
    1223             121 :                                 memcpy(*buf, p, mapped);
    1224             121 :                                 (*buf)[mapped] = '\0';
    1225                 :                         }
    1226                 : 
    1227             121 :                         php_stream_mmap_unmap(src);
    1228                 : 
    1229             121 :                         return mapped;
    1230                 :                 }
    1231                 :         }
    1232                 : 
    1233               3 :         if (maxlen > 0) {
    1234               0 :                 ptr = *buf = pemalloc_rel_orig(maxlen + 1, persistent);
    1235               0 :                 while ((len < maxlen) & !php_stream_eof(src)) {
    1236               0 :                         ret = php_stream_read(src, ptr, maxlen - len);
    1237               0 :                         len += ret;
    1238               0 :                         ptr += ret;
    1239                 :                 }
    1240               0 :                 *ptr = '\0';
    1241               0 :                 return len;
    1242                 :         }
    1243                 : 
    1244                 :         /* avoid many reallocs by allocating a good sized chunk to begin with, if
    1245                 :          * we can.  Note that the stream may be filtered, in which case the stat
    1246                 :          * result may be inaccurate, as the filter may inflate or deflate the
    1247                 :          * number of bytes that we can read.  In order to avoid an upsize followed
    1248                 :          * by a downsize of the buffer, overestimate by the step size (which is
    1249                 :          * 2K).  */
    1250               3 :         if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
    1251               0 :                 max_len = ssbuf.sb.st_size + step;
    1252                 :         } else {
    1253               3 :                 max_len = step;
    1254                 :         }
    1255                 : 
    1256               3 :         ptr = *buf = pemalloc_rel_orig(max_len, persistent);
    1257                 : 
    1258               9 :         while((ret = php_stream_read(src, ptr, max_len - len))) {
    1259               3 :                 len += ret;
    1260               3 :                 if (len + min_room >= max_len) {
    1261               0 :                         *buf = perealloc_rel_orig(*buf, max_len + step, persistent);
    1262               0 :                         max_len += step;
    1263               0 :                         ptr = *buf + len;
    1264                 :                 } else {
    1265               3 :                         ptr += ret;
    1266                 :                 }
    1267                 :         }
    1268               3 :         if (len) {
    1269               3 :                 *buf = perealloc_rel_orig(*buf, len + 1, persistent);
    1270               3 :                 (*buf)[len] = '\0';
    1271                 :         } else {
    1272               0 :                 pefree(*buf, persistent);
    1273               0 :                 *buf = NULL;
    1274                 :         }
    1275               3 :         return len;
    1276                 : }
    1277                 : 
    1278                 : PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC)
    1279               0 : {
    1280                 :         char buf[CHUNK_SIZE];
    1281                 :         size_t readchunk;
    1282               0 :         size_t haveread = 0;
    1283                 :         size_t didread;
    1284                 :         php_stream_statbuf ssbuf;
    1285                 : 
    1286               0 :         if (maxlen == 0) {
    1287               0 :                 return 0;
    1288                 :         }
    1289                 : 
    1290               0 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1291               0 :                 maxlen = 0;
    1292                 :         }
    1293                 : 
    1294               0 :         if (php_stream_stat(src, &ssbuf) == 0) {
    1295                 :                 /* in the event that the source file is 0 bytes, return 1 to indicate success
    1296                 :                  * because opening the file to write had already created a copy */
    1297               0 :                 if (ssbuf.sb.st_size == 0
    1298                 : #ifdef S_ISFIFO
    1299                 :                  && !S_ISFIFO(ssbuf.sb.st_mode)
    1300                 : #endif
    1301                 : #ifdef S_ISCHR
    1302                 :                  && !S_ISCHR(ssbuf.sb.st_mode)
    1303                 : #endif
    1304                 :                 ) {
    1305               0 :                         return 1;
    1306                 :                 }
    1307                 :         }
    1308                 : 
    1309               0 :         if (php_stream_mmap_possible(src)) {
    1310                 :                 char *p;
    1311                 :                 size_t mapped;
    1312                 : 
    1313               0 :                 p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1314                 : 
    1315               0 :                 if (p) {
    1316               0 :                         haveread = php_stream_write(dest, p, mapped);
    1317                 : 
    1318               0 :                         php_stream_mmap_unmap(src);
    1319                 : 
    1320               0 :                         return mapped;
    1321                 :                 }
    1322                 :         }
    1323                 : 
    1324                 :         while(1) {
    1325               0 :                 readchunk = sizeof(buf);
    1326                 : 
    1327               0 :                 if (maxlen && (maxlen - haveread) < readchunk)
    1328               0 :                         readchunk = maxlen - haveread;
    1329                 : 
    1330               0 :                 didread = php_stream_read(src, buf, readchunk);
    1331                 : 
    1332               0 :                 if (didread) {
    1333                 :                         /* extra paranoid */
    1334                 :                         size_t didwrite, towrite;
    1335                 :                         char *writeptr;
    1336                 : 
    1337               0 :                         towrite = didread;
    1338               0 :                         writeptr = buf;
    1339               0 :                         haveread += didread;
    1340                 : 
    1341               0 :                         while(towrite) {
    1342               0 :                                 didwrite = php_stream_write(dest, writeptr, towrite);
    1343               0 :                                 if (didwrite == 0) {
    1344               0 :                                         return 0;       /* error */
    1345                 :                                 }
    1346                 : 
    1347               0 :                                 towrite -= didwrite;
    1348               0 :                                 writeptr += didwrite;
    1349                 :                         }
    1350                 :                 } else {
    1351               0 :                         return haveread;
    1352                 :                 }
    1353                 : 
    1354               0 :                 if (maxlen - haveread == 0) {
    1355               0 :                         break;
    1356                 :                 }
    1357               0 :         }
    1358               0 :         return haveread;
    1359                 : 
    1360                 : }
    1361                 : /* }}} */
    1362                 : 
    1363                 : /* {{{ wrapper init and registration */
    1364                 : 
    1365                 : static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
    1366            1434 : {
    1367            1434 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1368                 :         /* set the return value for pclose */
    1369            1434 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1370            1434 : }
    1371                 : 
    1372                 : static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
    1373               0 : {
    1374               0 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1375               0 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1376               0 : }
    1377                 : 
    1378                 : void php_shutdown_stream_hashes(TSRMLS_D)
    1379             219 : {
    1380             219 :         if (FG(stream_wrappers)) {
    1381               0 :                 zend_hash_destroy(FG(stream_wrappers));
    1382               0 :                 efree(FG(stream_wrappers));
    1383               0 :                 FG(stream_wrappers) = NULL;
    1384                 :         }
    1385                 : 
    1386             219 :         if (FG(stream_filters)) {
    1387               0 :                 zend_hash_destroy(FG(stream_filters));
    1388               0 :                 efree(FG(stream_filters));
    1389               0 :                 FG(stream_filters) = NULL;
    1390                 :         }
    1391             219 : }
    1392                 : 
    1393                 : int php_init_stream_wrappers(int module_number TSRMLS_DC)
    1394             220 : {
    1395             220 :         le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number);
    1396             220 :         le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number);
    1397                 : 
    1398                 :         /* Filters are cleaned up by the streams they're attached to */
    1399             220 :         le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number);
    1400                 : 
    1401             220 :         return (
    1402                 :                         zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
    1403                 :                         && 
    1404                 :                         zend_hash_init(php_get_stream_filters_hash_global(), 0, NULL, NULL, 1) == SUCCESS
    1405                 :                         &&
    1406                 :                         zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS
    1407                 :                         &&
    1408                 :                         php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1409                 :                         &&
    1410                 :                         php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1411                 : #if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE))
    1412                 :                         &&
    1413                 :                         php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1414                 :                         &&
    1415                 :                         php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1416                 : #endif
    1417                 :                 ) ? SUCCESS : FAILURE;
    1418                 : }
    1419                 : 
    1420                 : int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
    1421             219 : {
    1422             219 :         zend_hash_destroy(&url_stream_wrappers_hash);
    1423             219 :         zend_hash_destroy(php_get_stream_filters_hash_global());
    1424             219 :         zend_hash_destroy(php_stream_xport_get_hash());
    1425             219 :         return SUCCESS;
    1426                 : }
    1427                 : 
    1428                 : /* Validate protocol scheme names during registration
    1429                 :  * Must conform to /^[a-zA-Z0-9+.-]+$/
    1430                 :  */
    1431                 : static inline int php_stream_wrapper_scheme_validate(char *protocol, int protocol_len)
    1432            1100 : {
    1433                 :         int i;
    1434                 : 
    1435            5060 :         for(i = 0; i < protocol_len; i++) {
    1436            3960 :                 if (!isalnum((int)protocol[i]) &&
    1437                 :                         protocol[i] != '+' &&
    1438                 :                         protocol[i] != '-' &&
    1439                 :                         protocol[i] != '.') {
    1440               0 :                         return FAILURE;
    1441                 :                 }
    1442                 :         }
    1443                 : 
    1444            1100 :         return SUCCESS;
    1445                 : }
    1446                 : 
    1447                 : /* API for registering GLOBAL wrappers */
    1448                 : PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
    1449            1100 : {
    1450            1100 :         int protocol_len = strlen(protocol);
    1451                 : 
    1452            1100 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    1453               0 :                 return FAILURE;
    1454                 :         }
    1455                 : 
    1456            1100 :         return zend_hash_add(&url_stream_wrappers_hash, protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
    1457                 : }
    1458                 : 
    1459                 : PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC)
    1460             657 : {
    1461             657 :         return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol) + 1);
    1462                 : }
    1463                 : 
    1464                 : static void clone_wrapper_hash(TSRMLS_D)
    1465               0 : {
    1466                 :         php_stream_wrapper *tmp;
    1467                 : 
    1468               0 :         ALLOC_HASHTABLE(FG(stream_wrappers));
    1469               0 :         zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1);
    1470               0 :         zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL, &tmp, sizeof(tmp));
    1471               0 : }
    1472                 : 
    1473                 : /* API for registering VOLATILE wrappers */
    1474                 : PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
    1475               0 : {
    1476               0 :         int protocol_len = strlen(protocol);
    1477                 : 
    1478               0 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    1479               0 :                 return FAILURE;
    1480                 :         }
    1481                 : 
    1482               0 :         if (!FG(stream_wrappers)) {
    1483               0 :                 clone_wrapper_hash(TSRMLS_C);
    1484                 :         }
    1485                 : 
    1486               0 :         return zend_hash_add(FG(stream_wrappers), protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
    1487                 : }
    1488                 : 
    1489                 : PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC)
    1490               0 : {
    1491               0 :         if (!FG(stream_wrappers)) {
    1492               0 :                 clone_wrapper_hash(TSRMLS_C);
    1493                 :         }
    1494                 : 
    1495               0 :         return zend_hash_del(FG(stream_wrappers), protocol, strlen(protocol) + 1);
    1496                 : }
    1497                 : /* }}} */
    1498                 : 
    1499                 : /* {{{ php_stream_locate_url_wrapper */
    1500                 : PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC)
    1501            4004 : {
    1502            4004 :         HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    1503            4004 :         php_stream_wrapper **wrapperpp = NULL;
    1504            4004 :         const char *p, *protocol = NULL;
    1505            4004 :         int n = 0;
    1506                 : 
    1507            4004 :         if (path_for_open) {
    1508            2366 :                 *path_for_open = (char*)path;
    1509                 :         }
    1510                 : 
    1511            4004 :         if (options & IGNORE_URL) {
    1512               0 :                 return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
    1513                 :         }
    1514                 : 
    1515            9037 :         for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
    1516            5033 :                 n++;
    1517                 :         }
    1518                 : 
    1519            4116 :         if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || !memcmp("data", path, 4))) {
    1520             112 :                 protocol = path;
    1521            3892 :         } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) {
    1522                 :                 /* BC with older php scripts and zlib wrapper */
    1523               0 :                 protocol = "compress.zlib";
    1524               0 :                 n = 13;
    1525               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead.");
    1526                 :         }
    1527                 : 
    1528            4004 :         if (protocol) {
    1529             112 :                 char *tmp = estrndup(protocol, n);
    1530             112 :                 if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
    1531               0 :                         php_strtolower(tmp, n);
    1532               0 :                         if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
    1533                 :                                 char wrapper_name[32];
    1534                 : 
    1535               0 :                                 if (n >= sizeof(wrapper_name)) {
    1536               0 :                                         n = sizeof(wrapper_name) - 1;
    1537                 :                                 }
    1538               0 :                                 PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
    1539                 :                         
    1540               0 :                                 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name);
    1541                 : 
    1542               0 :                                 wrapperpp = NULL;
    1543               0 :                                 protocol = NULL;
    1544                 :                         }
    1545                 :                 }
    1546             112 :                 efree(tmp);
    1547                 :         }
    1548                 :         /* TODO: curl based streams probably support file:// properly */
    1549            4004 :         if (!protocol || !strncasecmp(protocol, "file", n))   {
    1550            3892 :                 if (protocol) {
    1551               0 :                         int localhost = 0;
    1552                 : 
    1553               0 :                         if (!strncasecmp(path, "file://localhost/", 17)) {
    1554               0 :                                 localhost = 1;
    1555                 :                         }
    1556                 : 
    1557                 : #ifdef PHP_WIN32
    1558                 :                         if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':')        {
    1559                 : #else
    1560               0 :                         if (localhost == 0 && path[n+3] != '/') {
    1561                 : #endif
    1562               0 :                                 if (options & REPORT_ERRORS) {
    1563               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path);
    1564                 :                                 }
    1565               0 :                                 return NULL;
    1566                 :                         }
    1567                 : 
    1568               0 :                         if (path_for_open) {
    1569                 :                                 /* skip past protocol and :/, but handle windows correctly */
    1570               0 :                                 *path_for_open = (char*)path + n + 1;
    1571               0 :                                 if (localhost == 1) {
    1572               0 :                                         (*path_for_open) += 11;
    1573                 :                                 }
    1574               0 :                                 while (*(++*path_for_open)=='/');
    1575                 : #ifdef PHP_WIN32
    1576                 :                                 if (*(*path_for_open + 1) != ':')
    1577                 : #endif
    1578               0 :                                         (*path_for_open)--;
    1579                 :                         }
    1580                 :                 }
    1581                 : 
    1582            3892 :                 if (options & STREAM_LOCATE_WRAPPERS_ONLY) {
    1583               0 :                         return NULL;
    1584                 :                 }
    1585                 :                 
    1586            3892 :                 if (FG(stream_wrappers)) {
    1587                 :                         /* The file:// wrapper may have been disabled/overridden */
    1588                 : 
    1589               0 :                         if (wrapperpp) {
    1590                 :                                 /* It was found so go ahead and provide it */
    1591               0 :                                 return *wrapperpp;
    1592                 :                         }
    1593                 :                         
    1594                 :                         /* Check again, the original check might have not known the protocol name */
    1595               0 :                         if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == SUCCESS) {
    1596               0 :                                 return *wrapperpp;
    1597                 :                         }
    1598                 : 
    1599               0 :                         if (options & REPORT_ERRORS) {
    1600               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Plainfiles wrapper disabled");
    1601                 :                         }
    1602               0 :                         return NULL;
    1603                 :                 }
    1604                 : 
    1605                 :                 /* fall back on regular file access */          
    1606            3892 :                 return &php_plain_files_wrapper;
    1607                 :         }
    1608                 : 
    1609             112 :         if ((wrapperpp && (*wrapperpp)->is_url) && (!PG(allow_url_fopen) || ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include))) ) {
    1610               0 :                 if (options & REPORT_ERRORS) {
    1611               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
    1612                 :                 }
    1613               0 :                 return NULL;
    1614                 :         }
    1615                 : 
    1616             112 :         return *wrapperpp;
    1617                 : }
    1618                 : /* }}} */
    1619                 : 
    1620                 : /* {{{ _php_stream_mkdir
    1621                 :  */
    1622                 : PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC)
    1623               0 : {
    1624               0 :         php_stream_wrapper *wrapper = NULL;
    1625                 : 
    1626               0 :         wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC);
    1627               0 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) {
    1628               0 :                 return 0;
    1629                 :         }
    1630                 : 
    1631               0 :         return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context TSRMLS_CC);
    1632                 : }
    1633                 : /* }}} */
    1634                 : 
    1635                 : /* {{{ _php_stream_rmdir
    1636                 :  */
    1637                 : PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC)
    1638               0 : {
    1639               0 :         php_stream_wrapper *wrapper = NULL;
    1640                 : 
    1641               0 :         wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC);
    1642               0 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) {
    1643               0 :                 return 0;
    1644                 :         }
    1645                 : 
    1646               0 :         return wrapper->wops->stream_rmdir(wrapper, path, options, context TSRMLS_CC);
    1647                 : }
    1648                 : /* }}} */
    1649                 : 
    1650                 : /* {{{ _php_stream_stat_path */
    1651                 : PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
    1652             827 : {
    1653             827 :         php_stream_wrapper *wrapper = NULL;
    1654             827 :         char *path_to_open = path;
    1655                 :         int ret;
    1656                 : 
    1657                 :         /* Try to hit the cache first */
    1658             827 :         if (flags & PHP_STREAM_URL_STAT_LINK) {
    1659               0 :                 if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
    1660               0 :                         memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
    1661               0 :                         return 0;
    1662                 :                 }
    1663                 :         } else {
    1664             827 :                 if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
    1665              53 :                         memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
    1666              53 :                         return 0;
    1667                 :                 }
    1668                 :         }
    1669                 : 
    1670             774 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
    1671             774 :         if (wrapper && wrapper->wops->url_stat) {
    1672             774 :                 ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC);
    1673             774 :                 if (ret == 0) {
    1674                 :                         /* Drop into cache */
    1675             769 :                         if (flags & PHP_STREAM_URL_STAT_LINK) {
    1676               0 :                                 if (BG(CurrentLStatFile)) {
    1677               0 :                                         efree(BG(CurrentLStatFile));
    1678                 :                                 }
    1679               0 :                                 BG(CurrentLStatFile) = estrdup(path);
    1680               0 :                                 memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
    1681                 :                         } else {
    1682             769 :                                 if (BG(CurrentStatFile)) {
    1683             765 :                                         efree(BG(CurrentStatFile));
    1684                 :                                 }
    1685             769 :                                 BG(CurrentStatFile) = estrdup(path);
    1686             769 :                                 memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
    1687                 :                         }
    1688                 :                 }
    1689             774 :                 return ret;
    1690                 :         }
    1691               0 :         return -1;
    1692                 : }
    1693                 : /* }}} */
    1694                 : 
    1695                 : /* {{{ php_stream_opendir */
    1696                 : PHPAPI php_stream *_php_stream_opendir(char *path, int options,
    1697                 :                 php_stream_context *context STREAMS_DC TSRMLS_DC)
    1698              14 : {
    1699              14 :         php_stream *stream = NULL;
    1700              14 :         php_stream_wrapper *wrapper = NULL;
    1701                 :         char *path_to_open;
    1702                 : 
    1703              14 :         if (!path || !*path) {
    1704               0 :                 return NULL;
    1705                 :         }
    1706                 : 
    1707              14 :         path_to_open = path;
    1708                 : 
    1709              14 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
    1710                 : 
    1711              28 :         if (wrapper && wrapper->wops->dir_opener) {
    1712              14 :                 stream = wrapper->wops->dir_opener(wrapper,
    1713                 :                                 path_to_open, "r", options ^ REPORT_ERRORS, NULL,
    1714                 :                                 context STREAMS_REL_CC TSRMLS_CC);
    1715                 : 
    1716              14 :                 if (stream) {
    1717              14 :                         stream->wrapper = wrapper;
    1718              14 :                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
    1719                 :                 }
    1720               0 :         } else if (wrapper) {
    1721               0 :                 php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
    1722                 :         }
    1723              14 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    1724               0 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
    1725                 :         }
    1726              14 :         php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
    1727                 : 
    1728              14 :         return stream;
    1729                 : }
    1730                 : /* }}} */
    1731                 : 
    1732                 : /* {{{ _php_stream_readdir */
    1733                 : PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC)
    1734             730 : {
    1735                 : 
    1736             730 :         if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) {
    1737             716 :                 return ent;
    1738                 :         }
    1739                 : 
    1740              14 :         return NULL;
    1741                 : }
    1742                 : /* }}} */
    1743                 : 
    1744                 : /* {{{ php_stream_open_wrapper_ex */
    1745                 : PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options,
    1746                 :                 char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
    1747             756 : {
    1748             756 :         php_stream *stream = NULL;
    1749             756 :         php_stream_wrapper *wrapper = NULL;
    1750                 :         char *path_to_open;
    1751             756 :         int persistent = options & STREAM_OPEN_PERSISTENT;
    1752             756 :         char *copy_of_path = NULL;
    1753                 : 
    1754                 :         
    1755             756 :         if (opened_path) {
    1756             116 :                 *opened_path = NULL;
    1757                 :         }
    1758                 : 
    1759             756 :         if (!path || !*path) {
    1760               1 :                 return NULL;
    1761                 :         }
    1762                 : 
    1763             755 :         path_to_open = path;
    1764                 : 
    1765             755 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
    1766             755 :         if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) {
    1767               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function may only be used against URLs.");
    1768               0 :                 return NULL;
    1769                 :         }
    1770                 : 
    1771             755 :         if (wrapper) {
    1772             755 :                 if (!wrapper->wops->stream_opener) {
    1773               0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC,
    1774                 :                                         "wrapper does not support stream open");
    1775                 :                 } else {
    1776             755 :                         stream = wrapper->wops->stream_opener(wrapper,
    1777                 :                                 path_to_open, mode, options ^ REPORT_ERRORS,
    1778                 :                                 opened_path, context STREAMS_REL_CC TSRMLS_CC);
    1779                 :                 }
    1780                 : 
    1781                 :                 /* if the caller asked for a persistent stream but the wrapper did not
    1782                 :                  * return one, force an error here */
    1783             755 :                 if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) {
    1784               0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC,
    1785                 :                                         "wrapper does not support persistent streams");
    1786               0 :                         php_stream_close(stream);
    1787               0 :                         stream = NULL;
    1788                 :                 }
    1789                 :                 
    1790             755 :                 if (stream) {
    1791             755 :                         stream->wrapper = wrapper;
    1792                 :                 }
    1793                 :         }
    1794                 : 
    1795             755 :         if (stream) {
    1796             755 :                 if (stream->orig_path) {
    1797               0 :                         pefree(stream->orig_path, persistent);
    1798                 :                 }
    1799             755 :                 copy_of_path = pestrdup(path, persistent);
    1800             755 :                 stream->orig_path = copy_of_path;
    1801                 :         }
    1802                 : 
    1803             755 :         if (stream != NULL && (options & STREAM_MUST_SEEK)) {
    1804                 :                 php_stream *newstream;
    1805                 : 
    1806               0 :                 switch(php_stream_make_seekable_rel(stream, &newstream,
    1807                 :                                         (options & STREAM_WILL_CAST)
    1808                 :                                                 ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) {
    1809                 :                         case PHP_STREAM_UNCHANGED:
    1810               0 :                                 return stream;
    1811                 :                         case PHP_STREAM_RELEASED:
    1812               0 :                                 newstream->orig_path = pestrdup(path, persistent);
    1813               0 :                                 return newstream;
    1814                 :                         default:
    1815               0 :                                 php_stream_close(stream);
    1816               0 :                                 stream = NULL;
    1817               0 :                                 if (options & REPORT_ERRORS) {
    1818               0 :                                         char *tmp = estrdup(path);
    1819               0 :                                         php_strip_url_passwd(tmp);
    1820               0 :                                         php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "could not make seekable - %s",
    1821                 :                                                         tmp);
    1822               0 :                                         efree(tmp);
    1823                 : 
    1824               0 :                                         options ^= REPORT_ERRORS;
    1825                 :                                 }
    1826                 :                 }
    1827                 :         }
    1828                 : 
    1829             755 :         if (stream && stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && strchr(mode, 'a') && stream->position == 0) {
    1830               4 :                 off_t newpos = 0;
    1831                 : 
    1832                 :                 /* if opened for append, we need to revise our idea of the initial file position */
    1833               4 :                 if (0 == stream->ops->seek(stream, 0, SEEK_CUR, &newpos TSRMLS_CC)) {
    1834               4 :                         stream->position = newpos;
    1835                 :                 }
    1836                 :         }
    1837                 : 
    1838             755 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    1839               0 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
    1840               0 :                 if (opened_path && *opened_path) {
    1841               0 :                         efree(*opened_path);
    1842               0 :                         *opened_path = NULL;
    1843                 :                 }
    1844                 :         }
    1845             755 :         php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
    1846                 : #if ZEND_DEBUG
    1847                 :         if (stream == NULL && copy_of_path != NULL) {
    1848                 :                 pefree(copy_of_path, persistent);
    1849                 :         }
    1850                 : #endif
    1851             755 :         return stream;
    1852                 : }
    1853                 : /* }}} */
    1854                 : 
    1855                 : /* {{{ context API */
    1856                 : PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context)
    1857               0 : {
    1858               0 :         php_stream_context *oldcontext = stream->context;
    1859               0 :         stream->context = context;
    1860               0 :         return oldcontext;
    1861                 : }
    1862                 : 
    1863                 : PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity,
    1864                 :                 char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
    1865               0 : {
    1866               0 :         if (context && context->notifier)
    1867               0 :                 context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr TSRMLS_CC);
    1868               0 : }
    1869                 : 
    1870                 : PHPAPI void php_stream_context_free(php_stream_context *context)
    1871             118 : {
    1872             118 :         if (context->options) {
    1873               0 :                 zval_ptr_dtor(&context->options);
    1874               0 :                 context->options = NULL;
    1875                 :         }
    1876             118 :         if (context->notifier) {
    1877               0 :                 php_stream_notification_free(context->notifier);
    1878               0 :                 context->notifier = NULL;
    1879                 :         }
    1880             118 :         if (context->links) {
    1881               0 :                 zval_ptr_dtor(&context->links);
    1882               0 :                 context->links = NULL;
    1883                 :         }
    1884             118 :         efree(context);
    1885             118 : }
    1886                 : 
    1887                 : PHPAPI php_stream_context *php_stream_context_alloc(void)
    1888             118 : {
    1889                 :         php_stream_context *context;
    1890                 : 
    1891             118 :         context = ecalloc(1, sizeof(php_stream_context));
    1892             118 :         context->notifier = NULL;
    1893             118 :         MAKE_STD_ZVAL(context->options);
    1894             118 :         array_init(context->options);
    1895                 : 
    1896             118 :         context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context());
    1897             118 :         return context;
    1898                 : }
    1899                 : 
    1900                 : PHPAPI php_stream_notifier *php_stream_notification_alloc(void)
    1901               0 : {
    1902               0 :         return ecalloc(1, sizeof(php_stream_notifier));
    1903                 : }
    1904                 : 
    1905                 : PHPAPI void php_stream_notification_free(php_stream_notifier *notifier)
    1906               0 : {
    1907               0 :         if (notifier->dtor) {
    1908               0 :                 notifier->dtor(notifier);
    1909                 :         }
    1910               0 :         efree(notifier);
    1911               0 : }
    1912                 : 
    1913                 : PHPAPI int php_stream_context_get_option(php_stream_context *context,
    1914                 :                 const char *wrappername, const char *optionname, zval ***optionvalue)
    1915               0 : {
    1916                 :         zval **wrapperhash;
    1917                 : 
    1918               0 :         if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) {
    1919               0 :                 return FAILURE;
    1920                 :         }
    1921               0 :         return zend_hash_find(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)optionvalue);
    1922                 : }
    1923                 : 
    1924                 : PHPAPI int php_stream_context_set_option(php_stream_context *context,
    1925                 :                 const char *wrappername, const char *optionname, zval *optionvalue)
    1926               0 : {
    1927                 :         zval **wrapperhash;
    1928                 :         zval *category, *copied_val;
    1929                 : 
    1930               0 :         ALLOC_INIT_ZVAL(copied_val);
    1931               0 :         *copied_val = *optionvalue;
    1932               0 :         zval_copy_ctor(copied_val);
    1933               0 :         INIT_PZVAL(copied_val);
    1934                 : 
    1935               0 :         if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) {
    1936               0 :                 MAKE_STD_ZVAL(category);
    1937               0 :                 array_init(category);
    1938               0 :                 if (FAILURE == zend_hash_update(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&category, sizeof(zval *), NULL)) {
    1939               0 :                         return FAILURE;
    1940                 :                 }
    1941                 : 
    1942               0 :                 wrapperhash = &category;
    1943                 :         }
    1944               0 :         return zend_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&copied_val, sizeof(zval *), NULL);
    1945                 : }
    1946                 : 
    1947                 : PHPAPI int php_stream_context_get_link(php_stream_context *context,
    1948                 :         const char *hostent, php_stream **stream)
    1949               0 : {
    1950                 :         php_stream **pstream;
    1951                 : 
    1952               0 :         if (!stream || !hostent || !context || !(context->links)) {
    1953               0 :                 return FAILURE;
    1954                 :         }
    1955               0 :         if (SUCCESS == zend_hash_find(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&pstream)) {
    1956               0 :                 *stream = *pstream;
    1957               0 :                 return SUCCESS;
    1958                 :         }
    1959               0 :         return FAILURE;
    1960                 : }
    1961                 : 
    1962                 : PHPAPI int php_stream_context_set_link(php_stream_context *context,
    1963                 :         const char *hostent, php_stream *stream)
    1964               0 : {
    1965               0 :         if (!context) {
    1966               0 :                 return FAILURE;
    1967                 :         }
    1968               0 :         if (!context->links) {
    1969               0 :                 ALLOC_INIT_ZVAL(context->links);
    1970               0 :                 array_init(context->links);
    1971                 :         }
    1972               0 :         if (!stream) {
    1973                 :                 /* Delete any entry for <hostent> */
    1974               0 :                 return zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1);
    1975                 :         }
    1976               0 :         return zend_hash_update(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&stream, sizeof(php_stream *), NULL);
    1977                 : }
    1978                 : 
    1979                 : PHPAPI int php_stream_context_del_link(php_stream_context *context,
    1980                 :         php_stream *stream)
    1981               0 : {
    1982                 :         php_stream **pstream;
    1983                 :         char *hostent;
    1984               0 :         int ret = SUCCESS;
    1985                 : 
    1986               0 :         if (!context || !context->links || !stream) {
    1987               0 :                 return FAILURE;
    1988                 :         }
    1989                 : 
    1990               0 :         for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(context->links));
    1991               0 :                 SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(context->links), (void**)&pstream);
    1992               0 :                 zend_hash_move_forward(Z_ARRVAL_P(context->links))) {
    1993               0 :                 if (*pstream == stream) {
    1994               0 :                         if (SUCCESS == zend_hash_get_current_key(Z_ARRVAL_P(context->links), &hostent, NULL, 0)) {
    1995               0 :                                 if (FAILURE == zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1)) {
    1996               0 :                                         ret = FAILURE;
    1997                 :                                 }
    1998                 :                         } else {
    1999               0 :                                 ret = FAILURE;
    2000                 :                         }
    2001                 :                 }
    2002                 :         }
    2003                 : 
    2004               0 :         return ret;
    2005                 : }
    2006                 : /* }}} */
    2007                 : 
    2008                 : /* {{{ php_stream_dirent_alphasort
    2009                 :  */
    2010                 : PHPAPI int php_stream_dirent_alphasort(const char **a, const char **b)
    2011               0 : {
    2012               0 :         return strcoll(*a, *b);
    2013                 : }
    2014                 : /* }}} */
    2015                 : 
    2016                 : /* {{{ php_stream_dirent_alphasortr
    2017                 :  */
    2018                 : PHPAPI int php_stream_dirent_alphasortr(const char **a, const char **b)
    2019               0 : {
    2020               0 :         return strcoll(*b, *a);
    2021                 : }
    2022                 : /* }}} */
    2023                 : 
    2024                 : /* {{{ php_stream_scandir
    2025                 :  */
    2026                 : PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_stream_context *context,
    2027                 :                           int (*compare) (const char **a, const char **b) TSRMLS_DC)
    2028               0 : {
    2029                 :         php_stream *stream;
    2030                 :         php_stream_dirent sdp;
    2031               0 :         char **vector = NULL;
    2032               0 :         int vector_size = 0;
    2033               0 :         int nfiles = 0;
    2034                 : 
    2035               0 :         if (!namelist) {
    2036               0 :                 return FAILURE;
    2037                 :         }
    2038                 : 
    2039               0 :         stream = php_stream_opendir(dirname, ENFORCE_SAFE_MODE | REPORT_ERRORS, context);
    2040               0 :         if (!stream) {
    2041               0 :                 return FAILURE;
    2042                 :         }
    2043                 : 
    2044               0 :         while (php_stream_readdir(stream, &sdp)) {
    2045               0 :                 if (nfiles == vector_size) {
    2046               0 :                         if (vector_size == 0) {
    2047               0 :                                 vector_size = 10;
    2048                 :                         } else {
    2049               0 :                                 vector_size *= 2;
    2050                 :                         }
    2051               0 :                         vector = (char **) erealloc(vector, vector_size * sizeof(char *));
    2052                 :                 }
    2053                 : 
    2054               0 :                 vector[nfiles] = estrdup(sdp.d_name);
    2055                 : 
    2056               0 :                 nfiles++;
    2057                 :         }
    2058               0 :         php_stream_closedir(stream);
    2059                 : 
    2060               0 :         *namelist = vector;
    2061                 : 
    2062               0 :         if (compare) {
    2063               0 :                 qsort(*namelist, nfiles, sizeof(char *), (int(*)(const void *, const void *))compare);
    2064                 :         }
    2065               0 :         return nfiles;
    2066                 : }
    2067                 : /* }}} */
    2068                 : 
    2069                 : /*
    2070                 :  * Local variables:
    2071                 :  * tab-width: 4
    2072                 :  * c-basic-offset: 4
    2073                 :  * End:
    2074                 :  * vim600: noet sw=4 ts=4 fdm=marker
    2075                 :  * vim<600: noet sw=4 ts=4
    2076                 :  */

Generated by: LTP GCOV extension version 1.5