/*
* LIB/BUFFER.C
*
* (c)Copyright 1997, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*
* Buffered I/O, may be used for reading or writing, but not both.
*
* Modified 12/4/1997 to include support for compressed data streams.
* Modifications (c) 1997, Christopher M Sedore, All Rights Reserved.
* Modifications may be distributed freely as long as this copyright
* notice is included without modification.
*
*/
#include "defs.h"
Prototype Buffer *bopen(int fd, int bsize);
Prototype void bsetfd(Buffer *b, int fd);
Prototype void bclose(Buffer *b, int closeFd);
Prototype void efree(char **pcopy, int *pcopymax);
Prototype char *bgets(Buffer *b, int *pbytes);
Prototype char *egets(Buffer *b, int *pbytes);
Prototype void bwrite(Buffer *b, const void *data, int bytes);
Prototype void bflush(Buffer *b);
Prototype int berror(Buffer *b);
Prototype off_t btell(Buffer *b);
Prototype int bsize(Buffer *b);
Prototype int bunget(Buffer *b, int n);
Prototype int bextfree(Buffer *b);
#ifdef USE_ZLIB
Prototype void bsetcompress(Buffer *b, gzFile *cfile);
Prototype int bzwrote(Buffer *b);
#endif
#if USE_ANON_MMAP || USE_ZERO_MMAP
#define NOMEXTSIZE (1024 * 1024) /* no cost */
#else
#define NOMEXTSIZE (128 * 1024) /* potential cost */
#endif
/*
* bopen() - open buffered I/O for a descriptor. If the buffer is to be used
* for writing, the descriptor may be -1.
*/
Buffer *
bopen(int fd, int bsize)
{
Buffer *b;
int pgSize;
b = pagealloc(&pgSize, bsize);
bzero(b, sizeof(Buffer));
b->bu_BufMax = pgSize - offsetof(Buffer, bu_Buf[0]) - sizeof(int);
b->bu_DataMax = b->bu_BufMax;
b->bu_Data = b->bu_Buf;
b->bu_Fd = fd;
b->bu_gzFile = NULL;
b->bu_CBuf = NULL;
b->bu_BufSize = bsize;
return(b);
}
void
bsetfd(Buffer *b, int fd)
{
b->bu_Fd = fd;
}
#ifdef USE_ZLIB
void
bsetcompress(Buffer *b, gzFile *cfile)
{
b->bu_gzFile = cfile;
}
int
bzwrote(Buffer *b)
{
return(b->bu_gzWrote);
}
#endif
void
bclose(Buffer *b, int closeFd)
{
if (b != NULL) {
if (closeFd && b->bu_Fd >= 0) {
#ifdef USE_ZLIB
if (b->bu_gzFile != NULL)
gzclose(b->bu_gzFile);
else
#endif
close(b->bu_Fd);
b->bu_Fd = -1;
b->bu_gzFile = NULL;
}
b->bu_Beg = 0;
b->bu_NLScan = 0;
b->bu_End = 0;
(void)bextfree(b);
pagefree(b, b->bu_BufSize);
}
}
/*
* bgets() - get a line from the input. Return NULL if no data
* ready to read (non-blocking only), -1 on EOF, or a
* pointer to the data. If ptr[bytes-1] != '\n', a full
* line could not be returned due to the limited buffer size.
*
* if a real pointer is returned, *pbytes will be non-zero.
*/
char *
bgets(Buffer *b, int *pbytes)
{
int i;
if (b->bu_Beg == b->bu_End) {
b->bu_Beg = 0;
b->bu_NLScan = 0;
b->bu_End = 0;
}
for (;;) {
/*
* look for a newline, return the line as appropriate. NLScan
* is an optimization so we do not rescan portions of the input
* buffer that we have already checked.
*/
for (i = b->bu_NLScan; i < b->bu_End; ++i) {
if (b->bu_Data[i] == '\n') {
char *p = b->bu_Data + b->bu_Beg;
/*
* terminate buffer, removing newline, or return
* exact size of buffer with nothing removed or terminated.
*/
if (pbytes) {
*pbytes = i + 1 - b->bu_Beg;
} else {
b->bu_Data[i] = 0;
}
/*
* note: cannot reset to 0/0 or bunget will not work.
*/
b->bu_Beg = i + 1;
b->bu_NLScan = b->bu_Beg;
return(p);
}
}
b->bu_NLScan = i;
/*
* If there is no room to append new data, attempt to
* make some.
*/
if (b->bu_End == b->bu_DataMax && b->bu_Beg != 0) {
memmove(b->bu_Data, b->bu_Data + b->bu_Beg, b->bu_End - b->bu_Beg);
b->bu_End -= b->bu_Beg;
b->bu_NLScan -= b->bu_Beg;
b->bu_Beg = 0;
}
/*
* If the buffer is full, we have an overflow problem.
*/
if (b->bu_End == b->bu_DataMax) {
char *p = b->bu_Data + b->bu_Beg;
if (pbytes == NULL) {
logit(LOG_ERR, "Line buffer overflow fd %d", b->bu_Fd);
p = (void *)-1;
} else {
*pbytes = b->bu_End - b->bu_Beg;
/*
* note: cannot reset to 0 or bunget will not work
*/
b->bu_Beg = b->bu_NLScan = b->bu_End;
}
return(p);
}
/*
* otherwise read some data and repeat
*/
{
int n = 0;
if (b->bu_CBuf) {
#ifdef USE_ZLIB
/*
* The block contains the mods to deal with compressed
* streams. cmsedore@maxwell.syr.edu 12/4/97
*/
int nc;
int rc;
int code;
int decompcount;
if (b->bu_CBuf->dataError)
return ((void *) -1);
while (!n) {
/*
* if we have bytes left over, process them before
* reading
*/
if (b->bu_CBuf -> z_str.avail_out != -1) {
rc = COMPRESS_BUFFER_LENGTH - b->bu_CBuf->z_str.avail_in;
/* check for decompressor buffer overflow */
if (rc == 0) {
logit(LOG_ERR, "decompressor buffer overflow fd %d",
b->bu_Fd);
return((void *)-1);
}
nc = read(b->bu_Fd, &b->bu_CBuf->cb_Buf[b->bu_CBuf->z_str.avail_in], rc);
if (nc < 1) {
logit(LOG_ERR,"eof (%i) on read",nc);
n = nc;
goto err_or_eof;
}
b->bu_CBuf->orig = b->bu_CBuf->orig + (double)nc;
b->bu_CBuf->z_str.next_in = b->bu_CBuf->cb_Buf;
rc = nc + b->bu_CBuf->z_str.avail_in;
b->bu_CBuf->z_str.avail_in = rc;
} else {
nc = 0;
}
decompcount = b->bu_DataMax-b->bu_End;
b->bu_CBuf->z_str.avail_out = decompcount;
b->bu_CBuf->z_str.next_out = b->bu_Data + b->bu_End;
code = inflate(&b->bu_CBuf->z_str, Z_PARTIAL_FLUSH);
/*
* Z_BUF_ERROR indicates that no progress was possible due
* to buffering constraints. We try again, this time
* we'll do a read() for sure. We may not have before
* because z_str.avail_out==0
*/
if (code == Z_BUF_ERROR) {
#ifdef COMPDEBUG
logit(LOG_ERR, "code=%i,read=%u,decomp=%u,avail_in=%u,avail_out=%u",
code, nc, decompcount,
b->bu_CBuf->z_str.avail_in,
b->bu_CBuf->z_str.avail_out
);
#endif
continue;
}
/*
* this indicates an inconsistency in the z_str structure
* members. We don't mess with them. I *think* this
* happens when we hit z_str.avail_in=0 and
* z_str.avail_out=0 simultaneously.
*
* at this point, we continue.
*/
if (code == Z_STREAM_ERROR) {
logit(LOG_ERR, "code=%i,read=%u,decomp=%u,avail_in=%u,avail_out=%u",
code, nc, decompcount,
b->bu_CBuf->z_str.avail_in,
b->bu_CBuf->z_str.avail_out
);
continue;
}
/*
* any other error probably means we're done. We set the
* dataError member so that when diablo reads again, we
* indicate that this stream is exhausted.
*
* future improvements might try recovery.
*/
if (code != Z_OK) {
logit(LOG_ERR, "inflate error (%i) on fd %d: %s",
code, b->bu_Fd, b->bu_CBuf->z_str.msg);
b->bu_CBuf->dataError = 1;
return((void *)-1);
}
decompcount -= b->bu_CBuf->z_str.avail_out;
if (b->bu_CBuf->z_str.avail_out==0) {
b->bu_CBuf->z_str.avail_out=-1;
} else {
if (b->bu_CBuf->z_str.avail_in) {
if ((char *)b->bu_CBuf->z_str.next_in !=
(char *)b->bu_CBuf->cb_Buf) {
memmove(b->bu_CBuf->cb_Buf,
b->bu_CBuf->z_str.next_in,
b->bu_CBuf->z_str.avail_in
);
b->bu_CBuf->z_str.next_in = b->bu_CBuf->cb_Buf;
}
}
}
n = decompcount;
b->bu_CBuf->decomp = b->bu_CBuf->decomp + (double)decompcount;
} /* while (!n) */
#else
logit(LOG_ERR, "Compression buffer is not initialised");
#endif /* USE_ZLIB */
} else {
errno = 0;
n = read(b->bu_Fd, b->bu_Data + b->bu_End, b->bu_DataMax - b->bu_End);
}
#ifdef USE_ZLIB
err_or_eof:
#endif
/*
* EOF
*/
if (n == 0) {
return((void *)-1);
}
if (n < 0) {
if (errno == EAGAIN || errno == EINTR ||
errno == EWOULDBLOCK ||
errno == EINPROGRESS
) {
return(NULL);
}
return((void *)-1);
}
b->bu_End += n;
}
}
/* not reached */
return((void *)-1);
}
/*
* egets() - same as bgets, but will allocate an extended buffer to
* fit the line if necessary.
*/
char *
egets(Buffer *b, int *pbytes)
{
char *ptr = NULL;
while ((ptr = bgets(b, pbytes)) != NULL && ptr != (char *)-1) {
/*
* looking for newline
*/
if (ptr[*pbytes-1] == '\n') {
break;
}
/*
* if an overflow occurs, b->bu_Beg is guarenteed to be 0
*/
{
int cpyLen;
char *cpy = pagealloc(&cpyLen, *pbytes * 3 / 2);
bcopy(b->bu_Data, cpy, b->bu_End);
if (b->bu_Data != b->bu_Buf)
pagefree(b->bu_Data, b->bu_DataMax);
b->bu_Data = cpy;
b->bu_DataMax = cpyLen;
}
}
return(ptr);
}
/*
* bunget() partially undoes the most recent read. You cannot bunget more
* then the number of bytes in the last read.
*/
int
bunget(Buffer *b, int n)
{
int r = -1;
if (n <= b->bu_Beg) {
b->bu_Beg -= n;
r = 0;
}
return(r);
}
void
bwrite(Buffer *b, const void *data, int bytes)
{
while (bytes > 0) {
int n = b->bu_DataMax - b->bu_End;
if (n > bytes)
n = bytes;
memcpy(b->bu_Data + b->bu_End, data, n);
b->bu_End += n;
data = (const char *)data + n;
bytes -= n;
if (b->bu_End == b->bu_DataMax)
bflush(b);
}
}
/*
* bfreebuf() - free extended buffer if possible. Return
* 0 on success, -1 on failure.
*/
int
bextfree(Buffer *b)
{
int r = 0;
if (b->bu_Data != b->bu_Buf) {
r = -1;
if (b->bu_End - b->bu_Beg <= b->bu_BufMax) {
r = 0;
if (b->bu_Beg != b->bu_End)
bcopy(b->bu_Data, b->bu_Buf, b->bu_End - b->bu_Beg);
b->bu_NLScan -= b->bu_Beg;
b->bu_End -= b->bu_Beg;
b->bu_Beg = 0;
pagefree(b->bu_Data, b->bu_DataMax);
b->bu_Data = b->bu_Buf; /* not really necessary */
b->bu_DataMax = b->bu_BufMax; /* not really necessary */
}
}
return(r);
}
/*
* bflush() - If we have a valid descriptor, we write out pending data
* and free up any externally allocated space. If we do not
* have a descriptor we allocate or reallocate more space.
*
* note: the write/write-extension buffering is only designed to
* deal with buffer space for article headers. It is not designed
* to cache entire articles even though it can.
*/
void
bflush(Buffer *b)
{
int n;
if (b->bu_Fd < 0) {
if (b->bu_End == b->bu_DataMax) {
int cpyLen;
char *cpy;
cpy = pagealloc(
&cpyLen,
(b->bu_DataMax < NOMEXTSIZE) ? NOMEXTSIZE : b->bu_DataMax * 2
);
bcopy(b->bu_Data, cpy, b->bu_End);
if (b->bu_Data != b->bu_Buf)
pagefree(b->bu_Data, b->bu_DataMax);
b->bu_Data = cpy;
b->bu_DataMax = cpyLen;
}
} else {
if (b->bu_Error == 0) {
if (b->bu_gzFile != NULL) {
#ifdef USE_ZLIB
while (b->bu_Beg != b->bu_End) {
n = gzwrite(
b->bu_gzFile,
b->bu_Data + b->bu_Beg,
b->bu_End - b->bu_Beg
);
if (n == 0) {
b->bu_Error = 1;
break;
}
b->bu_Beg += n;
b->bu_gzWrote += n;
}
#endif
} else while (b->bu_Beg != b->bu_End) {
n = write(
b->bu_Fd,
b->bu_Data + b->bu_Beg,
b->bu_End - b->bu_Beg
);
if (n < 0) {
b->bu_Error = errno;
break;
}
b->bu_Beg += n;
}
}
b->bu_Beg = b->bu_End = b->bu_NLScan = 0;
if (b->bu_Data != b->bu_Buf)
(void)bextfree(b);
}
}
int
bsize(Buffer *b)
{
return(b->bu_End - b->bu_Beg);
}
off_t
btell(Buffer *b)
{
return(lseek(b->bu_Fd, 0L, 1) + (b->bu_End - b->bu_Beg));
}
int
berror(Buffer *b)
{
return(b->bu_Error);
}
syntax highlighted by Code2HTML, v. 0.9.1