/*
 *	tbuff.c
 *
 *	Manage dynamic temporary buffers.
 *	Note that some temp-buffs are never freed, for speed
 *
 *	To do:	add 'itb_ins()' and 'itb_del()' to support cursor-level command
 *		editing.
 *
 * $Header: /usr/build/vile/vile/RCS/itbuff.c,v 1.26 2010/09/06 18:26:15 tom Exp $
 *
 */

#include "estruct.h"
#include "edef.h"

#define	NCHUNK	NLINE

/*******(testing)************************************************************/
#if NO_LEAKS
typedef struct _itb_list {
    struct _itb_list *link;
    ITBUFF *buff;
} ITB_LIST;

static ITB_LIST *all_tbuffs;

#define	AllocatedBuffer(q)	itb_remember(q)
#define	FreedBuffer(q)		itb_forget(q)

static void
itb_remember(ITBUFF * p)
{
    ITB_LIST *q;

    beginDisplay();
    if ((q = typealloc(ITB_LIST)) != 0) {
	q->buff = p;
	q->link = all_tbuffs;
	all_tbuffs = q;
    }
    endofDisplay();
}

static void
itb_forget(ITBUFF * p)
{
    ITB_LIST *q, *r;

    beginDisplay();
    for (q = all_tbuffs, r = 0; q != 0; r = q, q = q->link) {
	if (q->buff == p) {
	    if (r != 0)
		r->link = q->link;
	    else
		all_tbuffs = q->link;
	    free(q);
	    break;
	}
    }
    endofDisplay();
}

void
itb_leaks(void)
{
    while (all_tbuffs != 0) {
	ITBUFF *q = all_tbuffs->buff;
	FreedBuffer(q);
	itb_free(&q);
    }
}

#else
#define	AllocatedBuffer(q)
#define	FreedBuffer(q)
#endif

/*******(initialization)*****************************************************/

/*
 * ensure that the given temp-buff has as much space as specified
 */
ITBUFF *
itb_alloc(ITBUFF ** p, size_t n)
{
    ITBUFF *q = *p;

    beginDisplay();
    if (q == 0) {
	if ((q = *p = typealloc(ITBUFF)) != 0) {
	    if ((q->itb_data = typeallocn(int, q->itb_size = n)) != 0) {
		q->itb_used = 0;
		q->itb_last = 0;
		q->itb_endc = esc_c;
		AllocatedBuffer(q);
	    } else {
		itb_free(&q);
	    }
	}
    } else if (n >= q->itb_size) {
	q->itb_data = typereallocn(int, q->itb_data,
				   q->itb_size = (n * 2));
	if (q->itb_data == 0)
	    itb_free(&q);
    }
    endofDisplay();
    return q;
}

/*
 * (re)initialize a temp-buff
 */
ITBUFF *
itb_init(ITBUFF ** p, int c)
{
    ITBUFF *q = *p;
    if (q == 0)
	q = itb_alloc(p, (size_t) NCHUNK);
    if (q != 0) {
	q->itb_used = 0;
	q->itb_last = 0;
	q->itb_endc = c;	/* code to return if no-more-data */
    }
    return (*p = q);
}

/*
 * deallocate a temp-buff
 */
void
itb_free(ITBUFF ** p)
{
    ITBUFF *q = *p;

    beginDisplay();
    if (q != 0) {
	FreedBuffer(q);
	FreeIfNeeded(q->itb_data);
	free(q);
    }
    *p = 0;
    endofDisplay();
}

/*******(storage)************************************************************/

/*
 * put a character c at the nth position of the temp-buff.  make it the last.
 */
static ITBUFF *
itb_put(ITBUFF ** p, size_t n, int c)
{
    ITBUFF *q;

    if ((q = itb_alloc(p, n + 1)) != 0) {
	q->itb_data[n] = c;
	q->itb_used = n + 1;
    }
    return q;
}

/*
 * stuff the nth character into the temp-buff -- assumes space already there
 *  it's sort of the opposite of itb_peek
 */
void
itb_stuff(ITBUFF * p, int c)
{
    if (p != 0) {
	if (p->itb_last < p->itb_used)
	    p->itb_data[p->itb_last] = c;
	else
	    p->itb_endc = c;
    }
}

/*
 * append a character to the temp-buff
 */
ITBUFF *
itb_append(ITBUFF ** p, int c)
{
    ITBUFF *q = *p;
    size_t n = (q != 0) ? q->itb_used : 0;

    return itb_put(p, n, c);
}

/*
 * Copy one temp-buff to another
 */
