wez             Tue Apr 17 10:03:18 2001 EDT

  Added files:                 
    /php4/main  php_streams.h streams.c 

  Modified files:              
    /php4/main  Makefile.in php.h 
  Log:
  Added files for PHP streams
  
  
Index: php4/main/Makefile.in
diff -u php4/main/Makefile.in:1.13 php4/main/Makefile.in:1.14
--- php4/main/Makefile.in:1.13  Fri Feb 23 14:07:14 2001
+++ php4/main/Makefile.in       Tue Apr 17 10:03:18 2001
@@ -5,6 +5,7 @@
        safe_mode.c fopen_wrappers.c alloca.c \
        php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \
        strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \
+                streams.c \
        network.c php_open_temporary_file.c php_logos.c
 
 include $(top_srcdir)/build/ltlib.mk
Index: php4/main/php.h
diff -u php4/main/php.h:1.134 php4/main/php.h:1.135
--- php4/main/php.h:1.134       Mon Feb 26 10:14:30 2001
+++ php4/main/php.h     Tue Apr 17 10:03:18 2001
@@ -1,4 +1,4 @@
-/* 
+/*
    +----------------------------------------------------------------------+
    | PHP version 4.0                                                      |
    +----------------------------------------------------------------------+
@@ -17,7 +17,7 @@
    +----------------------------------------------------------------------+
  */
 
-/* $Id: php.h,v 1.134 2001/02/26 18:14:30 andi Exp $ */
+/* $Id: php.h,v 1.135 2001/04/17 17:03:18 wez Exp $ */
 
 #ifndef PHP_H
 #define PHP_H
@@ -44,9 +44,9 @@
 #ifdef PHP_WIN32
 #include "win95nt.h"
 #      ifdef PHP_EXPORTS
-#      define PHPAPI __declspec(dllexport) 
+#      define PHPAPI __declspec(dllexport)
 #      else
-#      define PHPAPI __declspec(dllimport) 
+#      define PHPAPI __declspec(dllimport)
 #      endif
 #define PHP_DIR_SEPARATOR '\\'
 #else
@@ -127,11 +127,11 @@
 #endif
 #if HAVE_STDARG_H
 #include <stdarg.h>
-#else   
+#else
 # if HAVE_SYS_VARARGS_H
 # include <sys/varargs.h>
-# endif 
-#endif 
+# endif
+#endif
 
 
 #include "zend_hash.h"
@@ -156,6 +156,7 @@
 char *strerror(int);
 #endif
 
+#include "php_streams.h"
 #include "fopen_wrappers.h"
 
 #if (REGEX == 1 || REGEX == 0) && !defined(NO_REGEX_EXTRA_H)
@@ -307,7 +308,7 @@
 
 #define XtOffset(p_type,field) ((unsigned int)&(((p_type)NULL)->field))
 
-#endif /* !CRAY2 */  
+#endif /* !CRAY2 */
 #endif /* __STDC__ */
 #else /* ! (CRAY || __arm) */
 

Index: php4/main/php_streams.h
+++ php4/main/php_streams.h
/*
   +----------------------------------------------------------------------+
   | PHP version 4.0                                                      |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
   +----------------------------------------------------------------------+
   | This source file is subject to version 2.02 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://www.php.net/license/2_02.txt.                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors:                                                             |
   | Wez Furlong ([EMAIL PROTECTED])                                   |
   +----------------------------------------------------------------------+
 */

#ifndef PHP_STREAMS_H
#define PHP_STREAMS_H

#if HAVE_PHP_STREAM

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif


typedef struct _php_stream php_stream;

typedef struct _php_stream_ops  {
/* stdio like functions - these are mandatory! */
        size_t (*write)(php_stream * stream, const char * buf, size_t count);
        size_t (*read)(php_stream * stream, char * buf, size_t count);
        int    (*close)(php_stream * stream);
        int    (*flush)(php_stream * stream);
/* these are optional */
        int    (*seek)(php_stream * stream, off_t offset, int whence);
/* used only in unbuffered mode */
        char * (*gets)(php_stream * stream, char * buf, size_t size);
        int (*cast)(php_stream * stream, int castas, void ** ret);
        const char * label; /* label for this ops structure */
} php_stream_ops;

