/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/

# include	"list.h"

static ref_t	*hd_rstr = NULL;

ref_t *
new_rp()
{
	return (ref_t *) vm_alloc(sizeof(ref_t), (void **) &hd_rstr);
}
ref_t *
r_alloc(size)
int	size;
{	register ref_t *rp = new_rp();

	rp->r_ref = 1;
	rp->r_size = size;
	rp->r_ptr = (char *) chk_alloc(size);
	return rp;
}
/**********************************************************************/
/*   Create  a  reference  to  a  new  object. Strings are allocated  */
/*   memory  to  be  copied  into,  but  lists are donated so we can  */
/*   just point to them.					      */
/**********************************************************************/
ref_t *
r_init(type, new_string, len)
OPCODE	type;
char	*new_string;
int	len;
{
	ref_t	*rp = NULL;

	switch (type) {
	  case F_LIST:
	  case F_RLIST:
		type = F_LIST;
	  	rp = new_rp();
		rp->r_ref = 1;
		rp->r_ptr = new_string;
		rp->r_size = len;
	  	break;
	  default:
		rp = r_alloc(len);
		if (rp == NULL)
			return NULL;
		if (new_string == NULL)
			len = 0;
		else
			memcpy(rp->r_ptr, new_string, rp->r_size);
		break;
	  }
	rp->r_used = len;
	rp->r_type = type;
	return rp;
}
/**********************************************************************/
/*   Append  a  string  onto  the  end  of the object referenced. We  */
/*   need  to  take  into  account  the  NULLs  at  the  end  of the  */
/*   original string and the new string.			      */
/**********************************************************************/
ref_t *
r_cat(rp, str)
register ref_t *rp;
char	*str;
{	int	len = strlen(str);
	int	new_used = rp->r_used + len;
	ref_t	*rp1;

	if (rp->r_ref == 1) {
		if (rp->r_size <= new_used) {
			rp->r_ptr = (char *) chk_realloc(rp->r_ptr, new_used);
			rp->r_size = new_used;
			}
		memcpy(rp->r_ptr + rp->r_used - 1, str, len+1);
		rp->r_used = new_used;
		return rp;		
		}
	rp1 = r_alloc(new_used);
	rp1->r_used = new_used;
	rp1->r_type = F_STR;
	memcpy(rp1->r_ptr, rp->r_ptr, rp->r_used - 1);
	memcpy(rp1->r_ptr + rp->r_used - 1, str, len+1);
	/***********************************************/
	/*   Free up the original object.	       */
	/***********************************************/
	r_dec(rp);
	return rp1;
}
/**********************************************************************/
/*   Append  a  piece  of  memory  to  the end of an object. We dont  */
/*   need  to  worry  about  NULLs.  If we need to expand the object  */
/*   then  'expansion'  telss  us  how much extra memory to allocate  */
/*   to avoid having to continuously re-expand.			      */
/**********************************************************************/
ref_t *
r_append(rp, mem, len, expansion)
register ref_t	*rp;
char	*mem;
int	len;
int	expansion;
{	int	new_used = rp->r_used + len;
	ref_t	*rp1;

	if (rp->r_ref == 1) {
		if (rp->r_size <= new_used) {
			rp->r_size = new_used + expansion;
			rp->r_ptr = (char *) chk_realloc(rp->r_ptr, rp->r_size);
			}
		memcpy(rp->r_ptr + rp->r_used, mem, len);
		rp->r_used += len;
		return rp;
		}
	rp1 = r_alloc(new_used);
	rp1->r_used = new_used;
	rp1->r_type = F_STR;
	memcpy(rp1->r_ptr, rp->r_ptr, rp->r_used);
	memcpy(rp1->r_ptr + rp->r_used, mem, len);
	/***********************************************/
	/*   Free up the original object.	       */
	/***********************************************/
	r_dec(rp);
	return rp1;
}
ref_t *
r_inc(rp)
ref_t *rp;
{
	rp->r_ref++;
	return rp;
}
void
r_dec(rp)
ref_t *rp;
{
	if (rp && --rp->r_ref <= 0) {
		if (rp->r_ptr) {
			if (rp->r_type == F_LIST)
				free_list((LIST *) rp->r_ptr);
			else
				chk_free(rp->r_ptr);
			}
		vm_free((void *) rp, (void **) &hd_rstr);
		}
}
