/* cStringBuffer: a file-like string buffer.
   Copyright (C) 2004 Mooneer Salem <mooneer@translator.cx>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* Include necessary includes. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include "config.h"

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif

#include "csb_private.h"
#include "csb.h"

/* csb_getc(): Retrieves a character from current position.
   Parameters:
     buf: The buffer to operate on.
   Returns: the next character in the input.
*/
int csb_getc(csb_buf *buf) /*@modifies buf->ungetc_buffer, buf->fileptr, buf->ungetc_length@*/ {
	/* Declare variables. */
	char ch;

	/* Perform sanity checks to make sure we have a valid buffer. */
	assert(buf != NULL && buf->string != NULL);

	/* Retrieve character at current position and move file pointer
	   by 1. */
	if (buf->ungetc_buffer != NULL) {
		ch = *buf->ungetc_buffer;
		memmove(buf->ungetc_buffer, buf->ungetc_buffer + 1, buf->ungetc_length--);
		if (buf->ungetc_length == 0) {
			free(buf->ungetc_buffer);
			buf->ungetc_buffer = NULL;
		} else {
			buf->ungetc_buffer = realloc(buf->ungetc_buffer, buf->ungetc_length);
		}
	} else {
		ch = *buf->fileptr++;
	}

	/* Return end-of-file if the character retrieved is ASCII 0 (null). */
	return (ch != '\0' ? (int)ch : EOF);
}

/* csb_ungetc(): Stuffs character back into buffer at the current position
     without destroying other data.
   Parameters:
     buf: The buffer to operate on.
     ch: The character to insert.
   Returns: nothing.
*/
void csb_ungetc(csb_buf *buf, const char ch) /*@modifies buf->ungetc_buffer, buf->ungetc_length@*/ {
	/* Perform sanity checks to make sure we have a valid buffer. */
	assert(buf != NULL && buf->string != NULL);

	/* Call memmove() to non-destructively shift the data to the right by
	   one character. After doing this, then insert the character at the
	   current file pointer position so it can be read again. */
	if (buf->ungetc_buffer != NULL) {
		buf->ungetc_buffer = realloc(buf->ungetc_buffer, buf->ungetc_length);
		if (buf->ungetc_buffer == NULL) {
			/* eeek! return! */
			return;
		}
		memmove(buf->ungetc_buffer + 1, buf->ungetc_buffer, buf->ungetc_length++);
	} else {
		buf->ungetc_buffer = malloc((size_t)1);
		if (buf->ungetc_buffer == NULL) {
			/* eeek! return! */
			return;
		}
		buf->ungetc_length = (size_t)1;
	}
	buf->ungetc_buffer[0] = ch;
}

/* csb_read(): Read binary data from string.
   Parameters:
     buf: The buffer to operate on.
     data: The variable to place read data in.
     length: The amount to read.
   Returns: the amount of data read, or 0 if there is no more.
*/
size_t csb_read(csb_buf *buf, void *data, size_t length) /*@modifies buf->fileptr, data, buf->fileptr, buf->ungetc_buffer, buf->ungetc_length@*/ {
	/* Declare variables.*/
	unsigned long difference;
	size_t offset = 0;

	/* Perform sanity checks to make sure we have a valid buffer. */
	assert(buf != NULL && buf->string != NULL);

	/* If the length passed in would place the file pointer past
	   the end of the string, modify the string so it will just
	   read whatever's left. */
	difference = (unsigned long)(buf->fileptr) - (unsigned long)(buf->string);
	if (length + difference > buf->stringsize + buf->ungetc_length) {
		length = buf->stringsize - difference;
	}

	/* If the length is 0 as a result of above, just return EOF. */
	if (length == 0) {
		return (0);
	}

	/* Copy the data from the string to data and move the file pointer
	   to reflect that. */
	if (buf->ungetc_buffer != NULL) {
		memcpy(data, buf->ungetc_buffer, length <= buf->ungetc_length ? length : buf->ungetc_length);
		if (length < buf->ungetc_length) {
			memmove(buf->ungetc_buffer, buf->ungetc_buffer + length, buf->ungetc_length - length);
			offset = 0;
			length = 0;
		} else {
			free(buf->ungetc_buffer);
			buf->ungetc_buffer = NULL;
			offset = buf->ungetc_length;
			length -= offset;
			buf->ungetc_length = 0;
		}
		
	}

	if (length > 0) {
		memcpy(data + offset, buf->fileptr, length);
	}

	/* Restore file pointer. */
	CSB_SEEK_NORESET(buf, (size_t)(buf->fileptr + length));

	/* Return the number of bytes read. */
	return (length + offset);
}


syntax highlighted by Code2HTML, v. 0.9.1