typedef struct _php_stream_buffer       {
        char * buffer;
        size_t buflen;

        int dirty;      /* 1 if we need to commit data */

        off_t readpos;
        off_t writepos;

        size_t chunksize;       /* amount to commit in one operation */
        int persistent;
} php_stream_buffer;

PHPAPI int php_stream_buf_init(php_stream_buffer * buffer, int persistent, size_t 
chunksize);
PHPAPI int php_stream_buf_cleanup(php_stream_buffer * buffer);
/* add data into buffer, growing it if required */
PHPAPI int php_stream_buf_append(php_stream_buffer * buffer, const char * buf, size_t 
size);
/* read data out of buffer */
PHPAPI size_t php_stream_buf_read(php_stream_buffer * buffer, char * buf, size_t size);
PHPAPI int php_stream_buf_overwrite(php_stream_buffer * buffer, const char * buf, 
size_t size);

struct _php_stream  {
        php_stream_ops * ops;
        void * abstract;                /* convenience pointer for abstraction */
        int eof;

        /* for convenience for sockets */
        int is_blocked;
        struct timeval timeout;
        int timeout_event;

        int readahead;                  /* number of chunks to read-ahead */

        int is_persistent;
        char mode[16];                  /* "rwb" etc. ala stdio */
        /* the stream can be buffered  */
        int is_buffered;
        php_stream_buffer readbuf;

        FILE * stdiocast;    /* cache this, otherwise we might leak! */
}; /* php_stream */

/* allocate a new stream for a particular ops */
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, size_t 
bufsize, int persistent, const char * mode);

PHPAPI int php_stream_free(php_stream * stream, int call_dtor);
#define php_stream_close(stream)        php_stream_free(stream, 1)

/* seeking is only supported for reading! */
PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
PHPAPI off_t php_stream_tell(php_stream * stream);
PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count);
PHPAPI int php_stream_eof(php_stream * stream);
PHPAPI int php_stream_getc(php_stream * stream);
PHPAPI int php_stream_flush(php_stream * stream);
PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);

/* operations for a stdio FILE; the FILE * must be placed in stream->abstract */
extern php_stream_ops php_stream_stdio_ops;
/* like fopen, but returns a stream */
PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode);

/* coerce the stream into some other form */
/* cast as a stdio FILE * */
#define PHP_STREAM_AS_STDIO     0
/* cast as a POSIX fd or socketd */
#define PHP_STREAM_AS_FD                1
/* cast as a socketd */
#define PHP_STREAM_AS_SOCKETD   2

/* warning: once you have cast a stream as a FILE*, you probably should not use
   the php_stream_XXX api after that point, or you will confuse the buffering
   in FILE* and/or php_stream *
*/
PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err);
/* use this to check if a stream can be cast into another form */
#define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0)

#endif /* HAVE_PHP_STREAM */

#endif

Index: php4/main/streams.c
+++ php4/main/streams.c
/*
   +----------------------------------------------------------------------+
   | PHP version 4.0                                                      |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
   +----------------------------------------------------------------------+
   | This source file is subject to version 2.02 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://www.php.net/license/2_02.txt.                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors:                                                             |
   | Wez Furlong ([EMAIL PROTECTED])                                   |
   +----------------------------------------------------------------------+
 */

#define _GNU_SOURCE
#include "php.h"

#if HAVE_PHP_STREAM

#define MAX_CHUNK_SIZE  8192

#define TOREAD(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos)
#define TOWRITE(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos)

#define READPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.readpos)
#define WRITEPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.writepos)

#define READ_MAX(stream, max) if (stream->is_blocked) stream_read_total(sock, max); 
else stream_readahead(sock)


PHPAPI int php_stream_buf_init(php_stream_buffer * buffer, int persistent, size_t 
chunksize)
{
        memset(buffer, 0, sizeof(php_stream_buffer));

        /* defer memory allocation until first use */
        buffer->persistent = persistent;
        buffer->chunksize = chunksize;

        return SUCCESS;
}

PHPAPI int php_stream_buf_cleanup(php_stream_buffer * buffer)
{
        if (buffer->buffer)     {
                pefree(buffer->buffer, buffer->persistent);
                buffer->buffer = NULL;
        }
        return SUCCESS;
}

