/*
* dynbuff.cpp
*
* (C) 1998-2002 Murat Deligonul
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "autoconf.h"
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <sys/socket.h>
#include <string.h>
#include "dynbuff.h"
#include "debug.h"
/* create a new dynbuff
* Maxsize can be 0 or (< minsize) for unlimited length.
*/
dynbuff::dynbuff(unsigned int minsize, unsigned int maxsize)
{
if (maxsize < minsize)
maxsize = 0;
if (minsize == 0)
minsize = 1024;
min = minsize;
max = (maxsize == 0) ? 0xFFFFFFF : maxsize;
/* add a 0 to the end */
contents = new char[minsize + 1];
alloced = minsize;
contents[alloced] = 0;
bytes_in = 0;
}
dynbuff::~dynbuff()
{
delete[] contents;
}
/* add()
* return: # of bytes added */
int dynbuff::add(const char *data, unsigned int len)
{
if (len == 0)
len = strlen(data);
if (len == 0)
return 0;
return -(bytes_in - append(data, len));
}
/* add()
* read from a file into buffer
* return: > 0 - (num bytes added)
* = 0 - nothing to add (or no data waiting on socket)
* < 0 - error condition */
int dynbuff::add(int fd, unsigned int len)
{
int ob = bytes_in;
if (len == 0)
{
ioctl(fd, FIONREAD, &len);
if (len == 0)
return 0;
}
if ((signed) len + bytes_in > max)
len = max - bytes_in;
if (!len)
return DYNBUFF_ERR_FULL;
char *tmp = new char[len];
if (!tmp)
return DYNBUFF_ERR_MEM;
switch ((signed) (len = read(fd, tmp, len)))
{
case -1:
delete[] tmp;
return (errno == EAGAIN) ? 0 : DYNBUFF_ERR_READ;
case 0:
delete[] tmp;
return DYNBUFF_ERR_READ;
}
fd = append(tmp, len);
delete[] tmp;
if (fd < 0)
return DYNBUFF_ERR_FULL;
return (long) (fd - ob);
}
/* copy_line()
* Copies an arbitrary line from this buffer.
* Arguments:
* line - 0 based index
* buffer - where to store line
* maxsize - how much to store at most
* leave - leave the line in the internal buffer or remove it?
* (optional, defaults to 0)
* must_have_crlf - line must end w/ cr, lf, or both.
*
* Entire line will be removed if leave is 0, even if the length of the
* whole line is less than maxsize.
*
* Return values: 0+ how long the string is in buffer. Can be 0,
* if line was empty, as the ending \n or \r is not copied.
* -1: line was not found
*
* FIXME: we check for 'out of bounds': this is wrong. What is going on?
* if we can peek out of bounds it's a segfault waiting to happen.
*/
int dynbuff::copy_line(unsigned line, char *buffer, unsigned int maxsize, bool leave,
bool need_crlf)
{
const char *p, *cur = contents;
unsigned curline = 0;
/* Keep finding \n's until we find the line we need */
while ((p = strchr(cur, '\n')) && (curline != line)
&& ((unsigned) (p - contents) < bytes_in)) {
cur = p + 1;
curline++;
}
if (unsigned(cur - contents) == bytes_in)
return -1; /* Oops, we've reached the end */
if (curline == line && bytes_in)
{
const char *next = strchr(cur, '\n');
if (!next && need_crlf)
return -1; /* incomplete line, ignore it */
if (!next ||
((unsigned) (next - contents) > bytes_in)) /* any more \n's ? */
next = contents + bytes_in; /* or is it out of bounds? */
else
next++; /* include this \n so it will be removed */
u_long len = next - cur;
u_long real_len = len;
if (len >= maxsize)
len = maxsize - 1;
memcpy(buffer, cur, len);
buffer[len] = 0;
/* get rid of any lf or cr at the end */
if ((buffer[len - 1] == '\r') || (buffer[len - 1] == '\n'))
buffer[--len] = 0;
if (len && buffer[len - 1] == '\r')
buffer[--len] = 0;
/* remove this line if requested */
if (!leave)
remove(u_long(cur - contents), real_len);
return len;
}
return -1;
}
/* append()
* adds data to the end of the buffer.
* resizing is done if needed.
* returns:
* > 0 -- new size of buffer
* <= 0 -- error conditions */
int dynbuff::append(const char *data, unsigned int len)
{
if (bytes_in == max)
return DYNBUFF_ERR_FULL;
if (len == 0)
return 0;
unsigned int newsize = (len + bytes_in) > max ? max : len + bytes_in;
unsigned int bytes2copy = newsize - bytes_in;
if (newsize > alloced)
{
/*
* The new size will be too big for this buffer.
* we need to allocate bigger one.
*/
char *newbuff;
newbuff = new char[newsize + 1];
memcpy(newbuff, contents, bytes_in);
memcpy(newbuff + bytes_in, data, bytes2copy);
/* clean up */
delete[] contents;
contents = newbuff;
contents[newsize] = 0; /* add a null char */
alloced = newsize;
bytes_in += bytes2copy;
}
else if (newsize <= alloced)
{
memcpy(contents + bytes_in, data, bytes2copy);
bytes_in += bytes2copy;
}
contents[bytes_in] = 0;
return bytes_in;
}
/* flush()
* Attempt to dump all contents to a file descriptor.
* return: < 0 -- error condition
* > 0 -- # of bytes dumped down the pipe */
int dynbuff::flush(int fd, unsigned int bytes)
{
int ob = bytes_in;
if (!bytes || bytes > bytes_in)
bytes = bytes_in;
int wr = write(fd, contents, bytes);
if (wr < 0)
return DYNBUFF_ERR_WRITE;
/* NEVER assume all of the contents were written */
return ob - remove(0, wr);
}
/* remove()
* Starts at 'start' and removes 'howmuch' stuff
* return: new size of buffer */
int dynbuff::remove(unsigned int start, unsigned int howmuch)
{
u_long end = start + howmuch;
u_long part2 = (end > bytes_in) ? end : bytes_in - end;
/* start at 'end'. move it to 'start' */
memmove(contents + start, contents + end, part2);
/* re-adjust */
bytes_in = start + part2;
contents[alloced] = 0;
contents[bytes_in] = 0;
return bytes_in;
}
/* optimize()
* Optimize memory usage, i.e:
* if allocated amount is > minimum and bytes_in is also
* < min, then resize it. (But make size no smaller than min) */
int dynbuff::optimize(void)
{
if (alloced > min && bytes_in <= min)
{
char *_new = new char[min + 1];
if (!_new)
return 0;
memcpy(_new, contents, bytes_in);
delete[] contents;
alloced = min;
contents = _new;
contents[alloced] = 0;
contents[bytes_in] = 0;
}
return alloced;
}
/* transfer()
* add contents to target dynbuff
* return # of bytes transfered */
int dynbuff::transfer(dynbuff * target)
{
/* If target is empty, then
* it's easy ... */
if (!target->bytes_in)
{
delete[] target->contents;
target->contents = contents;
target->bytes_in = bytes_in;
target->alloced = alloced;
contents = new char[min + 1];
alloced = min;
contents[alloced] = 0;
bytes_in = 0;
return target->bytes_in;
}
else {
int b = bytes_in;
if (target->append(contents, bytes_in) <= 0)
return 0;
reset();
return b;
}
}
/*************************
* SSL SUPPORT FUNCTIONS
*************************/
#ifdef _USE_SSL
int dynbuff::add(SSL *ssl, unsigned int len)
{
int ob = bytes_in;
int i;
char tmp[1024];
again:
do {
switch ((signed long) (len = SSL_read(ssl, tmp, 1024)))
{
case -1:
switch (SSL_get_error(ssl,len)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_X509_LOOKUP:
goto again;
}
return DYNBUFF_ERR_READ;
}
i = append(tmp, len);
} while(SSL_pending(ssl));
return long(i - ob);
}
int dynbuff::flush(SSL * ssl, unsigned int bytes)
{
int ob = bytes_in;
if (!bytes || bytes > bytes_in)
bytes = bytes_in;
again:
int i = SSL_write(ssl, contents, bytes);
if (i==-1) {
int err=SSL_get_error(ssl,i);
if ((err==SSL_ERROR_WANT_READ)||(err==SSL_ERROR_WANT_WRITE)||(err==SSL_ERROR_WANT_X509_LOOKUP))
goto again;
}
DEBUG("SSL_write() wrote: %d\n",i);
return ob - remove(0, i);
}
#endif
/************
* db_parser
************/
db_parser::db_parser(dynbuff * target)
{
buff = target;
next_line_ptr = buff->contents;
}
/* get_next_line()
* Line by line dissasembly off the buffer
* null char is placed at crlf location and
* next_line_ptr is advanced
* args:
* crlf -- require cr and/or lf at end
* return:
* direct pointer to line in buff */
char * db_parser::get_next_line(bool crlf)
{
char * orig = next_line_ptr;
if (!orig)
return 0;
next_line_ptr = strchr(next_line_ptr, '\n');
if (!next_line_ptr)
{
if (crlf)
{
next_line_ptr = orig;
return 0;
}
next_line_ptr = 0;
return orig;
}
/* strip LF, (and CR if it's there) */
*next_line_ptr = 0;
if (*(next_line_ptr - 1) == '\r')
*(next_line_ptr - 1) = 0;
/* advance the pointer -- if we hit the end,
* there'll be a null character to greet us on the next
* call */
++next_line_ptr;
return orig;
}
syntax highlighted by Code2HTML, v. 0.9.1