/* $Id: tblock.cc,v 1.3 1997/03/21 20:34:11 dps Exp $ */
/* Dynamically growinf text block */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <iostream.h>
#include "tblock.h"

const struct tblock::block tblock::dummy_init={0,0,NULL,NULL};

tblock::~tblock(void)
{
    struct block *blk, *nblk;
    blk=dummy_block.next;
    while (blk!=NULL)
    {
	nblk=blk->next;
	free(blk->text);
	free(blk);
	blk=nblk;
    }
}

void tblock::zero(void)
{		
    struct block *blk, *nblk;
    blk=dummy_block.next;
    while (blk!=NULL)
    {
	nblk=blk->next;
	free(blk->text);
	free(blk);
	blk=nblk;
    }
    dummy_block=dummy_init;
    head=&dummy_block;
}

int tblock::add(char c)
{
    struct block *blk, *nb;
    blk=head;
    if (blk->limit==blk->pos)
    {
	if ((nb=(struct block *) malloc(sizeof(struct block)))==NULL)
	    return 0;
	if ((nb->text=(char *) malloc(block_size))==NULL)
	{
	    free(nb);
	    return 0;
	}
	nb->limit=1024;
	nb->pos=0;
	nb->next=NULL;
	head->next=nb;
	head=nb;
    }
    head->text[(head->pos)++]=c;
    return 1;
}

int tblock::add(const char *s, int len)
{
    struct block *nb;
    const char *t;
    int size;

    if (*s=='\0')
	return 1;

    t=s;

    while (len>0)
    {
	if (head->limit==head->pos)
	{
	    if ((nb=(struct block *) malloc(sizeof(struct block)))==NULL)
		return 0;
	    if ((nb->text=(char *) malloc(block_size))==NULL)
	    {
		free(nb);
		return 0;
	    }
	    nb->limit=block_size;
	    nb->pos=0;
	    nb->next=NULL;
	    head->next=nb;
	    head=nb;
	}
	size=head->limit-head->pos;
	if (size>len)
	    size=len;
	memcpy(head->text+head->pos, t, size);
	head->pos+=size;
	len-=size;
	t+=size;
    }
    return 1;
}

const char *tblock::collect(void) const
{
    struct block *blk;
    char *s,*t;
    size_t size;

    size=0;
    blk=dummy_block.next;
    while (blk!=NULL)
    {
	size+=blk->pos;
	blk=blk->next;
    }
    
    if ((t=(char *) malloc(size+1))==NULL)
    {
        cerr<<"tblock::collect malloc failure\n";
	return NULL;
    }

    s=t; blk=dummy_block.next;
    while (blk!=NULL)
    {
	memcpy(s, blk->text, blk->pos);
	s+=blk->pos;
	blk=blk->next;
    }
    *s='\0';
    return t;
}


tblock &tblock::operator=(const tblock &arg)
{
    struct block *nb,*blk;
    const char *s;
    
    blk=dummy_block.next;
    while (blk!=NULL)
    {
	nb=blk->next;
	free(blk->text);
	free(blk);
	blk=nb;
    }

    if ((s=arg.collect())==NULL)
    {
	cerr<<"tblock:operator= fatal malloc failure\n";
	exit(2);
    }

    if ((nb=(struct block *) malloc(sizeof(struct block)))==NULL)
    {
	cerr<<"tblock:operator= fatal malloc failure\n";
	exit(2);
    }

    nb->limit=strlen(s)+1;	// Limit includes '\0'
    nb->pos=strlen(s);		// Do include '\0' in used characters
    nb->text=(char *) s;
    nb->next=NULL;
    dummy_block.next=nb;
    head=nb;

    return *this;
}

tblock::tblock(const tblock &cpy)
{
    struct block *nb;
    const char *s;
    
    if ((s=cpy.collect())==NULL)
    {
	cerr<<"tblock::tblock(const tblock &) fatal malloc failure\n";
	exit(2);
    }

    if ((nb=(struct block *) malloc(sizeof(struct block)))==NULL)
    {
	cerr<<"tblock::tblock(const tblock &) fatal malloc failure\n";
	exit(2);
    }

    nb->limit=strlen(s)+1;	// Inlcude '\0' in limit
    nb->pos=strlen(s);		// DO not include '\0' in used
    nb->text=(char *) s;
    nb->next=NULL;
    dummy_block.next=nb;
    dummy_block.pos=0;
    dummy_block.limit=0;
    dummy_block.text=NULL;
    head=nb;
}


tblock::operator const char *()
{
    struct block *nb;
    const char *s;
    

    if (dummy_block.next==head)
    {
	*(head->text+head->pos)='\0';
	return ((const char *) (head->text));
    }

    if ((s=this->collect())==NULL)
    {
	cerr<<"tblock::const char * fatal malloc failure\n";
	exit(2);
    }

    if ((nb=(struct block *) malloc(sizeof(struct block)))==NULL)
    {
	cerr<<"tblock::const char * fatal malloc failure\n";
	exit(2);
    }

    nb->limit=strlen(s)+1;	// Inlcude '\0' in limit
    nb->pos=strlen(s);		// DO not include '\0' in used
    nb->text=(char *) s;
    nb->next=NULL;
    this->zero();		// Wipe current data
    dummy_block.next=nb;
    dummy_block.pos=0;
    dummy_block.limit=0;
    dummy_block.text=NULL;
    head=nb;

    return s;
}