ITBUFF *
itb_copy(ITBUFF ** d, ITBUFF * s)
{
    ITBUFF *p;

    if (s != 0) {
	if ((p = itb_init(d, s->itb_endc)) != 0) {
	    int *ptr = s->itb_data;
	    size_t len = s->itb_used;
	    while ((len-- != 0) && itb_append(&p, *ptr++) != 0) {
		;
	    }
	}
    } else
	p = itb_init(d, esc_c);
    return p;
}

#if VILE_NEEDED
/*
 * append a binary data to the temp-buff
 */
ITBUFF *
itb_bappend(ITBUFF ** p, const char *s, size_t len)
{
    while ((len-- != 0) && itb_append(p, (int) (*s++)) != 0) {
	;
    }
    return *p;
}

/*
 * append a string to the temp-buff
 */
ITBUFF *
itb_sappend(ITBUFF ** p, const char *s)
{
    if (!s)
	return *p;
    while (*s && itb_append(p, (int) (*s++)) != 0) {
	;
    }
    return *p;
}

void
itb_delete(ITBUFF * p, size_t cnt)
{
    int *from, *to, *used;

    if (p != 0) {
	to = &p->itb_data[p->itb_last];
	from = to + cnt;

	used = &p->itb_data[p->itb_used];

	if (from >= used) {
	    p->itb_used = p->itb_last;
	    return;
	}

	while (from < used) {
	    *to++ = *from++;
	}

	p->itb_used -= cnt;
    }
}

ITBUFF *
itb_insert(ITBUFF ** p, int c)
{
    ITBUFF *q = *p;
    int *last, *to;

    /* force enough room for another character */
    q = itb_put(p, q->itb_used, 0 /* any value */ );

    /* copy up */
    to = &q->itb_data[q->itb_used - 1];
    last = &q->itb_data[q->itb_last];
    while (to > last) {
	*to = *(to - 1);
	to--;
    }

    /* put in the new one */
    itb_stuff(q, c);

    return *p;
}
#endif /* NEEDED */

/*******(retrieval)************************************************************/

/*
 * get the nth character from the temp-buff
 */
int
itb_get(ITBUFF * p, size_t n)
{
    int c = esc_c;

    if (p != 0)
	c = (n < p->itb_used) ? p->itb_data[n] : p->itb_endc;

    return c;
}

#if VILE_NEEDED
/*
 * undo the last 'itb_put'
 */
void
itb_unput(ITBUFF * p)
{
    if (p != 0
	&& p->itb_used != 0)
	p->itb_used -= 1;
}
#endif

/*******(iterators)************************************************************/

/*
 * Reset the iteration-count
 */
static size_t
itb_seek(ITBUFF * p, size_t seekto, int whence)
{
    size_t olast;

    if (p == 0)
	return 0;

    olast = p->itb_last;

    if (whence == 0)
	p->itb_last = seekto;
    else if (whence == 1)
	p->itb_last += seekto;
    else if (whence == 2)
	p->itb_last = p->itb_used - seekto;
    return olast;
}

void
itb_first(ITBUFF * p)
{
    (void) itb_seek(p, (size_t) 0, 0);
}

/*
 * Returns true iff the iteration-count has not gone past the end of temp-buff.
 */
int
itb_more(ITBUFF * p)
{
    return (p != 0) ? (p->itb_last < p->itb_used) : FALSE;
}

/*
 * get the next character from the temp-buff
 */
int
itb_next(ITBUFF * p)
{
    if (p != 0)
	return itb_get(p, p->itb_last++);
    return esc_c;
}

/*
 * get the last character from the temp-buff, and shorten
 * (opposite of itb_append)
 */
int
itb_last(ITBUFF * p)
{
    int c;

    if (p != 0 && p->itb_used > 0) {
	c = itb_get(p, p->itb_used - 1);
	p->itb_used--;
	return c;
    }
    return esc_c;
}

#if VILE_NEEDED
/*
 * undo a itb_next
 */
void
itb_unnext(ITBUFF * p)
{
    if (p == 0)
	return;
    if (p->itb_last > 0)
	p->itb_last--;
}
#endif

/*
 * get the next character from the temp-buff w/o incrementing index
 */
int
itb_peek(ITBUFF * p)
{
    if (p != 0)
	return itb_get(p, p->itb_last);
    return esc_c;
}

/*******(bulk-data)************************************************************/

/*
 * returns a pointer to data, assumes it is one long string
 */
int *
itb_values(ITBUFF * p)
{
    return (p != 0) ? p->itb_data : 0;
}

/*
 * returns the length of the data
 */
size_t
itb_length(ITBUFF * p)
{
    return (p != 0 && p->itb_data != 0) ? p->itb_used : 0;
}