/* append data to the buffer ready for reading */
PHPAPI int php_stream_buf_append(php_stream_buffer * buffer, const char * buf, size_t 
size)
{
        if (!buffer->dirty && buffer->buffer && (buffer->writepos + size > 
buffer->buflen))     {
                /* if a lot of memory is sitting idle, reclaim it, but only if we are 
"clean" */
                if (buffer->readpos > 4 * buffer->chunksize)    {
                        memmove(buffer->buffer + buffer->readpos, buffer->buffer, 
buffer->writepos - buffer->readpos);

                        buffer->writepos -= buffer->readpos;
                        buffer->readpos = 0;
                }
        }
        while (buffer->writepos + size > buffer->buflen)        {
                /* grow it */
                buffer->buflen += buffer->chunksize;
                buffer->buffer = perealloc(buffer->buffer, buffer->buflen, 
buffer->persistent);
        }
        memcpy(buffer->buffer + buffer->writepos, buf, size);
        buffer->writepos += size;
        return SUCCESS;
}

/* write data into the buffer at the present read position.
   When done, if we overlapped the writepos, move it to so that
   it occurs just after the zone we wrote.
*/
PHPAPI int php_stream_buf_overwrite(php_stream_buffer * buffer, const char * buf, 
size_t size)
{
        /* ensure that there it enough memory */
        while (buffer->readpos + size > buffer->buflen) {
                buffer->buflen += buffer->chunksize;
                buffer->buffer = perealloc(buffer->buffer, buffer->buflen, 
buffer->persistent);
        }
        memcpy(buffer->buffer + buffer->readpos, buf, size);
        if (buffer->readpos + size > buffer->writepos)
                buffer->writepos = buffer->readpos + size;

        buffer->dirty = 1;

        return SUCCESS;
}

/* read data out of buffer */
PHPAPI size_t php_stream_buf_read(php_stream_buffer * buffer, char * buf, size_t size)
{
        size_t ret;

        ret = MIN(size, buffer->writepos - buffer->readpos);

        if (ret == 0)   {
                if (buf)
                        buf[0] = 0;
        }
        else    {
                if (buf)
                        memcpy(buf, buffer->buffer + buffer->readpos, ret);
                buffer->readpos += ret;
        }
        return ret;
}



/* allocate a new stream for a particular ops */
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, size_t 
bufsize, int persistent, const char * mode)
{
        php_stream * ret;

        ret = (php_stream*)pemalloc(sizeof(php_stream), persistent);

        memset(ret, 0, sizeof(php_stream));

        ret->ops = ops;
        ret->abstract = abstract;
        ret->is_persistent = persistent;

        strncpy(ret->mode, mode, sizeof(ret->mode));

        if (bufsize)    {
                ret->is_buffered = 1;
                php_stream_buf_init(&ret->readbuf, persistent, bufsize);
        }
        return ret;
}

PHPAPI int php_stream_free(php_stream * stream, int call_dtor)
{
        int ret = 1;

        if (call_dtor)  {
                ret = stream->ops->close(stream);
        }
        php_stream_buf_cleanup(&stream->readbuf);
        pefree(stream, stream->is_persistent);

        return ret;
}

/* get a chunk into the stream read buffer */
static size_t stream_read_chunk(php_stream * stream)
{
        size_t nr, ret = 0;
        char buf[MAX_CHUNK_SIZE];

        /* do timeout check here ? */

        nr = stream->ops->read(stream, buf, stream->readbuf.chunksize);

        if (nr > 0)     {
                if (php_stream_buf_append(&stream->readbuf, buf, nr))
                        ret = nr;
        }
        else if (nr == 0 || (nr < 0 && errno != EWOULDBLOCK))   {
                stream->eof = 1;
        }
        return ret;
}


/* read 1 + readahead chunks into buffer, if possible */
static size_t stream_readahead(php_stream * stream)
{
        size_t nr_bytes;
        size_t nr_read = 0;
        int i;

        for(i = 0; !stream->eof && i < (stream->readahead + 1); i++) {
                nr_bytes = stream_read_chunk(stream);
                if(nr_bytes == 0)
                        break;
                nr_read += nr_bytes;
        }
        return nr_read;
}

static void stream_read_total(php_stream * stream, size_t size)
{
        while(!stream->eof && TOREAD(stream) < size && !stream->timeout_event) {
                stream_readahead(stream);
        }
}


PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t size)
{
        size_t ret = 0;

        if (stream->is_buffered)        {
                /* fill the buffer with enough bytes */
                stream_read_total(stream, size);

                if(size < 0)
                        return ret;

                ret = php_stream_buf_read(&stream->readbuf, buf, size);
        }
        else
                ret = stream->ops->read(stream, buf, size);

        return ret;
}

PHPAPI int php_stream_eof(php_stream * stream)
{
        int ret = 0;

        if (stream->is_buffered)        {

                if(!stream->is_blocked)
                        stream_read_chunk(stream);

                if(!TOREAD(stream) && stream->eof)
                        ret = 1;
        }
        else    {
                /* we will define our stream reading function so that it
                   must return EOF when an EOF condition occurs, when
                   working in unbuffered mode and called with these args */
                ret = stream->ops->read(stream, NULL, 0) == EOF ? 1 : 0;
        }
        return ret;
}

PHPAPI int php_stream_getc(php_stream * stream)
{
        char buf;

        if (php_stream_read(stream, &buf, 1) > 0)
                return buf;
        return EOF;
}


#define SEARCHCR()      p = memchr(READPTR(stream), '\n', MIN(TOREAD(stream), maxlen))

PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen)
{

        if (maxlen == 0)        {
                buf[0] = 0;
                return buf;
        }

        if (stream->is_buffered)        {
                /* buffered fgets */
                char * p = NULL;
                size_t amount = 0;

                SEARCHCR();

                if (!p) {
                        if (stream->is_blocked) {
                                while (!p && !stream->eof && !stream->timeout_event && 
TOREAD(stream) < maxlen)
                                {
                                        stream_read_chunk(stream);
                                        SEARCHCR();
                                }
                        }
                        else    {
                                stream_read_chunk(stream);
                                SEARCHCR();
                        }
                }

                if (p)
                        amount = (ptrdiff_t)p - (ptrdiff_t)READPTR(stream) + 1;
                else
                        amount = TOREAD(stream);

                amount = MIN(amount, maxlen);
                php_stream_buf_read(&stream->readbuf, buf, amount);
                buf[amount] = '\0';

                /* signal error only if we don't return data from this call
                        and there is not data to read and if the eof flag is set */

                if (amount || TOREAD(stream) || !stream->eof)   {
                        return buf;
                }

                return NULL;
        }
        else if (stream->ops->gets)     {
                return stream->ops->gets(stream, buf, maxlen);
        }
        else    {
                /* unbuffered fgets - poor performance ! */
                size_t n = 0;
                char * c = buf;

                /* TODO: look at error returns? */

                while(n < maxlen && stream->ops->read(stream, c, 1) > 0)        {
                        n++;
                        if (*c == '\n') {
                                c++;
                                break;
                        }
                        c++;
                }
                *c = 0;
                return buf;
        }
}

static int stream_commit(php_stream * stream)
{
        zend_error(E_WARNING, "buffered writes not yet implemented!");
        return FAILURE;
}

PHPAPI int php_stream_flush(php_stream * stream)
{
        if (!stream->is_buffered && stream->ops->flush)
        {
                return stream->ops->flush(stream);
        }
        zend_error(E_WARNING, "php_stream_flush is not yet implemented on buffered 
streams!");
        return EOF;
}

PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count)
{
        size_t ret = 0;

        if (strchr(stream->mode, 'w') == NULL)  {
                zend_error(E_WARNING, "%s(): stream was not opened for writing", 
get_active_function_name());
                return 0;
        }
        
        if (stream->is_buffered)        {
                /* commit buffer before appending, to preserve memory */
                stream_commit(stream);

                /* dump it into the buffer */
                php_stream_buf_overwrite(&stream->readbuf, buf, count);

                /* commit if it makes sense */
                stream_commit(stream);

                ret = count;
        }
        else
                ret = stream->ops->write(stream, buf, count);

        return ret;
}

PHPAPI off_t php_stream_tell(php_stream * stream)
{
        off_t ret = -1;
        if (stream->ops->seek)  {
                ret = stream->ops->seek(stream, 0, SEEK_CUR);
        }
        return ret;
}

PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence)
{
        if (stream->is_buffered)        {
                /*TODO: implement!
                stream_commit(stream);
                stream->readbuf.readpos = 0;
                stream->readbuf.writepos = 0;
                if (stream->ops->seek)
                        return stream->ops->seek(stream, offset, whence);
                */
                goto cant_seek;
        }
        else if (stream->ops->seek)     {
                return stream->ops->seek(stream, offset, whence);
        }

cant_seek:
        zend_error(E_ERROR, "streams of type %s do not support seeking", 
stream->ops->label);
        return -1;
}


