/*
 * General purpose unbounded character buffers.  This allows
 * us to buffer our scanning without arbitrary limitations
 * on any lexeme size.
 */

#include <sys/types.h>
#include <stdlib.h>
#include "ansi.h"
#include "buffer.h"
#include "allocate.h"
#include "config.h"

#undef NULL
#define NULL	0L

#define NEXT_INDEX(x)			(((x) + 1) & (MAX_OUTBUF_LEN - 1))

static buffer_t *free_list;

static void
reclaim(buf)
	buffer_t *buf;
{
	buf->next = free_list;
	free_list = buf;
}

void
buf_destroy(buf)
	buffer_t *buf;
{
	buffer_t *next, *p;

	for (p = buf->next; p; p = next) {
		next = p->next;
		reclaim(p);
	}

	buf->next = NULL;
	buf->last = NULL;
	buf->head = 0;
	buf->tail = 0;
}

void
buf_init(buf)
	buffer_t *buf;
{
	buf->next = NULL;
	buf->last = NULL;
	buf->head = 0;
	buf->tail = 0;
}

int
is_buffer_empty(buf)
	buffer_t *buf;
{
	buffer_t *next;

	if (buf == NULL) {
		return 1;
	}

	if (buf->head == buf->tail) {
		next = buf->next;
		if (next != NULL) {
			*buf = *next;
			if (next == buf->last) {
				buf->last = NULL;
			}

			reclaim(next);
			return 0;
		}

		return 1;
	}

	return 0;
}

char
buf_get(buf)
	buffer_t *buf;
{
	if (buf->head == buf->tail) {
		if (buf_empty(buf)) {
			/* End of file */
			return 0;
		}
	}

	buf->head = NEXT_INDEX(buf->head);
	return buf->buf[buf->head];
}

static buffer_t*
gen_overflow_buf()
{
	buffer_t *buf;

	if (buf = free_list) {
		free_list = buf->next;
	}
	else {
		buf = (buffer_t*) malloc(sizeof(buffer_t));
		assert(buf != NULL);
	}

	buf_init(buf);
	return buf;
}

void
buf_add(buf, c)
	buffer_t *buf;
	int c;
{
	int index;
	buffer_t *l;

	if (buf == NULL) return;

	l = buf->last;
	if (l != NULL) {
		if (l->tail == (MAX_OUTBUF_LEN - 1)) {
			l->next = gen_overflow_buf();
			buf->last = l->next;
			l = l->next;
		}

		l->tail = NEXT_INDEX(l->tail);
		l->buf[l->tail] = (char)c;
		return;
	}

	index = NEXT_INDEX(buf->tail);
	if (index == buf->head) {
		buf->next = gen_overflow_buf();
		l = buf->last = buf->next;
		l->buf[1] = (char)c;
		l->tail = 1;
		return;
	}

	buf->buf[index] = (char)c;
	buf->tail = index;
}

void
buf_concat(buf, from_buf)
	buffer_t *buf, *from_buf;
{
	char c;

	for (;;) {
		c = buf_get(from_buf);
		if (c == 0) break;
		buf_add(buf, (int)c);
	}
}

void
buf_add_str(buf, str)
	buffer_t *buf;
	char *str;
{
	if (str == NULL) {
		return;
	}

	for (; *str; str++) {
		buf_add(buf, (int) *str);
	}
}

int
buf_count(buf)
	buffer_t *buf;
{
	buffer_t *p;
	int count;

	if (buf == NULL) {
		return 0;
	}

	if (buf->tail >= buf->head) {
		count = buf->tail - buf->head;
	}
	else {
		count = MAX_OUTBUF_LEN - buf->head + buf->tail;
	}

	for (p = buf->next; p; p = p->next) {
		count += p->tail;
	}

	return count;
}

void
buf_move_to(buf, str)
	buffer_t *buf;
	char *str;
{
	int index = buf->head;
	int tail = buf->tail;
	buffer_t *next, *p;

	while (index != tail) {
		index = NEXT_INDEX(index);
		*str++ = buf->buf[index];
	}

	for (p = buf->next; p; p = next) {
		next = p->next;
		for (index = 1; index <= p->tail; index++) {
			*str++ = p->buf[index];
		}
		reclaim(p);
	}

	*str = 0;
	buf_init(buf);
}

char*
buf_get_str(buf)
	buffer_t *buf;
{
	int len = buf_count(buf);
	char *str;

	if (len == 0) {
		return NULL;
	}

	str = (char*) malloc(len + 1);
	assert(str != NULL);

	buf_move_to(buf, str);

	return str;
}


syntax highlighted by Code2HTML, v. 0.9.1