This is buff.c in view mode; [Download] [Up]
/* ====================================================================
* Copyright (c) 1996,1997 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 5. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#include "httpd.h"
#include "http_main.h"
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef NO_UNISTD_H
#include <unistd.h>
#endif
#ifndef NO_WRITEV
#include <sys/types.h>
#include <sys/uio.h>
#endif
#ifdef HAVE_BSTRING_H
#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
#endif
#define DEFAULT_BUFSIZE (4096)
/*
* Buffered I/O routines.
* These are a replacement for the stdio routines.
* Advantages:
* Known semantics for handling of file-descriptors (on close etc.)
* No problems reading and writing simultanously to the same descriptor
* No limits on the number of open file handles.
* Only uses memory resources; no need to ensure the close routine
* is called.
* Extra code could be inserted between the buffered and un-buffered routines.
* Timeouts could be handled by using select or poll before read or write.
* Extra error handling could be introduced; e.g.
* keep an address to which we should longjump(), or
* keep a stack of routines to call on error.
*/
/* Notes:
* On reading EOF, EOF will set in the flags and no further Input will
* be done.
*
* On an error except for EAGAIN, ERROR will be set in the flags and no
* futher I/O will be done
*/
#ifdef WIN32
int
sendwithtimeout(int sock, const char *buf, int len, int flags)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
if(!(tv.tv_sec = check_alarm()))
return(send(sock, buf, len, flags));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
if(rv)
{
err = WSAGetLastError();
ap_assert(0);
}
rv = send(sock, buf, len, flags);
if(rv == SOCKET_ERROR)
{
err = WSAGetLastError();
if(err == WSAEWOULDBLOCK)
{
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv);
if(rv == 0)
{
ioctlsocket(sock, FIONBIO, &iostate);
check_alarm();
WSASetLastError(WSAEWOULDBLOCK);
return(SOCKET_ERROR);
}
rv = send(sock, buf, len, flags);
if(rv == SOCKET_ERROR)
err = WSAGetLastError();
}
}
ioctlsocket(sock, FIONBIO, &iostate);
if(rv == SOCKET_ERROR)
WSASetLastError(err);
return(rv);
}
int
recvwithtimeout(int sock, char *buf, int len, int flags)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
if(!(tv.tv_sec = check_alarm()))
return(recv(sock, buf, len, flags));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
ap_assert(!rv);
rv = recv(sock, buf, len, flags);
if(rv == SOCKET_ERROR)
{
err = WSAGetLastError();
if(err == WSAEWOULDBLOCK)
{
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
if(rv == 0)
{
ioctlsocket(sock, FIONBIO, &iostate);
check_alarm();
WSASetLastError(WSAEWOULDBLOCK);
return(SOCKET_ERROR);
}
rv = recv(sock, buf, len, flags);
if(rv == SOCKET_ERROR)
err = WSAGetLastError();
}
}
ioctlsocket(sock, FIONBIO, &iostate);
if(rv == SOCKET_ERROR)
WSASetLastError(err);
return(rv);
}
#endif /* WIN32 */
static inline int buff_read (BUFF *fb, void *buf, int nbyte)
{
int rv;
#if defined (WIN32)
if (fb->flags & B_SOCKET) {
rv = recvwithtimeout( fb->fd_in, buf, nbyte, 0 );
if (rv == SOCKET_ERROR)
errno = WSAGetLastError() - WSABASEERR;
}
else
rv = read( fb->fd_in, buf, nbyte );
#else
rv = read( fb->fd_in, buf, nbyte );
#endif /* WIN32 */
return rv;
}
static inline int buff_write (BUFF *fb, const void *buf, int nbyte)
{
int rv;
#if defined(WIN32)
if (fb->flags & B_SOCKET) {
rv = sendwithtimeout( fb->fd, buf, nbyte, 0);
if (rv == SOCKET_ERROR)
errno = WSAGetLastError() - WSABASEERR;
}
else
rv = write( fb->fd, buf, nbyte );
#elif defined (B_SFIO)
rv = sfwrite(fb->sf_out, buf, nbyte);
#else
rv = write(fb->fd, buf, nbyte);
#endif /* WIN32 */
return rv;
}
static void
doerror(BUFF *fb, int err)
{
int errsave = errno; /* Save errno to prevent overwriting it below */
if (err == B_RD)
fb->flags |= B_RDERR;
else
fb->flags |= B_WRERR;
if (fb->error != NULL) (*fb->error)(fb, err, fb->error_data);
errno = errsave;
}
/* Buffering routines */
/*
* Create a new buffered stream
*/
API_EXPORT(BUFF *) bcreate(pool *p, int flags)
{
BUFF *fb;
fb = palloc(p, sizeof(BUFF));
fb->pool=p;
fb->bufsiz = DEFAULT_BUFSIZE;
fb->flags = flags & (B_RDWR|B_SOCKET);
if (flags & B_RD) fb->inbase = palloc(p, fb->bufsiz);
else fb->inbase = NULL;
/* overallocate so that we can put a chunk trailer of CRLF into this
* buffer */
if (flags & B_WR) fb->outbase = palloc(p, fb->bufsiz + 2);
else fb->outbase = NULL;
fb->inptr = fb->inbase;
fb->incnt = 0;
fb->outcnt = 0;
fb->outchunk = -1;
fb->error = NULL;
fb->bytes_sent = 0L;
fb->fd = -1;
fb->fd_in = -1;
#ifdef B_SFIO
fb->sf_in = NULL;
fb->sf_out = NULL;
fb->sf_in = sfnew(fb->sf_in, NIL(Void_t*),
(size_t)SF_UNBOUND, 0, SF_READ);
fb->sf_out = sfnew(fb->sf_out, NIL(Void_t*),
(size_t)SF_UNBOUND, 1, SF_WRITE);
#endif
return fb;
}
/*
* Push some I/O file descriptors onto the stream
*/
API_EXPORT(void) bpushfd(BUFF *fb, int fd_in, int fd_out)
{
fb->fd = fd_out;
fb->fd_in = fd_in;
}
API_EXPORT(int) bsetopt(BUFF *fb, int optname, const void *optval)
{
if (optname == BO_BYTECT)
{
fb->bytes_sent = *(const long int *)optval - (long int)fb->outcnt;;
return 0;
} else
{
errno = EINVAL;
return -1;
}
}
API_EXPORT(int) bgetopt(BUFF *fb, int optname, void *optval)
{
if (optname == BO_BYTECT)
{
long int bs=fb->bytes_sent + fb->outcnt;
if (bs < 0L) bs = 0L;
*(long int *)optval = bs;
return 0;
} else
{
errno = EINVAL;
return -1;
}
}
/*
* start chunked encoding
*/
static void
start_chunk( BUFF *fb )
{
char chunksize[16]; /* Big enough for practically anything */
int chunk_header_size;
if (fb->outchunk != -1) {
/* already chunking */
return;
}
if (!(fb->flags & B_WR) || (fb->flags & (B_WRERR|B_EOUT))) {
/* unbuffered writes */
return;
}
/* we know that the chunk header is going to take at least 3 bytes... */
chunk_header_size = ap_snprintf( chunksize, sizeof(chunksize),
"%x\015\012", fb->bufsiz - fb->outcnt - 3 );
/* we need at least the header_len + at least 1 data byte
* remember that we've overallocated fb->outbase so that we can always
* fit the two byte CRLF trailer
*/
if( fb->bufsiz - fb->outcnt < chunk_header_size + 1 ) {
bflush(fb);
}
/* assume there's enough space now */
memcpy( &fb->outbase[fb->outcnt], chunksize, chunk_header_size );
fb->outchunk = fb->outcnt;
fb->outcnt += chunk_header_size;
fb->outchunk_header_size = chunk_header_size;
}
/*
* end a chunk -- tweak the chunk_header from start_chunk, and add a trailer
*/
static void
end_chunk( BUFF *fb )
{
int i;
if( fb->outchunk == -1 ) {
/* not chunking */
return;
}
if( fb->outchunk + fb->outchunk_header_size == fb->outcnt ) {
/* nothing was written into this chunk, and we can't write a 0 size
* chunk because that signifies EOF, so just erase it
*/
fb->outcnt = fb->outchunk;
fb->outchunk = -1;
return;
}
/* we know this will fit because of how we wrote it in start_chunk() */
i = ap_snprintf( (char *)&fb->outbase[fb->outchunk],
fb->outchunk_header_size,
"%x", fb->outcnt - fb->outchunk - fb->outchunk_header_size );
/* we may have to tack some trailing spaces onto the number we just wrote
* in case it was smaller than our estimated size. We've also written
* a \0 into the buffer with ap_snprintf so we might have to put a
* \r back in.
*/
i += fb->outchunk;
while( fb->outbase[i] != '\015' && fb->outbase[i] != '\012' ) {
fb->outbase[i++] = ' ';
}
if( fb->outbase[i] == '\012' ) {
/* we overwrote the \r, so put it back */
fb->outbase[i-1] = '\015';
}
/* tack on the trailing CRLF, we've reserved room for this */
fb->outbase[fb->outcnt++] = '\015';
fb->outbase[fb->outcnt++] = '\012';
fb->outchunk = -1;
}
/*
* Set a flag on (1) or off (0).
*/
API_EXPORT(int) bsetflag(BUFF *fb, int flag, int value)
{
if (value) {
fb->flags |= flag;
if( flag & B_CHUNK ) {
start_chunk(fb);
}
} else {
fb->flags &= ~flag;
if( flag & B_CHUNK ) {
end_chunk(fb);
}
}
return value;
}
/*
* This is called instead of read() everywhere in here. It implements
* the B_SAFEREAD functionality -- which is to force a flush() if a read()
* would block. It also deals with the EINTR errno result from read().
* return code is like read() except EINTR is eliminated.
*/
#if !defined (B_SFIO) || defined (WIN32)
#define saferead saferead_guts
#else
static int
saferead(BUFF *fb, char *buf, int nbyte)
{
return sfread(fb->sf_in, buf, nbyte);
}
#endif
static int
saferead_guts(BUFF *fb, void *buf, int nbyte)
{
int rv;
if( fb->flags & B_SAFEREAD ) {
fd_set fds;
struct timeval tv;
/* test for a block */
do {
FD_ZERO( &fds );
FD_SET( fb->fd_in, &fds );
tv.tv_sec = 0;
tv.tv_usec = 0;
rv = ap_select( fb->fd_in + 1, &fds, NULL, NULL, &tv );
} while( rv < 0 && errno == EINTR );
/* treat any error as if it would block as well */
if( rv != 1 ) {
bflush(fb);
}
}
do {
rv = buff_read (fb, buf, nbyte);
} while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
return( rv );
}
#ifdef B_SFIO
int bsfio_read(Sfio_t *f, char *buf, int nbyte, apache_sfio *disc)
{
int rv;
BUFF *fb = disc->buff;
rv = saferead_guts(fb, buf, nbyte);
buf[rv] = '\0';
f->next = 0;
return(rv);
}
int bsfio_write(Sfio_t *f, char *buf, int nbyte, apache_sfio *disc)
{
return write(disc->buff->fd, buf, nbyte);
}
Sfdisc_t *bsfio_new(pool *p, BUFF *b)
{
apache_sfio* disc;
if(!(disc = (apache_sfio*)palloc(p, sizeof(apache_sfio))) )
return (Sfdisc_t *)disc;
disc->disc.readf = (Sfread_f)bsfio_read;
disc->disc.writef = (Sfwrite_f)bsfio_write;
disc->disc.seekf = (Sfseek_f)NULL;
disc->disc.exceptf = (Sfexcept_f)NULL;
disc->buff = b;
return (Sfdisc_t *)disc;
}
#endif
/*
* Read up to nbyte bytes into buf.
* If fewer than byte bytes are currently available, then return those.
* Returns 0 for EOF, -1 for error.
*/
API_EXPORT(int) bread(BUFF *fb, void *buf, int nbyte)
{
int i, nrd;
if (fb->flags & B_RDERR) return -1;
if (nbyte == 0) return 0;
if (!(fb->flags & B_RD))
{
/* Unbuffered reading */
i = saferead( fb, buf, nbyte );
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
return i;
}
nrd = fb->incnt;
/* can we fill the buffer */
if (nrd >= nbyte)
{
memcpy(buf, fb->inptr, nbyte);
fb->incnt = nrd - nbyte;
fb->inptr += nbyte;
return nbyte;
}
if (nrd > 0)
{
memcpy(buf, fb->inptr, nrd);
nbyte -= nrd;
buf = nrd + (char *)buf;
fb->incnt = 0;
}
if (fb->flags & B_EOF) return nrd;
/* do a single read */
if (nbyte >= fb->bufsiz)
{
/* read directly into buffer */
i = saferead( fb, buf, nbyte );
if (i == -1)
{
if (nrd == 0)
{
if (errno != EAGAIN) doerror(fb, B_RD);
return -1;
}
else return nrd;
} else if (i == 0) fb->flags |= B_EOF;
} else
{
/* read into hold buffer, then memcpy */
fb->inptr = fb->inbase;
i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1)
{
if (nrd == 0)
{
if (errno != EAGAIN) doerror(fb, B_RD);
return -1;
}
else return nrd;
} else if (i == 0) fb->flags |= B_EOF;
fb->incnt = i;
if (i > nbyte) i = nbyte;
memcpy(buf, fb->inptr, i);
fb->incnt -= i;
fb->inptr += i;
}
return nrd + i;
}
/*
* Reads from the stream into the array pointed to by buff, until
* a (CR)LF sequence is read, or end-of-file condition is encountered
* or until n-1 bytes have been stored in buff. If a CRLF sequence is
* read, it is replaced by a newline character. The string is then
* terminated with a null character.
*
* Returns the number of bytes stored in buff, or zero on end of
* transmission, or -1 on an error.
*
* Notes:
* If null characters are exepected in the data stream, then
* buff should not be treated as a null terminated C string; instead
* the returned count should be used to determine the length of the
* string.
* CR characters in the byte stream not immediately followed by a LF
* will be preserved.
*/
API_EXPORT(int) bgets(char *buff, int n, BUFF *fb)
{
int i, ch, ct;
/* Can't do bgets on an unbuffered stream */
if (!(fb->flags & B_RD))
{
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR) return -1;
ct = 0;
i = 0;
for (;;)
{
if (i == fb->incnt)
{
/* no characters left */
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) break;
i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1)
{
buff[ct] = '\0';
if (ct == 0)
{
if (errno != EAGAIN) doerror(fb, B_RD);
return -1;
}
else return ct;
}
fb->incnt = i;
if (i == 0)
{
fb->flags |= B_EOF;
break; /* EOF */
}
i = 0;
continue; /* restart with the new data */
}
ch = fb->inptr[i++];
if (ch == '\012') /* got LF */
{
if (ct == 0) buff[ct++] = '\n';
/* if just preceeded by CR, replace CR with LF */
else if (buff[ct-1] == '\015') buff[ct-1] = '\n';
else if (ct < n-1) buff[ct++] = '\n';
else i--; /* no room for LF */
break;
}
if (ct == n-1)
{
i--; /* push back ch */
break;
}
buff[ct++] = ch;
}
fb->incnt -= i;
fb->inptr += i;
buff[ct] = '\0';
return ct;
}
/*
* Looks at the stream fb and places the first character into buff
* without removing it from the stream buffer.
*
* Returns 1 on success, zero on end of transmission, or -1 on an error.
*
*/
API_EXPORT(int) blookc(char *buff, BUFF *fb)
{
int i;
*buff = '\0';
if (!(fb->flags & B_RD)) { /* Can't do blookc on an unbuffered stream */
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR) return -1;
if (fb->incnt == 0) { /* no characters left in stream buffer */
fb->inptr = fb->inbase;
if (fb->flags & B_EOF)
return 0;
i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1) {
if (errno != EAGAIN)
doerror(fb, B_RD);
return -1;
}
if (i == 0) {
fb->flags |= B_EOF;
return 0; /* EOF */
}
else fb->incnt = i;
}
*buff = fb->inptr[0];
return 1;
}
/*
* Skip data until a linefeed character is read
* Returns 1 on success, 0 if no LF found, or -1 on error
*/
API_EXPORT(int) bskiplf(BUFF *fb)
{
unsigned char *x;
int i;
/* Can't do bskiplf on an unbuffered stream */
if (!(fb->flags & B_RD))
{
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR) return -1;
for (;;)
{
x = (unsigned char *)memchr(fb->inptr, '\012', fb->incnt);
if (x != NULL)
{
x++;
fb->incnt -= x - fb->inptr;
fb->inptr = x;
return 1;
}
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) return 0;
i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == 0) fb->flags |= B_EOF;
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
if (i == 0 || i == -1) return i;
fb->incnt = i;
}
}
/*
* Emtpy the buffer after putting a single character in it
*/
API_EXPORT(int) bflsbuf(int c, BUFF *fb)
{
char ss[1];
ss[0] = c;
return bwrite(fb, ss, 1);
}
/*
* Fill the buffer and read a character from it
*/
API_EXPORT(int) bfilbuf(BUFF *fb)
{
int i;
char buf[1];
i = bread(fb, buf, 1);
if (i == 0) errno = 0; /* no error; EOF */
if (i != 1) return EOF;
else return buf[0];
}
/*
* When doing chunked encodings we really have to write everything in the
* chunk before proceeding onto anything else. This routine either writes
* nbytes and returns 0 or returns -1 indicating a failure.
*
* This is *seriously broken* if used on a non-blocking fd. It will poll.
*/
static int
write_it_all(BUFF *fb, const void *buf, int nbyte)
{
int i;
if (fb->flags & (B_WRERR|B_EOUT))
return -1;
while (nbyte > 0) {
i = buff_write( fb, buf, nbyte );
if (i < 0) {
if (errno != EAGAIN && errno != EINTR) {
return -1;
}
}
else {
nbyte -= i;
buf = i + (const char *)buf;
}
if (fb->flags & B_EOUT)
return -1;
}
return 0;
}
/*
* A hook to write() that deals with chunking. This is really a protocol-
* level issue, but we deal with it here because it's simpler; this is
* an interim solution pending a complete rewrite of all this stuff in
* 2.0, using something like sfio stacked disciplines or BSD's funopen().
*/
static int
bcwrite(BUFF *fb, const void *buf, int nbyte)
{
char chunksize[16]; /* Big enough for practically anything */
#ifndef NO_WRITEV
struct iovec vec[3];
int i, rv;
#endif
if (fb->flags & (B_WRERR|B_EOUT))
return -1;
if (!(fb->flags & B_CHUNK)) {
return buff_write(fb, buf, nbyte);
}
#ifdef NO_WRITEV
/* without writev() this has poor performance, too bad */
ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte);
if (write_it_all(fb, chunksize, strlen(chunksize)) == -1)
return -1;
if (write_it_all(fb, buf, nbyte) == -1)
return -1;
if (write_it_all(fb, "\015\012", 2) == -1)
return -1;
return nbyte;
#else
#define NVEC (sizeof(vec)/sizeof(vec[0]))
vec[0].iov_base = chunksize;
vec[0].iov_len = ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012",
nbyte);
vec[1].iov_base = (void *)buf; /* cast is to avoid const warning */
vec[1].iov_len = nbyte;
vec[2].iov_base = "\r\n";
vec[2].iov_len = 2;
/* while it's nice an easy to build the vector and crud, it's painful
* to deal with a partial writev()
*/
for( i = 0; i < NVEC; ) {
do rv = writev( fb->fd, &vec[i], NVEC - i );
while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
if (rv == -1)
return -1;
/* recalculate vec to deal with partial writes */
while (rv > 0) {
if( rv <= vec[i].iov_len ) {
vec[i].iov_base = (char *)vec[i].iov_base + rv;
vec[i].iov_len -= rv;
rv = 0;
if( vec[i].iov_len == 0 ) {
++i;
}
} else {
rv -= vec[i].iov_len;
++i;
}
}
if (fb->flags & B_EOUT)
return -1;
}
/* if we got here, we wrote it all */
return nbyte;
#undef NVEC
#endif
}
/*
* Write nbyte bytes.
* Only returns fewer than nbyte if an error ocurred.
* Returns -1 if no bytes were written before the error ocurred.
* It is worth noting that if an error occurs, the buffer is in an unknown
* state.
*/
API_EXPORT(int) bwrite(BUFF *fb, const void *buf, int nbyte)
{
int i, nwr;
if (fb->flags & (B_WRERR|B_EOUT)) return -1;
if (nbyte == 0) return 0;
if (!(fb->flags & B_WR))
{
/* unbuffered write -- have to use bcwrite since we aren't taking care
* of chunking any other way */
do i = bcwrite(fb, buf, nbyte);
while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
if (i == 0) { /* return of 0 means non-blocking */
errno = EAGAIN;
return -1;
}
else if (i < 0) {
if (errno != EAGAIN)
doerror(fb, B_WR);
return -1;
}
fb->bytes_sent += i;
if (fb->flags & B_EOUT)
return -1;
else
return i;
}
/*
* Whilst there is data in the buffer, keep on adding to it and writing it
* out
*/
nwr = 0;
while (fb->outcnt > 0)
{
/* can we accept some data? */
i = fb->bufsiz - fb->outcnt;
if (i > 0)
{
if (i > nbyte) i = nbyte;
memcpy(fb->outbase + fb->outcnt, buf, i);
fb->outcnt += i;
nbyte -= i;
buf = i + (const char *)buf;
nwr += i;
if (nbyte == 0) return nwr; /* return if none left */
}
/* the buffer must be full */
if (fb->flags & B_CHUNK) {
end_chunk(fb);
/* it is just too painful to try to re-cram the buffer while
* chunking
*/
i = (write_it_all(fb, fb->outbase, fb->outcnt) == -1) ?
-1 : fb->outcnt;
} else {
do {
i = buff_write(fb, fb->outbase, fb->outcnt);
} while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
}
if (i <= 0) {
if (i == 0) /* return of 0 means non-blocking */
errno = EAGAIN;
if (nwr == 0) {
if (errno != EAGAIN) doerror(fb, B_WR);
return -1;
}
else return nwr;
}
fb->bytes_sent += i;
/* deal with a partial write */
if (i < fb->outcnt)
{
int j, n=fb->outcnt;
unsigned char *x=fb->outbase;
for (j=i; j < n; j++) x[j-i] = x[j];
fb->outcnt -= i;
}
else
fb->outcnt = 0;
if (fb->flags & B_EOUT)
return -1;
}
/* we have emptied the file buffer. Now try to write the data from the
* original buffer until there is less than bufsiz left. Note that we
* use bcwrite() to do this for us, it will do the chunking so that
* we don't have to dink around building a chunk in our own buffer.
*/
while (nbyte >= fb->bufsiz)
{
do i = bcwrite(fb, buf, nbyte);
while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
if (i <= 0) {
if (i == 0) /* return of 0 means non-blocking */
errno = EAGAIN;
if (nwr == 0) {
if (errno != EAGAIN) doerror(fb, B_WR);
return -1;
}
else return nwr;
}
fb->bytes_sent += i;
buf = i + (const char *)buf;
nwr += i;
nbyte -= i;
if (fb->flags & B_EOUT)
return -1;
}
/* copy what's left to the file buffer */
fb->outcnt = 0;
if( fb->flags & B_CHUNK ) start_chunk( fb );
if (nbyte > 0) memcpy(fb->outbase + fb->outcnt, buf, nbyte);
fb->outcnt += nbyte;
nwr += nbyte;
return nwr;
}
/*
* Flushes the buffered stream.
* Returns 0 on success or -1 on error
*/
API_EXPORT(int) bflush(BUFF *fb)
{
int i;
if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) return 0;
if (fb->flags & B_WRERR) return -1;
if (fb->flags & B_CHUNK) end_chunk(fb);
while (fb->outcnt > 0)
{
/* the buffer must be full */
do {
i = buff_write(fb, fb->outbase, fb->outcnt);
} while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
if (i == 0) {
errno = EAGAIN;
return -1; /* return of 0 means non-blocking */
}
else if (i < 0) {
if (errno != EAGAIN) doerror(fb, B_WR);
return -1;
}
fb->bytes_sent += i;
/*
* We should have written all the data, but if the fd was in a
* strange (non-blocking) mode, then we might not have done so.
*/
if (i < fb->outcnt)
{
int j, n=fb->outcnt;
unsigned char *x=fb->outbase;
for (j=i; j < n; j++) x[j-i] = x[j];
}
fb->outcnt -= i;
/* If a soft timeout occurs while flushing, the handler should
* have set the buffer flag B_EOUT.
*/
if (fb->flags & B_EOUT)
return -1;
}
return 0;
}
/*
* Flushes and closes the file, even if an error occurred.
* Discards an data that was not read, or not written by bflush()
* Sets the EOF flag to indicate no futher data can be read,
* and the EOUT flag to indicate no further data can be written.
*/
API_EXPORT(int) bclose(BUFF *fb)
{
int rc1, rc2, rc3;
if (fb->flags & B_WR) rc1 = bflush(fb);
else rc1 = 0;
#ifdef WIN32
if (fb->flags & B_SOCKET) {
rc2 = closesocket(fb->fd);
if (fb->fd_in != fb->fd) {
rc3 = closesocket(fb->fd_in);
} else {
rc3 = 0;
}
} else {
#endif
rc2 = close(fb->fd);
if (fb->fd_in != fb->fd) {
rc3 = close(fb->fd_in);
} else {
rc3 = 0;
}
#ifdef WIN32
}
#endif
fb->inptr = fb->inbase;
fb->incnt = 0;
fb->outcnt = 0;
fb->flags |= B_EOF | B_EOUT;
fb->fd = -1;
fb->fd_in = -1;
#ifdef B_SFIO
sfclose(fb->sf_in);
sfclose(fb->sf_out);
#endif
if (rc1 != 0) return rc1;
else if (rc2 != 0) return rc2;
else return rc3;
}
/*
* returns the number of bytes written or -1 on error
*/
API_EXPORT(int) bputs(const char *x, BUFF *fb)
{
int i, j=strlen(x);
i = bwrite(fb, x, j);
if (i != j) return -1;
else return j;
}
/*
* returns the number of bytes written or -1 on error
*/
API_EXPORT_NONSTD(int) bvputs(BUFF *fb, ...)
{
int i, j, k;
va_list v;
const char *x;
va_start(v, fb);
for (k=0;;)
{
x = va_arg(v, const char *);
if (x == NULL) break;
j = strlen(x);
i = bwrite(fb, x, j);
if (i != j)
{
va_end(v);
return -1;
}
k += i;
}
va_end(v);
return k;
}
API_EXPORT(void) bonerror(BUFF *fb, void (*error)(BUFF *, int, void *),
void *data)
{
fb->error = error;
fb->error_data = data;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.