PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - var/php_gcov/PHP_5_2/main/streams - streams.c
Test: PHP Code Coverage
Date: 2009-11-19 Instrumented lines: 934
Code covered: 83.1 % Executed lines: 776
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Thu, 19 Nov 2009 08:20:29 +0000 (5 days ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.