/*------- STDIO stream implementation -------*/

static size_t php_stdiop_write(php_stream * stream, const char * buf, size_t count)
{
        return fwrite(buf, 1, count, (FILE*)stream->abstract);
}

static size_t php_stdiop_read(php_stream * stream, char * buf, size_t count)
{
        if (buf == NULL && count == 0)  {
                /* check for EOF condition */
                if (feof((FILE*)stream->abstract))      {
                        return EOF;
                }
                return 0;
        }
        return fread(buf, 1, count, (FILE*)stream->abstract);
}

static int php_stdiop_close(php_stream * stream)
{
        return fclose((FILE*)stream->abstract);
}

static int php_stdiop_flush(php_stream * stream)
{
        return fflush((FILE*)stream->abstract);
}

static int php_stdiop_seek(php_stream * stream, off_t offset, int whence)
{
        return fseek((FILE*)stream->abstract, offset, whence);
}

static char * php_stdiop_gets(php_stream * stream, char * buf, size_t size)
{
        return fgets(buf, size, (FILE*)stream->abstract);
}
static int php_stdiop_cast(php_stream * stream, int castas, void ** ret)
{
        int fd;

        switch (castas) {
                case PHP_STREAM_AS_STDIO:
                        if (ret)
                                *ret = stream->abstract;
                        return SUCCESS;

                case PHP_STREAM_AS_FD:
                        fd = fileno((FILE*)stream->abstract);
                        if (fd < 0)
                                return FAILURE;
                        if (ret)
                                *ret = (void*)fd;
                        return SUCCESS;
                default:
                        return FAILURE;
        }
}

php_stream_ops  php_stream_stdio_ops = {
        php_stdiop_write, php_stdiop_read,
        php_stdiop_close, php_stdiop_flush, php_stdiop_seek,
        php_stdiop_gets, php_stdiop_cast,
        "STDIO"
};

PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
{
        FILE * fp = fopen(filename, mode);

        if (fp) {
                php_stream * ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, 
mode);

                if (ret)
                        return ret;

                fclose(fp);
        }
        return NULL;
}

#if HAVE_FOPENCOOKIE
static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size)
{
   return php_stream_read(((php_stream *)cookie), buffer, size);
}

static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size) {
   return php_stream_write(((php_stream *)cookie), (char *)buffer, size);
}

static int stream_cookie_seeker(void *cookie, off_t position, int whence) {
   return php_stream_seek(((php_stream *)cookie), position, whence);
}

static int stream_cookie_closer(void *cookie) {
   return php_stream_close(((php_stream *)cookie));
}

static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
{
        stream_cookie_reader, stream_cookie_writer,
        stream_cookie_seeker, stream_cookie_closer
};
#endif

PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err)
{
        if (castas == PHP_STREAM_AS_STDIO)      {
                if (stream->stdiocast)  {
                        if (ret)
                                *ret = stream->stdiocast;
                        return SUCCESS;
                }

                if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == 
SUCCESS)
                        goto exit_success;


#if HAVE_FOPENCOOKIE
                /* if just checking, say yes we can be a FILE*, but don't actually 
create it yet */
                if (ret == NULL)
                        goto exit_success;

                *ret = fopencookie(stream, stream->mode, stream_cookie_functions);

                if (*ret != NULL)
                        goto exit_success;

                /* must be either:
                        a) programmer error
                        b) no memory
                        -> lets bail
                 */
                zend_error(E_ERROR, "%s(): fopencookie failed", 
get_active_function_name());
                return FAILURE;
#endif

                goto exit_fail;
        }
        if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS)
                goto exit_success;


exit_fail:
        if (show_err)   {
                const char * cast_names[3] = { "STDIO FILE*", "File Descriptor", 
"Socket Descriptor" };
                zend_error(E_WARNING, "%s(): cannot represent a stream of type %s as a 
%s",
                        get_active_function_name(),
                        stream->ops->label,
                        cast_names[castas]
                        );
        }

        return FAILURE;

exit_success:
        if (castas == PHP_STREAM_AS_STDIO && ret)
                stream->stdiocast = *ret;

        return SUCCESS;

}

#endif

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]

Reply via email to